mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-13 14:10:46 +02:00
Compare commits
65 Commits
v4.0-beta.
...
v4.0-rc.3
Author | SHA1 | Date | |
---|---|---|---|
219a7443c0 | |||
6deb408d80 | |||
c6e1ff2164 | |||
bc0918a115 | |||
55ff9173be | |||
d4d548846f | |||
ef4527f140 | |||
0b1e7242bb | |||
30b6ac2a06 | |||
a7fa7cb5e4 | |||
f3e87f9016 | |||
a983af6c28 | |||
114c841f0c | |||
e271048577 | |||
b8c5925e82 | |||
9bda6c8869 | |||
d1608d308c | |||
b8e1e1d33a | |||
8099a037e7 | |||
af23c932a6 | |||
4edabbb186 | |||
37a5bea79b | |||
40cdc7d713 | |||
49825aca48 | |||
1d57c4e705 | |||
87ae5787ee | |||
20f16c25a3 | |||
6f1ec79d9b | |||
18c7eea89c | |||
f73060aeb6 | |||
2f653b83b6 | |||
445dec907d | |||
927316d24b | |||
3957453ed6 | |||
0296c704cb | |||
1e7fe972de | |||
c95bc656ea | |||
a082d95b04 | |||
6866dd4801 | |||
2186da416e | |||
22d859fcde | |||
39514b69b3 | |||
c384736840 | |||
507657f273 | |||
60641742ed | |||
0fc6f07986 | |||
1b2bdc0580 | |||
9bac239f77 | |||
371acb2d2a | |||
454f82e139 | |||
e8da249353 | |||
c7950c53da | |||
b5502478e4 | |||
4480a7e486 | |||
7c7dff743b | |||
c568cd3f2e | |||
6ec2bc6f21 | |||
af3b6f3a97 | |||
d855118610 | |||
c9992d9fe8 | |||
85fe2636cc | |||
35f4a31a76 | |||
1e494ebb70 | |||
ed93627505 | |||
b9b4b0036f |
@ -128,6 +128,7 @@ dependencies {
|
||||
implementation "com.mikepenz:iconics-core:${versions.iconics}"
|
||||
implementation "com.mikepenz:iconics-views:${versions.iconics}"
|
||||
implementation "com.mikepenz:community-material-typeface:${versions.font_cmd}@aar"
|
||||
implementation "com.mikepenz:materialize:1.2.1"
|
||||
|
||||
implementation "com.github.kuba2k2:NavLib:${versions.navlib}"
|
||||
|
||||
|
@ -31,6 +31,12 @@
|
||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
|
||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
|
||||
|
||||
-keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); }
|
||||
-keepclassmembernames class androidx.appcompat.view.menu.StandardMenuPopup { private *; }
|
||||
-keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); }
|
||||
|
||||
-keepclassmembernames class com.mikepenz.materialdrawer.widget.MiniDrawerSliderView { private *; }
|
||||
|
||||
-keep class .R
|
||||
-keep class **.R$* {
|
||||
<fields>;
|
||||
|
@ -62,7 +62,7 @@
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay">
|
||||
android:theme="@style/AppTheme.Dark.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
@ -84,7 +84,7 @@
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay" />
|
||||
android:theme="@style/AppTheme.Dark.NoDisplay" />
|
||||
<!-- NOTIFICATIONS -->
|
||||
<receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider"
|
||||
android:label="@string/widget_notifications_title">
|
||||
|
@ -1,12 +1,13 @@
|
||||
<h3>Wersja 4.0-beta.12, 2020-03-10</h3>
|
||||
<h3>Wersja 4.0-rc.3, 2020-03-29</h3>
|
||||
<ul>
|
||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkość oraz poprawność pobieranych danych.</li>
|
||||
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkość oraz poprawność pobieranych danych</li>
|
||||
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li>
|
||||
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
|
||||
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
|
||||
<li>Nowe <b>Oceny</b> - z możliwością zmiany wartości plusów oraz minusów oraz wyłączenia niektórych ocen ze średniej</li>
|
||||
<li>Opcja wyłączenia wybranych powiadomień z aplikacji</li>
|
||||
<li>Znaczki nieprzeczytanych informacji na obrazkach profili.</li>
|
||||
<br>
|
||||
<br>
|
||||
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
|
||||
@ -21,18 +22,8 @@
|
||||
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
|
||||
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</li>
|
||||
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
|
||||
<li><strike>Występują natomiast nowe błędy, dlatego proszę o ich zgłaszanie :)</strike></li>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<b>Uwaga.</b> Ponieważ to wersja <i>beta</i>, niektóre funkcje mogą nie działać prawidłowo.<br>
|
||||
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
|
||||
<ul>
|
||||
<li>Wysyłanie wiadomości może nie działać w pełni prawidłowo - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
Dzięki za korzystanie ze Szkolnego!<br>
|
||||
<i>© Kuba Szczodrzyński, Kacper Ziubryniewicz 2020</i>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
/*secret password - removed for source code publication*/
|
||||
static toys AES_IV[16] = {
|
||||
0xb8, 0x59, 0x75, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
0xa7, 0x84, 0xf9, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||
|
||||
|
@ -28,9 +28,6 @@ import com.hypertrack.hyperlog.HyperLog
|
||||
import com.mikepenz.iconics.Iconics
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import im.wangchao.mhttp.MHttp
|
||||
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar
|
||||
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache
|
||||
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor
|
||||
import kotlinx.coroutines.*
|
||||
import me.leolin.shortcutbadger.ShortcutBadger
|
||||
import okhttp3.OkHttpClient
|
||||
@ -41,6 +38,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.network.NetworkUtils
|
||||
import pl.szczodrzynski.edziennik.network.cookie.DumbCookieJar
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
|
||||
@ -125,7 +123,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
.followSslRedirects(false)
|
||||
.build()
|
||||
}
|
||||
val cookieJar by lazy { PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(this)) }
|
||||
val cookieJar by lazy { DumbCookieJar(this) }
|
||||
|
||||
/* _____ _ _
|
||||
/ ____(_) | |
|
||||
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.databinding.BindingAdapter;
|
||||
|
||||
public class Binding {
|
||||
@BindingAdapter("strikeThrough")
|
||||
public static void strikeThrough(TextView textView, Boolean strikeThrough) {
|
||||
if (strikeThrough) {
|
||||
textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
} else {
|
||||
textView.setPaintFlags(textView.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
}
|
||||
}
|
||||
}
|
20
app/src/main/java/pl/szczodrzynski/edziennik/Binding.kt
Normal file
20
app/src/main/java/pl/szczodrzynski/edziennik/Binding.kt
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.graphics.Paint
|
||||
import android.widget.TextView
|
||||
import androidx.databinding.BindingAdapter
|
||||
|
||||
object Binding {
|
||||
@JvmStatic
|
||||
@BindingAdapter("strikeThrough")
|
||||
fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
|
||||
if (strikeThrough) {
|
||||
textView.paintFlags = textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||
} else {
|
||||
textView.paintFlags = textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
@ -10,6 +12,7 @@ import android.content.res.Resources
|
||||
import android.database.Cursor
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.Rect
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
@ -23,6 +26,7 @@ import android.util.Base64
|
||||
import android.util.Base64.NO_WRAP
|
||||
import android.util.Base64.encodeToString
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.CheckBox
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.RadioButton
|
||||
@ -51,6 +55,7 @@ import okhttp3.RequestBody
|
||||
import okhttp3.TlsVersion
|
||||
import okio.Buffer
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApiException
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
@ -1003,6 +1008,7 @@ fun Context.getNotificationTitle(type: Int): String {
|
||||
Notification.TYPE_FEEDBACK_MESSAGE -> R.string.notification_type_feedback_message
|
||||
Notification.TYPE_NEW_ANNOUNCEMENT -> R.string.notification_type_new_announcement
|
||||
Notification.TYPE_AUTO_ARCHIVING -> R.string.notification_type_auto_archiving
|
||||
Notification.TYPE_TEACHER_ABSENCE -> R.string.notification_type_new_teacher_absence
|
||||
Notification.TYPE_GENERAL -> R.string.notification_type_general
|
||||
else -> R.string.notification_type_general
|
||||
})
|
||||
@ -1080,6 +1086,7 @@ fun Throwable.toErrorCode() = when (this) {
|
||||
private fun ApiResponse.Error.toErrorCode() = when (this.code) {
|
||||
else -> ERROR_API_EXCEPTION
|
||||
}
|
||||
fun Throwable.toApiError(tag: String) = ApiError.fromThrowable(tag, this)
|
||||
|
||||
inline fun <A, B, R> ifNotNull(a: A?, b: B?, code: (A, B) -> R): R? {
|
||||
if (a != null && b != null) {
|
||||
@ -1092,3 +1099,71 @@ inline fun <A, B, R> ifNotNull(a: A?, b: B?, code: (A, B) -> R): R? {
|
||||
fun Iterable<Int>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
|
||||
@kotlin.jvm.JvmName("averageOrNullOfFloat")
|
||||
fun Iterable<Float>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
|
||||
|
||||
fun String.copyToClipboard(context: Context) {
|
||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipData = ClipData.newPlainText("Tekst", this)
|
||||
clipboard.primaryClip = clipData
|
||||
}
|
||||
|
||||
fun TextView.getTextPosition(range: IntRange): Rect {
|
||||
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
|
||||
// Initialize global value
|
||||
var parentTextViewRect = Rect()
|
||||
|
||||
// Initialize values for the computing of clickedText position
|
||||
//val completeText = parentTextView.text as SpannableString
|
||||
val textViewLayout = this.layout
|
||||
|
||||
val startOffsetOfClickedText = range.first//completeText.getSpanStart(clickedText)
|
||||
val endOffsetOfClickedText = range.last//completeText.getSpanEnd(clickedText)
|
||||
var startXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal(startOffsetOfClickedText)
|
||||
var endXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal(endOffsetOfClickedText)
|
||||
|
||||
// Get the rectangle of the clicked text
|
||||
val currentLineStartOffset = textViewLayout.getLineForOffset(startOffsetOfClickedText)
|
||||
val currentLineEndOffset = textViewLayout.getLineForOffset(endOffsetOfClickedText)
|
||||
val keywordIsInMultiLine = currentLineStartOffset != currentLineEndOffset
|
||||
textViewLayout.getLineBounds(currentLineStartOffset, parentTextViewRect)
|
||||
|
||||
// Update the rectangle position to his real position on screen
|
||||
val parentTextViewLocation = intArrayOf(0, 0)
|
||||
this.getLocationOnScreen(parentTextViewLocation)
|
||||
|
||||
val parentTextViewTopAndBottomOffset = (parentTextViewLocation[1] - this.scrollY + this.compoundPaddingTop)
|
||||
parentTextViewRect.top += parentTextViewTopAndBottomOffset
|
||||
parentTextViewRect.bottom += parentTextViewTopAndBottomOffset
|
||||
|
||||
// In the case of multi line text, we have to choose what rectangle take
|
||||
if (keywordIsInMultiLine) {
|
||||
val screenHeight = windowManager.defaultDisplay.height
|
||||
val dyTop = parentTextViewRect.top
|
||||
val dyBottom = screenHeight - parentTextViewRect.bottom
|
||||
val onTop = dyTop > dyBottom
|
||||
|
||||
if (onTop) {
|
||||
endXCoordinatesOfClickedText = textViewLayout.getLineRight(currentLineStartOffset);
|
||||
} else {
|
||||
parentTextViewRect = Rect()
|
||||
textViewLayout.getLineBounds(currentLineEndOffset, parentTextViewRect);
|
||||
parentTextViewRect.top += parentTextViewTopAndBottomOffset;
|
||||
parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;
|
||||
startXCoordinatesOfClickedText = textViewLayout.getLineLeft(currentLineEndOffset);
|
||||
}
|
||||
}
|
||||
|
||||
parentTextViewRect.left += (
|
||||
parentTextViewLocation[0] +
|
||||
startXCoordinatesOfClickedText +
|
||||
this.compoundPaddingLeft -
|
||||
this.scrollX
|
||||
).toInt()
|
||||
parentTextViewRect.right = (
|
||||
parentTextViewRect.left +
|
||||
endXCoordinatesOfClickedText -
|
||||
startXCoordinatesOfClickedText
|
||||
).toInt()
|
||||
|
||||
return parentTextViewRect
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile
|
||||
import com.mikepenz.materialdrawer.model.interfaces.*
|
||||
import com.mikepenz.materialdrawer.model.utils.withIsHiddenInMiniDrawer
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@ -49,6 +49,7 @@ import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
|
||||
@ -89,7 +90,6 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||
import pl.szczodrzynski.navlib.drawer.NavDrawer
|
||||
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
|
||||
import pl.szczodrzynski.navlib.drawer.items.withAppTitle
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
@ -360,7 +360,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
false
|
||||
}
|
||||
drawerProfileLongClickListener = { _, profile, _, view ->
|
||||
if (profile is ProfileDrawerItem) {
|
||||
if (view != null && profile is ProfileDrawerItem) {
|
||||
showProfileContextMenu(profile, view)
|
||||
true
|
||||
}
|
||||
@ -444,6 +444,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
|
||||
// WHAT'S NEW DIALOG
|
||||
if (app.config.appVersion < BuildConfig.VERSION_CODE) {
|
||||
// force an AppSync after update
|
||||
app.config.sync.lastAppSync = 0L
|
||||
ChangelogDialog(this)
|
||||
if (app.config.appVersion < 170) {
|
||||
//Intent intent = new Intent(this, ChangelogIntroActivity.class);
|
||||
@ -721,6 +723,15 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
)
|
||||
true
|
||||
}
|
||||
"createManualEvent" -> {
|
||||
val date = extras.getString("eventDate")?.let { Date.fromY_m_d(it) } ?: Date.getToday()
|
||||
EventManualDialog(
|
||||
this,
|
||||
App.profileId,
|
||||
defaultDate = date
|
||||
)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
if (handled && !navLoading) {
|
||||
@ -903,7 +914,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
bottomSheet.removeAllContextual()
|
||||
bottomSheet.toggleGroupEnabled = false
|
||||
drawer.close()
|
||||
drawer.setSelection(target.id, fireOnClick = false)
|
||||
if (drawer.getSelection() != target.id)
|
||||
drawer.setSelection(target.id, fireOnClick = false)
|
||||
navView.toolbar.setTitle(target.title ?: target.name)
|
||||
navView.bottomBar.fabEnable = false
|
||||
navView.bottomBar.fabExtended = false
|
||||
@ -1051,7 +1063,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
val item = DrawerPrimaryItem()
|
||||
.withIdentifier(target.id.toLong())
|
||||
.withName(target.name)
|
||||
.withHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id))
|
||||
.withIsHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id))
|
||||
.also { if (target.description != null) it.withDescription(target.description!!) }
|
||||
.also { if (target.icon != null) it.withIcon(target.icon!!) }
|
||||
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
|
||||
@ -1115,7 +1127,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
drawer.addProfileSettings(*drawerProfiles.toTypedArray())
|
||||
}
|
||||
|
||||
private fun showProfileContextMenu(profile: IProfile<*>, view: View) {
|
||||
private fun showProfileContextMenu(profile: IProfile, view: View) {
|
||||
val profileId = profile.identifier.toInt()
|
||||
val popupMenu = PopupMenu(this, view)
|
||||
popupMenu.menu.add(0, 1, 1, R.string.profile_menu_open_settings)
|
||||
@ -1128,7 +1140,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
}
|
||||
loadTarget(DRAWER_ITEM_SETTINGS, null)
|
||||
} else if (item.itemId == 2) {
|
||||
ProfileRemoveDialog(this, profileId, profile.name?.getText(this)?.toString() ?: "?")
|
||||
ProfileRemoveDialog(this, profileId, profile.name?.getText(this) ?: "?")
|
||||
}
|
||||
true
|
||||
}
|
||||
@ -1139,7 +1151,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
private var targetHomeId: Int = -1
|
||||
override fun onBackPressed() {
|
||||
if (!b.navView.onBackPressed()) {
|
||||
if (App.config.ui.openDrawerOnBackPressed) {
|
||||
if (App.config.ui.openDrawerOnBackPressed && ((navTarget.popTo == null && navTarget.popToHome)
|
||||
|| navTarget.id == DRAWER_ITEM_HOME)) {
|
||||
b.navView.drawer.toggle()
|
||||
} else {
|
||||
navigateUp()
|
||||
|
@ -105,11 +105,6 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
get() { mWidgetConfigs = mWidgetConfigs ?: values.get("widgetConfigs", JsonObject()); return mWidgetConfigs ?: JsonObject() }
|
||||
set(value) { set("widgetConfigs", value); mWidgetConfigs = value }
|
||||
|
||||
private var mLastAppSync: Long? = null
|
||||
var lastAppSync: Long
|
||||
get() { mLastAppSync = mLastAppSync ?: values.get("lastAppSync", 0L); return mLastAppSync ?: 0L }
|
||||
set(value) { set("lastAppSync", value); mLastAppSync = value }
|
||||
|
||||
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
|
||||
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
|
||||
init {
|
||||
|
@ -20,6 +20,11 @@ class ConfigSync(private val config: Config) {
|
||||
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
|
||||
set(value) { config.set("syncEnabled", value); mSyncEnabled = value }
|
||||
|
||||
private var mWebPushEnabled: Boolean? = null
|
||||
var webPushEnabled: Boolean
|
||||
get() { mWebPushEnabled = mWebPushEnabled ?: config.values.get("webPushEnabled", true); return mWebPushEnabled ?: true }
|
||||
set(value) { config.set("webPushEnabled", value); mWebPushEnabled = value }
|
||||
|
||||
private var mSyncOnlyWifi: Boolean? = null
|
||||
var onlyWifi: Boolean
|
||||
get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates }
|
||||
@ -35,6 +40,11 @@ class ConfigSync(private val config: Config) {
|
||||
get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true }
|
||||
set(value) { config.set("notifyAboutUpdates", value); mNotifyAboutUpdates = value }
|
||||
|
||||
private var mLastAppSync: Long? = null
|
||||
var lastAppSync: Long
|
||||
get() { mLastAppSync = mLastAppSync ?: config.values.get("lastAppSync", 0L); return mLastAppSync ?: 0L }
|
||||
set(value) { config.set("lastAppSync", value); mLastAppSync = value }
|
||||
|
||||
/* ____ _ _ _
|
||||
/ __ \ (_) | | | |
|
||||
| | | |_ _ _ ___| |_ | |__ ___ _ _ _ __ ___
|
||||
|
@ -21,11 +21,6 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
|
||||
get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES }
|
||||
set(value) { config.set("yearAverageMode", value); mYearAverageMode = value }
|
||||
|
||||
private var mCountZeroToAvg: Boolean? = null
|
||||
var countZeroToAvg: Boolean
|
||||
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true }
|
||||
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value }
|
||||
|
||||
private var mHideImproved: Boolean? = null
|
||||
var hideImproved: Boolean
|
||||
get() { mHideImproved = mHideImproved ?: config.values.get("hideImproved", false); return mHideImproved ?: false }
|
||||
@ -45,6 +40,11 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
|
||||
get() { mMinusValue = mMinusValue ?: config.values.getFloat("minusValue"); return mMinusValue }
|
||||
set(value) { config.set("minusValue", value); mMinusValue = value }
|
||||
|
||||
private var mDontCountEnabled: Boolean? = null
|
||||
var dontCountEnabled: Boolean
|
||||
get() { mDontCountEnabled = mDontCountEnabled ?: config.values.get("dontCountEnabled", false); return mDontCountEnabled ?: false }
|
||||
set(value) { config.set("dontCountEnabled", value); mDontCountEnabled = value }
|
||||
|
||||
private var mDontCountGrades: List<String>? = null
|
||||
var dontCountGrades: List<String>
|
||||
get() { mDontCountGrades = mDontCountGrades ?: config.values.get("dontCountGrades", listOf()); return mDontCountGrades ?: listOf() }
|
||||
|
@ -14,7 +14,7 @@ class ProfileConfigMigration(config: ProfileConfig) {
|
||||
|
||||
if (dataVersion < 1) {
|
||||
grades.colorMode = COLOR_MODE_WEIGHTED
|
||||
grades.countZeroToAvg = true
|
||||
grades.dontCountEnabled = false
|
||||
grades.yearAverageMode = YEAR_ALL_GRADES
|
||||
ui.agendaViewType = AGENDA_DEFAULT
|
||||
|
||||
|
@ -22,6 +22,7 @@ import pl.szczodrzynski.edziennik.data.api.task.ErrorReportTask
|
||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.toApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
@ -181,7 +182,7 @@ class ApiService : Service() {
|
||||
is SzkolnyTask -> task.run(taskCallback)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
|
||||
taskCallback.onError(e.toApiError(TAG))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,8 @@ const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
|
||||
const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
|
||||
const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED = 184
|
||||
const val ERROR_LIBRUS_API_DEVICE_REGISTERED = 185
|
||||
const val ERROR_LIBRUS_MESSAGES_NOT_FOUND = 186
|
||||
const val ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST = 187
|
||||
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
|
||||
@ -140,6 +142,7 @@ const val ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE = 214
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN = 216
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER = 217
|
||||
const val ERROR_MOBIDZIENNIK_WEB_SERVER_PROBLEM = 218
|
||||
|
||||
const val ERROR_LOGIN_VULCAN_INVALID_SYMBOL = 301
|
||||
const val ERROR_LOGIN_VULCAN_INVALID_TOKEN = 302
|
||||
|
@ -70,13 +70,13 @@ val librusLoginMethods = listOf(
|
||||
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java)
|
||||
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
||||
.withRequiredLoginMethod { profile, _ ->
|
||||
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
|
||||
if (profile?.hasStudentData("accountPassword") == false || true) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
|
||||
},
|
||||
|
||||
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java)
|
||||
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
||||
.withRequiredLoginMethod { profile, _ ->
|
||||
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
|
||||
if (profile?.hasStudentData("accountPassword") == false || true) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import kotlin.text.RegexOption.DOT_MATCHES_ALL
|
||||
import kotlin.text.RegexOption.IGNORE_CASE
|
||||
|
||||
object Regexes {
|
||||
val STYLE_CSS_COLOR by lazy {
|
||||
@ -126,7 +127,7 @@ object Regexes {
|
||||
|
||||
|
||||
val LIBRUS_ATTACHMENT_KEY by lazy {
|
||||
"""singleUseKey=([0-9A-f_]+)""".toRegex()
|
||||
"""singleUseKey=([0-9A-z_]+)""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
@ -199,4 +200,20 @@ object Regexes {
|
||||
val EDUDZIENNIK_TEACHERS by lazy {
|
||||
"""<div class="teacher">.*?<p>(.+?) (.+?)</p>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
val LINKIFY_DATE_YMD by lazy {
|
||||
"""(1\d{3}|20\d{2})[\-./](1[0-2]|0?\d)[\-./]([1-2]\d|3[0-1]|0?\d)""".toRegex()
|
||||
}
|
||||
val LINKIFY_DATE_DMY by lazy {
|
||||
"""(?<![\d\-./])([1-2]\d|3[0-1]|0?\d)[\-./](1[0-2]|0?\d)(?:[\-./](1\d{3}|2?0?\d{2}))?(?![\d\-/])""".toRegex()
|
||||
}
|
||||
val LINKIFY_DATE_ABSOLUTE by lazy {
|
||||
"""([1-3][0-9]|[1-9])\s(sty|lut|mar|kwi|maj|cze|lip|sie|wrz|paź|lis|gru).*?\s(1[0-9]{3}|20[0-9]{2})?""".toRegex(IGNORE_CASE)
|
||||
}
|
||||
val LINKIFY_DATE_RELATIVE by lazy {
|
||||
"""za\s([0-9]+)?\s?(dni|dzień|tydzień|tygodnie)""".toRegex(IGNORE_CASE)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
@ -43,8 +42,8 @@ open class EdudziennikWeb(open val data: DataEdudziennik, open val lastSync: Lon
|
||||
|
||||
if (semester == null && url.contains("start")) {
|
||||
profile?.also { profile ->
|
||||
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com")
|
||||
val semesterCookie = cookies.firstOrNull { it.name() == "semester" }?.value()?.toIntOrNull()
|
||||
val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
|
||||
val semesterCookie = cookies["semester"]?.toIntOrNull()
|
||||
|
||||
semesterCookie?.let { data.currentSemester = it }
|
||||
|
||||
@ -75,13 +74,7 @@ open class EdudziennikWeb(open val data: DataEdudziennik, open val lastSync: Lon
|
||||
}
|
||||
}
|
||||
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("sessionid")
|
||||
.value(data.webSessionId!!)
|
||||
.domain("dziennikel.appspot.com")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
data.app.cookieJar.set("dziennikel.appspot.com", "sessionid", data.webSessionId)
|
||||
|
||||
Request.builder()
|
||||
.url(url)
|
||||
|
@ -39,17 +39,16 @@ class EdudziennikWebEvents(override val data: DataEdudziennik,
|
||||
?: return@forEach
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
date,
|
||||
null,
|
||||
title,
|
||||
-1,
|
||||
Event.TYPE_CLASS_EVENT,
|
||||
false,
|
||||
-1,
|
||||
-1,
|
||||
data.teamClass?.id ?: -1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = date,
|
||||
time = null,
|
||||
topic = title,
|
||||
color = null,
|
||||
type = Event.TYPE_CLASS_EVENT,
|
||||
teacherId = -1,
|
||||
subjectId = -1,
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -56,17 +56,16 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
|
||||
val eventType = data.getEventType(eventTypeId, eventTypeName)
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
date,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
eventType.id,
|
||||
false,
|
||||
-1,
|
||||
subject.id,
|
||||
data.teamClass?.id ?: -1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = date,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = eventType.id,
|
||||
teacherId = -1,
|
||||
subjectId = subject.id,
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -52,17 +52,16 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
|
||||
val topic = homeworkElement.child(4).text()
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
date,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
Event.TYPE_HOMEWORK,
|
||||
false,
|
||||
teacher.id,
|
||||
subject.id,
|
||||
data.teamClass?.id ?: -1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = date,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = Event.TYPE_HOMEWORK,
|
||||
teacherId = teacher.id,
|
||||
subjectId = subject.id,
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -24,7 +24,7 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
|
||||
onSuccess()
|
||||
}
|
||||
else {
|
||||
data.app.cookieJar.clearForDomain("dziennikel.appspot.com")
|
||||
data.app.cookieJar.clear("dziennikel.appspot.com")
|
||||
if (data.loginEmail.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
|
||||
loginWithCredentials()
|
||||
}
|
||||
@ -59,8 +59,8 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
|
||||
}
|
||||
}
|
||||
|
||||
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com")
|
||||
val sessionId = cookies.firstOrNull { it.name() == "sessionid" }?.value()
|
||||
val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
|
||||
val sessionId = cookies["sessionid"]
|
||||
|
||||
if (sessionId == null) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID)
|
||||
|
@ -5,7 +5,6 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
|
||||
|
||||
import androidx.core.util.set
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_API
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_WEB
|
||||
@ -24,18 +23,8 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
|
||||
loginMethods.clear()
|
||||
if (isWebLoginValid()) {
|
||||
loginMethods += LOGIN_METHOD_IDZIENNIK_WEB
|
||||
app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("ASP.NET_SessionId_iDziennik")
|
||||
.value(webSessionId!!)
|
||||
.domain("iuczniowie.progman.pl")
|
||||
.secure().httpOnly().build(),
|
||||
Cookie.Builder()
|
||||
.name(".ASPXAUTH")
|
||||
.value(webAuth!!)
|
||||
.domain("iuczniowie.progman.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
app.cookieJar.set("iuczniowie.progman.pl", "ASP.NET_SessionId_iDziennik", webSessionId)
|
||||
app.cookieJar.set("iuczniowie.progman.pl", ".ASPXAUTH", webAuth)
|
||||
}
|
||||
if (isApiLoginValid())
|
||||
loginMethods += LOGIN_METHOD_IDZIENNIK_API
|
||||
|
@ -16,6 +16,8 @@ import pl.szczodrzynski.edziennik.data.db.entity.Announcement
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.getJsonObject
|
||||
import pl.szczodrzynski.edziennik.getLong
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class IdziennikWebAnnouncements(override val data: DataIdziennik,
|
||||
@ -43,11 +45,11 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik,
|
||||
for (jAnnouncementEl in json.getAsJsonArray("ListK")) {
|
||||
val jAnnouncement = jAnnouncementEl.asJsonObject
|
||||
// jAnnouncement
|
||||
val announcementId = jAnnouncement.get("Id").asLong
|
||||
val announcementId = jAnnouncement.getLong("Id") ?: -1
|
||||
|
||||
val rTeacher = data.getTeacherByFirstLast(jAnnouncement.get("Autor").asString)
|
||||
val addedDate = java.lang.Long.parseLong(jAnnouncement.get("DataDodania").asString.replace("[^\\d]".toRegex(), ""))
|
||||
val startDate = Date.fromMillis(java.lang.Long.parseLong(jAnnouncement.get("DataWydarzenia").asString.replace("[^\\d]".toRegex(), "")))
|
||||
val rTeacher = data.getTeacherByFirstLast(jAnnouncement.getString("Autor") ?: "")
|
||||
val addedDate = jAnnouncement.getString("DataDodania")?.replace("[^\\d]".toRegex(), "")?.toLongOrNull() ?: System.currentTimeMillis()
|
||||
val startDate = jAnnouncement.getString("DataWydarzenia")?.replace("[^\\d]".toRegex(), "")?.toLongOrNull()?.let { Date.fromMillis(it) }
|
||||
|
||||
val announcementObject = Announcement(
|
||||
profileId,
|
||||
|
@ -80,17 +80,16 @@ class IdziennikWebExams(override val data: DataIdziennik,
|
||||
}
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
examDate,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
eventType,
|
||||
false,
|
||||
teacherId,
|
||||
subjectId,
|
||||
data.teamClass?.id ?: -1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = examDate,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = eventType,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId,
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -67,17 +67,16 @@ class IdziennikWebHomework(override val data: DataIdziennik,
|
||||
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
Event.TYPE_HOMEWORK,
|
||||
false,
|
||||
teacherId,
|
||||
subjectId,
|
||||
data.teamClass?.id ?: -1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = eventDate,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = Event.TYPE_HOMEWORK,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId,
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
@ -24,22 +23,12 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
|
||||
init { run {
|
||||
if (data.isWebLoginValid()) {
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("ASP.NET_SessionId_iDziennik")
|
||||
.value(data.webSessionId!!)
|
||||
.domain("iuczniowie.progman.pl")
|
||||
.secure().httpOnly().build(),
|
||||
Cookie.Builder()
|
||||
.name(".ASPXAUTH")
|
||||
.value(data.webAuth!!)
|
||||
.domain("iuczniowie.progman.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
data.app.cookieJar.set("iuczniowie.progman.pl", "ASP.NET_SessionId_iDziennik", data.webSessionId)
|
||||
data.app.cookieJar.set("iuczniowie.progman.pl", ".ASPXAUTH", data.webAuth)
|
||||
onSuccess()
|
||||
}
|
||||
else {
|
||||
data.app.cookieJar.clearForDomain("iuczniowie.progman.pl")
|
||||
data.app.cookieJar.clear("iuczniowie.progman.pl")
|
||||
if (data.webSchoolName != null && data.webUsername != null && data.webPassword != null) {
|
||||
loginWithCredentials()
|
||||
}
|
||||
@ -62,11 +51,11 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
|
||||
// login succeeded: there is a start page
|
||||
if (text.contains("czyWyswietlicDostepMobilny")) {
|
||||
val cookies = data.app.cookieJar.getForDomain("iuczniowie.progman.pl")
|
||||
val cookies = data.app.cookieJar.getAll("iuczniowie.progman.pl")
|
||||
run {
|
||||
data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION
|
||||
data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH
|
||||
data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER
|
||||
data.webSessionId = cookies["ASP.NET_SessionId_iDziennik"] ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION
|
||||
data.webAuth = cookies[".ASPXAUTH"] ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH
|
||||
data.apiBearer = cookies["Bearer"]?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER
|
||||
data.loginExpiryTime = response.getUnixDate() + 30 * MINUTE /* after about 40 minutes the login didn't work already */
|
||||
data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.librus
|
||||
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_API
|
||||
@ -31,23 +30,11 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
loginMethods += LOGIN_METHOD_LIBRUS_API
|
||||
if (isSynergiaLoginValid()) {
|
||||
loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA
|
||||
app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("DZIENNIKSID")
|
||||
.value(synergiaSessionId!!)
|
||||
.domain("synergia.librus.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
app.cookieJar.set("synergia.librus.pl", "DZIENNIKSID", synergiaSessionId)
|
||||
}
|
||||
if (isMessagesLoginValid()) {
|
||||
loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES
|
||||
app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("DZIENNIKSID")
|
||||
.value(messagesSessionId!!)
|
||||
.domain("wiadomosci.librus.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", messagesSessionId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ val LibrusFeatures = listOf(
|
||||
Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf(
|
||||
ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API
|
||||
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
|
||||
!data.app.config.sync.tokenLibrusList.contains(data.profileId)
|
||||
(data as DataLibrus).isPremium && !data.app.config.sync.tokenLibrusList.contains(data.profileId)
|
||||
},
|
||||
|
||||
|
||||
@ -116,11 +116,11 @@ val LibrusFeatures = listOf(
|
||||
* Homework - using API.
|
||||
* Sync only if account has premium access.
|
||||
*/
|
||||
Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
|
||||
/*Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
|
||||
ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API
|
||||
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
|
||||
(data as DataLibrus).isPremium
|
||||
},
|
||||
},*/
|
||||
/**
|
||||
* Behaviour - using API.
|
||||
*/
|
||||
@ -227,9 +227,9 @@ val LibrusFeatures = listOf(
|
||||
*/
|
||||
Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
|
||||
ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK to LOGIN_METHOD_LIBRUS_SYNERGIA
|
||||
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)).withShouldSync { data ->
|
||||
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA))/*.withShouldSync { data ->
|
||||
!(data as DataLibrus).isPremium
|
||||
},
|
||||
}*/,
|
||||
|
||||
/**
|
||||
* Messages inbox - using messages website.
|
||||
|
@ -12,7 +12,6 @@ import im.wangchao.mhttp.body.MediaTypeUtils
|
||||
import im.wangchao.mhttp.callback.FileCallbackHandler
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import org.json.JSONObject
|
||||
import org.json.XML
|
||||
import org.jsoup.Jsoup
|
||||
@ -55,13 +54,20 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
}
|
||||
|
||||
when {
|
||||
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
|
||||
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
|
||||
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
|
||||
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
|
||||
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
|
||||
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN
|
||||
text.contains("<message>Nie odnaleziono wiadomości.</message>") -> ERROR_LIBRUS_MESSAGES_NOT_FOUND
|
||||
text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED
|
||||
text.contains("eAccessDeny") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
|
||||
text.contains("OffLine") -> ERROR_LIBRUS_MESSAGES_MAINTENANCE
|
||||
text.contains("<status>error</status>") -> ERROR_LIBRUS_MESSAGES_ERROR
|
||||
text.contains("<type>eVarWhitThisNameNotExists</type>") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
|
||||
text.contains("<error>") -> ERROR_LIBRUS_MESSAGES_OTHER
|
||||
else -> null
|
||||
}?.let { errorCode ->
|
||||
data.error(ApiError(tag, errorCode)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
@ -82,14 +88,7 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
}
|
||||
}
|
||||
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("DZIENNIKSID")
|
||||
.value(data.messagesSessionId!!)
|
||||
.domain("wiadomosci.librus.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
|
||||
data.app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", data.messagesSessionId)
|
||||
|
||||
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
val doc = docBuilder.newDocument()
|
||||
@ -139,13 +138,20 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
}
|
||||
|
||||
when {
|
||||
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
|
||||
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
|
||||
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
|
||||
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
|
||||
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
|
||||
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN
|
||||
text.contains("<message>Nie odnaleziono wiadomości.</message>") -> ERROR_LIBRUS_MESSAGES_NOT_FOUND
|
||||
text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED
|
||||
text.contains("eAccessDeny") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
|
||||
text.contains("OffLine") -> ERROR_LIBRUS_MESSAGES_MAINTENANCE
|
||||
text.contains("<status>error</status>") -> ERROR_LIBRUS_MESSAGES_ERROR
|
||||
text.contains("<type>eVarWhitThisNameNotExists</type>") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
|
||||
text.contains("<error>") -> ERROR_LIBRUS_MESSAGES_OTHER
|
||||
else -> null
|
||||
}?.let { errorCode ->
|
||||
data.error(ApiError(tag, errorCode)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
@ -166,14 +172,7 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
}
|
||||
}
|
||||
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("DZIENNIKSID")
|
||||
.value(data.messagesSessionId!!)
|
||||
.domain("wiadomosci.librus.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
|
||||
data.app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", data.messagesSessionId)
|
||||
|
||||
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
val doc = docBuilder.newDocument()
|
||||
@ -253,10 +252,11 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
fun sandboxGetFile(tag: String, action: String, targetFile: File, onSuccess: (file: File) -> Unit,
|
||||
fun sandboxGetFile(tag: String, url: String, targetFile: File, onSuccess: (file: File) -> Unit,
|
||||
method: Int = GET,
|
||||
onProgress: (written: Long, total: Long) -> Unit) {
|
||||
|
||||
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action")
|
||||
d(tag, "Request: Librus/Messages - $url")
|
||||
|
||||
val callback = object : FileCallbackHandler(targetFile) {
|
||||
override fun onSuccess(file: File?, response: Response?) {
|
||||
@ -292,9 +292,14 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("$LIBRUS_SANDBOX_URL$action")
|
||||
.url(url)
|
||||
.userAgent(SYNERGIA_USER_AGENT)
|
||||
.post()
|
||||
.also {
|
||||
when (method) {
|
||||
POST -> it.post()
|
||||
else -> it.get()
|
||||
}
|
||||
}
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
|
@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class LibrusApiAttendances(override val data: DataLibrus,
|
||||
override val lastSync: Long?,
|
||||
@ -42,10 +43,10 @@ class LibrusApiAttendances(override val data: DataLibrus,
|
||||
val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id")
|
||||
val semester = attendance.getInt("Semester") ?: return@forEach
|
||||
val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
|
||||
val typeObject = data.attendanceTypes.get(type)
|
||||
val typeObject = data.attendanceTypes[type] ?: null
|
||||
val topic = typeObject?.name ?: ""
|
||||
|
||||
val startTime = data.lessonRanges.get(lessonNo).startTime
|
||||
val startTime = data.lessonRanges.get(lessonNo)?.startTime
|
||||
|
||||
val lesson = if (lessonId != -1L)
|
||||
data.librusLessons.singleOrNull { it.lessonId == lessonId }
|
||||
@ -59,14 +60,14 @@ class LibrusApiAttendances(override val data: DataLibrus,
|
||||
semester,
|
||||
topic,
|
||||
lessonDate,
|
||||
startTime,
|
||||
typeObject.type
|
||||
startTime ?: Time(0, 0, 0),
|
||||
typeObject?.type ?: Attendance.TYPE_CUSTOM
|
||||
)
|
||||
|
||||
val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach)
|
||||
|
||||
data.attendanceList.add(attendanceObject)
|
||||
if(typeObject.type != Attendance.TYPE_PRESENT) {
|
||||
if(typeObject?.type != Attendance.TYPE_PRESENT) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
|
@ -47,17 +47,16 @@ class LibrusApiEvents(override val data: DataLibrus,
|
||||
val addedDate = Date.fromIso(event.getString("AddDate"))
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
type,
|
||||
false,
|
||||
teacherId,
|
||||
subjectId,
|
||||
teamId
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = eventDate,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = type,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId,
|
||||
teamId = teamId
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -34,17 +34,16 @@ class LibrusApiHomework(override val data: DataLibrus,
|
||||
val addedDate = Date.fromY_m_d(homework.getString("Date"))
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate,
|
||||
null,
|
||||
topic,
|
||||
-1,
|
||||
-1,
|
||||
false,
|
||||
teacherId,
|
||||
-1,
|
||||
-1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = eventDate,
|
||||
time = null,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = -1,
|
||||
teacherId = teacherId,
|
||||
subjectId = -1,
|
||||
teamId = -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -4,15 +4,12 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.DAY
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.getInt
|
||||
import pl.szczodrzynski.edziennik.getJsonObject
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
@ -41,9 +38,10 @@ class LibrusApiLuckyNumber(override val data: DataLibrus,
|
||||
luckyNumber
|
||||
)
|
||||
|
||||
//if (luckyNumberDate > Date.getToday()) {
|
||||
if (luckyNumberDate >= Date.getToday())
|
||||
nextSync = luckyNumberDate.combineWith(Time(15, 0, 0))
|
||||
//}
|
||||
else
|
||||
nextSync = System.currentTimeMillis() + 6*HOUR*1000
|
||||
|
||||
data.luckyNumberList.add(luckyNumberObject)
|
||||
data.metadataList.add(
|
||||
|
@ -39,17 +39,16 @@ class LibrusApiPtMeetings(override val data: DataLibrus,
|
||||
}
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
Event.TYPE_PT_MEETING,
|
||||
false,
|
||||
teacherId,
|
||||
-1,
|
||||
data.teamClass?.id ?: -1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = eventDate,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = Event.TYPE_PT_MEETING,
|
||||
teacherId = teacherId,
|
||||
subjectId = -1,
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -21,6 +21,14 @@ class LibrusApiPushConfig(override val data: DataLibrus,
|
||||
}
|
||||
|
||||
init { data.app.config.sync.tokenLibrus?.also { tokenLibrus ->
|
||||
if(tokenLibrus.isEmpty()) {
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_PUSH_CONFIG, SYNC_ALWAYS)
|
||||
data.app.config.sync.tokenLibrusList =
|
||||
data.app.config.sync.tokenLibrusList + profileId
|
||||
onSuccess(ENDPOINT_LIBRUS_API_PUSH_CONFIG)
|
||||
return@also
|
||||
}
|
||||
|
||||
apiGet(TAG, "ChangeRegister", payload = JsonObject(
|
||||
"provider" to "FCM",
|
||||
"device" to tokenLibrus,
|
||||
|
@ -59,7 +59,7 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus,
|
||||
profileId,
|
||||
Metadata.TYPE_TEACHER_ABSENCE,
|
||||
id,
|
||||
profile?.empty ?: false,
|
||||
true,
|
||||
profile?.empty ?: false,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
|
@ -6,9 +6,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_FILE_DOWNLOAD
|
||||
import pl.szczodrzynski.edziennik.data.api.EXCEPTION_LIBRUS_MESSAGES_REQUEST
|
||||
import pl.szczodrzynski.edziennik.data.api.Regexes
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
|
||||
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
|
||||
@ -53,11 +51,10 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
|
||||
|
||||
val attachmentKey = keyMatcher[1]
|
||||
getAttachmentCheckKey(attachmentKey) {
|
||||
downloadAttachment(attachmentKey)
|
||||
downloadAttachment("${LIBRUS_SANDBOX_URL}CSDownload&singleUseKey=$attachmentKey", method = POST)
|
||||
}
|
||||
} else {
|
||||
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
|
||||
.withApiResponse(doc.toString()))
|
||||
downloadAttachment(downloadLink, method = GET)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,10 +88,10 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadAttachment(attachmentKey: String) {
|
||||
private fun downloadAttachment(url: String, method: Int = GET) {
|
||||
val targetFile = File(Utils.getStorageDir(), attachmentName)
|
||||
|
||||
sandboxGetFile(TAG, "CSDownload&singleUseKey=$attachmentKey", targetFile, { file ->
|
||||
sandboxGetFile(TAG, url, targetFile, { file ->
|
||||
|
||||
val event = AttachmentGetEvent(
|
||||
profileId,
|
||||
|
@ -109,9 +109,14 @@ class LibrusMessagesGetList(override val data: DataLibrus,
|
||||
id
|
||||
)
|
||||
|
||||
element.select("isAnyFileAttached")?.text()?.let {
|
||||
if (it == "1")
|
||||
messageObject.overrideHasAttachments = true
|
||||
}
|
||||
|
||||
data.messageIgnoreList.add(messageObject)
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
data.metadataList.add(Metadata(
|
||||
data.setSeenMetadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
id,
|
||||
|
@ -29,7 +29,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
||||
init { data.profile?.also { profile ->
|
||||
synergiaGet(TAG, "moje_zadania", method = POST, parameters = mapOf(
|
||||
"dataOd" to
|
||||
if (!data.profile.empty)
|
||||
if (profile.empty)
|
||||
profile.getSemesterStart(1).stringY_m_d
|
||||
else
|
||||
Date.getToday().stringY_m_d,
|
||||
@ -54,7 +54,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
||||
val teacherId = data.teacherList.singleOrNull { teacherName == it.fullName }?.id
|
||||
?: -1
|
||||
val topic = elements[2].text().trim()
|
||||
val addedDate = Date.fromY_m_d(elements[4].text().trim()).inMillis
|
||||
val addedDate = Date.fromY_m_d(elements[4].text().trim())
|
||||
val eventDate = Date.fromY_m_d(elements[6].text().trim())
|
||||
val id = "/podglad/([0-9]+)'".toRegex().find(
|
||||
elements[9].select("input").attr("onclick")
|
||||
@ -63,10 +63,25 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
||||
val lessons = data.db.timetableDao().getForDateNow(profileId, eventDate)
|
||||
val startTime = lessons.firstOrNull { it.subjectId == subjectId }?.startTime
|
||||
|
||||
val moreInfo = graphElements[2 * i + 1].select("td[title]")
|
||||
.attr("title").trim()
|
||||
val description = "Treść: (.*)".toRegex(RegexOption.DOT_MATCHES_ALL).find(moreInfo)
|
||||
?.get(1)?.replace("<br.*/>".toRegex(), "\n")?.trim()
|
||||
/*val moreInfo = graphElements[2 * i + 1].select("td[title]")
|
||||
.attr("title").trim()*/
|
||||
|
||||
var description = ""
|
||||
|
||||
graphElements.forEach { graphEl ->
|
||||
graphEl.select("td[title]")?.also {
|
||||
val title = it.attr("title")
|
||||
val r = "Temat: (.*?)<br.?/>Data udostępnienia: (.*?)<br.?/>Termin wykonania: (.*?)<br.?/>Treść: (.*)"
|
||||
.toRegex(RegexOption.DOT_MATCHES_ALL).find(title) ?: return@forEach
|
||||
val gTopic = r[1].trim()
|
||||
val gAddedDate = Date.fromY_m_d(r[2].trim())
|
||||
val gEventDate = Date.fromY_m_d(r[3].trim())
|
||||
if (gTopic == topic && gAddedDate == addedDate && gEventDate == eventDate) {
|
||||
description = r[4].replace("<br.?/>".toRegex(), "\n").trim()
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val seen = when (profile.empty) {
|
||||
true -> true
|
||||
@ -74,17 +89,16 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
||||
}
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate,
|
||||
startTime,
|
||||
"$topic\n$description",
|
||||
-1,
|
||||
Event.TYPE_HOMEWORK,
|
||||
false,
|
||||
teacherId,
|
||||
subjectId,
|
||||
data.teamClass?.id ?: -1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = eventDate,
|
||||
time = startTime,
|
||||
topic = "$topic\n$description",
|
||||
color = null,
|
||||
type = Event.TYPE_HOMEWORK,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId,
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
@ -94,7 +108,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
||||
id,
|
||||
seen,
|
||||
seen,
|
||||
addedDate
|
||||
addedDate.inMillis
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
return@portalGet
|
||||
}
|
||||
|
||||
val isParent = account.getString("group") == "parent"
|
||||
|
||||
val id = account.getInt("id") ?: continue
|
||||
val login = account.getString("login") ?: continue
|
||||
val token = account.getString("accessToken") ?: continue
|
||||
@ -69,7 +71,7 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
data.portalEmail,
|
||||
studentNameLong,
|
||||
studentNameShort,
|
||||
null
|
||||
if (isParent) studentNameLong else null /* temporarily - there is no parent name provided, only the type */
|
||||
).apply {
|
||||
studentData["accountId"] = id
|
||||
studentData["accountLogin"] = login
|
||||
|
@ -137,6 +137,7 @@ class LibrusLoginApi {
|
||||
"librus_change_password_error" -> ERROR_LOGIN_LIBRUS_API_CHANGE_PASSWORD_ERROR
|
||||
"librus_password_change_required" -> ERROR_LOGIN_LIBRUS_API_PASSWORD_CHANGE_REQUIRED
|
||||
"invalid_grant" -> ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN
|
||||
"invalid_request" -> ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST
|
||||
else -> ERROR_LOGIN_LIBRUS_API_OTHER
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
|
@ -8,7 +8,6 @@ import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
@ -64,21 +63,15 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
}
|
||||
|
||||
if (data.isMessagesLoginValid()) {
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("DZIENNIKSID")
|
||||
.value(data.messagesSessionId!!)
|
||||
.domain("wiadomosci.librus.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
data.app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", data.messagesSessionId)
|
||||
onSuccess()
|
||||
}
|
||||
else {
|
||||
data.app.cookieJar.clearForDomain("wiadomosci.librus.pl")
|
||||
data.app.cookieJar.clear("wiadomosci.librus.pl")
|
||||
if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) {
|
||||
loginWithSynergia()
|
||||
}
|
||||
else if (data.apiLogin != null && data.apiPassword != null) {
|
||||
else if (data.apiLogin != null && data.apiPassword != null && false) {
|
||||
loginWithCredentials()
|
||||
}
|
||||
else {
|
||||
@ -148,7 +141,7 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
}
|
||||
|
||||
private fun saveSessionId(response: Response?, text: String?) {
|
||||
var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID")
|
||||
var sessionId = data.app.cookieJar.get("wiadomosci.librus.pl", "DZIENNIKSID")
|
||||
sessionId = sessionId?.replace("-MAINT", "") // dunno what's this
|
||||
sessionId = sessionId?.replace("MAINT", "") // dunno what's this
|
||||
if (sessionId == null) {
|
||||
|
@ -37,19 +37,19 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
}
|
||||
else if (data.portalRefreshToken != null) {
|
||||
if (data.fakeLogin) {
|
||||
data.app.cookieJar.clearForDomain("librus.szkolny.eu")
|
||||
data.app.cookieJar.clear("librus.szkolny.eu")
|
||||
}
|
||||
else {
|
||||
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
||||
data.app.cookieJar.clear("portal.librus.pl")
|
||||
}
|
||||
accessToken(null, data.portalRefreshToken)
|
||||
}
|
||||
else {
|
||||
if (data.fakeLogin) {
|
||||
data.app.cookieJar.clearForDomain("librus.szkolny.eu")
|
||||
data.app.cookieJar.clear("librus.szkolny.eu")
|
||||
}
|
||||
else {
|
||||
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
||||
data.app.cookieJar.clear("portal.librus.pl")
|
||||
}
|
||||
authorize(if (data.fakeLogin) FAKE_LIBRUS_AUTHORIZE else LIBRUS_AUTHORIZE_URL)
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
@ -30,17 +29,11 @@ class LibrusLoginSynergia(override val data: DataLibrus, val onSuccess: () -> Un
|
||||
}
|
||||
|
||||
if (data.isSynergiaLoginValid()) {
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("DZIENNIKSID")
|
||||
.value(data.synergiaSessionId!!)
|
||||
.domain("synergia.librus.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
data.app.cookieJar.set("synergia.librus.pl", "DZIENNIKSID", data.synergiaSessionId)
|
||||
onSuccess()
|
||||
}
|
||||
else {
|
||||
data.app.cookieJar.clearForDomain("synergia.librus.pl")
|
||||
data.app.cookieJar.clear("synergia.librus.pl")
|
||||
if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_API)) {
|
||||
loginWithApi()
|
||||
}
|
||||
@ -92,7 +85,7 @@ class LibrusLoginSynergia(override val data: DataLibrus, val onSuccess: () -> Un
|
||||
}
|
||||
|
||||
if (location?.endsWith("centrum_powiadomien") == true) {
|
||||
val sessionId = data.app.cookieJar.getCookie("synergia.librus.pl", "DZIENNIKSID")
|
||||
val sessionId = data.app.cookieJar.get("synergia.librus.pl", "DZIENNIKSID")
|
||||
if (sessionId == null) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID)
|
||||
.withResponse(response)
|
||||
@ -117,7 +110,7 @@ class LibrusLoginSynergia(override val data: DataLibrus, val onSuccess: () -> Un
|
||||
}
|
||||
}
|
||||
|
||||
data.app.cookieJar.clearForDomain("synergia.librus.pl")
|
||||
data.app.cookieJar.clear("synergia.librus.pl")
|
||||
Request.builder()
|
||||
.url(LIBRUS_SYNERGIA_TOKEN_LOGIN_URL.replace("TOKEN", token) + "/uczen/widok/centrum_powiadomien")
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
|
@ -8,7 +8,6 @@ import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.FileCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
@ -26,6 +25,23 @@ open class MobidziennikWeb(open val data: DataMobidziennik, open val lastSync: L
|
||||
val profile
|
||||
get() = data.profile
|
||||
|
||||
/* TODO
|
||||
add error handling:
|
||||
|
||||
<!-- CONTENT -->
|
||||
<div id="content">
|
||||
<a name="tresc"></a>
|
||||
<h1>Ważna informacja!</h1>
|
||||
|
||||
<div class="okienko_informacyjne">
|
||||
Korzystasz z hasła, które zostało wygenerowane automatycznie.<hr/>
|
||||
Pamiętaj, aby je zmienić oraz uzupełnić dane w swoim profilu.<br/><br/>
|
||||
Obie te operacje można wykonać uruchamiając opcję
|
||||
<a href="https://poznan.mobidziennik.pl/dziennik/edytujprofil" title="Edycja profilu, możliwość zmiany hasła">Moje konto->Edycja profilu</a>.
|
||||
</div> </div>
|
||||
|
||||
*/
|
||||
|
||||
fun webGet(
|
||||
tag: String,
|
||||
endpoint: String,
|
||||
@ -65,6 +81,12 @@ open class MobidziennikWeb(open val data: DataMobidziennik, open val lastSync: L
|
||||
return
|
||||
}
|
||||
|
||||
if (text.contains("<h2>Problemy z wydajnością</h2>")) {
|
||||
data.error(ApiError(TAG, ERROR_MOBIDZIENNIK_WEB_SERVER_PROBLEM)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
onSuccess(text)
|
||||
} catch (e: Exception) {
|
||||
@ -82,18 +104,8 @@ open class MobidziennikWeb(open val data: DataMobidziennik, open val lastSync: L
|
||||
}
|
||||
}
|
||||
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name(data.webSessionKey!!)
|
||||
.value(data.webSessionValue!!)
|
||||
.domain("${data.loginServerName}.mobidziennik.pl")
|
||||
.secure().httpOnly().build(),
|
||||
Cookie.Builder()
|
||||
.name("SERVERID")
|
||||
.value(data.webServerId!!)
|
||||
.domain("${data.loginServerName}.mobidziennik.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
data.app.cookieJar.set("${data.loginServerName}.mobidziennik.pl", data.webSessionKey, data.webSessionValue)
|
||||
data.app.cookieJar.set("${data.loginServerName}.mobidziennik.pl", "SERVERID", data.webServerId)
|
||||
|
||||
Request.builder()
|
||||
.url(url)
|
||||
@ -164,18 +176,8 @@ open class MobidziennikWeb(open val data: DataMobidziennik, open val lastSync: L
|
||||
}
|
||||
}
|
||||
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name(data.webSessionKey!!)
|
||||
.value(data.webSessionValue!!)
|
||||
.domain("${data.loginServerName}.mobidziennik.pl")
|
||||
.secure().httpOnly().build(),
|
||||
Cookie.Builder()
|
||||
.name("SERVERID")
|
||||
.value(data.webServerId!!)
|
||||
.domain("${data.loginServerName}.mobidziennik.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
data.app.cookieJar.set("${data.loginServerName}.mobidziennik.pl", data.webSessionKey, data.webSessionValue)
|
||||
data.app.cookieJar.set("${data.loginServerName}.mobidziennik.pl", "SERVERID", data.webServerId)
|
||||
|
||||
Request.builder()
|
||||
.url(url)
|
||||
|
@ -51,17 +51,16 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List<String>) {
|
||||
|
||||
|
||||
val eventObject = Event(
|
||||
data.profileId,
|
||||
id,
|
||||
eventDate,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
type,
|
||||
false,
|
||||
teacherId,
|
||||
subjectId,
|
||||
teamId)
|
||||
profileId = data.profileId,
|
||||
id = id,
|
||||
date = eventDate,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = type,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId,
|
||||
teamId = teamId)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
data.metadataList.add(
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api
|
||||
|
||||
import android.text.Html
|
||||
import androidx.core.util.contains
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
@ -25,22 +26,21 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List<String>) {
|
||||
val id = cols[0].toLong()
|
||||
val teacherId = cols[7].toLong()
|
||||
val subjectId = cols[6].toLong()
|
||||
val topic = cols[1]
|
||||
val topic = Html.fromHtml(cols[1])?.toString() ?: ""
|
||||
val eventDate = Date.fromYmd(cols[2])
|
||||
val startTime = Time.fromYmdHm(cols[3])
|
||||
|
||||
val eventObject = Event(
|
||||
data.profileId,
|
||||
id,
|
||||
eventDate,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
Event.TYPE_HOMEWORK,
|
||||
false,
|
||||
teacherId,
|
||||
subjectId,
|
||||
teamId)
|
||||
profileId = data.profileId,
|
||||
id = id,
|
||||
date = eventDate,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = Event.TYPE_HOMEWORK,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId,
|
||||
teamId = teamId)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
data.metadataList.add(
|
||||
|
@ -44,7 +44,7 @@ class MobidziennikApiTeams(val data: DataMobidziennik, tableTeams: List<String>?
|
||||
|
||||
val studentId = cols[1].toInt()
|
||||
val teamId = cols[2].toLong()
|
||||
val studentNumber = cols[4].toInt()
|
||||
val studentNumber = cols[4].toIntOrNull() ?: -1
|
||||
|
||||
if (studentId != data.studentId)
|
||||
continue
|
||||
|
@ -61,26 +61,25 @@ class MobidziennikWebCalendar(override val data: DataMobidziennik,
|
||||
val title = event.getString("title")
|
||||
val comment = event.getString("comment")
|
||||
|
||||
var topic = title
|
||||
var topic = title ?: ""
|
||||
if (title != comment) {
|
||||
topic += "\n" + comment
|
||||
}
|
||||
|
||||
if (id == -1L) {
|
||||
id = crc16(topic?.toByteArray()).toLong()
|
||||
id = crc16(topic.toByteArray()).toLong()
|
||||
}
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate, null,
|
||||
topic,
|
||||
-1,
|
||||
eventType,
|
||||
false,
|
||||
-1,
|
||||
-1,
|
||||
data.teamClass?.id ?: -1
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = eventDate, time = null,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = eventType,
|
||||
teacherId = -1,
|
||||
subjectId = -1,
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -118,7 +118,7 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
|
||||
|
||||
// this needs to be at the end
|
||||
message.apply {
|
||||
this.body = body.html().replace("\n", "<br>")
|
||||
this.body = body.html()
|
||||
|
||||
clearAttachments()
|
||||
content.select("ul li").map { it.select("a").first() }.forEach {
|
||||
|
@ -32,15 +32,15 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
|
||||
|
||||
val doc = Jsoup.parse(text)
|
||||
|
||||
val listElement = doc.getElementsByClass("spis").first()
|
||||
val listElement = doc.getElementsByClass("spis")?.first()
|
||||
if (listElement == null) {
|
||||
data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, 7*DAY)
|
||||
onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL)
|
||||
return@webGet
|
||||
}
|
||||
val list = listElement.getElementsByClass("podswietl")
|
||||
for (item in list) {
|
||||
val id = item.attr("rel").replace("[^\\d]".toRegex(), "").toLongOrNull() ?: continue
|
||||
list?.forEach { item ->
|
||||
val id = item.attr("rel").replace("[^\\d]".toRegex(), "").toLongOrNull() ?: return@forEach
|
||||
|
||||
val subjectEl = item.select("td:eq(0) div").first()
|
||||
val subject = subjectEl.text()
|
||||
|
@ -36,9 +36,9 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
|
||||
|
||||
val doc = Jsoup.parse(text)
|
||||
|
||||
val list = doc.getElementsByClass("spis").first().getElementsByClass("podswietl")
|
||||
for (item in list) {
|
||||
val id = item.attr("rel").toLongOrNull() ?: continue
|
||||
val list = doc.getElementsByClass("spis")?.first()?.getElementsByClass("podswietl")
|
||||
list?.forEach { item ->
|
||||
val id = item.attr("rel").toLongOrNull() ?: return@forEach
|
||||
|
||||
val subjectEl = item.select("td:eq(0)").first()
|
||||
var hasAttachments = false
|
||||
|
@ -40,9 +40,9 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
|
||||
|
||||
val doc = Jsoup.parse(text)
|
||||
|
||||
val list = doc.getElementsByClass("spis").first().getElementsByClass("podswietl")
|
||||
for (item in list) {
|
||||
val id = item.attr("rel").toLongOrNull() ?: continue
|
||||
val list = doc.getElementsByClass("spis")?.first()?.getElementsByClass("podswietl")
|
||||
list?.forEach { item ->
|
||||
val id = item.attr("rel").toLongOrNull() ?: return@forEach
|
||||
|
||||
val subjectEl = item.select("td:eq(0)").first()
|
||||
var hasAttachments = false
|
||||
|
@ -26,7 +26,7 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit
|
||||
}
|
||||
else {
|
||||
if (data.loginServerName.isNotNullNorEmpty() && data.loginUsername.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
|
||||
data.app.cookieJar.clearForDomain(data.loginServerName + ".mobidziennik.pl")
|
||||
data.app.cookieJar.clear("${data.loginServerName}.mobidziennik.pl")
|
||||
loginWithCredentials()
|
||||
}
|
||||
else {
|
||||
@ -58,10 +58,10 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
val cookies = data.app.cookieJar.getForDomain("${data.loginServerName}.mobidziennik.pl")
|
||||
val cookie = cookies.singleOrNull { it.name().length > 32 }
|
||||
val sessionKey = cookie?.name()
|
||||
val sessionId = cookie?.value()
|
||||
val cookies = data.app.cookieJar.getAll("${data.loginServerName}.mobidziennik.pl")
|
||||
val cookie = cookies.entries.firstOrNull { it.key.length > 32 }
|
||||
val sessionKey = cookie?.key
|
||||
val sessionId = cookie?.value
|
||||
if (sessionId == null) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID)
|
||||
.withResponse(response)
|
||||
@ -71,7 +71,7 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit
|
||||
|
||||
data.webSessionKey = sessionKey
|
||||
data.webSessionValue = sessionId
|
||||
data.webServerId = data.app.cookieJar.getCookie("${data.loginServerName}.mobidziennik.pl", "SERVERID")
|
||||
data.webServerId = data.app.cookieJar.get("${data.loginServerName}.mobidziennik.pl", "SERVERID")
|
||||
data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
|
||||
onSuccess()
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.template
|
||||
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_TEMPLATE_API
|
||||
@ -28,13 +27,7 @@ class DataTemplate(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
|
||||
loginMethods.clear()
|
||||
if (isWebLoginValid()) {
|
||||
loginMethods += LOGIN_METHOD_TEMPLATE_WEB
|
||||
app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("AuthCookie")
|
||||
.value(webCookie!!)
|
||||
.domain("eregister.example.com")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
app.cookieJar.set("eregister.example.com", "AuthCookie", webCookie)
|
||||
}
|
||||
if (isApiLoginValid())
|
||||
loginMethods += LOGIN_METHOD_TEMPLATE_API
|
||||
|
@ -4,12 +4,11 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.template.login
|
||||
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_DATA_MISSING
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_PROFILE_MISSING
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.DataTemplate
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||
|
||||
class TemplateLoginWeb(val data: DataTemplate, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
@ -23,17 +22,11 @@ class TemplateLoginWeb(val data: DataTemplate, val onSuccess: () -> Unit) {
|
||||
}
|
||||
|
||||
if (data.isWebLoginValid()) {
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("AuthCookie")
|
||||
.value(data.webCookie!!)
|
||||
.domain("eregister.example.com")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
data.app.cookieJar.set("eregister.example.com", "AuthCookie", data.webCookie)
|
||||
onSuccess()
|
||||
}
|
||||
else {
|
||||
data.app.cookieJar.clearForDomain("eregister.example.com")
|
||||
data.app.cookieJar.clear("eregister.example.com")
|
||||
if (/*data.webLogin != null && data.webPassword != null && */true) {
|
||||
loginWithCredentials()
|
||||
}
|
||||
|
@ -10,7 +10,10 @@ import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_API
|
||||
import pl.szczodrzynski.edziennik.data.api.models.Data
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Team
|
||||
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.values
|
||||
|
||||
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||
|
||||
@ -26,6 +29,25 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
// during the first sync `profile.studentClassName` is already set
|
||||
if (teamList.values().none { it.type == Team.TYPE_CLASS }) {
|
||||
profile?.studentClassName?.also { name ->
|
||||
val id = Utils.crc16(name.toByteArray()).toLong()
|
||||
|
||||
val teamObject = Team(
|
||||
profileId,
|
||||
id,
|
||||
name,
|
||||
Team.TYPE_CLASS,
|
||||
"$schoolName:$name",
|
||||
-1
|
||||
)
|
||||
teamList.put(id, teamObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateUserCode() = "$schoolName:$studentId"
|
||||
|
||||
/**
|
||||
@ -188,11 +210,11 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
"SZ9" -> "http://hack.szkolny.eu"
|
||||
else -> null
|
||||
}
|
||||
return if (url != null) "$url/$symbol" else loginStore.getLoginData("apiUrl", null)
|
||||
return if (url != null) "$url/$symbol/" else loginStore.getLoginData("apiUrl", null)
|
||||
}
|
||||
|
||||
val fullApiUrl: String?
|
||||
get() {
|
||||
return "$apiUrl/$schoolSymbol"
|
||||
return "$apiUrl$schoolSymbol/"
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,6 @@ import io.github.wulkanowy.signer.android.signContent
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Team
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.net.HttpURLConnection
|
||||
import java.util.*
|
||||
@ -38,26 +36,10 @@ open class VulcanApi(open val data: DataVulcan, open val lastSync: Long?) {
|
||||
baseUrl: Boolean = false,
|
||||
onSuccess: (json: JsonObject, response: Response?) -> Unit
|
||||
) {
|
||||
val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}/$endpoint"
|
||||
val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}$endpoint"
|
||||
|
||||
d(tag, "Request: Vulcan/Api - $url")
|
||||
|
||||
if (data.teamList.size() == 0) {
|
||||
data.profile?.studentClassName?.also { name ->
|
||||
val id = Utils.crc16(name.toByteArray()).toLong()
|
||||
|
||||
val teamObject = Team(
|
||||
profileId,
|
||||
id,
|
||||
name,
|
||||
Team.TYPE_CLASS,
|
||||
"${data.schoolName}:$name",
|
||||
-1
|
||||
)
|
||||
data.teamList.put(id, teamObject)
|
||||
}
|
||||
}
|
||||
|
||||
val finalPayload = JsonObject()
|
||||
parameters.map { (name, value) ->
|
||||
when (value) {
|
||||
|
@ -72,17 +72,16 @@ class VulcanApiEvents(override val data: DataVulcan,
|
||||
val teamId = event.getLong("IdOddzial") ?: data.teamClass?.id ?: -1
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
type,
|
||||
false,
|
||||
teacherId,
|
||||
subjectId,
|
||||
teamId
|
||||
profileId = profileId,
|
||||
id = id,
|
||||
date = eventDate,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = null,
|
||||
type = type,
|
||||
teacherId = teacherId,
|
||||
subjectId = subjectId,
|
||||
teamId = teamId
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
|
@ -86,31 +86,36 @@ class VulcanApiTimetable(override val data: DataVulcan,
|
||||
data.teamList[id] = team
|
||||
}
|
||||
team.id
|
||||
} ?: data.studentClassId.toLong()
|
||||
} ?: data.teamClass?.id ?: -1
|
||||
|
||||
val subjectId = lesson.getLong("IdPrzedmiot")?.let {
|
||||
when (it) {
|
||||
0L -> {
|
||||
val subjectName = lesson.getString("PrzedmiotNazwa") ?: ""
|
||||
val subjectId = lesson.getLong("IdPrzedmiot").let { id ->
|
||||
// get the specified subject name
|
||||
val subjectName = lesson.getString("PrzedmiotNazwa") ?: ""
|
||||
|
||||
data.subjectList.singleOrNull { subject -> subject.longName == subjectName }?.id
|
||||
?: {
|
||||
/**
|
||||
* CREATE A NEW SUBJECT IF IT DOESN'T EXIST
|
||||
*/
|
||||
|
||||
val subjectObject = Subject(
|
||||
profileId,
|
||||
-1 * crc16(subjectName.toByteArray()).toLong(),
|
||||
subjectName,
|
||||
subjectName
|
||||
)
|
||||
data.subjectList.put(subjectObject.id, subjectObject)
|
||||
subjectObject.id
|
||||
}.invoke()
|
||||
}
|
||||
else -> it
|
||||
val condition = when (id) {
|
||||
// "special" subject - e.g. one time classes, a trip, etc.
|
||||
0L -> { subject: Subject -> subject.longName == subjectName }
|
||||
// normal subject, check if it exists
|
||||
else -> { subject: Subject -> subject.id == id }
|
||||
}
|
||||
|
||||
data.subjectList.singleOrNull(condition)?.id ?: {
|
||||
/**
|
||||
* CREATE A NEW SUBJECT IF IT DOESN'T EXIST
|
||||
*/
|
||||
|
||||
val subjectObject = Subject(
|
||||
profileId,
|
||||
if (id == null || id == 0L)
|
||||
-1 * crc16(subjectName.toByteArray()).toLong()
|
||||
else
|
||||
id,
|
||||
subjectName,
|
||||
subjectName
|
||||
)
|
||||
data.subjectList.put(subjectObject.id, subjectObject)
|
||||
subjectObject.id
|
||||
}()
|
||||
}
|
||||
|
||||
val lessonObject = Lesson(profileId, -1).apply {
|
||||
|
@ -137,7 +137,7 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("${data.apiUrl}/$VULCAN_API_ENDPOINT_CERTIFICATE")
|
||||
.url("${data.apiUrl}$VULCAN_API_ENDPOINT_CERTIFICATE")
|
||||
.userAgent(VULCAN_API_USER_AGENT)
|
||||
.addHeader("RequestMobileType", "RegisterDevice")
|
||||
.addParameter("PIN", data.apiPin)
|
||||
|
@ -12,10 +12,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.BuildConfig
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.DateAdapter
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.TimeAdapter
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
|
||||
@ -28,7 +25,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.md5
|
||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -80,7 +76,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
withContext(Dispatchers.Default) { block() }
|
||||
}
|
||||
catch (e: Exception) {
|
||||
errorSnackbar.addError(ApiError.fromThrowable(TAG, e)).show()
|
||||
errorSnackbar.addError(e.toApiError(TAG)).show()
|
||||
null
|
||||
}
|
||||
}
|
||||
@ -91,7 +87,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
catch (e: Exception) {
|
||||
ErrorDetailsDialog(
|
||||
activity,
|
||||
listOf(ApiError.fromThrowable(TAG, e)),
|
||||
listOf(e.toApiError(TAG)),
|
||||
R.string.error_occured
|
||||
)
|
||||
null
|
||||
@ -160,7 +156,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getEvents(profiles: List<Profile>, notifications: List<Notification>, blacklistedIds: List<Long>): List<EventFull> {
|
||||
fun getEvents(profiles: List<Profile>, notifications: List<Notification>, blacklistedIds: List<Long>, lastSyncTime: Long): List<EventFull> {
|
||||
val teams = app.db.teamDao().allNow
|
||||
|
||||
val response = api.serverSync(ServerSyncRequest(
|
||||
@ -185,19 +181,31 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
}
|
||||
}
|
||||
},
|
||||
lastSync = lastSyncTime,
|
||||
notifications = notifications.map { ServerSyncRequest.Notification(it.profileName ?: "", it.type, it.text) }
|
||||
)).execute()
|
||||
parseResponse(response)
|
||||
val (events, hasBrowsers) = parseResponse(response)
|
||||
|
||||
val events = mutableListOf<EventFull>()
|
||||
hasBrowsers?.let {
|
||||
app.config.sync.webPushEnabled = it
|
||||
}
|
||||
|
||||
response.body()?.data?.events?.forEach { event ->
|
||||
val eventList = mutableListOf<EventFull>()
|
||||
|
||||
events.forEach { event ->
|
||||
// skip blacklisted events
|
||||
if (event.id in blacklistedIds)
|
||||
return@forEach
|
||||
|
||||
// force nullable non-negative colors
|
||||
if (event.color == -1)
|
||||
event.color = null
|
||||
|
||||
// create the event for every matching team and profile
|
||||
teams.filter { it.code == event.teamCode }.onEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@onEach
|
||||
|
||||
events.add(EventFull(event).apply {
|
||||
eventList += EventFull(event).apply {
|
||||
profileId = team.profileId
|
||||
teamId = team.id
|
||||
addedManually = true
|
||||
@ -205,11 +213,11 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
notified = profile.empty
|
||||
|
||||
if (profile.userCode == event.sharedBy) sharedBy = "self"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events
|
||||
return eventList
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
@ -253,9 +261,8 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
browserId = browserId,
|
||||
pairToken = pairToken
|
||||
)).execute()
|
||||
parseResponse(response)
|
||||
|
||||
return response.body()?.data?.browsers ?: emptyList()
|
||||
return parseResponse(response).browsers
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
@ -265,9 +272,8 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
device = getDevice(),
|
||||
action = "listBrowsers"
|
||||
)).execute()
|
||||
parseResponse(response)
|
||||
|
||||
return response.body()?.data?.browsers ?: emptyList()
|
||||
return parseResponse(response).browsers
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
@ -278,9 +284,8 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
action = "unpairBrowser",
|
||||
browserId = browserId
|
||||
)).execute()
|
||||
parseResponse(response)
|
||||
|
||||
return response.body()?.data?.browsers ?: emptyList()
|
||||
return parseResponse(response).browsers
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
@ -307,9 +312,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
@Throws(Exception::class)
|
||||
fun getUpdate(channel: String): List<Update> {
|
||||
val response = api.updates(channel).execute()
|
||||
parseResponse(response)
|
||||
|
||||
return response.body()?.data ?: emptyList()
|
||||
return parseResponse(response)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
@ -321,8 +324,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
targetDeviceId = targetDeviceId,
|
||||
text = text
|
||||
)).execute()
|
||||
val data = parseResponse(response)
|
||||
|
||||
return data.message
|
||||
return parseResponse(response).message
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,6 @@ object Signing {
|
||||
|
||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||
return "$param1.MTIzNDU2Nzg5MDwj/ezwig===.$param2".sha256()
|
||||
return "$param1.MTIzNDU2Nzg5MDZhgrg9J9===.$param2".sha256()
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ data class EventShareRequest (
|
||||
|
||||
val action: String = "event",
|
||||
|
||||
val sharedByName: String,
|
||||
/* If null, the server shows an error */
|
||||
val sharedByName: String?,
|
||||
val shareTeamCode: String? = null,
|
||||
val unshareTeamCode: String? = null,
|
||||
val requesterName: String? = null,
|
||||
|
@ -11,6 +11,8 @@ data class ServerSyncRequest(
|
||||
val userCodes: List<String>,
|
||||
val users: List<User>? = null,
|
||||
|
||||
val lastSync: Long,
|
||||
|
||||
val notifications: List<Notification>? = null
|
||||
) {
|
||||
data class User(
|
||||
|
@ -6,4 +6,7 @@ package pl.szczodrzynski.edziennik.data.api.szkolny.response
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
|
||||
data class ServerSyncResponse(val events: List<EventFull>)
|
||||
data class ServerSyncResponse(
|
||||
val events: List<EventFull>,
|
||||
val hasBrowsers: Boolean? = null
|
||||
)
|
||||
|
@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class AppSync(val app: App, val notifications: MutableList<Notification>, val profiles: List<Profile>, val api: SzkolnyApi) {
|
||||
companion object {
|
||||
@ -24,26 +25,27 @@ class AppSync(val app: App, val notifications: MutableList<Notification>, val pr
|
||||
*
|
||||
* @return a number of events inserted to DB, possibly needing a notification
|
||||
*/
|
||||
fun run(): Int {
|
||||
val profiles = profiles.filter { it.registration == Profile.REGISTRATION_ENABLED && !it.archived }
|
||||
if (profiles.isNotEmpty()) {
|
||||
val blacklistedIds = app.db.eventDao().blacklistedIds;
|
||||
val events = api.getEvents(profiles, notifications, blacklistedIds)
|
||||
fun run(lastSyncTime: Long, markAsSeen: Boolean = false): Int {
|
||||
val blacklistedIds = app.db.eventDao().blacklistedIds
|
||||
val events = api.getEvents(profiles, notifications, blacklistedIds, lastSyncTime)
|
||||
|
||||
if (events.isNotEmpty()) {
|
||||
app.db.metadataDao().addAllIgnore(events.map { event ->
|
||||
Metadata(
|
||||
event.profileId,
|
||||
Metadata.TYPE_EVENT,
|
||||
event.id,
|
||||
event.seen,
|
||||
event.notified,
|
||||
event.addedDate
|
||||
)
|
||||
})
|
||||
return app.db.eventDao().addAll(events).size
|
||||
}
|
||||
app.config.sync.lastAppSync = System.currentTimeMillis()
|
||||
|
||||
if (events.isNotEmpty()) {
|
||||
val today = Date.getToday()
|
||||
app.db.metadataDao().addAllIgnore(events.map { event ->
|
||||
val isPast = event.date < today
|
||||
Metadata(
|
||||
event.profileId,
|
||||
Metadata.TYPE_EVENT,
|
||||
event.id,
|
||||
isPast || markAsSeen || event.seen,
|
||||
isPast || markAsSeen || event.notified,
|
||||
event.addedDate
|
||||
)
|
||||
})
|
||||
return app.db.eventDao().addAll(events).size
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
announcementNotifications()
|
||||
messageNotifications()
|
||||
luckyNumberNotifications()
|
||||
teacherAbsenceNotifications()
|
||||
}
|
||||
|
||||
private fun timetableNotifications() {
|
||||
@ -58,7 +59,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
}
|
||||
|
||||
private fun eventNotifications() {
|
||||
for (event in app.db.eventDao().notNotifiedNow.filter { it.eventDate >= today }) {
|
||||
for (event in app.db.eventDao().getNotNotifiedNow().filter { it.date >= today }) {
|
||||
val text = if (event.type == Event.TYPE_HOMEWORK)
|
||||
app.getString(
|
||||
if (event.subjectLongName.isNullOrEmpty())
|
||||
@ -66,7 +67,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
else
|
||||
R.string.notification_homework_format,
|
||||
event.subjectLongName,
|
||||
event.eventDate.formattedString
|
||||
event.date.formattedString
|
||||
)
|
||||
else
|
||||
app.getString(
|
||||
@ -74,8 +75,8 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
R.string.notification_event_no_subject_format
|
||||
else
|
||||
R.string.notification_event_format,
|
||||
event.typeName,
|
||||
event.eventDate.formattedString,
|
||||
event.typeName ?: "wydarzenie",
|
||||
event.date.formattedString,
|
||||
event.subjectLongName
|
||||
)
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
|
||||
@ -88,17 +89,17 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
|
||||
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
||||
addedDate = event.addedDate
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
fun sharedEventNotifications() {
|
||||
for (event in app.db.eventDao().notNotifiedNow.filter { it.eventDate >= today && it.sharedBy != null }) {
|
||||
for (event in app.db.eventDao().getNotNotifiedNow().filter { it.date >= today && it.sharedBy != null }) {
|
||||
val text = app.getString(
|
||||
R.string.notification_shared_event_format,
|
||||
event.sharedByName,
|
||||
event.typeName ?: "wydarzenie",
|
||||
event.eventDate.formattedString,
|
||||
event.date.formattedString,
|
||||
event.topic
|
||||
)
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
|
||||
@ -111,7 +112,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
|
||||
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
||||
addedDate = event.addedDate
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,4 +275,23 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun teacherAbsenceNotifications() {
|
||||
for (teacherAbsence in app.db.teacherAbsenceDao().getNotNotifiedNow()) {
|
||||
val message = app.getString(
|
||||
R.string.notification_teacher_absence_new_format,
|
||||
teacherAbsence.teacherFullName
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(teacherAbsence.profileId, Notification.TYPE_TEACHER_ABSENCE, teacherAbsence.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_TEACHER_ABSENCE),
|
||||
text = message,
|
||||
type = Notification.TYPE_TEACHER_ABSENCE,
|
||||
profileId = teacherAbsence.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == teacherAbsence.profileId }?.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_AGENDA,
|
||||
addedDate = teacherAbsence.addedDate
|
||||
).addExtra("eventDate", teacherAbsence.dateFrom.value.toLong())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,16 +31,23 @@ class SzkolnyTask(val app: App, val syncingProfiles: List<Profile>) : IApiTask(-
|
||||
val notifications = Notifications(app, notificationList, profiles)
|
||||
notifications.run()
|
||||
|
||||
val shouldAppSync = notificationList.isNotEmpty() || (System.currentTimeMillis() - app.config.lastAppSync > 24*HOUR*1000)
|
||||
// do an AppSync every 24 hours, or if WebPush has a notification
|
||||
val appSyncProfiles = profiles.filter { it.registration == Profile.REGISTRATION_ENABLED && !it.archived }
|
||||
// App Sync conditions:
|
||||
// - every 24 hours && any profile is registered
|
||||
// - if there are new notifications && any browser is paired
|
||||
val shouldAppSync =
|
||||
System.currentTimeMillis() - app.config.sync.lastAppSync > 24*HOUR*1000
|
||||
&& appSyncProfiles.isNotEmpty()
|
||||
|| notificationList.isNotEmpty()
|
||||
&& app.config.sync.webPushEnabled
|
||||
|
||||
if (shouldAppSync) {
|
||||
// send notifications to web push, get shared events
|
||||
val addedEvents = AppSync(app, notificationList, profiles, api).run()
|
||||
val addedEvents = AppSync(app, notificationList, appSyncProfiles, api).run(app.config.sync.lastAppSync)
|
||||
if (addedEvents > 0) {
|
||||
// create notifications for shared events (not present before app sync)
|
||||
notifications.sharedEventNotifications()
|
||||
}
|
||||
app.config.lastAppSync = System.currentTimeMillis()
|
||||
}
|
||||
d(TAG, "Created ${notificationList.count()} notifications.")
|
||||
|
||||
|
@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
|
||||
LibrusLesson::class,
|
||||
TimetableManual::class,
|
||||
Metadata::class
|
||||
], version = 79)
|
||||
], version = 81)
|
||||
@TypeConverters(
|
||||
ConverterTime::class,
|
||||
ConverterDate::class,
|
||||
@ -164,7 +164,9 @@ abstract class AppDb : RoomDatabase() {
|
||||
Migration76(),
|
||||
Migration77(),
|
||||
Migration78(),
|
||||
Migration79()
|
||||
Migration79(),
|
||||
Migration80(),
|
||||
Migration81()
|
||||
).allowMainThreadQueries().build()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.RawQuery
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
|
||||
@Dao
|
||||
interface BaseDao<T, F> {
|
||||
@RawQuery
|
||||
fun getRaw(query: SupportSQLiteQuery): LiveData<List<F>>
|
||||
fun getRaw(query: String) = getRaw(SimpleSQLiteQuery(query))
|
||||
@RawQuery
|
||||
fun getRawNow(query: SupportSQLiteQuery): List<F>
|
||||
fun getRawNow(query: String) = getRawNow(SimpleSQLiteQuery(query))
|
||||
@RawQuery
|
||||
fun getOneNow(query: SupportSQLiteQuery): F?
|
||||
fun getOneNow(query: String) = getOneNow(SimpleSQLiteQuery(query))
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun add(item: T): Long
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun addAll(items: List<T>): LongArray
|
||||
|
||||
fun clear(profileId: Int)
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.dao;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.room.Transaction;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_EVENT;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_HOMEWORK;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_LESSON_CHANGE;
|
||||
|
||||
@Dao
|
||||
public abstract class EventDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(Event event);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long[] addAll(List<Event> eventList);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventId = :id")
|
||||
public abstract void remove(int profileId, long id);
|
||||
@Query("DELETE FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND thingId = :thingId")
|
||||
public abstract void removeMetadata(int profileId, int thingType, long thingId);
|
||||
@Transaction
|
||||
public void remove(int profileId, long type, long id) {
|
||||
remove(profileId, id);
|
||||
removeMetadata(profileId, type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, id);
|
||||
}
|
||||
@Transaction
|
||||
public void remove(Event event) {
|
||||
remove(event.profileId, event.type, event.id);
|
||||
}
|
||||
@Transaction
|
||||
public void remove(int profileId, Event event) {
|
||||
remove(profileId, event.type, event.id);
|
||||
}
|
||||
@Query("DELETE FROM events WHERE teamId = :teamId AND eventId = :id")
|
||||
public abstract void removeByTeamId(long teamId, long id);
|
||||
|
||||
@RawQuery(observedEntities = {Event.class})
|
||||
abstract LiveData<List<EventFull>> getAll(SupportSQLiteQuery query);
|
||||
public LiveData<List<EventFull>> getAll(int profileId, String filter, String limit) {
|
||||
String query = "SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName,\n" +
|
||||
"eventTypes.eventTypeName AS typeName,\n" +
|
||||
"eventTypes.eventTypeColor AS typeColor\n" +
|
||||
"FROM events\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN eventTypes USING(profileId, eventType)\n" +
|
||||
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE events.profileId = "+profileId+" AND events.eventBlacklisted = 0 AND "+filter+"\n" +
|
||||
"GROUP BY eventId\n" +
|
||||
"ORDER BY eventDate, eventStartTime ASC "+limit;
|
||||
Log.d("DB", query);
|
||||
return getAll(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
public LiveData<List<EventFull>> getAll(int profileId) {
|
||||
return getAll(profileId, "1", "");
|
||||
}
|
||||
public List<EventFull> getAllNow(int profileId) {
|
||||
return getAllNow(profileId, "1");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllWhere(int profileId, String filter) {
|
||||
return getAll(profileId, filter, "");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByType(int profileId, long type, String filter) {
|
||||
return getAll(profileId, "eventType = "+type+" AND "+filter, "");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByDate(int profileId, @NonNull Date date) {
|
||||
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"'", "");
|
||||
}
|
||||
public List<EventFull> getAllByDateNow(int profileId, @NonNull Date date) {
|
||||
return getAllNow(profileId, "eventDate = '"+date.getStringY_m_d()+"'");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByDateTime(int profileId, @NonNull Date date, Time time) {
|
||||
if (time == null)
|
||||
return getAllByDate(profileId, date);
|
||||
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"' AND eventStartTime = '"+time.getStringValue()+"'", "");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllNearest(int profileId, @NonNull Date today, int limit) {
|
||||
return getAll(profileId, "eventDate >= '"+today.getStringY_m_d()+"'", "LIMIT "+limit);
|
||||
}
|
||||
|
||||
@RawQuery
|
||||
abstract List<EventFull> getAllNow(SupportSQLiteQuery query);
|
||||
public List<EventFull> getAllNow(int profileId, String filter) {
|
||||
return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName,\n" +
|
||||
"eventTypes.eventTypeName AS typeName,\n" +
|
||||
"eventTypes.eventTypeColor AS typeColor\n" +
|
||||
"FROM events \n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN eventTypes USING(profileId, eventType)\n" +
|
||||
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE events.profileId = "+profileId+" AND events.eventBlacklisted = 0 AND "+filter+"\n" +
|
||||
"GROUP BY eventId\n" +
|
||||
"ORDER BY eventStartTime, addedDate ASC"));
|
||||
}
|
||||
public List<EventFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
@Query("SELECT eventId FROM events WHERE profileId = :profileId AND eventBlacklisted = 1")
|
||||
public abstract List<Long> getBlacklistedIds(int profileId);
|
||||
@Query("SELECT eventId FROM events WHERE eventBlacklisted = 1")
|
||||
public abstract List<Long> getBlacklistedIds();
|
||||
|
||||
@Query("SELECT " +
|
||||
"*, " +
|
||||
"eventTypes.eventTypeName AS typeName, " +
|
||||
"eventTypes.eventTypeColor AS typeColor " +
|
||||
"FROM events " +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId) " +
|
||||
"LEFT JOIN eventTypes USING(profileId, eventType) " +
|
||||
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = events.profileId " +
|
||||
"WHERE events.eventBlacklisted = 0 AND notified = 0 " +
|
||||
"GROUP BY eventId " +
|
||||
"ORDER BY addedDate ASC")
|
||||
public abstract List<EventFull> getNotNotifiedNow();
|
||||
|
||||
public EventFull getByIdNow(int profileId, long eventId) {
|
||||
List<EventFull> eventList = getAllNow(profileId, "eventId = "+eventId);
|
||||
return eventList.size() == 0 ? null : eventList.get(0);
|
||||
}
|
||||
|
||||
@Query("UPDATE events SET eventAddedManually = 1 WHERE profileId = :profileId AND eventDate < :date")
|
||||
public abstract void convertOlderToManual(int profileId, Date date);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0")
|
||||
public abstract void removeNotManual(int profileId);
|
||||
|
||||
@RawQuery
|
||||
abstract long removeFuture(SupportSQLiteQuery query);
|
||||
@Transaction
|
||||
public void removeFuture(int profileId, Date todayDate, String filter) {
|
||||
removeFuture(new SimpleSQLiteQuery("DELETE FROM events WHERE profileId = " + profileId
|
||||
+ " AND eventAddedManually = 0 AND eventDate >= '" + todayDate.getStringY_m_d() + "'" +
|
||||
" AND " + filter));
|
||||
}
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType = :type")
|
||||
public abstract void removeFutureWithType(int profileId, Date todayDate, long type);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType != :exceptType")
|
||||
public abstract void removeFutureExceptType(int profileId, Date todayDate, long exceptType);
|
||||
|
||||
@Transaction
|
||||
public void removeFutureExceptTypes(int profileId, Date todayDate, List<Long> exceptTypes) {
|
||||
removeFuture(profileId, todayDate, "eventType NOT IN " + exceptTypes.toString().replace('[', '(').replace(']', ')'));
|
||||
}
|
||||
|
||||
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = "+TYPE_EVENT+" OR thingType = "+TYPE_LESSON_CHANGE+" OR thingType = "+TYPE_HOMEWORK+") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
|
||||
public abstract void setSeenByDate(int profileId, Date date, boolean seen);
|
||||
|
||||
@Query("UPDATE events SET eventBlacklisted = :blacklisted WHERE profileId = :profileId AND eventId = :eventId")
|
||||
public abstract void setBlacklisted(int profileId, long eventId, boolean blacklisted);
|
||||
}
|
||||
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.room.Transaction
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
@Dao
|
||||
abstract class EventDao : BaseDao<Event, EventFull> {
|
||||
companion object {
|
||||
private const val QUERY = """
|
||||
SELECT
|
||||
*,
|
||||
teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName,
|
||||
eventTypes.eventTypeName AS typeName,
|
||||
eventTypes.eventTypeColor AS typeColor
|
||||
FROM events
|
||||
LEFT JOIN subjects USING(profileId, subjectId)
|
||||
LEFT JOIN teachers USING(profileId, teacherId)
|
||||
LEFT JOIN teams USING(profileId, teamId)
|
||||
LEFT JOIN eventTypes USING(profileId, eventType)
|
||||
LEFT JOIN metadata ON eventId = thingId AND (thingType = ${Metadata.TYPE_EVENT} OR thingType = ${Metadata.TYPE_HOMEWORK}) AND metadata.profileId = events.profileId
|
||||
"""
|
||||
|
||||
private const val ORDER_BY = """GROUP BY eventId ORDER BY eventDate, eventTime, addedDate ASC"""
|
||||
private const val NOT_BLACKLISTED = """events.eventBlacklisted = 0"""
|
||||
}
|
||||
|
||||
//abstract fun queryRaw(query: SupportSQLiteQuery)
|
||||
//private fun queryRaw(query: String) = queryRaw(SimpleSQLiteQuery(query))
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId")
|
||||
abstract override fun clear(profileId: Int)
|
||||
|
||||
/*fun update(event: Event) =
|
||||
queryRaw("""UPDATE events SET
|
||||
eventDate = '${event.date.stringY_m_d}',
|
||||
eventTime = ${event.time?.stringValue},
|
||||
eventTopic = '${event.topic}'""")*/
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventId = :id")
|
||||
abstract fun remove(profileId: Int, id: Long)
|
||||
|
||||
@Query("DELETE FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND thingId = :thingId")
|
||||
abstract fun removeMetadata(profileId: Int, thingType: Int, thingId: Long)
|
||||
|
||||
@Transaction
|
||||
open fun remove(profileId: Int, type: Long, id: Long) {
|
||||
remove(profileId, id)
|
||||
removeMetadata(profileId, if (type == Event.TYPE_HOMEWORK) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT, id)
|
||||
}
|
||||
|
||||
@Transaction
|
||||
open fun remove(event: Event) {
|
||||
remove(event.profileId, event.type, event.id)
|
||||
}
|
||||
|
||||
@Transaction
|
||||
open fun remove(profileId: Int, event: Event) {
|
||||
remove(profileId, event.type, event.id)
|
||||
}
|
||||
|
||||
@RawQuery(observedEntities = [Event::class])
|
||||
abstract override fun getRaw(query: SupportSQLiteQuery): LiveData<List<EventFull>>
|
||||
|
||||
|
||||
|
||||
fun getAll(profileId: Int) =
|
||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId $ORDER_BY")
|
||||
fun getAllByType(profileId: Int, type: Long, filter: String = "1") =
|
||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventType = $type AND $filter $ORDER_BY")
|
||||
fun getAllByDate(profileId: Int, date: Date) =
|
||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
|
||||
fun getAllByDateTime(profileId: Int, date: Date, time: Time) =
|
||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' AND eventTime = ${time.stringValue}")
|
||||
fun getAllNearest(profileId: Int, today: Date, limit: Int) =
|
||||
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate >= '${today.stringY_m_d}' $ORDER_BY LIMIT $limit")
|
||||
|
||||
|
||||
|
||||
fun getAllNow(profileId: Int) =
|
||||
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId $ORDER_BY")
|
||||
fun getNotNotifiedNow() =
|
||||
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND notified = 0 $ORDER_BY")
|
||||
fun getNotNotifiedNow(profileId: Int) =
|
||||
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND notified = 0 $ORDER_BY")
|
||||
fun getAllByDateNow(profileId: Int, date: Date) =
|
||||
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
|
||||
|
||||
|
||||
|
||||
fun getByIdNow(profileId: Int, id: Long) =
|
||||
getOneNow("$QUERY WHERE events.profileId = $profileId AND eventId = $id")
|
||||
|
||||
|
||||
|
||||
@Query("SELECT eventId FROM events WHERE profileId = :profileId AND eventBlacklisted = 1")
|
||||
abstract fun getBlacklistedIds(profileId: Int): List<Long>
|
||||
|
||||
@get:Query("SELECT eventId FROM events WHERE eventBlacklisted = 1")
|
||||
abstract val blacklistedIds: List<Long>
|
||||
|
||||
@Query("UPDATE events SET eventAddedManually = 1 WHERE profileId = :profileId AND eventDate < :date")
|
||||
abstract fun convertOlderToManual(profileId: Int, date: Date?)
|
||||
|
||||
@Query("DELETE FROM events WHERE teamId = :teamId AND eventId = :id")
|
||||
abstract fun removeByTeamId(teamId: Long, id: Long)
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0")
|
||||
abstract fun removeNotManual(profileId: Int)
|
||||
|
||||
@RawQuery
|
||||
abstract fun removeFuture(query: SupportSQLiteQuery?): Long
|
||||
|
||||
@Transaction
|
||||
open fun removeFuture(profileId: Int, todayDate: Date, filter: String) {
|
||||
removeFuture(SimpleSQLiteQuery("DELETE FROM events WHERE profileId = " + profileId
|
||||
+ " AND eventAddedManually = 0 AND eventDate >= '" + todayDate.stringY_m_d + "'" +
|
||||
" AND " + filter))
|
||||
}
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType = :type")
|
||||
abstract fun removeFutureWithType(profileId: Int, todayDate: Date, type: Long)
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType != :exceptType")
|
||||
abstract fun removeFutureExceptType(profileId: Int, todayDate: Date, exceptType: Long)
|
||||
|
||||
@Transaction
|
||||
open fun removeFutureExceptTypes(profileId: Int, todayDate: Date, exceptTypes: List<Long>) {
|
||||
removeFuture(profileId, todayDate, "eventType NOT IN " + exceptTypes.toString().replace('[', '(').replace(']', ')'))
|
||||
}
|
||||
|
||||
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = " + Metadata.TYPE_EVENT + " OR thingType = " + Metadata.TYPE_LESSON_CHANGE + " OR thingType = " + Metadata.TYPE_HOMEWORK + ") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
|
||||
abstract fun setSeenByDate(profileId: Int, date: Date, seen: Boolean)
|
||||
|
||||
@Query("UPDATE events SET eventBlacklisted = :blacklisted WHERE profileId = :profileId AND eventId = :eventId")
|
||||
abstract fun setBlacklisted(profileId: Int, eventId: Long, blacklisted: Boolean)
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.dao;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType;
|
||||
|
||||
@Dao
|
||||
public interface EventTypeDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void add(EventType gradeCategory);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void addAll(List<EventType> gradeCategoryList);
|
||||
|
||||
@Query("DELETE FROM eventTypes WHERE profileId = :profileId")
|
||||
void clear(int profileId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
|
||||
EventType getByIdNow(int profileId, long typeId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
|
||||
LiveData<List<EventType>> getAll(int profileId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
|
||||
List<EventType> getAllNow(int profileId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes")
|
||||
List<EventType> getAllNow();
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.dao
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_CLASS_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ESSAY
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXAM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXCURSION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PROJECT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PT_MEETING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_READING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_SHORT_QUIZ
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_CLASS_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ESSAY
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXAM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXCURSION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PROJECT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PT_MEETING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_READING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_SHORT_QUIZ
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType
|
||||
|
||||
@Dao
|
||||
abstract class EventTypeDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract fun add(eventType: EventType)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract fun addAll(eventTypeList: List<EventType>)
|
||||
|
||||
@Query("DELETE FROM eventTypes WHERE profileId = :profileId")
|
||||
abstract fun clear(profileId: Int)
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
|
||||
abstract fun getByIdNow(profileId: Int, typeId: Long): EventType?
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
|
||||
abstract fun getAll(profileId: Int): LiveData<List<EventType>>
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
|
||||
abstract fun getAllNow(profileId: Int): List<EventType>
|
||||
|
||||
@get:Query("SELECT * FROM eventTypes")
|
||||
abstract val allNow: List<EventType>
|
||||
|
||||
fun addDefaultTypes(context: Context, profileId: Int): List<EventType> {
|
||||
val typeList = listOf(
|
||||
EventType(profileId, TYPE_HOMEWORK, context.getString(R.string.event_type_homework), COLOR_HOMEWORK),
|
||||
EventType(profileId, TYPE_DEFAULT, context.getString(R.string.event_other), COLOR_DEFAULT),
|
||||
EventType(profileId, TYPE_EXAM, context.getString(R.string.event_exam), COLOR_EXAM),
|
||||
EventType(profileId, TYPE_SHORT_QUIZ, context.getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ),
|
||||
EventType(profileId, TYPE_ESSAY, context.getString(R.string.event_essay), COLOR_ESSAY),
|
||||
EventType(profileId, TYPE_PROJECT, context.getString(R.string.event_project), COLOR_PROJECT),
|
||||
EventType(profileId, TYPE_PT_MEETING, context.getString(R.string.event_pt_meeting), COLOR_PT_MEETING),
|
||||
EventType(profileId, TYPE_EXCURSION, context.getString(R.string.event_excursion), COLOR_EXCURSION),
|
||||
EventType(profileId, TYPE_READING, context.getString(R.string.event_reading), COLOR_READING),
|
||||
EventType(profileId, TYPE_CLASS_EVENT, context.getString(R.string.event_class_event), COLOR_CLASS_EVENT),
|
||||
EventType(profileId, TYPE_INFORMATION, context.getString(R.string.event_information), COLOR_INFORMATION)
|
||||
)
|
||||
addAll(typeList)
|
||||
return typeList
|
||||
}
|
||||
}
|
@ -78,8 +78,8 @@ public abstract class MetadataDao {
|
||||
}
|
||||
}
|
||||
if (o instanceof Event) {
|
||||
if (add(new Metadata(profileId, ((Event) o).type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).id, seen, false, 0)) == -1) {
|
||||
updateSeen(profileId, ((Event) o).type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).id, seen);
|
||||
if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen, false, 0)) == -1) {
|
||||
updateSeen(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen);
|
||||
}
|
||||
}
|
||||
if (o instanceof LessonFull) {
|
||||
@ -117,8 +117,8 @@ public abstract class MetadataDao {
|
||||
}
|
||||
}
|
||||
if (o instanceof Event) {
|
||||
if (add(new Metadata(profileId, ((Event) o).type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).id, false, notified, 0)) == -1) {
|
||||
updateNotified(profileId, ((Event) o).type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).id, notified);
|
||||
if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), false, notified, 0)) == -1) {
|
||||
updateNotified(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), notified);
|
||||
}
|
||||
}
|
||||
if (o instanceof LessonFull) {
|
||||
@ -141,9 +141,9 @@ public abstract class MetadataDao {
|
||||
@Transaction
|
||||
public void setBoth(int profileId, Event o, boolean seen, boolean notified, long addedDate) {
|
||||
if (o != null) {
|
||||
if (add(new Metadata(profileId, o.type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.id, seen, notified, addedDate)) == -1) {
|
||||
updateSeen(profileId, o.type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.id, seen);
|
||||
updateNotified(profileId, o.type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.id, notified);
|
||||
if (add(new Metadata(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen, notified, addedDate)) == -1) {
|
||||
updateSeen(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen);
|
||||
updateNotified(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), notified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ interface ProfileDao {
|
||||
fun getIdsByLoginStoreIdNow(loginStoreId: Int): List<Int>
|
||||
|
||||
@get:Query("SELECT * FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
|
||||
val profilesForSyncNow: List<Profile>
|
||||
val profilesForFirebaseNow: List<Profile>
|
||||
|
||||
@get:Query("SELECT profileId FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
|
||||
val idsForSyncNow: List<Int>
|
||||
|
@ -49,6 +49,17 @@ interface TeacherAbsenceDao {
|
||||
"AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo")
|
||||
fun getAllByDateNow(profileId: Int, date: Date): List<TeacherAbsenceFull>
|
||||
|
||||
@Query("""
|
||||
SELECT *,
|
||||
teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName
|
||||
FROM teacherAbsence
|
||||
LEFT JOIN teachers USING (profileId, teacherId)
|
||||
LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = ${Metadata.TYPE_TEACHER_ABSENCE}
|
||||
AND teachers.profileId = metadata.profileId WHERE metadata.notified = 0
|
||||
ORDER BY addedDate DESC
|
||||
""")
|
||||
fun getNotNotifiedNow(): List<TeacherAbsenceFull>
|
||||
|
||||
@Query("DELETE FROM teacherAbsence WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.entity;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
|
||||
@Entity(tableName = "events",
|
||||
primaryKeys = {"profileId", "eventId"},
|
||||
indices = {@Index(value = {"profileId", "eventDate", "eventStartTime"}), @Index(value = {"profileId", "eventType"})})
|
||||
public class Event {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "eventId")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "eventDate")
|
||||
public Date eventDate;
|
||||
@ColumnInfo(name = "eventStartTime")
|
||||
@Nullable
|
||||
public Time startTime; // null for allDay
|
||||
@ColumnInfo(name = "eventTopic")
|
||||
public String topic;
|
||||
@ColumnInfo(name = "eventColor")
|
||||
public int color = -1;
|
||||
public static final long TYPE_UNDEFINED = -2;
|
||||
public static final long TYPE_HOMEWORK = -1;
|
||||
public static final long TYPE_DEFAULT = 0;
|
||||
public static final long TYPE_EXAM = 1;
|
||||
public static final long TYPE_SHORT_QUIZ = 2;
|
||||
public static final long TYPE_ESSAY = 3;
|
||||
public static final long TYPE_PROJECT = 4;
|
||||
public static final long TYPE_PT_MEETING = 5;
|
||||
public static final long TYPE_EXCURSION = 6;
|
||||
public static final long TYPE_READING = 7;
|
||||
public static final long TYPE_CLASS_EVENT = 8;
|
||||
public static final long TYPE_INFORMATION = 9;
|
||||
public static final long TYPE_TEACHER_ABSENCE = 10;
|
||||
public static final int COLOR_HOMEWORK = 0xff795548;
|
||||
public static final int COLOR_DEFAULT = 0xffffc107;
|
||||
public static final int COLOR_EXAM = 0xfff44336;
|
||||
public static final int COLOR_SHORT_QUIZ = 0xff76ff03;
|
||||
public static final int COLOR_ESSAY = 0xFF4050B5;
|
||||
public static final int COLOR_PROJECT = 0xFF673AB7;
|
||||
public static final int COLOR_PT_MEETING = 0xff90caf9;
|
||||
public static final int COLOR_EXCURSION = 0xFF4CAF50;
|
||||
public static final int COLOR_READING = 0xFFFFEB3B;
|
||||
public static final int COLOR_CLASS_EVENT = 0xff388e3c;
|
||||
public static final int COLOR_INFORMATION = 0xff039be5;
|
||||
public static final int COLOR_TEACHER_ABSENCE = 0xff039be5;
|
||||
@ColumnInfo(name = "eventType")
|
||||
public long type = TYPE_DEFAULT;
|
||||
@ColumnInfo(name = "eventAddedManually")
|
||||
public boolean addedManually;
|
||||
@ColumnInfo(name = "eventSharedBy")
|
||||
public String sharedBy = null;
|
||||
@ColumnInfo(name = "eventSharedByName")
|
||||
public String sharedByName = null;
|
||||
@ColumnInfo(name = "eventBlacklisted")
|
||||
public boolean blacklisted = false;
|
||||
|
||||
|
||||
public long teacherId;
|
||||
public long subjectId;
|
||||
public long teamId;
|
||||
|
||||
@Ignore
|
||||
public Event() {}
|
||||
|
||||
public Event(int profileId, long id, Date eventDate, @Nullable Time startTime, String topic, int color, long type, boolean addedManually, long teacherId, long subjectId, long teamId)
|
||||
{
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.eventDate = eventDate;
|
||||
this.startTime = startTime;
|
||||
this.topic = topic;
|
||||
this.color = color;
|
||||
this.type = type;
|
||||
this.addedManually = addedManually;
|
||||
this.teacherId = teacherId;
|
||||
this.subjectId = subjectId;
|
||||
this.teamId = teamId;
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public EventFull withMetadata(Metadata metadata) {
|
||||
return new EventFull(this, metadata);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public Calendar getStartTimeCalendar() {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.set(
|
||||
eventDate.year,
|
||||
eventDate.month - 1,
|
||||
eventDate.day,
|
||||
(startTime == null) ? 0 : startTime.hour,
|
||||
(startTime == null) ? 0 : startTime.minute,
|
||||
(startTime == null) ? 0 : startTime.second
|
||||
);
|
||||
return c;
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public Calendar getEndTimeCalendar() {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTimeInMillis(getStartTimeCalendar().getTimeInMillis() + (45 * 60 * 1000));
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event clone() {
|
||||
Event event = new Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate.clone(),
|
||||
startTime == null ? null : startTime.clone(),
|
||||
topic,
|
||||
color,
|
||||
type,
|
||||
addedManually,
|
||||
subjectId,
|
||||
teacherId,
|
||||
teamId
|
||||
);
|
||||
event.sharedBy = sharedBy;
|
||||
event.sharedByName = sharedByName;
|
||||
event.blacklisted = blacklisted;
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Event{" +
|
||||
"profileId=" + profileId +
|
||||
", id=" + id +
|
||||
", eventDate=" + eventDate +
|
||||
", startTime=" + startTime +
|
||||
", topic='" + topic + '\'' +
|
||||
", color=" + color +
|
||||
", type=" + type +
|
||||
", addedManually=" + addedManually +
|
||||
", sharedBy='" + sharedBy + '\'' +
|
||||
", sharedByName='" + sharedByName + '\'' +
|
||||
", teacherId=" + teacherId +
|
||||
", subjectId=" + subjectId +
|
||||
", teamId=" + teamId +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.Index
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import pl.szczodrzynski.edziennik.MINUTE
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import java.util.*
|
||||
|
||||
@Entity(tableName = "events",
|
||||
primaryKeys = ["profileId", "eventId"],
|
||||
indices = [
|
||||
Index(value = ["profileId", "eventDate", "eventTime"]),
|
||||
Index(value = ["profileId", "eventType"])
|
||||
])
|
||||
open class Event(
|
||||
/* This needs to be mutable: see SzkolnyApi.getEvents() */
|
||||
var profileId: Int,
|
||||
@ColumnInfo(name = "eventId")
|
||||
var id: Long,
|
||||
@ColumnInfo(name = "eventDate")
|
||||
@SerializedName("eventDate")
|
||||
var date: Date,
|
||||
@ColumnInfo(name = "eventTime")
|
||||
@SerializedName("startTime")
|
||||
var time: Time?,
|
||||
|
||||
@ColumnInfo(name = "eventTopic")
|
||||
var topic: String,
|
||||
@ColumnInfo(name = "eventColor")
|
||||
var color: Int?,
|
||||
@ColumnInfo(name = "eventType")
|
||||
var type: Long,
|
||||
|
||||
var teacherId: Long,
|
||||
var subjectId: Long,
|
||||
var teamId: Long
|
||||
) {
|
||||
companion object {
|
||||
const val TYPE_UNDEFINED = -2L
|
||||
const val TYPE_HOMEWORK = -1L
|
||||
const val TYPE_DEFAULT = 0L
|
||||
const val TYPE_EXAM = 1L
|
||||
const val TYPE_SHORT_QUIZ = 2L
|
||||
const val TYPE_ESSAY = 3L
|
||||
const val TYPE_PROJECT = 4L
|
||||
const val TYPE_PT_MEETING = 5L
|
||||
const val TYPE_EXCURSION = 6L
|
||||
const val TYPE_READING = 7L
|
||||
const val TYPE_CLASS_EVENT = 8L
|
||||
const val TYPE_INFORMATION = 9L
|
||||
const val TYPE_TEACHER_ABSENCE = 10L
|
||||
const val COLOR_HOMEWORK = 0xff795548.toInt()
|
||||
const val COLOR_DEFAULT = 0xffffc107.toInt()
|
||||
const val COLOR_EXAM = 0xfff44336.toInt()
|
||||
const val COLOR_SHORT_QUIZ = 0xff76ff03.toInt()
|
||||
const val COLOR_ESSAY = 0xFF4050B5.toInt()
|
||||
const val COLOR_PROJECT = 0xFF673AB7.toInt()
|
||||
const val COLOR_PT_MEETING = 0xff90caf9.toInt()
|
||||
const val COLOR_EXCURSION = 0xFF4CAF50.toInt()
|
||||
const val COLOR_READING = 0xFFFFEB3B.toInt()
|
||||
const val COLOR_CLASS_EVENT = 0xff388e3c.toInt()
|
||||
const val COLOR_INFORMATION = 0xff039be5.toInt()
|
||||
const val COLOR_TEACHER_ABSENCE = 0xff039be5.toInt()
|
||||
}
|
||||
|
||||
@ColumnInfo(name = "eventAddedManually")
|
||||
var addedManually: Boolean = false
|
||||
@ColumnInfo(name = "eventSharedBy")
|
||||
var sharedBy: String? = null
|
||||
@ColumnInfo(name = "eventSharedByName")
|
||||
var sharedByName: String? = null
|
||||
@ColumnInfo(name = "eventBlacklisted")
|
||||
var blacklisted: Boolean = false
|
||||
|
||||
var homeworkBody: String? = null
|
||||
var attachmentIds: List<Long>? = null
|
||||
var attachmentNames: List<String>? = null
|
||||
|
||||
@Ignore
|
||||
var showAsUnseen = false
|
||||
|
||||
val startTimeCalendar: Calendar
|
||||
get() = Calendar.getInstance().also { it.set(
|
||||
date.year,
|
||||
date.month - 1,
|
||||
date.day,
|
||||
time?.hour ?: 0,
|
||||
time?.minute ?: 0,
|
||||
time?.second ?: 0
|
||||
) }
|
||||
|
||||
val endTimeCalendar: Calendar
|
||||
get() = startTimeCalendar.also {
|
||||
it.timeInMillis += 45 * MINUTE * 1000
|
||||
}
|
||||
|
||||
@Ignore
|
||||
fun withMetadata(metadata: Metadata) = EventFull(this, metadata)
|
||||
}
|
@ -52,6 +52,7 @@ data class Notification(
|
||||
const val TYPE_NEW_ANNOUNCEMENT = 15
|
||||
const val TYPE_FEEDBACK_MESSAGE = 16
|
||||
const val TYPE_AUTO_ARCHIVING = 17
|
||||
const val TYPE_TEACHER_ABSENCE = 19
|
||||
|
||||
fun buildId(profileId: Int, type: Int, itemId: Long): Long {
|
||||
return 1000000000000 + profileId*10000000000 + type*100000000 + itemId;
|
||||
|
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.full;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
|
||||
|
||||
public class EventFull extends Event {
|
||||
public String typeName = "";
|
||||
public int typeColor = -1;
|
||||
|
||||
public String teacherFullName = "";
|
||||
|
||||
public String subjectLongName = "";
|
||||
public String subjectShortName = "";
|
||||
|
||||
public String teamName = "";
|
||||
public String teamCode = null;
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;
|
||||
|
||||
public EventFull() {}
|
||||
|
||||
public EventFull(Event event) {
|
||||
super(
|
||||
event.profileId,
|
||||
event.id,
|
||||
event.eventDate.clone(),
|
||||
event.startTime == null ? null : event.startTime.clone(),
|
||||
event.topic,
|
||||
event.color,
|
||||
event.type,
|
||||
event.addedManually,
|
||||
event.teacherId,
|
||||
event.subjectId,
|
||||
event.teamId
|
||||
);
|
||||
|
||||
this.sharedBy = event.sharedBy;
|
||||
this.sharedByName = event.sharedByName;
|
||||
this.blacklisted = event.blacklisted;
|
||||
}
|
||||
public EventFull(EventFull event) {
|
||||
super(
|
||||
event.profileId,
|
||||
event.id,
|
||||
event.eventDate.clone(),
|
||||
event.startTime == null ? null : event.startTime.clone(),
|
||||
event.topic,
|
||||
event.color,
|
||||
event.type,
|
||||
event.addedManually,
|
||||
event.teacherId,
|
||||
event.subjectId,
|
||||
event.teamId
|
||||
);
|
||||
|
||||
this.sharedBy = event.sharedBy;
|
||||
this.sharedByName = event.sharedByName;
|
||||
this.blacklisted = event.blacklisted;
|
||||
this.typeName = event.typeName;
|
||||
this.typeColor = event.typeColor;
|
||||
this.teacherFullName = event.teacherFullName;
|
||||
this.subjectLongName = event.subjectLongName;
|
||||
this.subjectShortName = event.subjectShortName;
|
||||
this.teamName = event.teamName;
|
||||
this.teamCode = event.teamCode;
|
||||
this.seen = event.seen;
|
||||
this.notified = event.notified;
|
||||
this.addedDate = event.addedDate;
|
||||
}
|
||||
|
||||
public EventFull(Event event, Metadata metadata) {
|
||||
this(event);
|
||||
|
||||
this.seen = metadata.seen;
|
||||
this.notified = metadata.notified;
|
||||
this.addedDate = metadata.addedDate;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return color == -1 ? typeColor : color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EventFull{" +
|
||||
"profileId=" + profileId +
|
||||
", id=" + id +
|
||||
", eventDate=" + eventDate +
|
||||
", startTime=" + startTime +
|
||||
", topic='" + topic + '\'' +
|
||||
", color=" + color +
|
||||
", type=" + type +
|
||||
", addedManually=" + addedManually +
|
||||
", sharedBy='" + sharedBy + '\'' +
|
||||
", sharedByName='" + sharedByName + '\'' +
|
||||
", blacklisted=" + blacklisted +
|
||||
", teacherId=" + teacherId +
|
||||
", subjectId=" + subjectId +
|
||||
", teamId=" + teamId +
|
||||
", typeName='" + typeName + '\'' +
|
||||
", teacherFullName='" + teacherFullName + '\'' +
|
||||
", subjectLongName='" + subjectLongName + '\'' +
|
||||
", subjectShortName='" + subjectShortName + '\'' +
|
||||
", teamName='" + teamName + '\'' +
|
||||
", seen=" + seen +
|
||||
", notified=" + notified +
|
||||
", addedDate=" + addedDate +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.full
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class EventFull(
|
||||
profileId: Int, id: Long, date: Date, time: Time?,
|
||||
topic: String, color: Int?, type: Long,
|
||||
teacherId: Long, subjectId: Long, teamId: Long
|
||||
) : Event(
|
||||
profileId, id, date, time,
|
||||
topic, color, type,
|
||||
teacherId, subjectId, teamId
|
||||
) {
|
||||
constructor(event: Event, metadata: Metadata? = null) : this(
|
||||
event.profileId, event.id, event.date, event.time,
|
||||
event.topic, event.color, event.type,
|
||||
event.teacherId, event.subjectId, event.teamId) {
|
||||
event.let {
|
||||
addedManually = it.addedManually
|
||||
sharedBy = it.sharedBy
|
||||
sharedByName = it.sharedByName
|
||||
blacklisted = it.blacklisted
|
||||
homeworkBody = it.homeworkBody
|
||||
attachmentIds = it.attachmentIds
|
||||
attachmentNames = it.attachmentNames
|
||||
}
|
||||
metadata?.let {
|
||||
seen = it.seen
|
||||
notified = it.notified
|
||||
addedDate = it.addedDate
|
||||
}
|
||||
}
|
||||
|
||||
var typeName: String? = null
|
||||
var typeColor: Int? = null
|
||||
|
||||
var teacherName: String? = null
|
||||
var subjectLongName: String? = null
|
||||
var subjectShortName: String? = null
|
||||
var teamName: String? = null
|
||||
var teamCode: String? = null
|
||||
// metadata
|
||||
var seen = false
|
||||
var notified = false
|
||||
var addedDate: Long = 0
|
||||
|
||||
val eventColor
|
||||
get() = color ?: typeColor ?: 0xff2196f3.toInt()
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-27.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.migration
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration80 : Migration(79, 80) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// The Homework Update
|
||||
database.execSQL("ALTER TABLE events RENAME TO _events;")
|
||||
database.execSQL("""CREATE TABLE events (
|
||||
profileId INTEGER NOT NULL,
|
||||
eventId INTEGER NOT NULL,
|
||||
eventDate TEXT NOT NULL,
|
||||
eventTime TEXT,
|
||||
eventTopic TEXT NOT NULL,
|
||||
eventColor INTEGER,
|
||||
eventType INTEGER NOT NULL,
|
||||
teacherId INTEGER NOT NULL,
|
||||
subjectId INTEGER NOT NULL,
|
||||
teamId INTEGER NOT NULL,
|
||||
eventAddedManually INTEGER NOT NULL DEFAULT 0,
|
||||
eventSharedBy TEXT DEFAULT NULL,
|
||||
eventSharedByName TEXT DEFAULT NULL,
|
||||
eventBlacklisted INTEGER NOT NULL DEFAULT 0,
|
||||
homeworkBody TEXT DEFAULT NULL,
|
||||
attachmentIds TEXT DEFAULT NULL,
|
||||
attachmentNames TEXT DEFAULT NULL,
|
||||
PRIMARY KEY(profileId, eventId)
|
||||
)""")
|
||||
database.execSQL("DROP INDEX IF EXISTS index_events_profileId_eventDate_eventStartTime")
|
||||
database.execSQL("DROP INDEX IF EXISTS index_events_profileId_eventType")
|
||||
database.execSQL("CREATE INDEX index_events_profileId_eventDate_eventTime ON events (profileId, eventDate, eventTime)")
|
||||
database.execSQL("CREATE INDEX index_events_profileId_eventType ON events (profileId, eventType)")
|
||||
database.execSQL("""
|
||||
INSERT INTO events (profileId, eventId, eventDate, eventTime, eventTopic, eventColor, eventType, teacherId, subjectId, teamId, eventAddedManually, eventSharedBy, eventSharedByName, eventBlacklisted)
|
||||
SELECT profileId, eventId, eventDate, eventStartTime, eventTopic,
|
||||
CASE eventColor WHEN -1 THEN NULL ELSE eventColor END,
|
||||
eventType, teacherId, subjectId, teamId,
|
||||
eventAddedManually, eventSharedBy, eventSharedByName, eventBlacklisted
|
||||
FROM _events
|
||||
""")
|
||||
database.execSQL("DROP TABLE _events")
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.migration
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
|
||||
class Migration81 : Migration(80, 81) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE metadata SET seen = 1, notified = 1 WHERE thingType = ${Metadata.TYPE_TEACHER_ABSENCE}")
|
||||
}
|
||||
}
|
@ -9,8 +9,8 @@ import android.app.PendingIntent
|
||||
import android.app.PendingIntent.CanceledException
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.google.firebase.iid.zzaq
|
||||
import com.google.firebase.iid.zzv
|
||||
import com.google.firebase.iid.zzad
|
||||
import com.google.firebase.iid.zzaz
|
||||
import com.google.firebase.messaging.zzc
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
@ -36,7 +36,7 @@ open class FirebaseService : zzc() {
|
||||
|
||||
// apparently this gets the correct intent from some
|
||||
// kind of queue inside Firebase's InstanceID Receiver
|
||||
final override fun zza(intent: Intent?) = zzaq.zza()?.zzb()
|
||||
final override fun zza(intent: Intent?) = zzaz.zza()?.zzb()
|
||||
final override fun zzb(intent: Intent?): Boolean {
|
||||
val action = intent?.action
|
||||
if (action == "com.google.firebase.messaging.NOTIFICATION_OPEN") {
|
||||
@ -80,7 +80,7 @@ open class FirebaseService : zzc() {
|
||||
val ackBundle = Bundle(
|
||||
"google.message_id" to messageId
|
||||
)
|
||||
zzv.zza(this).zza(2, ackBundle)
|
||||
zzad.zza(this).zza(2, ackBundle)
|
||||
}
|
||||
// check for duplicate message
|
||||
// and add it to queue
|
||||
|
@ -36,7 +36,7 @@ class MyFirebaseService : FirebaseService(), CoroutineScope {
|
||||
putString(System.currentTimeMillis().toString(), message.toString())
|
||||
apply()
|
||||
}
|
||||
val profiles = app.db.profileDao().profilesForSyncNow
|
||||
val profiles = app.db.profileDao().profilesForFirebaseNow
|
||||
when (message.from) {
|
||||
"640759989760" -> SzkolnyAppFirebase(app, profiles, message)
|
||||
"747285019373" -> SzkolnyMobidziennikFirebase(app, profiles, message)
|
||||
|
@ -101,27 +101,27 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
val notificationList = mutableListOf<Notification>()
|
||||
|
||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId }
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||
if (profile.registration != Profile.REGISTRATION_ENABLED)
|
||||
return@forEach
|
||||
val event = Event(
|
||||
team.profileId,
|
||||
json.getLong("id") ?: return,
|
||||
json.getInt("eventDate")?.let { Date.fromValue(it) } ?: return,
|
||||
json.getInt("startTime")?.let { Time.fromValue(it) },
|
||||
json.getString("topic") ?: "",
|
||||
json.getInt("color") ?: -1,
|
||||
json.getLong("type") ?: 0,
|
||||
true,
|
||||
json.getLong("teacherId") ?: -1,
|
||||
json.getLong("subjectId") ?: -1,
|
||||
team.id
|
||||
profileId = team.profileId,
|
||||
id = json.getLong("id") ?: return,
|
||||
date = json.getInt("eventDate")?.let { Date.fromValue(it) } ?: return,
|
||||
time = json.getInt("startTime")?.let { Time.fromValue(it) },
|
||||
topic = json.getString("topic") ?: "",
|
||||
color = json.getInt("color"),
|
||||
type = json.getLong("type") ?: 0,
|
||||
teacherId = json.getLong("teacherId") ?: -1,
|
||||
subjectId = json.getLong("subjectId") ?: -1,
|
||||
teamId = team.id
|
||||
)
|
||||
|
||||
// TODO? i guess - this comment is here for like a year
|
||||
//val oldEvent: Event? = app.db.eventDao().getByIdNow(profile?.id ?: -1, event.id)
|
||||
if (event.color == -1)
|
||||
event.color = null
|
||||
|
||||
event.sharedBy = json.getString("sharedBy")
|
||||
event.sharedByName = json.getString("sharedByName")
|
||||
if (profile?.userCode == event.sharedBy) event.sharedBy = "self"
|
||||
if (profile.userCode == event.sharedBy) event.sharedBy = "self"
|
||||
|
||||
val metadata = Metadata(
|
||||
event.profileId,
|
||||
@ -132,18 +132,6 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
json.getLong("addedDate") ?: System.currentTimeMillis()
|
||||
)
|
||||
|
||||
//val eventType = eventTypes.firstOrNull { it.profileId == profile?.id && it.id == event.type }
|
||||
|
||||
/*val text = app.getString(
|
||||
if (oldEvent == null)
|
||||
R.string.notification_shared_event_format
|
||||
else
|
||||
R.string.notification_shared_event_modified_format,
|
||||
event.sharedByName,
|
||||
eventType?.name ?: "wydarzenie",
|
||||
event.eventDate.formattedString,
|
||||
event.topic
|
||||
)*/
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
|
||||
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter
|
||||
|
||||
@ -153,11 +141,11 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
title = app.getNotificationTitle(type),
|
||||
text = message,
|
||||
type = type,
|
||||
profileId = profile?.id,
|
||||
profileName = profile?.name,
|
||||
profileId = profile.id,
|
||||
profileName = profile.name,
|
||||
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
||||
addedDate = metadata.addedDate
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong())
|
||||
notificationList += notification
|
||||
}
|
||||
|
||||
@ -177,18 +165,20 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
val notificationList = mutableListOf<Notification>()
|
||||
|
||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId }
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||
if (profile.registration != Profile.REGISTRATION_ENABLED)
|
||||
return@forEach
|
||||
val notificationFilter = app.config.getFor(team.profileId).sync.notificationFilter
|
||||
|
||||
if (!notificationFilter.contains(Notification.TYPE_REMOVED_SHARED_EVENT)) {
|
||||
val notification = Notification(
|
||||
id = Notification.buildId(profile?.id
|
||||
id = Notification.buildId(profile.id
|
||||
?: 0, Notification.TYPE_REMOVED_SHARED_EVENT, eventId),
|
||||
title = app.getNotificationTitle(Notification.TYPE_REMOVED_SHARED_EVENT),
|
||||
text = message,
|
||||
type = Notification.TYPE_REMOVED_SHARED_EVENT,
|
||||
profileId = profile?.id,
|
||||
profileName = profile?.name,
|
||||
profileId = profile.id,
|
||||
profileName = profile.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_AGENDA
|
||||
)
|
||||
notificationList += notification
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-24.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.network.cookie
|
||||
|
||||
import okhttp3.Cookie
|
||||
|
||||
class DumbCookie(var cookie: Cookie) {
|
||||
|
||||
constructor(domain: String, name: String, value: String, expiresAt: Long? = null) : this(
|
||||
Cookie.Builder()
|
||||
.name(name)
|
||||
.value(value)
|
||||
.also { if (expiresAt != null) it.expiresAt(expiresAt) }
|
||||
.domain(domain)
|
||||
.build()
|
||||
)
|
||||
|
||||
init {
|
||||
cookie = Cookie.Builder()
|
||||
.name(cookie.name())
|
||||
.value(cookie.value())
|
||||
.expiresAt(cookie.expiresAt())
|
||||
.domain(cookie.domain())
|
||||
.build()
|
||||
}
|
||||
|
||||
fun domainMatches(host: String): Boolean {
|
||||
val domain = cookie.domain()
|
||||
return host == domain || host.endsWith(".$domain")
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is DumbCookie) return false
|
||||
if (this.cookie === other.cookie) return true
|
||||
|
||||
return cookie.name() == other.cookie.name()
|
||||
&& cookie.domain() == other.cookie.domain()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var hash = 17
|
||||
hash = 31 * hash + cookie.name().hashCode()
|
||||
hash = 31 * hash + cookie.domain().hashCode()
|
||||
return hash
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-24.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.network.cookie
|
||||
|
||||
import android.content.Context
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
/**
|
||||
* A simple cookie jar that does not care about the [Cookie.secure], [Cookie.hostOnly],
|
||||
* [Cookie.httpOnly] and [Cookie.path] attributes.
|
||||
*/
|
||||
class DumbCookieJar(
|
||||
/**
|
||||
* A context to create the shared prefs file.
|
||||
*/
|
||||
context: Context,
|
||||
|
||||
/**
|
||||
* Whether to persist session cookies as well, when [Cookie.persistent] is false.
|
||||
*/
|
||||
private val persistAll: Boolean = false
|
||||
) : CookieJar {
|
||||
|
||||
private val prefs = context.getSharedPreferences("cookies", Context.MODE_PRIVATE)
|
||||
private val sessionCookies = mutableSetOf<DumbCookie>()
|
||||
private val savedCookies = mutableSetOf<DumbCookie>()
|
||||
private fun save(dc: DumbCookie) {
|
||||
sessionCookies.remove(dc)
|
||||
sessionCookies.add(dc)
|
||||
if (dc.cookie.persistent() || persistAll) {
|
||||
savedCookies.remove(dc)
|
||||
savedCookies.add(dc)
|
||||
}
|
||||
}
|
||||
private fun delete(vararg toRemove: DumbCookie) {
|
||||
sessionCookies.removeAll(toRemove)
|
||||
savedCookies.removeAll(toRemove)
|
||||
}
|
||||
|
||||
override fun saveFromResponse(url: HttpUrl?, cookies: List<Cookie>) {
|
||||
for (cookie in cookies) {
|
||||
val dc = DumbCookie(cookie)
|
||||
save(dc)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||
return sessionCookies.filter {
|
||||
it.cookie.matches(url)
|
||||
}.map { it.cookie }
|
||||
}
|
||||
|
||||
fun get(domain: String, name: String): String? {
|
||||
return sessionCookies.firstOrNull {
|
||||
it.domainMatches(domain) && it.cookie.name() == name
|
||||
}?.cookie?.value()
|
||||
}
|
||||
|
||||
fun set(domain: String, name: String, value: String?, isSession: Boolean) = set(
|
||||
domain, name, value,
|
||||
if (isSession) null
|
||||
else System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000L
|
||||
)
|
||||
|
||||
/**
|
||||
* Add a cookie to the cache.
|
||||
* By default a session cookie is added. If [expiresAt] is set, the cookie is
|
||||
* additionally persisted.
|
||||
*/
|
||||
fun set(domain: String, name: String?, value: String?, expiresAt: Long? = null) {
|
||||
name ?: return
|
||||
if (value == null) {
|
||||
remove(domain, name)
|
||||
return
|
||||
}
|
||||
val dc = DumbCookie(domain, name, value, expiresAt)
|
||||
save(dc)
|
||||
}
|
||||
|
||||
fun getAll(domain: String): Map<String, String> {
|
||||
return sessionCookies.filter {
|
||||
it.domainMatches(domain)
|
||||
}.map { it.cookie.name() to it.cookie.value() }.toMap()
|
||||
}
|
||||
|
||||
fun remove(domain: String, name: String) {
|
||||
val toRemove = sessionCookies.filter {
|
||||
it.domainMatches(domain) && it.cookie.name() == name
|
||||
}
|
||||
delete(*toRemove.toTypedArray())
|
||||
}
|
||||
|
||||
fun clear(domain: String) {
|
||||
val toRemove = sessionCookies.filter {
|
||||
it.domainMatches(domain)
|
||||
}
|
||||
delete(*toRemove.toTypedArray())
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ class QrScannerDialog(
|
||||
onShowListener?.invoke(TAG)
|
||||
app = activity.applicationContext as App
|
||||
scannerView = ZXingScannerView(activity)
|
||||
scannerView.setPadding(0, 16.dp, 0, 0)
|
||||
scannerView.setPadding(0, 16.dp, 2.dp, 0)
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.qr_scanner_dialog_title)
|
||||
.setView(scannerView)
|
||||
@ -59,4 +59,4 @@ class QrScannerDialog(
|
||||
}
|
||||
scannerView.startCamera()
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@ -47,6 +48,8 @@ class EventDetailsDialog(
|
||||
SzkolnyApi(app)
|
||||
}
|
||||
|
||||
private var progressDialog: AlertDialog? = null
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
@ -64,6 +67,7 @@ class EventDetailsDialog(
|
||||
}
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
progressDialog?.dismiss()
|
||||
}
|
||||
.show()
|
||||
|
||||
@ -83,11 +87,11 @@ class EventDetailsDialog(
|
||||
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
|
||||
|
||||
try {
|
||||
b.monthName = app.resources.getStringArray(R.array.months_day_of_array)[event.eventDate.month - 1]
|
||||
b.monthName = app.resources.getStringArray(R.array.months_day_of_array)[event.date.month - 1]
|
||||
}
|
||||
catch (_: Exception) {}
|
||||
|
||||
b.typeColor.background?.setTintColor(event.getColor())
|
||||
b.typeColor.background?.setTintColor(event.eventColor)
|
||||
|
||||
b.details = mutableListOf(
|
||||
event.subjectLongName,
|
||||
@ -98,14 +102,14 @@ class EventDetailsDialog(
|
||||
when (event.sharedBy) {
|
||||
null -> when {
|
||||
event.addedManually -> R.string.event_details_added_by_self_format
|
||||
event.teacherFullName == null -> R.string.event_details_added_by_unknown_format
|
||||
event.teacherName == null -> R.string.event_details_added_by_unknown_format
|
||||
else -> R.string.event_details_added_by_format
|
||||
}
|
||||
"self" -> R.string.event_details_shared_by_self_format
|
||||
else -> R.string.event_details_shared_by_format
|
||||
},
|
||||
Date.fromMillis(event.addedDate).formattedString,
|
||||
event.sharedByName ?: event.teacherFullName ?: ""
|
||||
event.sharedByName ?: event.teacherName ?: ""
|
||||
)
|
||||
|
||||
b.editButton.visibility = if (event.addedManually) View.VISIBLE else View.GONE
|
||||
@ -121,7 +125,7 @@ class EventDetailsDialog(
|
||||
|
||||
b.goToTimetableButton.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
val dateStr = event.eventDate?.stringY_m_d ?: return@setOnClickListener
|
||||
val dateStr = event.date.stringY_m_d
|
||||
|
||||
val intent =
|
||||
if (activity is MainActivity && activity.navTargetId == MainActivity.DRAWER_ITEM_TIMETABLE)
|
||||
@ -156,6 +160,23 @@ class EventDetailsDialog(
|
||||
Toast.makeText(activity, R.string.hint_edit_event, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
|
||||
b.topic.text = event.topic
|
||||
BetterLink.attach(b.topic) {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showRemovingProgressDialog() {
|
||||
if (progressDialog != null) {
|
||||
return
|
||||
}
|
||||
|
||||
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(R.string.event_removing_text)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showRemoveEventDialog() {
|
||||
@ -186,11 +207,14 @@ class EventDetailsDialog(
|
||||
launch {
|
||||
if (eventShared && eventOwn) {
|
||||
// unshare + remove own event
|
||||
Toast.makeText(activity, R.string.event_manual_unshare_remove, Toast.LENGTH_SHORT).show()
|
||||
showRemovingProgressDialog()
|
||||
|
||||
api.runCatching(activity) {
|
||||
unshareEvent(event)
|
||||
} ?: return@launch
|
||||
} ?: run {
|
||||
progressDialog?.dismiss()
|
||||
return@launch
|
||||
}
|
||||
|
||||
finishRemoving()
|
||||
} else if (eventShared && !eventOwn) {
|
||||
@ -202,6 +226,7 @@ class EventDetailsDialog(
|
||||
Toast.makeText(activity, R.string.event_manual_remove, Toast.LENGTH_SHORT).show()
|
||||
finishRemoving()
|
||||
}
|
||||
progressDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +245,7 @@ class EventDetailsDialog(
|
||||
}
|
||||
|
||||
private fun openInCalendar() { launch {
|
||||
val title = (event.typeName ?: "") +
|
||||
val title = event.typeName ?: "" +
|
||||
(if (event.typeName.isNotNullNorBlank() && event.subjectLongName.isNotNullNorBlank()) " - " else " ") +
|
||||
(event.subjectLongName ?: "")
|
||||
|
||||
@ -229,12 +254,12 @@ class EventDetailsDialog(
|
||||
putExtra(Events.TITLE, title)
|
||||
putExtra(Events.DESCRIPTION, event.topic)
|
||||
|
||||
if (event.startTime == null) {
|
||||
if (event.time == null) {
|
||||
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, event.eventDate.inMillis)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, event.eventDate.inMillis)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, event.date.inMillis)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, event.date.inMillis)
|
||||
} else {
|
||||
val startTime = event.eventDate.combineWith(event.startTime)
|
||||
val startTime = event.date.combineWith(event.time)
|
||||
val endTime = startTime + 45 * 60 * 1000 /* 45 min */
|
||||
|
||||
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, false)
|
||||
|
@ -50,10 +50,10 @@ class EventListAdapter(
|
||||
b.topic.text = event.topic
|
||||
|
||||
b.details.text = mutableListOf<CharSequence?>(
|
||||
if (showWeekDay) Week.getFullDayName(event.eventDate.weekDay) else null,
|
||||
if (showDate) event.eventDate.getRelativeString(context, 7) ?: event.eventDate.formattedStringShort else null,
|
||||
if (showWeekDay) Week.getFullDayName(event.date.weekDay) else null,
|
||||
if (showDate) event.date.getRelativeString(context, 7) ?: event.date.formattedStringShort else null,
|
||||
event.typeName,
|
||||
if (simpleMode) null else event.startTime?.stringHM ?: app.getString(R.string.event_all_day),
|
||||
if (simpleMode) null else event.time?.stringHM ?: app.getString(R.string.event_all_day),
|
||||
if (simpleMode) null else event.subjectLongName
|
||||
).concat(bullet)
|
||||
|
||||
@ -61,18 +61,18 @@ class EventListAdapter(
|
||||
when (event.sharedBy) {
|
||||
null -> when {
|
||||
event.addedManually -> R.string.event_list_added_by_self_format
|
||||
event.teacherFullName == null -> R.string.event_list_added_by_unknown_format
|
||||
event.teacherName == null -> R.string.event_list_added_by_unknown_format
|
||||
else -> R.string.event_list_added_by_format
|
||||
}
|
||||
"self" -> R.string.event_list_shared_by_self_format
|
||||
else -> R.string.event_list_shared_by_format
|
||||
},
|
||||
Date.fromMillis(event.addedDate).formattedString,
|
||||
event.sharedByName ?: event.teacherFullName ?: "",
|
||||
event.sharedByName ?: event.teacherName ?: "",
|
||||
event.teamName?.let { bullet+it } ?: ""
|
||||
)
|
||||
|
||||
b.typeColor.background?.setTintColor(event.getColor())
|
||||
b.typeColor.background?.setTintColor(event.eventColor)
|
||||
|
||||
b.editButton.visibility = if (event.addedManually && !simpleMode) View.VISIBLE else View.GONE
|
||||
b.editButton.onClick {
|
||||
|
@ -29,9 +29,11 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationEnableDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.views.TimeDropdown.Companion.DISPLAY_LESSONS
|
||||
import pl.szczodrzynski.edziennik.utils.Anim
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
@ -62,12 +64,12 @@ class EventManualDialog(
|
||||
private val app by lazy { activity.application as App }
|
||||
private lateinit var b: DialogEventManualV2Binding
|
||||
private lateinit var dialog: AlertDialog
|
||||
private var profile: Profile? = null
|
||||
|
||||
private var customColor: Int? = null
|
||||
private val editingShared = editingEvent?.sharedBy != null
|
||||
private val editingOwn = editingEvent?.sharedBy == "self"
|
||||
private var removeEventDialog: AlertDialog? = null
|
||||
private var defaultLoaded = false
|
||||
|
||||
private val api by lazy {
|
||||
SzkolnyApi(app)
|
||||
@ -76,6 +78,8 @@ class EventManualDialog(
|
||||
private var enqueuedWeekDialog: AlertDialog? = null
|
||||
private var enqueuedWeekStart = Date.getToday()
|
||||
|
||||
private var progressDialog: AlertDialog? = null
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
@ -95,6 +99,8 @@ class EventManualDialog(
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
EventBus.getDefault().unregister(this@EventManualDialog)
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
progressDialog?.dismiss()
|
||||
}
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
@ -168,7 +174,7 @@ class EventManualDialog(
|
||||
enqueuedWeekStart = weekStart
|
||||
|
||||
EdziennikTask.syncProfile(
|
||||
profileId = App.profileId,
|
||||
profileId = profileId,
|
||||
viewIds = listOf(
|
||||
MainActivity.DRAWER_ITEM_TIMETABLE to 0
|
||||
),
|
||||
@ -178,13 +184,40 @@ class EventManualDialog(
|
||||
).enqueue(activity)
|
||||
}
|
||||
|
||||
private fun showSharingProgressDialog() {
|
||||
if (progressDialog != null) {
|
||||
return
|
||||
}
|
||||
|
||||
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(R.string.event_sharing_text)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showRemovingProgressDialog() {
|
||||
if (progressDialog != null) {
|
||||
return
|
||||
}
|
||||
|
||||
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(R.string.event_removing_text)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||
if (event.profileId == App.profileId) {
|
||||
if (event.profileId == profileId) {
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
enqueuedWeekDialog = null
|
||||
progressDialog?.dismiss()
|
||||
launch {
|
||||
b.timeDropdown.loadItems()
|
||||
b.timeDropdown.selectDefault(editingEvent?.time)
|
||||
b.timeDropdown.selectDefault(defaultLesson?.displayStartTime ?: defaultTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,19 +226,22 @@ class EventManualDialog(
|
||||
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
enqueuedWeekDialog = null
|
||||
progressDialog?.dismiss()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
|
||||
dialog.dismiss()
|
||||
enqueuedWeekDialog?.dismiss()
|
||||
enqueuedWeekDialog = null
|
||||
progressDialog?.dismiss()
|
||||
}
|
||||
|
||||
private fun loadLists() { launch {
|
||||
profile = withContext(Dispatchers.Default) { app.db.profileDao().getByIdNow(profileId) }
|
||||
|
||||
with (b.dateDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
profileId = this@EventManualDialog.profileId
|
||||
showWeekDays = false
|
||||
showDays = true
|
||||
showOtherDate = true
|
||||
@ -215,7 +251,7 @@ class EventManualDialog(
|
||||
nextLessonTeamId = it.displayTeamId
|
||||
}
|
||||
loadItems()
|
||||
selectDefault(editingEvent?.eventDate)
|
||||
selectDefault(editingEvent?.date)
|
||||
selectDefault(defaultLesson?.displayDate ?: defaultDate)
|
||||
onDateSelected = { date, lesson ->
|
||||
b.timeDropdown.deselect()
|
||||
@ -233,14 +269,16 @@ class EventManualDialog(
|
||||
|
||||
with (b.timeDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
profileId = this@EventManualDialog.profileId
|
||||
showAllDay = true
|
||||
showCustomTime = true
|
||||
lessonsDate = b.dateDropdown.getSelected() as? Date ?: Date.getToday()
|
||||
displayMode = DISPLAY_LESSONS
|
||||
if (!loadItems())
|
||||
syncTimetable(lessonsDate ?: Date.getToday())
|
||||
selectDefault(editingEvent?.startTime)
|
||||
selectDefault(editingEvent?.time)
|
||||
if (editingEvent != null && editingEvent.time == null)
|
||||
select(0L)
|
||||
selectDefault(defaultLesson?.displayStartTime ?: defaultTime)
|
||||
onLessonSelected = { lesson ->
|
||||
lesson.displaySubjectId?.let { b.subjectDropdown.selectSubject(it) } ?: b.subjectDropdown.deselect()
|
||||
@ -251,7 +289,7 @@ class EventManualDialog(
|
||||
|
||||
with (b.teamDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
profileId = this@EventManualDialog.profileId
|
||||
showNoTeam = true
|
||||
loadItems()
|
||||
selectTeamClass()
|
||||
@ -261,7 +299,7 @@ class EventManualDialog(
|
||||
|
||||
with (b.subjectDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
profileId = this@EventManualDialog.profileId
|
||||
showNoSubject = true
|
||||
showCustomSubject = false
|
||||
loadItems()
|
||||
@ -271,7 +309,7 @@ class EventManualDialog(
|
||||
|
||||
with (b.teacherDropdown) {
|
||||
db = app.db
|
||||
profileId = App.profileId
|
||||
profileId = this@EventManualDialog.profileId
|
||||
showNoTeacher = true
|
||||
loadItems()
|
||||
selectDefault(editingEvent?.teacherId)
|
||||
@ -281,7 +319,12 @@ class EventManualDialog(
|
||||
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
// get the event type list
|
||||
val eventTypes = app.db.eventTypeDao().getAllNow(profileId)
|
||||
var eventTypes = app.db.eventTypeDao().getAllNow(profileId)
|
||||
|
||||
if (eventTypes.none { it.id in -1L..10L }) {
|
||||
eventTypes = app.db.eventTypeDao().addDefaultTypes(activity, profileId)
|
||||
}
|
||||
|
||||
b.typeDropdown.clear()
|
||||
b.typeDropdown += eventTypes.map { TextInputDropDown.Item(it.id, it.name, tag = it) }
|
||||
}
|
||||
@ -300,10 +343,10 @@ class EventManualDialog(
|
||||
// copy IDs from event being edited
|
||||
editingEvent?.let {
|
||||
b.topic.setText(it.topic)
|
||||
b.typeDropdown.select(it.type.toLong())?.let { item ->
|
||||
b.typeDropdown.select(it.type)?.let { item ->
|
||||
customColor = (item.tag as EventType).color
|
||||
}
|
||||
if (it.color != -1)
|
||||
if (it.color != null && it.color != -1)
|
||||
customColor = it.color
|
||||
}
|
||||
|
||||
@ -323,7 +366,7 @@ class EventManualDialog(
|
||||
}
|
||||
|
||||
b.typeColor.onClick {
|
||||
val currentColor = (b.typeDropdown?.selected?.tag as EventType?)?.color ?: Event.COLOR_DEFAULT
|
||||
val currentColor = (b.typeDropdown.selected?.tag as EventType?)?.color ?: Event.COLOR_DEFAULT
|
||||
val colorPickerDialog = ColorPickerDialog.newBuilder()
|
||||
.setColor(currentColor)
|
||||
.create()
|
||||
@ -365,16 +408,24 @@ class EventManualDialog(
|
||||
|
||||
private fun saveEvent() {
|
||||
val date = b.dateDropdown.getSelected() as? Date
|
||||
val startTimePair = b.timeDropdown.getSelected() as? Pair<*, *>
|
||||
val startTime = startTimePair?.first as? Time
|
||||
val timeSelected = b.timeDropdown.getSelected()
|
||||
val teamId = b.teamDropdown.getSelected() as? Long
|
||||
val type = b.typeDropdown.selected?.id
|
||||
val topic = b.topic.text?.toString()
|
||||
val subjectId = b.subjectDropdown.getSelected() as? Long
|
||||
val teacherId = b.teacherDropdown.getSelected() as? Long
|
||||
val teacherId = b.teacherDropdown.getSelected()
|
||||
|
||||
val share = b.shareSwitch.isChecked
|
||||
|
||||
if (share && profile?.registration != Profile.REGISTRATION_ENABLED) {
|
||||
RegistrationEnableDialog(activity, profileId).showEventShareDialog {
|
||||
if (it != null)
|
||||
profile = it
|
||||
saveEvent()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
b.dateDropdown.error = null
|
||||
b.teamDropdown.error = null
|
||||
b.typeDropdown.error = null
|
||||
@ -384,41 +435,59 @@ class EventManualDialog(
|
||||
|
||||
if (date == null) {
|
||||
b.dateDropdown.error = app.getString(R.string.dialog_event_manual_date_choose)
|
||||
b.dateDropdown.requestFocus()
|
||||
isError = true
|
||||
}
|
||||
|
||||
if (timeSelected !is Pair<*, *> && timeSelected != 0L) {
|
||||
b.timeDropdown.error = app.getString(R.string.dialog_event_manual_time_choose)
|
||||
if (!isError) b.timeDropdown.parent.requestChildFocus(b.timeDropdown, b.timeDropdown)
|
||||
isError = true
|
||||
}
|
||||
|
||||
if (share && teamId == null) {
|
||||
b.teamDropdown.error = app.getString(R.string.dialog_event_manual_team_choose)
|
||||
if (!isError) b.teamDropdown.parent.requestChildFocus(b.teamDropdown, b.teamDropdown)
|
||||
isError = true
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
b.typeDropdown.error = app.getString(R.string.dialog_event_manual_type_choose)
|
||||
if (!isError) b.typeDropdown.requestFocus()
|
||||
isError = true
|
||||
}
|
||||
|
||||
if (topic.isNullOrBlank()) {
|
||||
b.topic.error = app.getString(R.string.dialog_event_manual_topic_choose)
|
||||
if (!isError) b.topic.requestFocus()
|
||||
isError = true
|
||||
}
|
||||
|
||||
val startTime = if (timeSelected == 0L)
|
||||
null
|
||||
else
|
||||
(timeSelected as? Pair<*, *>)?.first as? Time
|
||||
|
||||
if (isError) return
|
||||
date ?: return
|
||||
topic ?: return
|
||||
|
||||
val id = System.currentTimeMillis()
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
editingEvent?.id ?: id,
|
||||
date,
|
||||
startTime,
|
||||
topic,
|
||||
customColor ?: -1,
|
||||
type ?: Event.TYPE_DEFAULT,
|
||||
true,
|
||||
teacherId ?: -1,
|
||||
subjectId ?: -1,
|
||||
teamId ?: -1
|
||||
)
|
||||
profileId = profileId,
|
||||
id = editingEvent?.id ?: id,
|
||||
date = date,
|
||||
time = startTime,
|
||||
topic = topic,
|
||||
color = customColor,
|
||||
type = type ?: Event.TYPE_DEFAULT,
|
||||
teacherId = teacherId ?: -1,
|
||||
subjectId = subjectId ?: -1,
|
||||
teamId = teamId ?: -1
|
||||
).also {
|
||||
it.addedManually = true
|
||||
}
|
||||
|
||||
val metadataObject = Metadata(
|
||||
profileId,
|
||||
@ -436,7 +505,7 @@ class EventManualDialog(
|
||||
val profile = app.db.profileDao().getByIdNow(profileId)
|
||||
|
||||
if (!share && !editingShared) {
|
||||
Toast.makeText(activity, R.string.event_manual_saving, Toast.LENGTH_SHORT).show()
|
||||
//Toast.makeText(activity, R.string.event_manual_saving, Toast.LENGTH_SHORT).show()
|
||||
finishAdding(eventObject, metadataObject)
|
||||
}
|
||||
else if (editingShared && !editingOwn) {
|
||||
@ -444,7 +513,7 @@ class EventManualDialog(
|
||||
// TODO
|
||||
}
|
||||
else if (!share && editingShared) {
|
||||
Toast.makeText(activity, R.string.event_manual_unshare, Toast.LENGTH_SHORT).show()
|
||||
showSharingProgressDialog()
|
||||
|
||||
eventObject.apply {
|
||||
sharedBy = null
|
||||
@ -453,13 +522,16 @@ class EventManualDialog(
|
||||
|
||||
api.runCatching(activity) {
|
||||
unshareEvent(eventObject)
|
||||
} ?: return@launch
|
||||
} ?: run {
|
||||
progressDialog?.dismiss()
|
||||
return@launch
|
||||
}
|
||||
|
||||
eventObject.sharedByName = null
|
||||
finishAdding(eventObject, metadataObject)
|
||||
}
|
||||
else if (share) {
|
||||
Toast.makeText(activity, R.string.event_manual_share, Toast.LENGTH_SHORT).show()
|
||||
showSharingProgressDialog()
|
||||
|
||||
eventObject.apply {
|
||||
sharedBy = profile?.userCode
|
||||
@ -470,7 +542,10 @@ class EventManualDialog(
|
||||
|
||||
api.runCatching(activity) {
|
||||
shareEvent(eventObject.withMetadata(metadataObject))
|
||||
} ?: return@launch
|
||||
} ?: run {
|
||||
progressDialog?.dismiss()
|
||||
return@launch
|
||||
}
|
||||
|
||||
eventObject.sharedBy = "self"
|
||||
finishAdding(eventObject, metadataObject)
|
||||
@ -478,6 +553,7 @@ class EventManualDialog(
|
||||
else {
|
||||
Toast.makeText(activity, "Unknown action :(", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
progressDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,11 +561,14 @@ class EventManualDialog(
|
||||
launch {
|
||||
if (editingShared && editingOwn) {
|
||||
// unshare + remove own event
|
||||
Toast.makeText(activity, R.string.event_manual_unshare_remove, Toast.LENGTH_SHORT).show()
|
||||
showRemovingProgressDialog()
|
||||
|
||||
api.runCatching(activity) {
|
||||
unshareEvent(editingEvent!!)
|
||||
} ?: return@launch
|
||||
} ?: run {
|
||||
progressDialog?.dismiss()
|
||||
return@launch
|
||||
}
|
||||
|
||||
finishRemoving()
|
||||
} else if (editingShared && !editingOwn) {
|
||||
@ -498,9 +577,10 @@ class EventManualDialog(
|
||||
// TODO
|
||||
} else {
|
||||
// remove event
|
||||
Toast.makeText(activity, R.string.event_manual_remove, Toast.LENGTH_SHORT).show()
|
||||
//Toast.makeText(activity, R.string.event_manual_remove, Toast.LENGTH_SHORT).show()
|
||||
finishRemoving()
|
||||
}
|
||||
progressDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@ -518,6 +598,7 @@ class EventManualDialog(
|
||||
activity.reloadTarget()
|
||||
}
|
||||
private fun finishRemoving() {
|
||||
editingEvent ?: return
|
||||
launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
app.db.eventDao().remove(editingEvent)
|
||||
|
@ -58,7 +58,7 @@ class GradeDetailsDialog(
|
||||
b.weightText = manager.getWeightString(app, grade)
|
||||
b.commentVisible = false
|
||||
b.devMode = App.debugMode
|
||||
b.gradeName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.3) 0x99000000.toInt() else 0x99ffffff.toInt())
|
||||
b.gradeName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.3) 0xaa000000.toInt() else 0xccffffff.toInt())
|
||||
b.gradeName.background.setTintColor(gradeColor)
|
||||
|
||||
b.gradeValue = if (grade.weight == 0f || grade.value < 0f) -1f else manager.getGradeValue(grade)
|
||||
|
@ -20,6 +20,7 @@ import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_SEM
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
||||
import java.util.*
|
||||
|
||||
class GradesConfigDialog(
|
||||
val activity: AppCompatActivity,
|
||||
@ -88,14 +89,34 @@ class GradesConfigDialog(
|
||||
else -> null
|
||||
}?.isChecked = true
|
||||
|
||||
b.dontCountZeroToAverage.isChecked = !profileConfig.countZeroToAvg
|
||||
b.dontCountGrades.isChecked = profileConfig.dontCountEnabled && profileConfig.dontCountGrades.isNotEmpty()
|
||||
b.hideImproved.isChecked = profileConfig.hideImproved
|
||||
b.averageWithoutWeight.isChecked = profileConfig.averageWithoutWeight
|
||||
|
||||
if (profileConfig.dontCountGrades.isEmpty()) {
|
||||
b.dontCountGradesText.setText("nb, 0, bz, bd")
|
||||
}
|
||||
else {
|
||||
b.dontCountGradesText.setText(profileConfig.dontCountGrades.join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveConfig() {
|
||||
profileConfig.plusValue = if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null
|
||||
profileConfig.minusValue = if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null
|
||||
|
||||
b.dontCountGradesText.setText(
|
||||
b.dontCountGradesText
|
||||
.text
|
||||
?.toString()
|
||||
?.toLowerCase(Locale.getDefault())
|
||||
?.replace(", ", ",")
|
||||
)
|
||||
profileConfig.dontCountEnabled = b.dontCountGrades.isChecked
|
||||
profileConfig.dontCountGrades = b.dontCountGradesText.text
|
||||
?.split(",")
|
||||
?.map { it.trim() }
|
||||
?: listOf()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
@ -127,7 +148,6 @@ class GradesConfigDialog(
|
||||
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM }
|
||||
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM }
|
||||
|
||||
b.dontCountZeroToAverage.onChange { _, isChecked -> profileConfig.countZeroToAvg = !isChecked }
|
||||
b.hideImproved.onChange { _, isChecked -> profileConfig.hideImproved = isChecked }
|
||||
b.averageWithoutWeight.onChange { _, isChecked -> profileConfig.averageWithoutWeight = isChecked }
|
||||
|
||||
|
@ -37,7 +37,8 @@ class NotificationFilterDialog(
|
||||
Notification.TYPE_NEW_ANNOUNCEMENT to R.string.notification_type_new_announcement,
|
||||
Notification.TYPE_NEW_SHARED_EVENT to R.string.notification_type_new_shared_event,
|
||||
Notification.TYPE_NEW_SHARED_HOMEWORK to R.string.notification_type_new_shared_homework,
|
||||
Notification.TYPE_REMOVED_SHARED_EVENT to R.string.notification_type_removed_shared_event
|
||||
Notification.TYPE_REMOVED_SHARED_EVENT to R.string.notification_type_removed_shared_event,
|
||||
Notification.TYPE_TEACHER_ABSENCE to R.string.notification_type_new_teacher_absence
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-3-15.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.sync
|
||||
|
||||
import android.text.Html
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.api.task.AppSync
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class RegistrationEnableDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val profileId: Int
|
||||
) : CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "RegistrationEnableDialog"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
// local variables go here
|
||||
private var progressDialog: AlertDialog? = null
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
app = activity.applicationContext as App
|
||||
}}
|
||||
|
||||
fun showEventShareDialog(onSuccess: (profile: Profile?) -> Unit) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.event_manual_need_registration_title)
|
||||
.setMessage(R.string.event_manual_need_registration_text)
|
||||
.setPositiveButton(R.string.ok) { dialog, which ->
|
||||
enableRegistration(onSuccess)
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showEnableDialog(onSuccess: (profile: Profile?) -> Unit) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.registration_enable_dialog_title)
|
||||
.setMessage(Html.fromHtml(app.getString(R.string.registration_enable_dialog_text)))
|
||||
.setPositiveButton(R.string.ok) { dialog, which ->
|
||||
enableRegistration(onSuccess)
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun enableRegistration(onSuccess: (profile: Profile?) -> Unit) { launch {
|
||||
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(R.string.registration_enable_progress_text)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
|
||||
val profile = withContext(Dispatchers.Default) {
|
||||
val profile = app.db.profileDao().getByIdNow(profileId) ?: return@withContext null
|
||||
profile.registration = Profile.REGISTRATION_ENABLED
|
||||
|
||||
// force full registration of the user
|
||||
App.config.getFor(profile.id).hash = ""
|
||||
|
||||
AppSync(app, mutableListOf(), listOf(profile), SzkolnyApi(app)).run(0L, markAsSeen = true)
|
||||
app.db.profileDao().add(profile)
|
||||
if (profile.id == App.profileId) {
|
||||
App.profile.registration = profile.registration
|
||||
}
|
||||
return@withContext profile
|
||||
}
|
||||
|
||||
progressDialog?.dismiss()
|
||||
onSuccess(profile)
|
||||
}}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user