Compare commits

...

51 Commits

Author SHA1 Message Date
a0244841ad [4.13.4] Update build.gradle, signing and changelog. 2022-12-26 14:45:29 +01:00
12c0c6f2ec [UI] Always show event subject dropdown for university school. 2022-12-26 14:43:42 +01:00
aaa3b8626e [UI] Update event types for university school. 2022-12-26 14:01:25 +01:00
48c9e2dfe3 [4.13.3] Update build.gradle, signing and changelog. 2022-12-06 10:35:23 +01:00
81d4801d27 [UI] Add snowfall to CounterActivity. Enable in February as well. 2022-12-06 10:34:12 +01:00
5f8016061d [API/Vulcan] Fix wrong serializing of null in JSON causing API error. 2022-12-06 10:22:47 +01:00
5007587192 [UI/Agenda] Allow prioritizing event subject over event type. 2022-11-30 11:29:43 +01:00
dfd1083e41 [UI/Timetable] Show lesson replacing notes in all places. 2022-11-30 10:41:43 +01:00
678baf46e5 [4.13.2] Update build.gradle, signing and changelog. 2022-11-28 20:30:11 +01:00
4077fe448d [4.13.2-rc.4] Update build.gradle, signing and changelog. 2022-11-25 16:52:16 +01:00
f085e17ef7 [API/Vulcan] Once again fix ignoring 404 response on Addressbook. 2022-11-25 16:51:35 +01:00
7fd2cad46b [4.13.2-rc.3] Update build.gradle, signing and changelog. 2022-11-25 16:13:58 +01:00
93dc2ac9ab [API/Vulcan] Fix ignoring 404 response on Addressbook. 2022-11-25 16:11:50 +01:00
ac53e267fc [4.13.2-rc.2] Update build.gradle, signing and changelog. 2022-11-25 14:55:49 +01:00
86eb1a0f42 [API/Vulcan] Actually ignore 404 response on Addressbook. 2022-11-25 14:54:05 +01:00
710d82da27 [4.13.2-rc.1] Update build.gradle, signing and changelog. 2022-11-25 14:40:39 +01:00
0123f50810 [API/Vulcan] Ignore 404 response on Addressbook. 2022-11-25 14:20:22 +01:00
6d3eb65445 [API/Mobidziennik] Do not clear email field if not set. 2022-11-15 22:20:00 +01:00
a9a0630226 [4.13.1] Update build.gradle, signing and changelog. 2022-11-03 22:59:40 +01:00
ec7577f999 [App] Revert to use old devMode config key. 2022-11-03 22:29:22 +01:00
05c7c0012c [UI] Fix home cards order not saving. 2022-11-03 22:28:59 +01:00
d65c6db954 [API/Librus] Fix getting read date in messages for multiple receivers. (#154) 2022-11-03 21:53:01 +01:00
771dc437e6 [Strings] Translate home timetable card "all lessons" to English. (#152) 2022-10-30 12:45:27 +01:00
3d5d3847cc [API/Librus] Fix getting teacher name in notices. (#151) 2022-10-30 12:44:59 +01:00
18cc60a80b [4.13] Update build.gradle, signing and changelog. 2022-10-26 20:55:50 +02:00
fedde9f739 [UI/Home] Show all next lessons before school day start. 2022-10-26 20:34:48 +02:00
9fde97bef0 [App] Share Lesson and Event notes to specific team only. 2022-10-26 11:14:32 +02:00
742bd03e9e [Lab] Fix JSON page crashing because of serializing AppDb. 2022-10-26 10:31:03 +02:00
62ffc652ab [4.13-rc.5] Update build.gradle, signing and changelog. 2022-10-25 20:50:19 +02:00
bfd2e9883a [App] Refactor getting profile config. 2022-10-25 20:48:10 +02:00
00e077d01f [UI] Fix notes not showing in note list dialog. 2022-10-25 20:07:48 +02:00
c21d89cf60 [UI] Fix SettingsAboutCard having duplicate items. 2022-10-25 19:58:26 +02:00
f52cc1b197 [UI] Make shared notes for lessons use a stable ID. 2022-10-25 19:58:09 +02:00
c90ad97f55 [UI] Remove "enable shared events" setting. Reorder settings a bit. 2022-10-25 17:33:38 +02:00
845e09d875 [API/Usos] Add prefixes to classroom and building names. 2022-10-25 12:36:11 +02:00
158b69a8d3 [Lab] Fix full sync buttons. 2022-10-25 12:35:51 +02:00
9535f53563 [App] Refactor profile methods as extensions. 2022-10-25 12:19:59 +02:00
eeb3fc4621 [4.13-rc.4] Update build.gradle, signing and changelog. 2022-10-24 23:33:54 +02:00
41693a9fc8 [App] Respect user setting before notifying about updates. 2022-10-24 23:33:02 +02:00
d3599b8c89 [UI] Fix DateDropdown next week Friday not visible. 2022-10-24 23:32:21 +02:00
ffd81f8b82 [UI] Add setting to share events/notes by default. 2022-10-24 23:32:09 +02:00
2c34924052 [UI] Mark Firebase-received events as manual. Update legend icons. 2022-10-24 22:41:15 +02:00
26ad6373e6 [4.13-rc.3] Update build.gradle, signing and changelog. 2022-10-23 23:16:23 +02:00
cac8f94407 [Gradle] Update Chucker to fix Android 12 crash issue. 2022-10-23 23:16:08 +02:00
6628b97faf [API/Usos] Fix detecting term start and end date. 2022-10-23 23:11:49 +02:00
8424414317 [Login] Fix configOverrides NPE during login. 2022-10-23 23:11:20 +02:00
d8bb927703 [4.13-rc.2] Update build.gradle, signing and changelog. 2022-10-22 22:34:24 +02:00
c8e8c172a2 [App] Rework update handling. 2022-10-22 22:10:04 +02:00
0d4dee765a [Lab] Allow setting custom API key. 2022-10-22 12:56:15 +02:00
fd407b2b03 [Config] Set highest data version by default. 2022-10-22 12:31:40 +02:00
40ed5a221f [App] Fix crashes while deserializing AppData and Config. 2022-10-22 12:19:23 +02:00
138 changed files with 3933 additions and 858 deletions

View File

@ -1,6 +1,7 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="ALLOW_TRAILING_COMMA" value="true" /> <option name="ALLOW_TRAILING_COMMA" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>

View File

@ -156,6 +156,7 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:2.5.2" implementation "androidx.navigation:navigation-fragment-ktx:2.5.2"
implementation "androidx.recyclerview:recyclerview:1.2.1" implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation "androidx.room:room-runtime:2.4.3" implementation "androidx.room:room-runtime:2.4.3"
implementation "androidx.room:room-ktx:2.4.3"
implementation "androidx.work:work-runtime-ktx:2.7.1" implementation "androidx.work:work-runtime-ktx:2.7.1"
kapt "androidx.room:room-compiler:2.4.3" kapt "androidx.room:room-compiler:2.4.3"
@ -208,7 +209,7 @@ dependencies {
implementation "com.daimajia.swipelayout:library:1.2.0@aar" implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation "com.github.Applandeo:Material-Calendar-View:15de569cbc" // https://github.com/Applandeo/Material-Calendar-View implementation "com.github.Applandeo:Material-Calendar-View:15de569cbc" // https://github.com/Applandeo/Material-Calendar-View
implementation "com.github.CanHub:Android-Image-Cropper:2.2.2" // https://github.com/CanHub/Android-Image-Cropper implementation "com.github.CanHub:Android-Image-Cropper:2.2.2" // https://github.com/CanHub/Android-Image-Cropper
implementation "com.github.ChuckerTeam.Chucker:library:3.0.1" // https://github.com/ChuckerTeam/chucker implementation "com.github.ChuckerTeam.Chucker:library:3.5.2" // https://github.com/ChuckerTeam/chucker
implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2" // https://github.com/antonKozyriatskyi/CircularProgressIndicator implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2" // https://github.com/antonKozyriatskyi/CircularProgressIndicator
implementation "com.github.bassaer:chatmessageview:2.0.1" // https://github.com/bassaer/ChatMessageView implementation "com.github.bassaer:chatmessageview:2.0.1" // https://github.com/bassaer/ChatMessageView
implementation "com.github.hypertrack:hyperlog-android:0.0.10" // https://github.com/hypertrack/hyperlog-android implementation "com.github.hypertrack:hyperlog-android:0.0.10" // https://github.com/hypertrack/hyperlog-android

View File

@ -22,6 +22,7 @@
-keep class android.support.v7.widget.** { *; } -keep class android.support.v7.widget.** { *; }
-keep class pl.szczodrzynski.edziennik.utils.models.** { *; } -keep class pl.szczodrzynski.edziennik.utils.models.** { *; }
-keep class pl.szczodrzynski.edziennik.data.db.enums.* { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; } -keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; } -keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage { *; } -keep class pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage { *; }
@ -31,6 +32,9 @@
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
-keep class pl.szczodrzynski.edziennik.config.AppData { *; }
-keep class pl.szczodrzynski.edziennik.config.AppData$** { *; }
-keep class pl.szczodrzynski.edziennik.utils.managers.TextStylingManager$HtmlMode { *; }
-keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); } -keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); }
-keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); } -keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); }

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
<h3>Wersja 4.13-rc.1, 2022-10-22</h3> <h3>Wersja 4.13.4, 2022-12-26</h3>
<ul> <ul>
<li>Poprawione powiadomienia na Androidzie 13. @santoni0</li> <li>USOS: zaktualizowano rodzaje wydarzeń. Wybór przedmiotu jest teraz zawsze widoczny.</li>
<li>Możliwość dostosowania wyświetlania planu lekcji.</li> <li>Opcja wyświetlania nazwy przedmiotu w miejscu rodzaju wydarzenia.</li>
<li>Opcja kolorowania bloków w planie lekcji.</li> <li>Notatki zastępujące treść lekcji są teraz wyświetlane wszędzie.</li>
<li><b>USOS</b> - pierwsza wersja obsługi systemu. Osobne rodzaje wydarzeń (oraz wygląd niektórych części aplikacji) lepiej dostosowany do nauki na studiach.</li> <li>Na ekranie odliczania czasu lekcji również pada śnieg.</li>
<li>Poprawione opcje filtrowania powiadomień i wyboru przycisków menu bocznego.</li> <li>Poprawiono synchronizację w Mobidzienniku bez ustawionego adresu e-mail.</li>
<li>Poprawiono błąd synchronizacji w Vulcanie.</li>
</ul> </ul>
<br> <br>
<br> <br>
Dzięki za korzystanie ze Szkolnego!<br> Dzięki za korzystanie ze Szkolnego!<br>
<i>&copy; [Kuba Szczodrzyński](@kuba2k2), [Kacper Ziubryniewicz](@kapi2289) 2022</i> <i>&copy; [Kuba Szczodrzyński](@kuba2k2) 2022</i>

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/ /*secret password - removed for source code publication*/
static toys AES_IV[16] = { static toys AES_IV[16] = {
0xb7, 0x04, 0xed, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 0x4b, 0x43, 0x7e, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat); unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -28,7 +28,11 @@ import com.google.gson.Gson
import com.hypertrack.hyperlog.HyperLog import com.hypertrack.hyperlog.HyperLog
import com.mikepenz.iconics.Iconics import com.mikepenz.iconics.Iconics
import im.wangchao.mhttp.MHttp import im.wangchao.mhttp.MHttp
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.leolin.shortcutbadger.ShortcutBadger import me.leolin.shortcutbadger.ShortcutBadger
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
@ -55,7 +59,19 @@ import pl.szczodrzynski.edziennik.utils.PermissionChecker
import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.managers.* import pl.szczodrzynski.edziennik.utils.managers.AttendanceManager
import pl.szczodrzynski.edziennik.utils.managers.AvailabilityManager
import pl.szczodrzynski.edziennik.utils.managers.BuildManager
import pl.szczodrzynski.edziennik.utils.managers.EventManager
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
import pl.szczodrzynski.edziennik.utils.managers.MessageManager
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
import pl.szczodrzynski.edziennik.utils.managers.PermissionManager
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager
import pl.szczodrzynski.edziennik.utils.managers.TimetableManager
import pl.szczodrzynski.edziennik.utils.managers.UpdateManager
import pl.szczodrzynski.edziennik.utils.managers.UserActionManager
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -80,18 +96,19 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
} }
val api by lazy { SzkolnyApi(this) } val api by lazy { SzkolnyApi(this) }
val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
val userActionManager by lazy { UserActionManager(this) }
val gradesManager by lazy { GradesManager(this) }
val timetableManager by lazy { TimetableManager(this) }
val eventManager by lazy { EventManager(this) }
val permissionManager by lazy { PermissionManager(this) }
val attendanceManager by lazy { AttendanceManager(this) } val attendanceManager by lazy { AttendanceManager(this) }
val buildManager by lazy { BuildManager(this) }
val availabilityManager by lazy { AvailabilityManager(this) } val availabilityManager by lazy { AvailabilityManager(this) }
val textStylingManager by lazy { TextStylingManager(this) } val buildManager by lazy { BuildManager(this) }
val eventManager by lazy { EventManager(this) }
val gradesManager by lazy { GradesManager(this) }
val messageManager by lazy { MessageManager(this) } val messageManager by lazy { MessageManager(this) }
val noteManager by lazy { NoteManager(this) } val noteManager by lazy { NoteManager(this) }
val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
val permissionManager by lazy { PermissionManager(this) }
val textStylingManager by lazy { TextStylingManager(this) }
val timetableManager by lazy { TimetableManager(this) }
val updateManager by lazy { UpdateManager(this) }
val userActionManager by lazy { UserActionManager(this) }
val db val db
get() = App.db get() = App.db
@ -190,12 +207,6 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
Iconics.init(applicationContext) Iconics.init(applicationContext)
Iconics.respectFontBoundsDefault = true Iconics.respectFontBoundsDefault = true
if (devMode) {
HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this))
}
// initialize companion object values // initialize companion object values
AppData.read(this) AppData.read(this)
App.db = AppDb(this) App.db = AppDb(this)
@ -204,6 +215,12 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
devMode = config.devMode ?: debugMode devMode = config.devMode ?: debugMode
enableChucker = config.enableChucker ?: devMode enableChucker = config.enableChucker ?: devMode
if (devMode) {
HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this))
}
if (!profileLoadById(config.lastProfileId)) { if (!profileLoadById(config.lastProfileId)) {
val success = db.profileDao().firstId?.let { profileLoadById(it) } val success = db.profileDao().firstId?.let { profileLoadById(it) }
if (success != true) if (success != true)
@ -405,6 +422,12 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
try { try {
App.data = AppData.get(profile.loginStoreType) App.data = AppData.get(profile.loginStoreType)
d("App", "Loaded AppData: ${App.data}") d("App", "Loaded AppData: ${App.data}")
// apply newly-added config overrides, if not changed by the user yet
for ((key, value) in App.data.configOverrides) {
val config = App.profile.config
if (!config.has(key))
config.set(key, value)
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e("App", "Cannot load AppData", e) Log.e("App", "Cannot load AppData", e)
Toast.makeText(this, R.string.app_cannot_load_data, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.app_cannot_load_data, Toast.LENGTH_LONG).show()
@ -445,6 +468,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
} }
fun profileSave() = profileSave(profile) fun profileSave() = profileSave(profile)
fun profileSave(profile: Profile) { fun profileSave(profile: Profile) {
if (profile.id == profileId)
App.profile = profile
launch(Dispatchers.Default) { launch(Dispatchers.Default) {
App.db.profileDao().add(profile) App.db.profileDao().add(profile)
} }

View File

@ -46,6 +46,7 @@ import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
import pl.szczodrzynski.edziennik.sync.SyncWorker import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateStateEvent
import pl.szczodrzynski.edziennik.sync.UpdateWorker import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.base.MainSnackbar import pl.szczodrzynski.edziennik.ui.base.MainSnackbar
import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget
@ -56,6 +57,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegisterUnavailableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.ServerMessageDialog import pl.szczodrzynski.edziennik.ui.dialogs.sync.ServerMessageDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateAvailableDialog import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateAvailableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateProgressDialog
import pl.szczodrzynski.edziennik.ui.error.ErrorDetailsDialog import pl.szczodrzynski.edziennik.ui.error.ErrorDetailsDialog
import pl.szczodrzynski.edziennik.ui.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.error.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
@ -320,7 +322,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
// IT'S WINTER MY DUDES // IT'S WINTER MY DUDES
val today = Date.getToday() val today = Date.getToday()
if ((today.month % 11 == 1) && app.config.ui.snowfall) { if ((today.month / 3 % 4 == 0) && app.config.ui.snowfall) {
b.rootFrame.addView(layoutInflater.inflate(R.layout.snowfall, b.rootFrame, false)) b.rootFrame.addView(layoutInflater.inflate(R.layout.snowfall, b.rootFrame, false))
} else if (app.config.ui.eggfall && BigNightUtil().isDataWielkanocyNearDzisiaj()) { } else if (app.config.ui.eggfall && BigNightUtil().isDataWielkanocyNearDzisiaj()) {
val eggfall = layoutInflater.inflate( val eggfall = layoutInflater.inflate(
@ -536,6 +538,14 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
UpdateAvailableDialog(this, event).show() UpdateAvailableDialog(this, event).show()
} }
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onUpdateStateEvent(event: UpdateStateEvent) {
if (!event.running)
return
EventBus.getDefault().removeStickyEvent(event)
UpdateProgressDialog(this, event.update ?: return, event.downloadId).show()
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onRegisterAvailabilityEvent(event: RegisterAvailabilityEvent) { fun onRegisterAvailabilityEvent(event: RegisterAvailabilityEvent) {
EventBus.getDefault().removeStickyEvent(event) EventBus.getDefault().removeStickyEvent(event)
@ -699,6 +709,10 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
if (extras?.containsKey("action") == true) { if (extras?.containsKey("action") == true) {
val handled = when (extras.getString("action")) { val handled = when (extras.getString("action")) {
"updateRequest" -> {
UpdateAvailableDialog(this, app.config.update).show()
true
}
"serverMessage" -> { "serverMessage" -> {
ServerMessageDialog( ServerMessageDialog(
this, this,

View File

@ -59,10 +59,11 @@ data class AppData(
val lessonHeight: Int, val lessonHeight: Int,
val enableMarkAsReadAnnouncements: Boolean, val enableMarkAsReadAnnouncements: Boolean,
val enableNoticePoints: Boolean, val enableNoticePoints: Boolean,
val eventManualShowSubjectDropdown: Boolean,
) )
data class EventType( data class EventType(
val id: Int, val id: Long,
val color: String, val color: String,
val name: String, val name: String,
) )

View File

@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.ext.takePositive
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
abstract class BaseConfig( abstract class BaseConfig(
@Transient
val db: AppDb, val db: AppDb,
val profileId: Int? = null, val profileId: Int? = null,
protected var entries: List<ConfigEntry>? = null, protected var entries: List<ConfigEntry>? = null,
@ -42,4 +43,6 @@ abstract class BaseConfig(
db.configDao().add(ConfigEntry(profileId ?: -1, key, value)) db.configDao().add(ConfigEntry(profileId ?: -1, key, value))
} }
} }
fun has(key: String) = values.containsKey(key)
} }

View File

@ -24,7 +24,7 @@ class Config(db: AppDb) : BaseConfig(db) {
val timetable by lazy { ConfigTimetable(this) } val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) } val grades by lazy { ConfigGrades(this) }
var dataVersion by config<Int>(0) var dataVersion by config<Int>(DATA_VERSION)
var hash by config<String>("") var hash by config<String>("")
var lastProfileId by config<Int>(0) var lastProfileId by config<Int>(0)
@ -33,12 +33,13 @@ class Config(db: AppDb) : BaseConfig(db) {
var update by config<Update?>(null) var update by config<Update?>(null)
var updatesChannel by config<String>("release") var updatesChannel by config<String>("release")
var devMode by config<Boolean?>(null) var devMode by config<Boolean?>("debugMode", null)
var devModePassword by config<String?>(null) var devModePassword by config<String?>(null)
var enableChucker by config<Boolean?>(null) var enableChucker by config<Boolean?>(null)
var apiAvailabilityCheck by config<Boolean>(true) var apiAvailabilityCheck by config<Boolean>(true)
var apiInvalidCert by config<String?>(null) var apiInvalidCert by config<String?>(null)
var apiKeyCustom by config<String?>(null)
var appInstalledTime by config<Long>(0L) var appInstalledTime by config<Long>(0L)
var appRateSnackbarTime by config<Long>(0L) var appRateSnackbarTime by config<Long>(0L)
var appVersion by config<Int>(BuildConfig.VERSION_CODE) var appVersion by config<Int>(BuildConfig.VERSION_CODE)
@ -49,15 +50,14 @@ class Config(db: AppDb) : BaseConfig(db) {
var widgetConfigs by config<JsonObject> { JsonObject() } var widgetConfigs by config<JsonObject> { JsonObject() }
fun migrate(app: App) { fun migrate(app: App) {
if (dataVersion < DATA_VERSION) if (dataVersion < DATA_VERSION || hash == "")
// migrate old data version OR freshly installed app (or updated from 3.x)
ConfigMigration(app, this) ConfigMigration(app, this)
} }
fun getFor(profileId: Int): ProfileConfig { operator fun get(profileId: Int): ProfileConfig {
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, entries).also { return profileConfigs[profileId] ?: ProfileConfig(db, profileId, entries).also {
profileConfigs[profileId] = it profileConfigs[profileId] = it
} }
} }
fun forProfile() = getFor(App.profileId)
} }

View File

@ -115,9 +115,9 @@ class ConfigDelegate<T>(
is Boolean -> value is Boolean -> value
// enums, maps & collections // enums, maps & collections
is Enum<*> -> value.toInt() is Enum<*> -> value.toInt()
is Collection<*> -> JsonArray(value.map { is Collection<*> -> value.map {
if (it is Number || it is Boolean) it else serialize(it, serializeObjects = false) if (it is Number || it is Boolean) it else serialize(it, serializeObjects = false)
}) }.toJsonElement()
is Map<*, *> -> gson.toJson(value.mapValues { (_, it) -> is Map<*, *> -> gson.toJson(value.mapValues { (_, it) ->
if (it is Number || it is Boolean) it else serialize(it, serializeObjects = false) if (it is Number || it is Boolean) it else serialize(it, serializeObjects = false)
}) })

View File

@ -15,7 +15,7 @@ class ProfileConfig(
entries: List<ConfigEntry>?, entries: List<ConfigEntry>?,
) : BaseConfig(db, profileId, entries) { ) : BaseConfig(db, profileId, entries) {
companion object { companion object {
const val DATA_VERSION = 4 const val DATA_VERSION = 5
} }
val grades by lazy { ProfileConfigGrades(this) } val grades by lazy { ProfileConfigGrades(this) }
@ -26,9 +26,11 @@ class ProfileConfig(
val timetable by lazy { ConfigTimetable(this) } val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) }*/ val grades by lazy { ConfigGrades(this) }*/
var dataVersion by config<Int>(0) var dataVersion by config<Int>(DATA_VERSION)
var hash by config<String>("") var hash by config<String>("")
var shareByDefault by config<Boolean>(false)
init { init {
if (dataVersion < DATA_VERSION) if (dataVersion < DATA_VERSION)
ProfileConfigMigration(this) ProfileConfigMigration(this)

View File

@ -15,6 +15,7 @@ class ProfileConfigUI(base: ProfileConfig) {
var agendaGroupByType by base.config<Boolean>(false) var agendaGroupByType by base.config<Boolean>(false)
var agendaLessonChanges by base.config<Boolean>(true) var agendaLessonChanges by base.config<Boolean>(true)
var agendaTeacherAbsence by base.config<Boolean>(true) var agendaTeacherAbsence by base.config<Boolean>(true)
var agendaSubjectImportant by base.config<Boolean>(false)
var agendaElearningMark by base.config<Boolean>(false) var agendaElearningMark by base.config<Boolean>(false)
var agendaElearningGroup by base.config<Boolean>(true) var agendaElearningGroup by base.config<Boolean>(true)

View File

@ -16,9 +16,9 @@ import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.math.abs import kotlin.math.abs
class AppConfigMigrationV3(p: SharedPreferences, config: Config) { class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
init { config.apply { init {
val s = "app.appConfig" val s = "app.appConfig"
if (dataVersion < 1) { config.apply {
ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1 ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1
sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true
sync.interval = p.getString("$s.registerSyncInterval", null)?.toIntOrNull() ?: 3600 sync.interval = p.getString("$s.registerSyncInterval", null)?.toIntOrNull() ?: 3600
@ -37,9 +37,6 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
NavTarget.HOMEWORK, NavTarget.HOMEWORK,
NavTarget.SETTINGS NavTarget.SETTINGS
) )
dataVersion = 1
}
if (dataVersion < 2) {
devModePassword = p.getString("$s.devModePassword", null).fix() devModePassword = p.getString("$s.devModePassword", null).fix()
sync.tokenApp = p.getString("$s.fcmToken", null).fix() sync.tokenApp = p.getString("$s.fcmToken", null).fix()
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0 timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
@ -86,9 +83,8 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
LoginType.LIBRUS.id -> sync.tokenLibrus = token LoginType.LIBRUS.id -> sync.tokenLibrus = token
} }
} }
dataVersion = 2
} }
}} }
private fun String?.fix(): String? { private fun String?.fix(): String? {
return this?.replace("\"", "")?.let { if (it == "null") null else it } return this?.replace("\"", "")?.let { if (it == "null") null else it }

View File

@ -5,12 +5,9 @@
package pl.szczodrzynski.edziennik.config.utils package pl.szczodrzynski.edziennik.config.utils
import android.content.Context import android.content.Context
import androidx.core.content.edit
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.config.Config import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.ext.HOUR
import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.math.abs import kotlin.math.abs
@ -22,6 +19,9 @@ class ConfigMigration(app: App, config: Config) {
// migrate appConfig from app version 3.x and lower. // migrate appConfig from app version 3.x and lower.
// Updates dataVersion to level 2. // Updates dataVersion to level 2.
AppConfigMigrationV3(p, config) AppConfigMigrationV3(p, config)
p.edit {
remove("app.appConfig.appTheme")
}
} }
if (dataVersion < 11) { if (dataVersion < 11) {
@ -43,5 +43,7 @@ class ConfigMigration(app: App, config: Config) {
dataVersion = 11 dataVersion = 11
} }
hash = "invalid"
}} }}
} }

View File

@ -16,6 +16,8 @@ import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_AL
class ProfileConfigMigration(config: ProfileConfig) { class ProfileConfigMigration(config: ProfileConfig) {
init { config.apply { init { config.apply {
val profile = db.profileDao().getByIdNow(profileId ?: -1)
if (dataVersion < 2) { if (dataVersion < 2) {
sync.notificationFilter = sync.notificationFilter + NotificationType.TEACHER_ABSENCE sync.notificationFilter = sync.notificationFilter + NotificationType.TEACHER_ABSENCE
@ -37,11 +39,23 @@ class ProfileConfigMigration(config: ProfileConfig) {
// switch to new event types (USOS) // switch to new event types (USOS)
dataVersion = 4 dataVersion = 4
val profile = db.profileDao().getByIdNow(profileId ?: -1)
if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) { if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) {
db.eventTypeDao().clear(profileId ?: -1) db.eventTypeDao().clear(profileId ?: -1)
db.eventTypeDao().addDefaultTypes(profile) db.eventTypeDao().addDefaultTypes(profile)
} }
} }
if (dataVersion < 5) {
// update USOS event types and the appropriate events (2022-12-25)
dataVersion = 5
if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) {
db.eventTypeDao().getAllWithDefaults(profile)
// wejściówka (4) -> kartkówka (3)
db.eventDao().getRawNow("UPDATE events SET eventType = 3 WHERE profileId = $profileId AND eventType = 4;")
// zadanie (6) -> zadanie domowe (-1)
db.eventDao().getRawNow("UPDATE events SET eventType = -1 WHERE profileId = $profileId AND eventType = 6;")
}
}
}} }}
} }

View File

@ -28,6 +28,8 @@ import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.ext.isBeforeYear
import pl.szczodrzynski.edziennik.ext.shouldArchive
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.managers.AvailabilityManager.Error.Type import pl.szczodrzynski.edziennik.utils.managers.AvailabilityManager.Error.Type

View File

@ -52,28 +52,29 @@ class ProfileArchiver(val app: App, val profile: Profile) {
when (profile.loginStoreType) { when (profile.loginStoreType) {
LoginType.LIBRUS -> { LoginType.LIBRUS -> {
profile.removeStudentData("isPremium") profile.studentData.remove("isPremium")
profile.removeStudentData("pushDeviceId") profile.studentData.remove("pushDeviceId")
profile.removeStudentData("startPointsSemester1") profile.studentData.remove("startPointsSemester1")
profile.removeStudentData("startPointsSemester2") profile.studentData.remove("startPointsSemester2")
profile.removeStudentData("enablePointGrades") profile.studentData.remove("enablePointGrades")
profile.removeStudentData("enableDescriptiveGrades") profile.studentData.remove("enableDescriptiveGrades")
} }
LoginType.MOBIDZIENNIK -> {} LoginType.MOBIDZIENNIK -> {}
LoginType.VULCAN -> { LoginType.VULCAN -> {
// DataVulcan.isApiLoginValid() returns false so it will update the semester // DataVulcan.isApiLoginValid() returns false so it will update the semester
profile.removeStudentData("currentSemesterEndDate") profile.studentData.remove("currentSemesterEndDate")
profile.removeStudentData("studentSemesterId") profile.studentData.remove("studentSemesterId")
profile.removeStudentData("studentSemesterNumber") profile.studentData.remove("studentSemesterNumber")
profile.removeStudentData("semester1Id") profile.studentData.remove("semester1Id")
profile.removeStudentData("semester2Id") profile.studentData.remove("semester2Id")
profile.removeStudentData("studentClassId") profile.studentData.remove("studentClassId")
} }
LoginType.IDZIENNIK -> { LoginType.IDZIENNIK -> {
profile.removeStudentData("schoolYearId") profile.studentData.remove("schoolYearId")
} }
LoginType.EDUDZIENNIK -> {} LoginType.EDUDZIENNIK -> {}
LoginType.PODLASIE -> {} LoginType.PODLASIE -> {}
LoginType.USOS -> {}
LoginType.DEMO -> {} LoginType.DEMO -> {}
LoginType.TEMPLATE -> {} LoginType.TEMPLATE -> {}
} }

View File

@ -10,7 +10,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.ext.currentTimeUnix import pl.szczodrzynski.edziennik.ext.currentTimeUnix
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.set
class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
@ -117,7 +119,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiLogin: String? = null private var mApiLogin: String? = null
var apiLogin: String? var apiLogin: String?
get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin } get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin }
set(value) { profile?.putStudentData("accountLogin", value); mApiLogin = value } set(value) { profile["accountLogin"] = value; mApiLogin = value }
/** /**
* A Synergia password. * A Synergia password.
* Used: for login (API Login Method) in Synergia mode. * Used: for login (API Login Method) in Synergia mode.
@ -126,7 +128,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiPassword: String? = null private var mApiPassword: String? = null
var apiPassword: String? var apiPassword: String?
get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword } get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword }
set(value) { profile?.putStudentData("accountPassword", value); mApiPassword = value } set(value) { profile["accountPassword"] = value; mApiPassword = value }
/** /**
* A JST login Code. * A JST login Code.
@ -135,7 +137,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiCode: String? = null private var mApiCode: String? = null
var apiCode: String? var apiCode: String?
get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode } get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode }
set(value) { profile?.putStudentData("accountCode", value); mApiCode = value } set(value) { profile["accountCode"] = value; mApiCode = value }
/** /**
* A JST login PIN. * A JST login PIN.
* Used only during first login in JST mode. * Used only during first login in JST mode.
@ -143,7 +145,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiPin: String? = null private var mApiPin: String? = null
var apiPin: String? var apiPin: String?
get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin } get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin }
set(value) { profile?.putStudentData("accountPin", value); mApiPin = value } set(value) { profile["accountPin"] = value; mApiPin = value }
/** /**
* A Synergia API access token. * A Synergia API access token.
@ -154,7 +156,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiAccessToken: String? = null private var mApiAccessToken: String? = null
var apiAccessToken: String? var apiAccessToken: String?
get() { mApiAccessToken = mApiAccessToken ?: profile?.getStudentData("accountToken", null); return mApiAccessToken } get() { mApiAccessToken = mApiAccessToken ?: profile?.getStudentData("accountToken", null); return mApiAccessToken }
set(value) { mApiAccessToken = value; profile?.putStudentData("accountToken", value) ?: return; } set(value) { mApiAccessToken = value; profile["accountToken"] = value ?: return; }
/** /**
* A Synergia API refresh token. * A Synergia API refresh token.
* Used when refreshing the [apiAccessToken] in JST, Synergia modes. * Used when refreshing the [apiAccessToken] in JST, Synergia modes.
@ -162,7 +164,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiRefreshToken: String? = null private var mApiRefreshToken: String? = null
var apiRefreshToken: String? var apiRefreshToken: String?
get() { mApiRefreshToken = mApiRefreshToken ?: profile?.getStudentData("accountRefreshToken", null); return mApiRefreshToken } get() { mApiRefreshToken = mApiRefreshToken ?: profile?.getStudentData("accountRefreshToken", null); return mApiRefreshToken }
set(value) { mApiRefreshToken = value; profile?.putStudentData("accountRefreshToken", value) ?: return; } set(value) { mApiRefreshToken = value; profile["accountRefreshToken"] = value ?: return; }
/** /**
* The expiry time for [apiAccessToken], as a UNIX timestamp. * The expiry time for [apiAccessToken], as a UNIX timestamp.
* Used when refreshing the [apiAccessToken] in JST, Synergia modes. * Used when refreshing the [apiAccessToken] in JST, Synergia modes.
@ -171,7 +173,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiTokenExpiryTime: Long? = null private var mApiTokenExpiryTime: Long? = null
var apiTokenExpiryTime: Long var apiTokenExpiryTime: Long
get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L } get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L }
set(value) { mApiTokenExpiryTime = value; profile?.putStudentData("accountTokenTime", value) ?: return; } set(value) { mApiTokenExpiryTime = value; profile["accountTokenTime"] = value; }
/** /**
* A push device ID, generated by Librus when registering * A push device ID, generated by Librus when registering
@ -181,7 +183,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mPushDeviceId: Int? = null private var mPushDeviceId: Int? = null
var pushDeviceId: Int var pushDeviceId: Int
get() { mPushDeviceId = mPushDeviceId ?: profile?.getStudentData("pushDeviceId", 0); return mPushDeviceId ?: 0 } get() { mPushDeviceId = mPushDeviceId ?: profile?.getStudentData("pushDeviceId", 0); return mPushDeviceId ?: 0 }
set(value) { mPushDeviceId = value; profile?.putStudentData("pushDeviceId", value) ?: return; } set(value) { mPushDeviceId = value; profile["pushDeviceId"] = value; }
/* _____ _ /* _____ _
/ ____| (_) / ____| (_)
@ -198,7 +200,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mSynergiaSessionId: String? = null private var mSynergiaSessionId: String? = null
var synergiaSessionId: String? var synergiaSessionId: String?
get() { mSynergiaSessionId = mSynergiaSessionId ?: profile?.getStudentData("accountSID", null); return mSynergiaSessionId } get() { mSynergiaSessionId = mSynergiaSessionId ?: profile?.getStudentData("accountSID", null); return mSynergiaSessionId }
set(value) { profile?.putStudentData("accountSID", value) ?: return; mSynergiaSessionId = value } set(value) { profile["accountSID"] = value; mSynergiaSessionId = value }
/** /**
* The expiry time for [synergiaSessionId], as a UNIX timestamp. * The expiry time for [synergiaSessionId], as a UNIX timestamp.
* Used in endpoints with Synergia login method. * Used in endpoints with Synergia login method.
@ -207,7 +209,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mSynergiaSessionIdExpiryTime: Long? = null private var mSynergiaSessionIdExpiryTime: Long? = null
var synergiaSessionIdExpiryTime: Long var synergiaSessionIdExpiryTime: Long
get() { mSynergiaSessionIdExpiryTime = mSynergiaSessionIdExpiryTime ?: profile?.getStudentData("accountSIDTime", 0L); return mSynergiaSessionIdExpiryTime ?: 0L } get() { mSynergiaSessionIdExpiryTime = mSynergiaSessionIdExpiryTime ?: profile?.getStudentData("accountSIDTime", 0L); return mSynergiaSessionIdExpiryTime ?: 0L }
set(value) { profile?.putStudentData("accountSIDTime", value) ?: return; mSynergiaSessionIdExpiryTime = value } set(value) { profile["accountSIDTime"] = value; mSynergiaSessionIdExpiryTime = value }
/** /**
@ -217,7 +219,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mMessagesSessionId: String? = null private var mMessagesSessionId: String? = null
var messagesSessionId: String? var messagesSessionId: String?
get() { mMessagesSessionId = mMessagesSessionId ?: profile?.getStudentData("messagesSID", null); return mMessagesSessionId } get() { mMessagesSessionId = mMessagesSessionId ?: profile?.getStudentData("messagesSID", null); return mMessagesSessionId }
set(value) { profile?.putStudentData("messagesSID", value) ?: return; mMessagesSessionId = value } set(value) { profile["messagesSID"] = value; mMessagesSessionId = value }
/** /**
* The expiry time for [messagesSessionId], as a UNIX timestamp. * The expiry time for [messagesSessionId], as a UNIX timestamp.
* Used in endpoints with Messages login method. * Used in endpoints with Messages login method.
@ -226,7 +228,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mMessagesSessionIdExpiryTime: Long? = null private var mMessagesSessionIdExpiryTime: Long? = null
var messagesSessionIdExpiryTime: Long var messagesSessionIdExpiryTime: Long
get() { mMessagesSessionIdExpiryTime = mMessagesSessionIdExpiryTime ?: profile?.getStudentData("messagesSIDTime", 0L); return mMessagesSessionIdExpiryTime ?: 0L } get() { mMessagesSessionIdExpiryTime = mMessagesSessionIdExpiryTime ?: profile?.getStudentData("messagesSIDTime", 0L); return mMessagesSessionIdExpiryTime ?: 0L }
set(value) { profile?.putStudentData("messagesSIDTime", value) ?: return; mMessagesSessionIdExpiryTime = value } set(value) { profile["messagesSIDTime"] = value; mMessagesSessionIdExpiryTime = value }
/* ____ _ _ /* ____ _ _
/ __ \| | | | / __ \| | | |
@ -236,42 +238,42 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
\____/ \__|_| |_|\___|*/ \____/ \__|_| |_|\___|*/
var isPremium var isPremium
get() = profile?.getStudentData("isPremium", false) ?: false get() = profile?.getStudentData("isPremium", false) ?: false
set(value) { profile?.putStudentData("isPremium", value) } set(value) { profile["isPremium"] = value }
private var mSchoolName: String? = null private var mSchoolName: String? = null
var schoolName: String? var schoolName: String?
get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName } get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName }
set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value } set(value) { profile["schoolName"] = value; mSchoolName = value }
private var mUnitId: Long? = null private var mUnitId: Long? = null
var unitId: Long var unitId: Long
get() { mUnitId = mUnitId ?: profile?.getStudentData("unitId", 0L); return mUnitId ?: 0L } get() { mUnitId = mUnitId ?: profile?.getStudentData("unitId", 0L); return mUnitId ?: 0L }
set(value) { profile?.putStudentData("unitId", value) ?: return; mUnitId = value } set(value) { profile["unitId"] = value; mUnitId = value }
private var mStartPointsSemester1: Int? = null private var mStartPointsSemester1: Int? = null
var startPointsSemester1: Int var startPointsSemester1: Int
get() { mStartPointsSemester1 = mStartPointsSemester1 ?: profile?.getStudentData("startPointsSemester1", 0); return mStartPointsSemester1 ?: 0 } get() { mStartPointsSemester1 = mStartPointsSemester1 ?: profile?.getStudentData("startPointsSemester1", 0); return mStartPointsSemester1 ?: 0 }
set(value) { profile?.putStudentData("startPointsSemester1", value) ?: return; mStartPointsSemester1 = value } set(value) { profile["startPointsSemester1"] = value; mStartPointsSemester1 = value }
private var mStartPointsSemester2: Int? = null private var mStartPointsSemester2: Int? = null
var startPointsSemester2: Int var startPointsSemester2: Int
get() { mStartPointsSemester2 = mStartPointsSemester2 ?: profile?.getStudentData("startPointsSemester2", 0); return mStartPointsSemester2 ?: 0 } get() { mStartPointsSemester2 = mStartPointsSemester2 ?: profile?.getStudentData("startPointsSemester2", 0); return mStartPointsSemester2 ?: 0 }
set(value) { profile?.putStudentData("startPointsSemester2", value) ?: return; mStartPointsSemester2 = value } set(value) { profile["startPointsSemester2"] = value; mStartPointsSemester2 = value }
private var mEnablePointGrades: Boolean? = null private var mEnablePointGrades: Boolean? = null
var enablePointGrades: Boolean var enablePointGrades: Boolean
get() { mEnablePointGrades = mEnablePointGrades ?: profile?.getStudentData("enablePointGrades", true); return mEnablePointGrades ?: true } get() { mEnablePointGrades = mEnablePointGrades ?: profile?.getStudentData("enablePointGrades", true); return mEnablePointGrades ?: true }
set(value) { profile?.putStudentData("enablePointGrades", value) ?: return; mEnablePointGrades = value } set(value) { profile["enablePointGrades"] = value; mEnablePointGrades = value }
private var mEnableDescriptiveGrades: Boolean? = null private var mEnableDescriptiveGrades: Boolean? = null
var enableDescriptiveGrades: Boolean var enableDescriptiveGrades: Boolean
get() { mEnableDescriptiveGrades = mEnableDescriptiveGrades ?: profile?.getStudentData("enableDescriptiveGrades", true); return mEnableDescriptiveGrades ?: true } get() { mEnableDescriptiveGrades = mEnableDescriptiveGrades ?: profile?.getStudentData("enableDescriptiveGrades", true); return mEnableDescriptiveGrades ?: true }
set(value) { profile?.putStudentData("enableDescriptiveGrades", value) ?: return; mEnableDescriptiveGrades = value } set(value) { profile["enableDescriptiveGrades"] = value; mEnableDescriptiveGrades = value }
private var mTimetableNotPublic: Boolean? = null private var mTimetableNotPublic: Boolean? = null
var timetableNotPublic: Boolean var timetableNotPublic: Boolean
get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false } get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false }
set(value) { profile?.putStudentData("timetableNotPublic", value) ?: return; mTimetableNotPublic = value } set(value) { profile["timetableNotPublic"] = value; mTimetableNotPublic = value }
/** /**
* Set to false when Recaptcha helper doesn't provide a working token. * Set to false when Recaptcha helper doesn't provide a working token.

View File

@ -36,7 +36,7 @@ class LibrusApiNotices(override val data: DataLibrus,
val id = note.getLong("Id") ?: return@forEach val id = note.getLong("Id") ?: return@forEach
val text = note.getString("Text") ?: "" val text = note.getString("Text") ?: ""
val categoryId = note.getJsonObject("Category")?.getLong("Id") ?: -1 val categoryId = note.getJsonObject("Category")?.getLong("Id") ?: -1
val teacherId = note.getJsonObject("AddedBy")?.getLong("Id") ?: -1 val teacherId = note.getJsonObject("Teacher")?.getLong("Id") ?: -1
val addedDate = note.getString("Date")?.let { Date.fromY_m_d(it) } ?: return@forEach val addedDate = note.getString("Date")?.let { Date.fromY_m_d(it) } ?: return@forEach
val type = when (note.getInt("Positive")) { val type = when (note.getInt("Positive")) {

View File

@ -190,6 +190,7 @@ class LibrusApiTimetables(override val data: DataLibrus,
} }
lessonObject.id = lessonObject.buildId() lessonObject.id = lessonObject.buildId()
lessonObject.ownerId = lessonObject.buildOwnerId()
val seen = profile.empty || lessonDate < Date.getToday() val seen = profile.empty || lessonDate < Date.getToday()

View File

@ -125,7 +125,7 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
val receiverId = teacher?.id ?: -1 val receiverId = teacher?.id ?: -1
teacher?.loginId = receiverLoginId teacher?.loginId = receiverLoginId
val readDateText = message.select("readed").text() val readDateText = receiver.select("readed").text()
val readDate = when (readDateText.isNotNullNorEmpty()) { val readDate = when (readDateText.isNotNullNorEmpty()) {
true -> Date.fromIso(readDateText) true -> Date.fromIso(readDateText)
else -> 0 else -> 0

View File

@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.HOUR import pl.szczodrzynski.edziennik.ext.HOUR
import pl.szczodrzynski.edziennik.ext.get import pl.szczodrzynski.edziennik.ext.get
import pl.szczodrzynski.edziennik.ext.getSemesterStart
import pl.szczodrzynski.edziennik.ext.singleOrNull import pl.szczodrzynski.edziennik.ext.singleOrNull
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date

View File

@ -11,7 +11,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.ext.currentTimeUnix import pl.szczodrzynski.edziennik.ext.currentTimeUnix
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.set
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
@ -85,7 +87,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
private var mStudentId: Int? = null private var mStudentId: Int? = null
var studentId: Int var studentId: Int
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 } get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 }
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value } set(value) { profile["studentId"] = value; mStudentId = value }
/* __ __ _ /* __ __ _
\ \ / / | | \ \ / / | |
@ -125,7 +127,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
*/ */
var globalId: String? var globalId: String?
get() { mGlobalId = mGlobalId ?: profile?.getStudentData("globalId", null); return mGlobalId } get() { mGlobalId = mGlobalId ?: profile?.getStudentData("globalId", null); return mGlobalId }
set(value) { profile?.putStudentData("globalId", value) ?: return; mGlobalId = value } set(value) { profile["globalId"] = value; mGlobalId = value }
private var mGlobalId: String? = null private var mGlobalId: String? = null
/** /**
@ -135,7 +137,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
*/ */
var loginEmail: String? var loginEmail: String?
get() { mLoginEmail = mLoginEmail ?: profile?.getStudentData("email", null); return mLoginEmail } get() { mLoginEmail = mLoginEmail ?: profile?.getStudentData("email", null); return mLoginEmail }
set(value) { profile?.putStudentData("email", value); mLoginEmail = value } set(value) { profile["email"] = value; mLoginEmail = value }
private var mLoginEmail: String? = null private var mLoginEmail: String? = null
/** /**
@ -144,7 +146,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
*/ */
var loginId: String? var loginId: String?
get() { mLoginId = mLoginId ?: profile?.getStudentData("loginId", null); return mLoginId } get() { mLoginId = mLoginId ?: profile?.getStudentData("loginId", null); return mLoginId }
set(value) { profile?.putStudentData("loginId", value) ?: return; mLoginId = value } set(value) { profile["loginId"] = value; mLoginId = value }
private var mLoginId: String? = null private var mLoginId: String? = null
/** /**
@ -152,7 +154,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
*/ */
var ciasteczkoAutoryzacji: String? var ciasteczkoAutoryzacji: String?
get() { mCiasteczkoAutoryzacji = mCiasteczkoAutoryzacji ?: profile?.getStudentData("ciasteczkoAutoryzacji", null); return mCiasteczkoAutoryzacji } get() { mCiasteczkoAutoryzacji = mCiasteczkoAutoryzacji ?: profile?.getStudentData("ciasteczkoAutoryzacji", null); return mCiasteczkoAutoryzacji }
set(value) { profile?.putStudentData("ciasteczkoAutoryzacji", value) ?: return; mCiasteczkoAutoryzacji = value } set(value) { profile["ciasteczkoAutoryzacji"] = value; mCiasteczkoAutoryzacji = value }
private var mCiasteczkoAutoryzacji: String? = null private var mCiasteczkoAutoryzacji: String? = null

View File

@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESE
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.dateToSemester
class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List<String>) { class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List<String>) {
init { run { init { run {

View File

@ -88,6 +88,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
} }
it.id = it.buildId() it.id = it.buildId()
it.ownerId = it.buildOwnerId()
val seen = profile.empty || date < Date.getToday() val seen = profile.empty || date < Date.getToday()

View File

@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBID
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.ext.DAY import pl.szczodrzynski.edziennik.ext.DAY
import pl.szczodrzynski.edziennik.ext.get import pl.szczodrzynski.edziennik.ext.get
import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank
class MobidziennikWebAccountEmail(override val data: DataMobidziennik, class MobidziennikWebAccountEmail(override val data: DataMobidziennik,
override val lastSync: Long?, override val lastSync: Long?,
@ -24,7 +25,8 @@ class MobidziennikWebAccountEmail(override val data: DataMobidziennik,
MobidziennikLuckyNumberExtractor(data, text) MobidziennikLuckyNumberExtractor(data, text)
val email = Regexes.MOBIDZIENNIK_ACCOUNT_EMAIL.find(text)?.let { it[1] } val email = Regexes.MOBIDZIENNIK_ACCOUNT_EMAIL.find(text)?.let { it[1] }
data.loginEmail = email if (email.isNotNullNorBlank())
data.loginEmail = email
data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL, if (email == null) 3* DAY else 7* DAY) data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL, if (email == null) 3* DAY else 7* DAY)
onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL) onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL)

View File

@ -21,6 +21,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_UNKNO
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.dateToSemester
import pl.szczodrzynski.edziennik.ext.fixName import pl.szczodrzynski.edziennik.ext.fixName
import pl.szczodrzynski.edziennik.ext.get import pl.szczodrzynski.edziennik.ext.get
import pl.szczodrzynski.edziennik.ext.singleOrNull import pl.szczodrzynski.edziennik.ext.singleOrNull

View File

@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.dateToSemester
import pl.szczodrzynski.edziennik.ext.fixWhiteSpaces import pl.szczodrzynski.edziennik.ext.fixWhiteSpaces
import pl.szczodrzynski.edziennik.ext.get import pl.szczodrzynski.edziennik.ext.get
import pl.szczodrzynski.edziennik.ext.singleOrNull import pl.szczodrzynski.edziennik.ext.singleOrNull

View File

@ -336,6 +336,7 @@ class MobidziennikWebTimetable(
} }
it.id = it.buildId() it.id = it.buildId()
it.ownerId = it.buildOwnerId()
it.isExtra = isExtra it.isExtra = isExtra
val seen = profile?.empty == false || lessonDate < Date.getToday() val seen = profile?.empty == false || lessonDate < Date.getToday()

View File

@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.ext.JsonObject import pl.szczodrzynski.edziennik.ext.JsonObject
import pl.szczodrzynski.edziennik.ext.getJsonObject import pl.szczodrzynski.edziennik.ext.getJsonObject
import pl.szczodrzynski.edziennik.ext.getString import pl.szczodrzynski.edziennik.ext.getString
import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
@ -77,7 +78,9 @@ class MobidziennikLoginApi2(val data: DataMobidziennik, val onSuccess: () -> Uni
} }
} }
data.loginEmail = json.getString("email") val email = json.getString("email")
if (email.isNotNullNorBlank())
data.loginEmail = email
data.globalId = json.getString("id_global") data.globalId = json.getString("id_global")
data.loginId = json.getString("login") data.loginId = json.getString("login")
onSuccess() onSuccess()

View File

@ -10,7 +10,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.ext.crc32 import pl.szczodrzynski.edziennik.ext.crc32
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.set
class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
@ -40,7 +42,7 @@ class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
private var mApiUrl: String? = null private var mApiUrl: String? = null
var apiUrl: String? var apiUrl: String?
get() { mApiUrl = mApiUrl ?: profile?.getStudentData("apiUrl", null); return mApiUrl } get() { mApiUrl = mApiUrl ?: profile?.getStudentData("apiUrl", null); return mApiUrl }
set(value) { profile?.putStudentData("apiUrl", value) ?: return; mApiUrl = value } set(value) { profile["apiUrl"] = value; mApiUrl = value }
/* ____ _ _ /* ____ _ _
/ __ \| | | | / __ \| | | |
@ -51,32 +53,32 @@ class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
private var mStudentId: String? = null private var mStudentId: String? = null
var studentId: String? var studentId: String?
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", null); return mStudentId } get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", null); return mStudentId }
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value } set(value) { profile["studentId"] = value; mStudentId = value }
private var mStudentLogin: String? = null private var mStudentLogin: String? = null
var studentLogin: String? var studentLogin: String?
get() { mStudentLogin = mStudentLogin ?: profile?.getStudentData("studentLogin", null); return mStudentLogin } get() { mStudentLogin = mStudentLogin ?: profile?.getStudentData("studentLogin", null); return mStudentLogin }
set(value) { profile?.putStudentData("studentLogin", value) ?: return; mStudentLogin = value } set(value) { profile["studentLogin"] = value; mStudentLogin = value }
private var mSchoolName: String? = null private var mSchoolName: String? = null
var schoolName: String? var schoolName: String?
get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName } get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName }
set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value } set(value) { profile["schoolName"] = value; mSchoolName = value }
private var mClassName: String? = null private var mClassName: String? = null
var className: String? var className: String?
get() { mClassName = mClassName ?: profile?.getStudentData("className", null); return mClassName } get() { mClassName = mClassName ?: profile?.getStudentData("className", null); return mClassName }
set(value) { profile?.putStudentData("className", value) ?: return; mClassName = value } set(value) { profile["className"] = value; mClassName = value }
private var mSchoolYear: String? = null private var mSchoolYear: String? = null
var schoolYear: String? var schoolYear: String?
get() { mSchoolYear = mSchoolYear ?: profile?.getStudentData("schoolYear", null); return mSchoolYear } get() { mSchoolYear = mSchoolYear ?: profile?.getStudentData("schoolYear", null); return mSchoolYear }
set(value) { profile?.putStudentData("schoolYear", value) ?: return; mSchoolYear = value } set(value) { profile["schoolYear"] = value; mSchoolYear = value }
private var mCurrentSemester: Int? = null private var mCurrentSemester: Int? = null
var currentSemester: Int var currentSemester: Int
get() { mCurrentSemester = mCurrentSemester ?: profile?.getStudentData("currentSemester", 0); return mCurrentSemester ?: 0 } get() { mCurrentSemester = mCurrentSemester ?: profile?.getStudentData("currentSemester", 0); return mCurrentSemester ?: 0 }
set(value) { profile?.putStudentData("currentSemester", value) ?: return; mCurrentSemester = value } set(value) { profile["currentSemester"] = value; mCurrentSemester = value }
val schoolShortName: String? val schoolShortName: String?
get() = studentLogin?.split('@')?.get(1)?.replace(".podlaskie.pl", "") get() = studentLogin?.split('@')?.get(1)?.replace(".podlaskie.pl", "")

View File

@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPO
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.getLong import pl.szczodrzynski.edziennik.ext.getLong
import pl.szczodrzynski.edziennik.ext.getSemesterStart
import pl.szczodrzynski.edziennik.ext.getString import pl.szczodrzynski.edziennik.ext.getString
class PodlasieApiFinalGrades(val data: DataPodlasie, val rows: List<JsonObject>) { class PodlasieApiFinalGrades(val data: DataPodlasie, val rows: List<JsonObject>) {

View File

@ -72,6 +72,7 @@ class PodlasieApiTimetable(val data: DataPodlasie, rows: List<JsonObject>) {
it.classroom = classroom it.classroom = classroom
it.id = it.buildId() it.id = it.buildId()
it.ownerId = it.buildOwnerId()
data.lessonList += it data.lessonList += it
} }
} }

View File

@ -10,7 +10,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.ext.currentTimeUnix import pl.szczodrzynski.edziennik.ext.currentTimeUnix
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.set
/** /**
* Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art * Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art
@ -43,12 +45,12 @@ class DataTemplate(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
private var mWebCookie: String? = null private var mWebCookie: String? = null
var webCookie: String? var webCookie: String?
get() { mWebCookie = mWebCookie ?: profile?.getStudentData("webCookie", null); return mWebCookie } get() { mWebCookie = mWebCookie ?: profile?.getStudentData("webCookie", null); return mWebCookie }
set(value) { profile?.putStudentData("webCookie", value) ?: return; mWebCookie = value } set(value) { profile["webCookie"] = value; mWebCookie = value }
private var mWebExpiryTime: Long? = null private var mWebExpiryTime: Long? = null
var webExpiryTime: Long var webExpiryTime: Long
get() { mWebExpiryTime = mWebExpiryTime ?: profile?.getStudentData("webExpiryTime", 0L); return mWebExpiryTime ?: 0L } get() { mWebExpiryTime = mWebExpiryTime ?: profile?.getStudentData("webExpiryTime", 0L); return mWebExpiryTime ?: 0L }
set(value) { profile?.putStudentData("webExpiryTime", value) ?: return; mWebExpiryTime = value } set(value) { profile["webExpiryTime"] = value; mWebExpiryTime = value }
/* _ /* _
/\ (_) /\ (_)
@ -61,10 +63,10 @@ class DataTemplate(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
private var mApiToken: String? = null private var mApiToken: String? = null
var apiToken: String? var apiToken: String?
get() { mApiToken = mApiToken ?: profile?.getStudentData("apiToken", null); return mApiToken } get() { mApiToken = mApiToken ?: profile?.getStudentData("apiToken", null); return mApiToken }
set(value) { profile?.putStudentData("apiToken", value) ?: return; mApiToken = value } set(value) { profile["apiToken"] = value; mApiToken = value }
private var mApiExpiryTime: Long? = null private var mApiExpiryTime: Long? = null
var apiExpiryTime: Long var apiExpiryTime: Long
get() { mApiExpiryTime = mApiExpiryTime ?: profile?.getStudentData("apiExpiryTime", 0L); return mApiExpiryTime ?: 0L } get() { mApiExpiryTime = mApiExpiryTime ?: profile?.getStudentData("apiExpiryTime", 0L); return mApiExpiryTime ?: 0L }
set(value) { profile?.putStudentData("apiExpiryTime", value) ?: return; mApiExpiryTime = value } set(value) { profile["apiExpiryTime"] = value; mApiExpiryTime = value }
} }

View File

@ -9,6 +9,8 @@ import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.set
class DataUsos( class DataUsos(
app: App, app: App,
@ -69,6 +71,6 @@ class DataUsos(
var studentId: Int var studentId: Int
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 } get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 }
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value } set(value) { profile["studentId"] = value; mStudentId = value }
private var mStudentId: Int? = null private var mStudentId: Int? = null
} }

View File

@ -41,24 +41,18 @@ class UsosApiTerms(
} }
private fun processResponse(json: JsonArray): Boolean { private fun processResponse(json: JsonArray): Boolean {
val dates = mutableSetOf<Date>() val today = Date.getToday()
for (term in json.asJsonObjectList()) { for (term in json.asJsonObjectList()) {
if (!term.getBoolean("is_active", false)) if (!term.getBoolean("is_active", false))
continue continue
val startDate = term.getString("start_date")?.let { Date.fromY_m_d(it) } val startDate = term.getString("start_date")?.let { Date.fromY_m_d(it) } ?: continue
val finishDate = term.getString("finish_date")?.let { Date.fromY_m_d(it) } val finishDate = term.getString("finish_date")?.let { Date.fromY_m_d(it) } ?: continue
if (startDate != null) if (today in startDate..finishDate) {
dates += startDate profile?.studentSchoolYearStart = startDate.year
if (finishDate != null) profile?.dateSemester1Start = startDate
dates += finishDate profile?.dateSemester2Start = finishDate
}
} }
val datesSorted = dates.sorted()
if (datesSorted.size != 3)
return false
profile?.studentSchoolYearStart = datesSorted[0].year
profile?.dateSemester1Start = datesSorted[0]
profile?.dateSemester2Start = datesSorted[1]
profile?.dateYearEnd = datesSorted[2]
return true return true
} }
} }

View File

@ -104,8 +104,9 @@ class UsosApiTimetable(
it.teacherId = lecturerIds?.firstOrNull() ?: -1L it.teacherId = lecturerIds?.firstOrNull() ?: -1L
it.teamId = unitId it.teamId = unitId
val groupName = classTypeId?.plus(groupNumber)?.let { s -> "($s)" } val groupName = classTypeId?.plus(groupNumber)?.let { s -> "($s)" }
it.classroom = "$buildingId / $roomNumber ${groupName ?: ""}" it.classroom = "Sala $roomNumber / bud. $buildingId ${groupName ?: ""}"
it.id = it.buildId() it.id = it.buildId()
it.ownerId = it.buildOwnerId()
it.color = when (classTypeId) { it.color = when (classTypeId) {
"WYK" -> 0xff0d6091 "WYK" -> 0xff0d6091

View File

@ -12,7 +12,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.ext.crc16 import pl.szczodrzynski.edziennik.ext.crc16
import pl.szczodrzynski.edziennik.ext.currentTimeUnix import pl.szczodrzynski.edziennik.ext.currentTimeUnix
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.set
import pl.szczodrzynski.fslogin.realm.RealmData import pl.szczodrzynski.fslogin.realm.RealmData
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
@ -57,7 +59,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mSymbol: String? = null private var mSymbol: String? = null
var symbol: String? var symbol: String?
get() { mSymbol = mSymbol ?: profile?.getStudentData("symbol", null); return mSymbol } get() { mSymbol = mSymbol ?: profile?.getStudentData("symbol", null); return mSymbol }
set(value) { profile?.putStudentData("symbol", value); mSymbol = value } set(value) { profile["symbol"] = value; mSymbol = value }
/** /**
* Group symbol/number of the student's school. * Group symbol/number of the student's school.
@ -69,7 +71,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mSchoolSymbol: String? = null private var mSchoolSymbol: String? = null
var schoolSymbol: String? var schoolSymbol: String?
get() { mSchoolSymbol = mSchoolSymbol ?: profile?.getStudentData("schoolSymbol", null); return mSchoolSymbol } get() { mSchoolSymbol = mSchoolSymbol ?: profile?.getStudentData("schoolSymbol", null); return mSchoolSymbol }
set(value) { profile?.putStudentData("schoolSymbol", value) ?: return; mSchoolSymbol = value } set(value) { profile["schoolSymbol"] = value; mSchoolSymbol = value }
/** /**
* Short name of the school, used in some places. * Short name of the school, used in some places.
@ -79,7 +81,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mSchoolShort: String? = null private var mSchoolShort: String? = null
var schoolShort: String? var schoolShort: String?
get() { mSchoolShort = mSchoolShort ?: profile?.getStudentData("schoolShort", null); return mSchoolShort } get() { mSchoolShort = mSchoolShort ?: profile?.getStudentData("schoolShort", null); return mSchoolShort }
set(value) { profile?.putStudentData("schoolShort", value) ?: return; mSchoolShort = value } set(value) { profile["schoolShort"] = value; mSchoolShort = value }
/** /**
* A school code consisting of the [symbol] and [schoolSymbol]. * A school code consisting of the [symbol] and [schoolSymbol].
@ -91,7 +93,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mSchoolCode: String? = null private var mSchoolCode: String? = null
var schoolCode: String? var schoolCode: String?
get() { mSchoolCode = mSchoolCode ?: profile?.getStudentData("schoolName", null); return mSchoolCode } get() { mSchoolCode = mSchoolCode ?: profile?.getStudentData("schoolName", null); return mSchoolCode }
set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolCode = value } set(value) { profile["schoolName"] = value; mSchoolCode = value }
/** /**
* ID of the student. * ID of the student.
@ -101,7 +103,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mStudentId: Int? = null private var mStudentId: Int? = null
var studentId: Int var studentId: Int
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 } get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 }
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value } set(value) { profile["studentId"] = value; mStudentId = value }
/** /**
* ID of the student's account. * ID of the student's account.
@ -111,7 +113,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mStudentLoginId: Int? = null private var mStudentLoginId: Int? = null
var studentLoginId: Int var studentLoginId: Int
get() { mStudentLoginId = mStudentLoginId ?: profile?.getStudentData("studentLoginId", 0); return mStudentLoginId ?: 0 } get() { mStudentLoginId = mStudentLoginId ?: profile?.getStudentData("studentLoginId", 0); return mStudentLoginId ?: 0 }
set(value) { profile?.putStudentData("studentLoginId", value) ?: return; mStudentLoginId = value } set(value) { profile["studentLoginId"] = value; mStudentLoginId = value }
/** /**
* ID of the student's class. * ID of the student's class.
@ -121,7 +123,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mStudentClassId: Int? = null private var mStudentClassId: Int? = null
var studentClassId: Int var studentClassId: Int
get() { mStudentClassId = mStudentClassId ?: profile?.getStudentData("studentClassId", 0); return mStudentClassId ?: 0 } get() { mStudentClassId = mStudentClassId ?: profile?.getStudentData("studentClassId", 0); return mStudentClassId ?: 0 }
set(value) { profile?.putStudentData("studentClassId", value) ?: return; mStudentClassId = value } set(value) { profile["studentClassId"] = value; mStudentClassId = value }
/** /**
* ListaUczniow/IdOkresKlasyfikacyjny, e.g. 321 * ListaUczniow/IdOkresKlasyfikacyjny, e.g. 321
@ -129,26 +131,26 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mStudentSemesterId: Int? = null private var mStudentSemesterId: Int? = null
var studentSemesterId: Int var studentSemesterId: Int
get() { mStudentSemesterId = mStudentSemesterId ?: profile?.getStudentData("studentSemesterId", 0); return mStudentSemesterId ?: 0 } get() { mStudentSemesterId = mStudentSemesterId ?: profile?.getStudentData("studentSemesterId", 0); return mStudentSemesterId ?: 0 }
set(value) { profile?.putStudentData("studentSemesterId", value) ?: return; mStudentSemesterId = value } set(value) { profile["studentSemesterId"] = value; mStudentSemesterId = value }
private var mStudentUnitId: Int? = null private var mStudentUnitId: Int? = null
var studentUnitId: Int var studentUnitId: Int
get() { mStudentUnitId = mStudentUnitId ?: profile?.getStudentData("studentUnitId", 0); return mStudentUnitId ?: 0 } get() { mStudentUnitId = mStudentUnitId ?: profile?.getStudentData("studentUnitId", 0); return mStudentUnitId ?: 0 }
set(value) { profile?.putStudentData("studentUnitId", value) ?: return; mStudentUnitId = value } set(value) { profile["studentUnitId"] = value; mStudentUnitId = value }
private var mStudentConstituentId: Int? = null private var mStudentConstituentId: Int? = null
var studentConstituentId: Int var studentConstituentId: Int
get() { mStudentConstituentId = mStudentConstituentId ?: profile?.getStudentData("studentConstituentId", 0); return mStudentConstituentId ?: 0 } get() { mStudentConstituentId = mStudentConstituentId ?: profile?.getStudentData("studentConstituentId", 0); return mStudentConstituentId ?: 0 }
set(value) { profile?.putStudentData("studentConstituentId", value) ?: return; mStudentConstituentId = value } set(value) { profile["studentConstituentId"] = value; mStudentConstituentId = value }
private var mSemester1Id: Int? = null private var mSemester1Id: Int? = null
var semester1Id: Int var semester1Id: Int
get() { mSemester1Id = mSemester1Id ?: profile?.getStudentData("semester1Id", 0); return mSemester1Id ?: 0 } get() { mSemester1Id = mSemester1Id ?: profile?.getStudentData("semester1Id", 0); return mSemester1Id ?: 0 }
set(value) { profile?.putStudentData("semester1Id", value) ?: return; mSemester1Id = value } set(value) { profile["semester1Id"] = value; mSemester1Id = value }
private var mSemester2Id: Int? = null private var mSemester2Id: Int? = null
var semester2Id: Int var semester2Id: Int
get() { mSemester2Id = mSemester2Id ?: profile?.getStudentData("semester2Id", 0); return mSemester2Id ?: 0 } get() { mSemester2Id = mSemester2Id ?: profile?.getStudentData("semester2Id", 0); return mSemester2Id ?: 0 }
set(value) { profile?.putStudentData("semester2Id", value) ?: return; mSemester2Id = value } set(value) { profile["semester2Id"] = value; mSemester2Id = value }
/** /**
* ListaUczniow/OkresNumer, e.g. 1 or 2 * ListaUczniow/OkresNumer, e.g. 1 or 2
@ -156,7 +158,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mStudentSemesterNumber: Int? = null private var mStudentSemesterNumber: Int? = null
var studentSemesterNumber: Int var studentSemesterNumber: Int
get() { mStudentSemesterNumber = mStudentSemesterNumber ?: profile?.getStudentData("studentSemesterNumber", 0); return mStudentSemesterNumber ?: 0 } get() { mStudentSemesterNumber = mStudentSemesterNumber ?: profile?.getStudentData("studentSemesterNumber", 0); return mStudentSemesterNumber ?: 0 }
set(value) { profile?.putStudentData("studentSemesterNumber", value) ?: return; mStudentSemesterNumber = value } set(value) { profile["studentSemesterNumber"] = value; mStudentSemesterNumber = value }
/** /**
* Date of the end of the current semester ([studentSemesterNumber]). * Date of the end of the current semester ([studentSemesterNumber]).
@ -166,7 +168,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mCurrentSemesterEndDate: Long? = null private var mCurrentSemesterEndDate: Long? = null
var currentSemesterEndDate: Long var currentSemesterEndDate: Long
get() { mCurrentSemesterEndDate = mCurrentSemesterEndDate ?: profile?.getStudentData("currentSemesterEndDate", 0L); return mCurrentSemesterEndDate ?: 0L } get() { mCurrentSemesterEndDate = mCurrentSemesterEndDate ?: profile?.getStudentData("currentSemesterEndDate", 0L); return mCurrentSemesterEndDate ?: 0L }
set(value) { profile?.putStudentData("currentSemesterEndDate", value) ?: return; mCurrentSemesterEndDate = value } set(value) { profile["currentSemesterEndDate"] = value; mCurrentSemesterEndDate = value }
/* _____ _____ ____ /* _____ _____ ____
/\ | __ \_ _| |___ \ /\ | __ \_ _| |___ \
@ -219,17 +221,17 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mHebeContext: String? = null private var mHebeContext: String? = null
var hebeContext: String? var hebeContext: String?
get() { mHebeContext = mHebeContext ?: profile?.getStudentData("hebeContext", null); return mHebeContext } get() { mHebeContext = mHebeContext ?: profile?.getStudentData("hebeContext", null); return mHebeContext }
set(value) { profile?.putStudentData("hebeContext", value) ?: return; mHebeContext = value } set(value) { profile["hebeContext"] = value; mHebeContext = value }
private var mMessageBoxKey: String? = null private var mMessageBoxKey: String? = null
var messageBoxKey: String? var messageBoxKey: String?
get() { mMessageBoxKey = mMessageBoxKey ?: profile?.getStudentData("messageBoxKey", null); return mMessageBoxKey } get() { mMessageBoxKey = mMessageBoxKey ?: profile?.getStudentData("messageBoxKey", null); return mMessageBoxKey }
set(value) { profile?.putStudentData("messageBoxKey", value) ?: return; mMessageBoxKey = value } set(value) { profile["messageBoxKey"] = value; mMessageBoxKey = value }
private var mMessageBoxName: String? = null private var mMessageBoxName: String? = null
var messageBoxName: String? var messageBoxName: String?
get() { mMessageBoxName = mMessageBoxName ?: profile?.getStudentData("messageBoxName", null); return mMessageBoxName } get() { mMessageBoxName = mMessageBoxName ?: profile?.getStudentData("messageBoxName", null); return mMessageBoxName }
set(value) { profile?.putStudentData("messageBoxName", value) ?: return; mMessageBoxName = value } set(value) { profile["messageBoxName"] = value; mMessageBoxName = value }
val apiUrl: String? val apiUrl: String?
get() { get() {

View File

@ -26,6 +26,7 @@ import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.HttpURLConnection.HTTP_NOT_FOUND
import java.net.URLEncoder import java.net.URLEncoder
import java.time.Instant import java.time.Instant
import java.time.LocalDateTime import java.time.LocalDateTime
@ -183,6 +184,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
payload: JsonElement? = null, payload: JsonElement? = null,
baseUrl: Boolean = false, baseUrl: Boolean = false,
firebaseToken: String? = null, firebaseToken: String? = null,
allow404: Boolean = false,
crossinline onSuccess: (json: T, response: Response?) -> Unit crossinline onSuccess: (json: T, response: Response?) -> Unit
) { ) {
val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}$endpoint" val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}$endpoint"
@ -295,6 +297,19 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
} }
override fun onFailure(response: Response?, throwable: Throwable?) { override fun onFailure(response: Response?, throwable: Throwable?) {
if (allow404 && response?.code() == HTTP_NOT_FOUND) {
try {
onSuccess(null as T, response)
} catch (e: Exception) {
data.error(
ApiError(tag, EXCEPTION_VULCAN_HEBE_REQUEST)
.withResponse(response)
.withThrowable(e)
)
}
return
}
data.error( data.error(
ApiError(tag, ERROR_REQUEST_FAILURE) ApiError(tag, ERROR_REQUEST_FAILURE)
.withResponse(response) .withResponse(response)
@ -338,6 +353,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
query: Map<String, String> = mapOf(), query: Map<String, String> = mapOf(),
baseUrl: Boolean = false, baseUrl: Boolean = false,
firebaseToken: String? = null, firebaseToken: String? = null,
allow404: Boolean = false,
crossinline onSuccess: (json: T, response: Response?) -> Unit crossinline onSuccess: (json: T, response: Response?) -> Unit
) { ) {
val queryPath = query.map { val queryPath = query.map {
@ -348,6 +364,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
if (query.isNotEmpty()) "$endpoint?$queryPath" else endpoint, if (query.isNotEmpty()) "$endpoint?$queryPath" else endpoint,
baseUrl = baseUrl, baseUrl = baseUrl,
firebaseToken = firebaseToken, firebaseToken = firebaseToken,
allow404 = allow404,
onSuccess = onSuccess onSuccess = onSuccess
) )
} }
@ -382,6 +399,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
messageBox: String? = null, messageBox: String? = null,
params: Map<String, String> = mapOf(), params: Map<String, String> = mapOf(),
includeFilterType: Boolean = true, includeFilterType: Boolean = true,
allow404: Boolean = false,
onSuccess: (data: List<JsonObject>, response: Response?) -> Unit onSuccess: (data: List<JsonObject>, response: Response?) -> Unit
) { ) {
val url = if (includeFilterType && filterType != null) val url = if (includeFilterType && filterType != null)
@ -427,8 +445,8 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
) )
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
apiGet(tag, url, query) { json: JsonArray, response -> apiGet(tag, url, query, allow404 = allow404) { json: JsonArray?, response ->
onSuccess(json.map { it.asJsonObject }, response) onSuccess(json?.map { it.asJsonObject } ?: listOf(), response)
} }
} }
} }

View File

@ -19,6 +19,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_PARENTS_
import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_STUDENT import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_STUDENT
import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_TEACHER import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_TEACHER
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import java.net.HttpURLConnection.HTTP_NOT_FOUND
class VulcanHebeAddressbook( class VulcanHebeAddressbook(
override val data: DataVulcan, override val data: DataVulcan,
@ -41,8 +42,15 @@ class VulcanHebeAddressbook(
VULCAN_HEBE_ENDPOINT_ADDRESSBOOK, VULCAN_HEBE_ENDPOINT_ADDRESSBOOK,
HebeFilterType.BY_PERSON, HebeFilterType.BY_PERSON,
lastSync = lastSync, lastSync = lastSync,
includeFilterType = false includeFilterType = false,
) { list, _ -> allow404 = true,
) { list, response ->
if (response?.code() == HTTP_NOT_FOUND) {
data.setSyncNext(ENDPOINT_VULCAN_HEBE_ADDRESSBOOK, 2 * DAY)
onSuccess(ENDPOINT_VULCAN_HEBE_ADDRESSBOOK)
return@apiGetList
}
list.forEach { person -> list.forEach { person ->
val id = person.getString("Id") ?: return@forEach val id = person.getString("Id") ?: return@forEach

View File

@ -247,7 +247,9 @@ class VulcanHebeTimetable(
} }
lessonObject.id = lessonObject.buildId() lessonObject.id = lessonObject.buildId()
lessonObject.ownerId = lessonObject.buildOwnerId()
lessonShift?.id = lessonShift?.buildId() ?: -1 lessonShift?.id = lessonShift?.buildId() ?: -1
lessonShift?.ownerId = lessonShift?.buildOwnerId() ?: -1
lessonList.add(lessonObject) lessonList.add(lessonObject)
lessonShift?.let { lessonList.add(it) } lessonShift?.let { lessonList.add(it) }

View File

@ -13,7 +13,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_API_INVALID_SIGNATURE import pl.szczodrzynski.edziennik.data.api.ERROR_API_INVALID_SIGNATURE
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.DateAdapter import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.DateAdapter
@ -128,16 +127,10 @@ class SzkolnyApi(val app: App) : CoroutineScope {
response: Response<ApiResponse<T>>, response: Response<ApiResponse<T>>,
updateDeviceHash: Boolean = false, updateDeviceHash: Boolean = false,
): T { ): T {
app.config.update = response.body()?.update?.let { update -> response.body()?.update?.let { update ->
if (update.versionCode > BuildConfig.VERSION_CODE) { // do not process "null" update, as it might not mean there's no update
if (update.updateMandatory // do not notify; silently check and show the home update card
&& EventBus.getDefault().hasSubscriberForEvent(update::class.java)) { app.updateManager.process(update, notify = false)
EventBus.getDefault().postSticky(update)
}
update
}
else
null
} }
response.body()?.registerAvailability?.let { registerAvailability -> response.body()?.registerAvailability?.let { registerAvailability ->
@ -205,7 +198,6 @@ class SzkolnyApi(val app: App) : CoroutineScope {
val teams = app.db.teamDao().allNow val teams = app.db.teamDao().allNow
val users = profiles.mapNotNull { profile -> val users = profiles.mapNotNull { profile ->
val config = app.config.getFor(profile.id)
val user = ServerSyncRequest.User( val user = ServerSyncRequest.User(
profile.userCode, profile.userCode,
profile.studentNameLong, profile.studentNameLong,
@ -214,9 +206,9 @@ class SzkolnyApi(val app: App) : CoroutineScope {
teams.filter { it.profileId == profile.id }.map { it.code } teams.filter { it.profileId == profile.id }.map { it.code }
) )
val hash = user.toString().md5() val hash = user.toString().md5()
if (hash == config.hash) if (hash == profile.config.hash)
return@mapNotNull null return@mapNotNull null
return@mapNotNull user to config return@mapNotNull user to profile.config
} }
val response = api.serverSync(ServerSyncRequest( val response = api.serverSync(ServerSyncRequest(
@ -265,12 +257,10 @@ class SzkolnyApi(val app: App) : CoroutineScope {
seen = profile.empty seen = profile.empty
notified = profile.empty notified = profile.empty
if (profile.userCode == event.sharedBy) { sharedBy = if (profile.userCode == event.sharedBy)
sharedBy = "self" "self"
addedManually = true else
} else { eventSharedBy
sharedBy = eventSharedBy
}
} }
} }
} }
@ -334,11 +324,14 @@ class SzkolnyApi(val app: App) : CoroutineScope {
} }
@Throws(Exception::class) @Throws(Exception::class)
fun shareNote(note: Note) { fun shareNote(note: Note, teamId: Long? = null) {
val profile = app.db.profileDao().getByIdNow(note.profileId) val profile = app.db.profileDao().getByIdNow(note.profileId)
?: throw NullPointerException("Profile is not found") ?: throw NullPointerException("Profile is not found")
val team = app.db.teamDao().getClassNow(note.profileId) val team = if (teamId == null)
?: throw NullPointerException("TeamClass is not found") app.db.teamDao().getClassNow(note.profileId)
else
app.db.teamDao().getByIdNow(note.profileId, teamId)
team ?: throw NullPointerException("TeamClass is not found")
val response = api.shareNote(NoteShareRequest( val response = api.shareNote(NoteShareRequest(
deviceId = app.deviceId, deviceId = app.deviceId,
@ -431,8 +424,8 @@ class SzkolnyApi(val app: App) : CoroutineScope {
} }
@Throws(Exception::class) @Throws(Exception::class)
fun getUpdate(channel: String): List<Update> { fun getUpdate(channel: Update.Type): List<Update> {
val response = api.updates(channel).execute() val response = api.updates(channel.name.lowercase()).execute()
return parseResponse(response) return parseResponse(response)
} }

View File

@ -12,10 +12,11 @@ import pl.szczodrzynski.edziennik.ext.bodyToString
import pl.szczodrzynski.edziennik.ext.currentTimeUnix import pl.szczodrzynski.edziennik.ext.currentTimeUnix
import pl.szczodrzynski.edziennik.ext.hmacSHA1 import pl.szczodrzynski.edziennik.ext.hmacSHA1
import pl.szczodrzynski.edziennik.ext.md5 import pl.szczodrzynski.edziennik.ext.md5
import pl.szczodrzynski.edziennik.ext.takeValue
class SignatureInterceptor(val app: App) : Interceptor { class SignatureInterceptor(val app: App) : Interceptor {
companion object { companion object {
private const val API_KEY = "szkolny_android_42a66f0842fc7da4e37c66732acf705a" const val API_KEY = "szkolny_android_42a66f0842fc7da4e37c66732acf705a"
} }
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
@ -27,7 +28,7 @@ class SignatureInterceptor(val app: App) : Interceptor {
return chain.proceed( return chain.proceed(
request.newBuilder() request.newBuilder()
.header("X-ApiKey", API_KEY) .header("X-ApiKey", app.config.apiKeyCustom?.takeValue() ?: API_KEY)
.header("X-AppVersion", BuildConfig.VERSION_CODE.toString()) .header("X-AppVersion", BuildConfig.VERSION_CODE.toString())
.header("X-Timestamp", timestamp.toString()) .header("X-Timestamp", timestamp.toString())
.header("X-Signature", sign(timestamp, body, url)) .header("X-Signature", sign(timestamp, body, url))

View File

@ -46,6 +46,6 @@ object Signing {
/*fun provideKey(param1: String, param2: Long): ByteArray {*/ /*fun provideKey(param1: String, param2: Long): ByteArray {*/
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray { fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
return "$param1.MTIzNDU2Nzg5MDyDtdBKln===.$param2".sha256() return "$param1.MTIzNDU2Nzg5MD4BikzMWC===.$param2".sha256()
} }
} }

View File

@ -5,12 +5,21 @@
package pl.szczodrzynski.edziennik.data.api.szkolny.response package pl.szczodrzynski.edziennik.data.api.szkolny.response
data class Update( data class Update(
val versionCode: Int, val versionCode: Int,
val versionName: String, val versionName: String,
val releaseDate: String, val releaseDate: String,
val releaseNotes: String?, val releaseNotes: String?,
val releaseType: String, val releaseType: String,
val isOnGooglePlay: Boolean, val isOnGooglePlay: Boolean,
val downloadUrl: String?, val downloadUrl: String?,
val updateMandatory: Boolean val updateMandatory: Boolean,
) ) {
enum class Type {
NIGHTLY,
DEV,
BETA,
RC,
RELEASE,
}
}

View File

@ -55,7 +55,7 @@ class SzkolnyTask(val app: App, val syncingProfiles: List<Profile>) : IApiTask(-
notificationList notificationList
.mapNotNull { it.profileId } .mapNotNull { it.profileId }
.distinct() .distinct()
.map { app.config.getFor(it).sync.notificationFilter } .map { app.config[it].sync.notificationFilter }
.forEach { filter -> .forEach { filter ->
filter.forEach { type -> filter.forEach { type ->
notificationList.removeAll { it.type == type } notificationList.removeAll { it.type == type }

View File

@ -44,7 +44,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
TimetableManual::class, TimetableManual::class,
Note::class, Note::class,
Metadata::class Metadata::class
], version = 99) ], version = 100)
@TypeConverters( @TypeConverters(
ConverterTime::class, ConverterTime::class,
ConverterDate::class, ConverterDate::class,
@ -188,6 +188,7 @@ abstract class AppDb : RoomDatabase() {
Migration97(), Migration97(),
Migration98(), Migration98(),
Migration99(), Migration99(),
Migration100(),
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()
} }
} }

View File

@ -5,13 +5,37 @@
package pl.szczodrzynski.edziennik.data.db.converter package pl.szczodrzynski.edziennik.data.db.converter
import androidx.room.TypeConverter import androidx.room.TypeConverter
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.data.db.enums.LoginMode
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.data.db.enums.NotificationType
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget
class ConverterEnums { class ConverterEnums {
@TypeConverter @TypeConverter
fun fromEnum(value: Enum<*>?) = value?.toInt() fun fromFeatureType(value: FeatureType?) = value?.id
@TypeConverter
fun fromLoginMethod(value: LoginMethod?) = value?.id
@TypeConverter
fun fromLoginMode(value: LoginMode?) = value?.id
@TypeConverter
fun fromLoginType(value: LoginType?) = value?.id
@TypeConverter
fun fromMetadataType(value: MetadataType?) = value?.id
@TypeConverter
fun fromNotificationType(value: NotificationType?) = value?.id
@TypeConverter
fun fromNavTarget(value: NavTarget?) = value?.id
@TypeConverter @TypeConverter
fun toFeatureType(value: Int?) = value.asFeatureTypeOrNull() fun toFeatureType(value: Int?) = value.asFeatureTypeOrNull()

View File

@ -25,6 +25,9 @@ abstract class EventTypeDao {
@Query("DELETE FROM eventTypes WHERE profileId = :profileId") @Query("DELETE FROM eventTypes WHERE profileId = :profileId")
abstract fun clear(profileId: Int) abstract fun clear(profileId: Int)
@Query("DELETE FROM eventTypes WHERE profileId = :profileId AND eventTypeSource = :source")
abstract fun clearBySource(profileId: Int, source: Int)
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId") @Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
abstract fun getByIdNow(profileId: Int, typeId: Long): EventType? abstract fun getByIdNow(profileId: Int, typeId: Long): EventType?
@ -43,7 +46,7 @@ abstract class EventTypeDao {
val typeList = data.eventTypes.map { val typeList = data.eventTypes.map {
EventType( EventType(
profileId = profile.id, profileId = profile.id,
id = it.id.toLong(), id = it.id,
name = it.name, name = it.name,
color = Color.parseColor(it.color), color = Color.parseColor(it.color),
order = order++, order = order++,
@ -53,4 +56,21 @@ abstract class EventTypeDao {
addAll(typeList) addAll(typeList)
return typeList return typeList
} }
fun getAllWithDefaults(profile: Profile): List<EventType> {
val eventTypes = getAllNow(profile.id)
val defaultIdsExpected = AppData.get(profile.loginStoreType).eventTypes
.map { it.id }
val defaultIdsFound = eventTypes.filter { it.source == SOURCE_DEFAULT }
.sortedBy { it.order }
.map { it.id }
if (defaultIdsExpected == defaultIdsFound)
return eventTypes
clearBySource(profile.id, SOURCE_DEFAULT)
addDefaultTypes(profile)
return eventTypes
}
} }

View File

@ -28,6 +28,9 @@ interface ProfileDao {
@Query("SELECT * FROM profiles WHERE profileId = :profileId") @Query("SELECT * FROM profiles WHERE profileId = :profileId")
fun getByIdNow(profileId: Int): Profile? fun getByIdNow(profileId: Int): Profile?
@Query("SELECT * FROM profiles WHERE profileId = :profileId")
suspend fun getByIdSuspend(profileId: Int): Profile?
@get:Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId") @get:Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId")
val all: LiveData<List<Profile>> val all: LiveData<List<Profile>>
@ -49,7 +52,7 @@ interface ProfileDao {
@get:Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId") @get:Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId")
val idsNow: List<Int> val idsNow: List<Int>
@Query("SELECT profiles.* FROM teams JOIN profiles USING(profileId) WHERE teamCode = :teamCode AND registration = " + Profile.REGISTRATION_ENABLED + " AND enableSharedEvents = 1") @Query("SELECT profiles.* FROM teams JOIN profiles USING(profileId) WHERE teamCode = :teamCode AND registration = " + Profile.REGISTRATION_ENABLED)
fun getByTeamCodeNowWithRegistration(teamCode: String?): List<Profile> fun getByTeamCodeNowWithRegistration(teamCode: String?): List<Profile>
@get:Query("SELECT profileId FROM profiles WHERE profileId > 0 ORDER BY profileId ASC LIMIT 1") @get:Query("SELECT profileId FROM profiles WHERE profileId > 0 ORDER BY profileId ASC LIMIT 1")

View File

@ -107,6 +107,9 @@ abstract class TimetableDao : BaseDao<Lesson, LessonFull> {
fun getByIdNow(profileId: Int, id: Long) = fun getByIdNow(profileId: Int, id: Long) =
getOneNow("$QUERY WHERE timetable.profileId = $profileId AND timetable.id = $id") getOneNow("$QUERY WHERE timetable.profileId = $profileId AND timetable.id = $id")
fun getByOwnerIdNow(profileId: Int, ownerId: Long) =
getOneNow("$QUERY WHERE timetable.profileId = $profileId AND timetable.ownerId = $ownerId")
@Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND isExtra = :isExtra AND type != -1 AND ((type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom))") @Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND isExtra = :isExtra AND type != -1 AND ((type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom))")
abstract fun dontKeepFromDate(profileId: Int, dateFrom: Date, isExtra: Boolean) abstract fun dontKeepFromDate(profileId: Int, dateFrom: Date, isExtra: Boolean)

View File

@ -73,13 +73,22 @@ open class Event(
const val COLOR_INFORMATION = 0xff039be5.toInt() const val COLOR_INFORMATION = 0xff039be5.toInt()
} }
/**
* Added manually - added by self, shared by self, or shared by someone else.
*/
@ColumnInfo(name = "eventAddedManually") @ColumnInfo(name = "eventAddedManually")
var addedManually: Boolean = false var addedManually: Boolean = false
get() = field || sharedBy == "self" get() = field || isShared
/**
* Shared by - user code who shared the event. Null if not shared.
* "Self" if shared by this app user.
*/
@ColumnInfo(name = "eventSharedBy") @ColumnInfo(name = "eventSharedBy")
var sharedBy: String? = null var sharedBy: String? = null
@ColumnInfo(name = "eventSharedByName") @ColumnInfo(name = "eventSharedByName")
var sharedByName: String? = null var sharedByName: String? = null
@ColumnInfo(name = "eventBlacklisted") @ColumnInfo(name = "eventBlacklisted")
var blacklisted: Boolean = false var blacklisted: Boolean = false
@ColumnInfo(name = "eventIsDone") @ColumnInfo(name = "eventIsDone")
@ -104,6 +113,27 @@ open class Event(
var attachmentIds: MutableList<Long>? = null var attachmentIds: MutableList<Long>? = null
var attachmentNames: MutableList<String>? = null var attachmentNames: MutableList<String>? = null
val isHomework
get() = type == TYPE_HOMEWORK
/**
* Whether the event is shared by anyone. Note that this implies [addedManually].
*/
val isShared
get() = sharedBy != null
/**
* Whether the event is shared by "self" (this app user).
*/
val isSharedSent
get() = sharedBy == "self"
/**
* Whether the event is shared by someone else from the class group.
*/
val isSharedReceived
get() = sharedBy != null && sharedBy != "self"
/** /**
* Add an attachment * Add an attachment
* @param id attachment ID * @param id attachment ID
@ -134,9 +164,6 @@ open class Event(
it.timeInMillis += 45 * MINUTE * 1000 it.timeInMillis += 45 * MINUTE * 1000
} }
val isHomework
get() = type == TYPE_HOMEWORK
@Ignore @Ignore
fun withMetadata(metadata: Metadata) = EventFull(this, metadata) fun withMetadata(metadata: Metadata) = EventFull(this, metadata)
} }

View File

@ -5,31 +5,6 @@ package pl.szczodrzynski.edziennik.data.db.entity
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
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_ELEARNING
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_ELEARNING
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
@Entity( @Entity(
tableName = "eventTypes", tableName = "eventTypes",
@ -55,35 +30,5 @@ class EventType(
const val SOURCE_REGISTER = 1 const val SOURCE_REGISTER = 1
const val SOURCE_CUSTOM = 2 const val SOURCE_CUSTOM = 2
const val SOURCE_SHARED = 3 const val SOURCE_SHARED = 3
fun getTypeColorMap() = mapOf(
TYPE_ELEARNING to COLOR_ELEARNING,
TYPE_HOMEWORK to COLOR_HOMEWORK,
TYPE_DEFAULT to COLOR_DEFAULT,
TYPE_EXAM to COLOR_EXAM,
TYPE_SHORT_QUIZ to COLOR_SHORT_QUIZ,
TYPE_ESSAY to COLOR_ESSAY,
TYPE_PROJECT to COLOR_PROJECT,
TYPE_PT_MEETING to COLOR_PT_MEETING,
TYPE_EXCURSION to COLOR_EXCURSION,
TYPE_READING to COLOR_READING,
TYPE_CLASS_EVENT to COLOR_CLASS_EVENT,
TYPE_INFORMATION to COLOR_INFORMATION
)
fun getTypeNameMap() = mapOf(
TYPE_ELEARNING to R.string.event_type_elearning,
TYPE_HOMEWORK to R.string.event_type_homework,
TYPE_DEFAULT to R.string.event_other,
TYPE_EXAM to R.string.event_exam,
TYPE_SHORT_QUIZ to R.string.event_short_quiz,
TYPE_ESSAY to R.string.event_essay,
TYPE_PROJECT to R.string.event_project,
TYPE_PT_MEETING to R.string.event_pt_meeting,
TYPE_EXCURSION to R.string.event_excursion,
TYPE_READING to R.string.event_reading,
TYPE_CLASS_EVENT to R.string.event_class_event,
TYPE_INFORMATION to R.string.event_information
)
} }
} }

View File

@ -50,6 +50,13 @@ open class Lesson(
var isExtra: Boolean = false var isExtra: Boolean = false
/**
* Stable ID denoting this lesson, used for note sharing (i.e. [profileId]-independent).
*
* This is simply the Unix timestamp of the lesson (in seconds).
*/
var ownerId: Long = id
val displayDate: Date? val displayDate: Date?
get() { get() {
if (type == TYPE_SHIFTED_SOURCE) if (type == TYPE_SHIFTED_SOURCE)
@ -69,7 +76,12 @@ open class Lesson(
val isChange val isChange
get() = type == TYPE_CHANGE || type == TYPE_SHIFTED_TARGET get() = type == TYPE_CHANGE || type == TYPE_SHIFTED_TARGET
fun buildId(): Long = (displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L + (hashCode() and 0xFFFF) fun buildId(): Long =
(displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L +
(hashCode() and 0xFFFF)
fun buildOwnerId(): Long =
(displayDate?.combineWith(displayStartTime) ?: 0L)
@Ignore @Ignore
var showAsUnseen = false var showAsUnseen = false

View File

@ -9,6 +9,7 @@ interface Noteable {
fun getNoteType(): Note.OwnerType fun getNoteType(): Note.OwnerType
fun getNoteOwnerProfileId(): Int fun getNoteOwnerProfileId(): Int
fun getNoteOwnerId(): Long fun getNoteOwnerId(): Long
fun getNoteShareTeamId(): Long? = null
var notes: MutableList<Note> var notes: MutableList<Note>

View File

@ -5,25 +5,18 @@
package pl.szczodrzynski.edziennik.data.db.entity package pl.szczodrzynski.edziennik.data.db.entity
import android.content.Context import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable
import android.widget.ImageView import android.widget.ImageView
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Ignore import androidx.room.Ignore
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.db.enums.LoginType import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.dateToSemester
import pl.szczodrzynski.edziennik.utils.ProfileImageHolder import pl.szczodrzynski.edziennik.ext.getDrawable
import pl.szczodrzynski.edziennik.ext.getHolder
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.navlib.ImageHolder
import pl.szczodrzynski.navlib.R
import pl.szczodrzynski.navlib.drawer.IDrawerProfile import pl.szczodrzynski.navlib.drawer.IDrawerProfile
import pl.szczodrzynski.navlib.getDrawableFromRes
@Entity(tableName = "profiles", primaryKeys = ["profileId"]) @Entity(tableName = "profiles", primaryKeys = ["profileId"])
open class Profile( open class Profile(
@ -60,9 +53,13 @@ open class Profile(
} }
override var image: String? = null override var image: String? = null
var empty = true var empty = true
var archived = false var archived = false
var syncEnabled = true
@ColumnInfo(name = "enableSharedEvents")
var unused1 = true
var registration = REGISTRATION_UNSPECIFIED
var userCode = ""
/** /**
* A unique ID matching [archived] profiles with current ones * A unique ID matching [archived] profiles with current ones
@ -70,117 +67,35 @@ open class Profile(
*/ */
var archiveId: Int? = null var archiveId: Int? = null
var syncEnabled = true
var enableSharedEvents = true
var registration = REGISTRATION_UNSPECIFIED
var userCode = ""
/** /**
* The student's number in the class register. * The student's number in the class register.
*/ */
var studentNumber = -1 var studentNumber = -1
var studentClassName: String? = null var studentClassName: String? = null
var studentSchoolYearStart = Date.getToday().let { if (it.month < 9) it.year - 1 else it.year } var studentSchoolYearStart = Date.getToday().let { if (it.month < 9) it.year - 1 else it.year }
var dateSemester1Start = Date(studentSchoolYearStart, 9, 1) var dateSemester1Start = Date(studentSchoolYearStart, 9, 1)
var dateSemester2Start = Date(studentSchoolYearStart + 1, 2, 1) var dateSemester2Start = Date(studentSchoolYearStart + 1, 2, 1)
var dateYearEnd = Date(studentSchoolYearStart + 1, 6, 30) var dateYearEnd = Date(studentSchoolYearStart + 1, 6, 30)
fun getSemesterStart(semester: Int) = if (semester == 1) dateSemester1Start else dateSemester2Start
fun getSemesterEnd(semester: Int) = if (semester == 1) dateSemester2Start.clone().stepForward(0, 0, -1) else dateYearEnd
fun dateToSemester(date: Date) = if (date >= dateSemester2Start) 2 else 1
@delegate:Ignore
val currentSemester by lazy { dateToSemester(Date.getToday()) }
fun shouldArchive(): Boolean {
// vulcan hotfix
if (dateYearEnd.month > 6) {
dateYearEnd.month = 6
dateYearEnd.day = 30
}
// fix for when versions <4.3 synced 2020/2021 year dates to older profiles during 2020 Jun-Aug
if (dateSemester1Start.year > studentSchoolYearStart) {
val diff = dateSemester1Start.year - studentSchoolYearStart
dateSemester1Start.year -= diff
dateSemester2Start.year -= diff
dateYearEnd.year -= diff
}
return App.config.archiverEnabled
&& Date.getToday() >= dateYearEnd
&& Date.getToday().year > studentSchoolYearStart
}
fun isBeforeYear() = false && Date.getToday() < dateSemester1Start
var disabledNotifications: List<Long>? = null var disabledNotifications: List<Long>? = null
var lastReceiversSync: Long = 0 var lastReceiversSync: Long = 0
fun hasStudentData(key: String) = studentData.has(key) val currentSemester
fun getStudentData(key: String, defaultValue: Boolean) = studentData.getBoolean(key) ?: defaultValue get() = dateToSemester(Date.getToday())
fun getStudentData(key: String, defaultValue: String?) = studentData.getString(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: Int) = studentData.getInt(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: Long) = studentData.getLong(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: Float) = studentData.getFloat(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: Char) = studentData.getChar(key) ?: defaultValue
fun putStudentData(key: String, value: Boolean) { studentData[key] = value }
fun putStudentData(key: String, value: String?) { studentData[key] = value }
fun putStudentData(key: String, value: Number) { studentData[key] = value }
fun putStudentData(key: String, value: Char) { studentData[key] = value }
fun removeStudentData(key: String) { studentData.remove(key) }
val isParent val isParent
get() = accountName != null get() = accountName != null
val accountOwnerName val accountOwnerName
get() = accountName ?: studentNameLong get() = accountName ?: studentNameLong
val registerName
@Ignore get() = loginStoreType.name.lowercase()
val registerName = loginStoreType.name.lowercase()
val canShare val canShare
get() = registration == REGISTRATION_ENABLED && !archived get() = registration == REGISTRATION_ENABLED && !archived
override fun getImageDrawable(context: Context): Drawable { @delegate:Ignore
if (archived) { @delegate:Transient
return context.getDrawableFromRes(pl.szczodrzynski.edziennik.R.drawable.profile_archived).also { val config by lazy { App.config[this.id] }
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
}
}
if (!image.isNullOrEmpty()) { override fun getImageDrawable(context: Context) = this.getDrawable(context)
try { override fun getImageHolder(context: Context) = this.getHolder()
return if (image?.endsWith(".gif", true) == true) {
GifDrawable(image ?: "")
} else {
RoundedBitmapDrawableFactory.create(context.resources, image ?: "")
//return Drawable.createFromPath(image ?: "") ?: throw Exception()
}
}
catch (e: Exception) {
e.printStackTrace()
}
}
return context.getDrawableFromRes(R.drawable.profile).also {
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
}
}
override fun getImageHolder(context: Context): ImageHolder {
if (archived) {
return ImageHolder(pl.szczodrzynski.edziennik.R.drawable.profile_archived, colorFromName(name))
}
return if (!image.isNullOrEmpty()) {
try {
ProfileImageHolder(image ?: "")
} catch (_: Exception) {
ImageHolder(R.drawable.profile, colorFromName(name))
}
}
else {
ImageHolder(R.drawable.profile, colorFromName(name))
}
}
override fun applyImageTo(imageView: ImageView) { override fun applyImageTo(imageView: ImageView) {
getImageHolder(imageView.context).applyTo(imageView) getImageHolder(imageView.context).applyTo(imageView)
} }

View File

@ -6,6 +6,8 @@ package pl.szczodrzynski.edziennik.data.db.enums
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.ext.getString
import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank
enum class LoginMethod( enum class LoginMethod(
val loginType: LoginType, val loginType: LoginType,
@ -26,7 +28,7 @@ enum class LoginMethod(
MOBIDZIENNIK_API2( MOBIDZIENNIK_API2(
loginType = LoginType.MOBIDZIENNIK, loginType = LoginType.MOBIDZIENNIK,
id = 1300, id = 1300,
isPossible = { profile, _ -> profile?.studentData?.has("email") ?: false }, isPossible = { profile, _ -> profile?.studentData?.getString("email").isNotNullNorBlank() },
), ),
LIBRUS_PORTAL( LIBRUS_PORTAL(
loginType = LoginType.LIBRUS, loginType = LoginType.LIBRUS,
@ -57,7 +59,7 @@ enum class LoginMethod(
VULCAN_WEB_MAIN( VULCAN_WEB_MAIN(
loginType = LoginType.VULCAN, loginType = LoginType.VULCAN,
id = 4100, id = 4100,
isPossible = { _, loginStore -> loginStore.hasLoginData("webHost") }, isPossible = { _, loginStore -> loginStore.getLoginData("webHost", null).isNotNullNorBlank() },
), ),
VULCAN_HEBE( VULCAN_HEBE(
loginType = LoginType.VULCAN, loginType = LoginType.VULCAN,

View File

@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Note import pl.szczodrzynski.edziennik.data.db.entity.Note
import pl.szczodrzynski.edziennik.data.db.entity.Noteable import pl.szczodrzynski.edziennik.data.db.entity.Noteable
import pl.szczodrzynski.edziennik.ext.takePositive
import pl.szczodrzynski.edziennik.ui.search.Searchable import pl.szczodrzynski.edziennik.ui.search.Searchable
import pl.szczodrzynski.edziennik.utils.html.BetterHtml import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -118,4 +119,5 @@ class EventFull(
override fun getNoteType() = Note.OwnerType.EVENT override fun getNoteType() = Note.OwnerType.EVENT
override fun getNoteOwnerProfileId() = profileId override fun getNoteOwnerProfileId() = profileId
override fun getNoteOwnerId() = id override fun getNoteOwnerId() = id
override fun getNoteShareTeamId() = teamId.takePositive()
} }

View File

@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.entity.Note import pl.szczodrzynski.edziennik.data.db.entity.Note
import pl.szczodrzynski.edziennik.data.db.entity.Noteable import pl.szczodrzynski.edziennik.data.db.entity.Noteable
import pl.szczodrzynski.edziennik.ext.takePositive
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
class LessonFull( class LessonFull(
@ -137,9 +138,10 @@ class LessonFull(
var seen: Boolean = false var seen: Boolean = false
var notified: Boolean = false var notified: Boolean = false
@Relation(parentColumn = "id", entityColumn = "noteOwnerId", entity = Note::class) @Relation(parentColumn = "ownerId", entityColumn = "noteOwnerId", entity = Note::class)
override lateinit var notes: MutableList<Note> override lateinit var notes: MutableList<Note>
override fun getNoteType() = Note.OwnerType.LESSON override fun getNoteType() = Note.OwnerType.LESSON
override fun getNoteOwnerProfileId() = profileId override fun getNoteOwnerProfileId() = profileId
override fun getNoteOwnerId() = id override fun getNoteOwnerId() = ownerId
override fun getNoteShareTeamId() = teamId.takePositive()
} }

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-25.
*/
package pl.szczodrzynski.edziennik.data.db.migration
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration100 : Migration(99, 100) {
override fun migrate(database: SupportSQLiteDatabase) {
// add Note Owner ID to Lesson, to make it profileId-independent
// calculate the new owner ID based on the old ID
database.execSQL("ALTER TABLE timetable ADD COLUMN ownerId INT NOT NULL DEFAULT 0;")
// set new ID for actual lessons
database.execSQL("UPDATE timetable SET ownerId = ROUND((id & ~65535) / 500000.0) * 300000;")
// copy the old ID (date value) for NO_LESSONS
database.execSQL("UPDATE timetable SET ownerId = id WHERE type = -1;")
// update ID for notes as well
database.execSQL("UPDATE notes SET noteOwnerId = ROUND((noteOwnerId & ~65535) / 500000.0) * 300000 WHERE noteOwnerType = 'LESSON' AND noteOwnerId > 2000000000000;")
// force full app sync to download notes with new IDs
database.execSQL("DELETE FROM config WHERE `key` = 'hash';")
database.execSQL("DELETE FROM config WHERE `key` = 'lastAppSync';")
}
}

View File

@ -6,7 +6,11 @@ package pl.szczodrzynski.edziennik.data.firebase
import com.google.gson.JsonParser import com.google.gson.JsonParser
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.events.FeedbackMessageEvent import pl.szczodrzynski.edziennik.data.api.events.FeedbackMessageEvent
@ -14,14 +18,18 @@ import pl.szczodrzynski.edziennik.data.api.events.RegisterAvailabilityEvent
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.data.api.task.PostNotifications import pl.szczodrzynski.edziennik.data.api.task.PostNotifications
import pl.szczodrzynski.edziennik.data.db.entity.* import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Note
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.data.db.enums.NotificationType import pl.szczodrzynski.edziennik.data.db.enums.NotificationType
import pl.szczodrzynski.edziennik.ext.getInt import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.getLong import pl.szczodrzynski.edziennik.ext.getLong
import pl.szczodrzynski.edziennik.ext.getString import pl.szczodrzynski.edziennik.ext.getString
import pl.szczodrzynski.edziennik.ext.resolveString import pl.szczodrzynski.edziennik.ext.resolveString
import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
@ -64,7 +72,10 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
message.data.getString("title") ?: "", message.data.getString("title") ?: "",
message.data.getString("message") ?: "" message.data.getString("message") ?: ""
) )
"appUpdate" -> launch { UpdateWorker.runNow(app, app.gson.fromJson(message.data.getString("update"), Update::class.java)) } "appUpdate" -> {
val update = app.gson.fromJson(message.data.getString("update"), Update::class.java)
app.updateManager.process(update, notify = true)
}
"feedbackMessage" -> launch { "feedbackMessage" -> launch {
val message = app.gson.fromJson(message.data.getString("message"), FeedbackMessage::class.java) ?: return@launch val message = app.gson.fromJson(message.data.getString("message"), FeedbackMessage::class.java) ?: return@launch
feedbackMessage(message) feedbackMessage(message)
@ -149,11 +160,11 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
if (event.color == -1) if (event.color == -1)
event.color = null event.color = null
event.addedManually = true
event.sharedBy = json.getString("sharedBy") event.sharedBy = json.getString("sharedBy")
event.sharedByName = json.getString("sharedByName") event.sharedByName = json.getString("sharedByName")
if (profile.userCode == event.sharedBy) { if (profile.userCode == event.sharedBy) {
event.sharedBy = "self" event.sharedBy = "self"
event.addedManually = true
} }
val metadata = Metadata( val metadata = Metadata(
@ -165,7 +176,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
) )
val type = if (event.isHomework) NotificationType.SHARED_HOMEWORK else NotificationType.SHARED_EVENT val type = if (event.isHomework) NotificationType.SHARED_HOMEWORK else NotificationType.SHARED_EVENT
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter val notificationFilter = app.config[event.profileId].sync.notificationFilter
if (!notificationFilter.contains(type) && event.sharedBy != "self" && event.date >= Date.getToday()) { if (!notificationFilter.contains(type) && event.sharedBy != "self" && event.date >= Date.getToday()) {
val notification = Notification( val notification = Notification(
@ -200,7 +211,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
if (!profile.canShare) if (!profile.canShare)
return@forEach return@forEach
val notificationFilter = app.config.getFor(team.profileId).sync.notificationFilter val notificationFilter = app.config[team.profileId].sync.notificationFilter
if (!notificationFilter.contains(NotificationType.REMOVED_SHARED_EVENT)) { if (!notificationFilter.contains(NotificationType.REMOVED_SHARED_EVENT)) {
val notification = Notification( val notification = Notification(
@ -254,7 +265,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
return@forEach return@forEach
val type = NotificationType.SHARED_NOTE val type = NotificationType.SHARED_NOTE
val notificationFilter = app.config.getFor(note.profileId).sync.notificationFilter val notificationFilter = app.config[note.profileId].sync.notificationFilter
if (!notificationFilter.contains(type) && note.sharedBy != "self") { if (!notificationFilter.contains(type) && note.sharedBy != "self") {
val notification = Notification( val notification = Notification(

View File

@ -6,12 +6,37 @@ package pl.szczodrzynski.edziennik.data.firebase
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.* import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ATTENDANCES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NOTICES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_POINT_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_PT_MEETINGS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_TEXT_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_TIMETABLES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_MESSAGES_RECEIVED
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.LoginType import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.ext.getString import pl.szczodrzynski.edziennik.ext.getString
import pl.szczodrzynski.edziennik.ext.getStudentData
class SzkolnyLibrusFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) { class SzkolnyLibrusFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {
/*{ /*{

View File

@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.LoginType import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.ext.getLong import pl.szczodrzynski.edziennik.ext.getLong
import pl.szczodrzynski.edziennik.ext.getString import pl.szczodrzynski.edziennik.ext.getString
import pl.szczodrzynski.edziennik.ext.getStudentData
class SzkolnyMobidziennikFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) { class SzkolnyMobidziennikFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {
/*{ /*{

View File

@ -4,15 +4,15 @@
package pl.szczodrzynski.edziennik.data.firebase package pl.szczodrzynski.edziennik.data.firebase
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.LoginType import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.ext.getInt import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.getString import pl.szczodrzynski.edziennik.ext.getString
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.toJsonObject import pl.szczodrzynski.edziennik.ext.toJsonObject
class SzkolnyVulcanFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) { class SzkolnyVulcanFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {

View File

@ -6,14 +6,9 @@ package pl.szczodrzynski.edziennik.ext
import android.util.LongSparseArray import android.util.LongSparseArray
import androidx.core.util.forEach import androidx.core.util.forEach
import com.google.android.material.datepicker.CalendarConstraints
import com.google.gson.JsonElement
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.config.AppData
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.Team import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id } fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
fun List<Teacher>.byNameFirstLast(nameFirstLast: String) = firstOrNull { it.name + " " + it.surname == nameFirstLast } fun List<Teacher>.byNameFirstLast(nameFirstLast: String) = firstOrNull { it.name + " " + it.surname == nameFirstLast }
@ -34,22 +29,3 @@ fun LongSparseArray<Team>.getById(id: Long): Team? {
} }
return null return null
} }
operator fun Profile.set(key: String, value: JsonElement) = this.studentData.add(key, value)
operator fun Profile.set(key: String, value: Boolean) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: String?) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: Number) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: Char) = this.studentData.addProperty(key, value)
fun Profile.getSchoolYearConstrains(): CalendarConstraints {
return CalendarConstraints.Builder()
.setStart(dateSemester1Start.inMillisUtc)
.setEnd(dateYearEnd.inMillisUtc)
.build()
}
fun Profile.hasFeature(featureType: FeatureType) = featureType in this.loginStoreType.features
fun Profile.hasUIFeature(featureType: FeatureType) = featureType.isUIAlwaysAvailable || hasFeature(featureType)
fun Profile.getAppData() =
if (App.profileId == this.id) App.data else AppData.get(this.loginStoreType)

View File

@ -50,13 +50,13 @@ inline fun <reified E : Enum<E>> Int.toEnum() = when (E::class.java) {
} as E } as E
fun <E : Enum<E>> Int.toEnum(type: Class<*>) = when (type) { fun <E : Enum<E>> Int.toEnum(type: Class<*>) = when (type) {
// enums commented out are not really used in Bundles // this is used for Config so all enums are here
FeatureType::class.java -> this.asFeatureType() FeatureType::class.java -> this.asFeatureType()
// LoginMethod::class.java -> this.asLoginMethod() LoginMethod::class.java -> this.asLoginMethod()
LoginMode::class.java -> this.asLoginMode() LoginMode::class.java -> this.asLoginMode()
LoginType::class.java -> this.asLoginType() LoginType::class.java -> this.asLoginType()
// MetadataType::class.java -> this.asMetadataType() MetadataType::class.java -> this.asMetadataType()
// NotificationType::class.java -> this.asNotificationType() NotificationType::class.java -> this.asNotificationType()
NavTarget::class.java -> this.asNavTarget() NavTarget::class.java -> this.asNavTarget()
else -> throw IllegalArgumentException("Unknown type $type") else -> throw IllegalArgumentException("Unknown type $type")
} as E } as E

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.ext package pl.szczodrzynski.edziennik.ext
import android.os.Bundle import android.os.Bundle
import com.google.gson.Gson
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
@ -48,6 +49,11 @@ fun JsonObject.putEnum(key: String, value: Enum<*>) = addProperty(key, value.toI
fun String.toJsonObject(): JsonObject? = try { JsonParser.parseString(this).asJsonObject } catch (ignore: Exception) { null } fun String.toJsonObject(): JsonObject? = try { JsonParser.parseString(this).asJsonObject } catch (ignore: Exception) { null }
fun String.toJsonArray(): JsonArray? = try { JsonParser.parseString(this).asJsonArray } catch (ignore: Exception) { null } fun String.toJsonArray(): JsonArray? = try { JsonParser.parseString(this).asJsonArray } catch (ignore: Exception) { null }
fun Any?.toJsonElement(): JsonElement = when (this) {
is Collection<*> -> JsonArray(this)
else -> Gson().toJsonTree(this)
}
operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value) operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value)
operator fun JsonObject.set(key: String, value: Boolean) = this.addProperty(key, value) operator fun JsonObject.set(key: String, value: Boolean) = this.addProperty(key, value)
operator fun JsonObject.set(key: String, value: String?) = this.addProperty(key, value) operator fun JsonObject.set(key: String, value: String?) = this.addProperty(key, value)
@ -67,6 +73,8 @@ fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject {
is Number -> addProperty(key, value) is Number -> addProperty(key, value)
is Boolean -> addProperty(key, value) is Boolean -> addProperty(key, value)
is Enum<*> -> addProperty(key, value.toInt()) is Enum<*> -> addProperty(key, value.toInt())
null -> add(key, null)
else -> add(key, value.toJsonElement())
} }
} }
} }
@ -98,6 +106,8 @@ fun JsonArray(properties: Collection<Any?>): JsonArray {
is Char -> add(property as Char?) is Char -> add(property as Char?)
is Number -> add(property as Number?) is Number -> add(property as Number?)
is Boolean -> add(property as Boolean?) is Boolean -> add(property as Boolean?)
is Enum<*> -> add(property.toInt())
else -> add(property.toJsonElement())
} }
} }
} }

View File

@ -76,4 +76,9 @@ fun pendingIntentFlag(): Int {
fun Int?.takeValue() = if (this == -1) null else this fun Int?.takeValue() = if (this == -1) null else this
fun Int?.takePositive() = if (this == -1 || this == 0) null else this fun Int?.takePositive() = if (this == -1 || this == 0) null else this
fun Long?.takeValue() = if (this == -1L) null else this
fun Long?.takePositive() = if (this == -1L || this == 0L) null else this
fun String?.takeValue() = if (this.isNullOrBlank()) null else this
fun Any?.ignore() = Unit fun Any?.ignore() = Unit

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-25.
*/
package pl.szczodrzynski.edziennik.ext
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import com.google.android.material.datepicker.CalendarConstraints
import com.google.gson.JsonElement
import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.config.AppData
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.utils.ProfileImageHolder
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.navlib.ImageHolder
import pl.szczodrzynski.navlib.getDrawableFromRes
// TODO refactor Data* fields and make the receiver non-nullable
operator fun Profile?.set(key: String, value: JsonElement) = this?.studentData?.add(key, value)
operator fun Profile?.set(key: String, value: Boolean) = this?.studentData?.addProperty(key, value)
operator fun Profile?.set(key: String, value: String?) = this?.studentData?.addProperty(key, value)
operator fun Profile?.set(key: String, value: Number) = this?.studentData?.addProperty(key, value)
operator fun Profile?.set(key: String, value: Char) = this?.studentData?.addProperty(key, value)
fun Profile.getStudentData(key: String, defaultValue: Boolean) =
studentData.getBoolean(key) ?: defaultValue
fun Profile.getStudentData(key: String, defaultValue: String?) =
studentData.getString(key) ?: defaultValue
fun Profile.getStudentData(key: String, defaultValue: Int) =
studentData.getInt(key) ?: defaultValue
fun Profile.getStudentData(key: String, defaultValue: Long) =
studentData.getLong(key) ?: defaultValue
fun Profile.getStudentData(key: String, defaultValue: Float) =
studentData.getFloat(key) ?: defaultValue
fun Profile.getStudentData(key: String, defaultValue: Char) =
studentData.getChar(key) ?: defaultValue
fun Profile.getSemesterStart(semester: Int) =
if (semester == 1) dateSemester1Start else dateSemester2Start
fun Profile.getSemesterEnd(semester: Int) =
if (semester == 1) dateSemester2Start.clone().stepForward(0, 0, -1) else dateYearEnd
fun Profile.dateToSemester(date: Date) = if (date >= dateSemester2Start) 2 else 1
fun Profile.isBeforeYear() = false && Date.getToday() < dateSemester1Start
fun Profile.getSchoolYearConstrains(): CalendarConstraints {
return CalendarConstraints.Builder()
.setStart(dateSemester1Start.inMillisUtc)
.setEnd(dateYearEnd.inMillisUtc)
.build()
}
fun Profile.hasFeature(featureType: FeatureType) = featureType in this.loginStoreType.features
fun Profile.hasUIFeature(featureType: FeatureType) =
featureType.isUIAlwaysAvailable || hasFeature(featureType)
fun Profile.getAppData() =
if (App.profileId == this.id) App.data else AppData.get(this.loginStoreType)
fun Profile.shouldArchive(): Boolean {
// vulcan hotfix
if (dateYearEnd.month > 6) {
dateYearEnd.month = 6
dateYearEnd.day = 30
}
// fix for when versions <4.3 synced 2020/2021 year dates to older profiles during 2020 Jun-Aug
if (dateSemester1Start.year > studentSchoolYearStart) {
val diff = dateSemester1Start.year - studentSchoolYearStart
dateSemester1Start.year -= diff
dateSemester2Start.year -= diff
dateYearEnd.year -= diff
}
return App.config.archiverEnabled && Date.getToday() >= dateYearEnd && Date.getToday().year > studentSchoolYearStart
}
fun Profile.getDrawable(context: Context): Drawable {
if (archived) {
return context.getDrawableFromRes(R.drawable.profile_archived).also {
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
}
}
if (!image.isNullOrEmpty()) {
try {
return if (image?.endsWith(".gif", true) == true) {
GifDrawable(image ?: "")
} else {
RoundedBitmapDrawableFactory.create(context.resources, image ?: "")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
return context.getDrawableFromRes(R.drawable.profile).also {
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
}
}
fun Profile.getHolder(): ImageHolder {
if (archived) {
return ImageHolder(R.drawable.profile_archived, colorFromName(name))
}
return if (!image.isNullOrEmpty()) {
try {
ProfileImageHolder(image ?: "")
} catch (_: Exception) {
ImageHolder(R.drawable.profile, colorFromName(name))
}
} else {
ImageHolder(R.drawable.profile, colorFromName(name))
}
}

View File

@ -8,6 +8,7 @@ import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Rect import android.graphics.Rect
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.widget.* import android.widget.*
import androidx.annotation.StringRes import androidx.annotation.StringRes
@ -161,3 +162,12 @@ val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener
} }
} }
fun View.removeFromParent() {
(parent as? ViewGroup)?.removeView(this)
}
fun View.appendView(child: View) {
val parent = parent as? ViewGroup ?: return
val index = parent.indexOfChild(this)
parent.addView(child, index + 1)
}

View File

@ -0,0 +1,9 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-22.
*/
package pl.szczodrzynski.edziennik.sync
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
class UpdateStateEvent(val running: Boolean, val update: Update?, val downloadId: Long)

View File

@ -5,25 +5,14 @@
package pl.szczodrzynski.edziennik.sync package pl.szczodrzynski.edziennik.sync
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.work.* import androidx.work.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.ext.DAY import pl.szczodrzynski.edziennik.ext.DAY
import pl.szczodrzynski.edziennik.ext.concat
import pl.szczodrzynski.edziennik.ext.formatDate import pl.szczodrzynski.edziennik.ext.formatDate
import pl.szczodrzynski.edziennik.ext.pendingIntentFlag
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -76,63 +65,6 @@ class UpdateWorker(val context: Context, val params: WorkerParameters) : Worker(
Utils.d(TAG, "Cancelling work by tag $TAG") Utils.d(TAG, "Cancelling work by tag $TAG")
WorkManager.getInstance(app).cancelAllWorkByTag(TAG) WorkManager.getInstance(app).cancelAllWorkByTag(TAG)
} }
suspend fun runNow(app: App, overrideUpdate: Update? = null) {
try {
val update = overrideUpdate
?: run {
withContext(Dispatchers.Default) {
SzkolnyApi(app).runCatching({
getUpdate("beta")
}, {
Toast.makeText(app, app.getString(R.string.notification_cant_check_update), Toast.LENGTH_SHORT).show()
})
} ?: return@run null
if (app.config.update == null
|| app.config.update?.versionCode ?: BuildConfig.VERSION_CODE <= BuildConfig.VERSION_CODE) {
app.config.update = null
Toast.makeText(app, app.getString(R.string.notification_no_update), Toast.LENGTH_SHORT).show()
return@run null
}
app.config.update
} ?: return
if (update.versionCode <= BuildConfig.VERSION_CODE) {
app.config.update = null
return
}
if (EventBus.getDefault().hasSubscriberForEvent(update::class.java)) {
if (!update.updateMandatory) // mandatory updates are posted by the SzkolnyApi
EventBus.getDefault().postSticky(update)
return
}
val notificationIntent = Intent(app, UpdateDownloaderService::class.java)
val pendingIntent = PendingIntent.getService(app, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT or pendingIntentFlag())
val notification = NotificationCompat.Builder(app, app.notificationChannelsManager.updates.key)
.setContentTitle(app.getString(R.string.notification_updates_title))
.setContentText(app.getString(R.string.notification_updates_text, update.versionName))
.setTicker(app.getString(R.string.notification_updates_summary))
.setSmallIcon(R.drawable.ic_notification)
.setStyle(NotificationCompat.BigTextStyle()
.bigText(listOf(
app.getString(R.string.notification_updates_text, update.versionName),
update.releaseNotes?.let { BetterHtml.fromHtml(context = null, it) }
).concat("\n")))
.setColor(0xff2196f3.toInt())
.setLights(0xFF00FFFF.toInt(), 2000, 2000)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setGroup(app.notificationChannelsManager.updates.key)
.setContentIntent(pendingIntent)
.setAutoCancel(false)
.build()
(app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notify(app.notificationChannelsManager.updates.id, notification)
} catch (ignore: Exception) { }
}
} }
private val job = Job() private val job = Job()
@ -146,22 +78,13 @@ class UpdateWorker(val context: Context, val params: WorkerParameters) : Worker(
return Result.success() return Result.success()
} }
launch { val channel = if (App.devMode)
runNow(app) Update.Type.BETA
} else
Update.Type.RELEASE
app.updateManager.checkNowSync(channel, notify = true)
rescheduleNext(this.context) rescheduleNext(this.context)
return Result.success() return Result.success()
} }
class JavaWrapper(app: App) : CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
init {
launch {
runNow(app)
}
}
}
} }

View File

@ -17,7 +17,12 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria
import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp import com.mikepenz.iconics.utils.sizeDp
import eu.szkolny.font.SzkolnyFont import eu.szkolny.font.SzkolnyFont
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
@ -53,7 +58,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
if (getActivity() == null || context == null) return null if (getActivity() == null || context == null) return null
activity = getActivity() as MainActivity activity = getActivity() as MainActivity
context?.theme?.applyStyle(Themes.appTheme, true) context?.theme?.applyStyle(Themes.appTheme, true)
type = app.config.forProfile().ui.agendaViewType type = app.profile.config.ui.agendaViewType
b = when (type) { b = when (type) {
Profile.AGENDA_DEFAULT -> FragmentAgendaDefaultBinding.inflate(inflater, container, false) Profile.AGENDA_DEFAULT -> FragmentAgendaDefaultBinding.inflate(inflater, container, false)
Profile.AGENDA_CALENDAR -> FragmentAgendaCalendarBinding.inflate(inflater, container, false) Profile.AGENDA_CALENDAR -> FragmentAgendaCalendarBinding.inflate(inflater, container, false)
@ -92,7 +97,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
activity.bottomSheet.close() activity.bottomSheet.close()
type = type =
if (type == Profile.AGENDA_DEFAULT) Profile.AGENDA_CALENDAR else Profile.AGENDA_DEFAULT if (type == Profile.AGENDA_DEFAULT) Profile.AGENDA_CALENDAR else Profile.AGENDA_DEFAULT
app.config.forProfile().ui.agendaViewType = type app.profile.config.ui.agendaViewType = type
activity.reloadTarget() activity.reloadTarget()
}, },
BottomSheetSeparatorItem(true), BottomSheetSeparatorItem(true),
@ -137,13 +142,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
private suspend fun checkEventTypes() { private suspend fun checkEventTypes() {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val eventTypes = app.db.eventTypeDao().getAllNow(app.profileId).map { app.db.eventTypeDao().getAllWithDefaults(app.profile)
it.id
}
val defaultEventTypes = EventType.getTypeColorMap().keys
if (!eventTypes.containsAll(defaultEventTypes)) {
app.db.eventTypeDao().addDefaultTypes(app.profile)
}
} }
} }

View File

@ -16,7 +16,12 @@ import com.github.tibolte.agendacalendarview.agenda.AgendaAdapter
import com.github.tibolte.agendacalendarview.models.BaseCalendarEvent import com.github.tibolte.agendacalendarview.models.BaseCalendarEvent
import com.github.tibolte.agendacalendarview.models.CalendarEvent import com.github.tibolte.agendacalendarview.models.CalendarEvent
import com.github.tibolte.agendacalendarview.models.IDayItem import com.github.tibolte.agendacalendarview.models.IDayItem
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
@ -49,7 +54,6 @@ class AgendaFragmentDefault(
private val unreadDates = mutableSetOf<Int>() private val unreadDates = mutableSetOf<Int>()
private val events = mutableListOf<CalendarEvent>() private val events = mutableListOf<CalendarEvent>()
private var isInitialized = false private var isInitialized = false
private val profileConfig by lazy { app.config.forProfile().ui }
private val listView private val listView
get() = b.agendaDefaultView.agendaView.agendaListView get() = b.agendaDefaultView.agendaView.agendaListView
@ -107,10 +111,10 @@ class AgendaFragmentDefault(
isInitialized = false isInitialized = false
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
if (profileConfig.agendaLessonChanges) if (app.profile.config.ui.agendaLessonChanges)
addLessonChanges(events) addLessonChanges(events)
if (profileConfig.agendaTeacherAbsence) if (app.profile.config.ui.agendaTeacherAbsence)
addTeacherAbsence(events) addTeacherAbsence(events)
} }
@ -127,7 +131,7 @@ class AgendaFragmentDefault(
val dateStart = app.profile.dateSemester1Start.asCalendar val dateStart = app.profile.dateSemester1Start.asCalendar
val dateEnd = app.profile.dateYearEnd.asCalendar val dateEnd = app.profile.dateYearEnd.asCalendar
val isCompactMode = profileConfig.agendaCompactMode val isCompactMode = app.profile.config.ui.agendaCompactMode
b.agendaDefaultView.init( b.agendaDefaultView.init(
events, events,
@ -247,7 +251,7 @@ class AgendaFragmentDefault(
) { ) {
events.removeAll { it is AgendaEvent || it is AgendaEventGroup } events.removeAll { it is AgendaEvent || it is AgendaEventGroup }
if (!profileConfig.agendaGroupByType) { if (!app.profile.config.ui.agendaGroupByType) {
events += eventList.map { events += eventList.map {
if (!it.seen) if (!it.seen)
unreadDates.add(it.date.value) unreadDates.add(it.date.value)

View File

@ -11,6 +11,7 @@ import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.github.tibolte.agendacalendarview.render.EventRenderer import com.github.tibolte.agendacalendarview.render.EventRenderer
import com.mikepenz.iconics.view.IconicsTextView import com.mikepenz.iconics.view.IconicsTextView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventBinding import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventBinding
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventCompactBinding import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventCompactBinding
@ -53,16 +54,24 @@ class AgendaEventRenderer(
else else
event.time!!.stringHM event.time!!.stringHM
val agendaSubjectImportant = App.profile.config.ui.agendaSubjectImportant
val eventSubtitle = listOfNotNull( val eventSubtitle = listOfNotNull(
timeText, timeText,
event.subjectLongName, event.subjectLongName.takeIf { !agendaSubjectImportant },
event.typeName.takeIf { agendaSubjectImportant },
event.teacherName, event.teacherName,
event.teamName event.teamName
).join(", ") ).join(", ")
card.foreground.setTintColor(event.eventColor) card.foreground.setTintColor(event.eventColor)
card.background.setTintColor(event.eventColor) card.background.setTintColor(event.eventColor)
manager.setEventTopic(title, event, doneIconColor = textColor) manager.setEventTopic(
title = title,
event = event,
doneIconColor = textColor,
showType = !agendaSubjectImportant,
showSubject = agendaSubjectImportant,
)
title.setTextColor(textColor) title.setTextColor(textColor)
subtitle?.text = eventSubtitle subtitle?.text = eventSubtitle
subtitle?.setTextColor(textColor) subtitle?.setTextColor(textColor)

View File

@ -18,7 +18,6 @@ import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.databinding.AttendanceFragmentBinding import pl.szczodrzynski.edziennik.databinding.AttendanceFragmentBinding
import pl.szczodrzynski.edziennik.ext.Bundle import pl.szczodrzynski.edziennik.ext.Bundle
@ -83,7 +82,7 @@ class AttendanceFragment : Fragment(), CoroutineScope {
activity.gainAttention() activity.gainAttention()
if (pageSelection == 1) if (pageSelection == 1)
pageSelection = app.config.forProfile().attendance.attendancePageSelection pageSelection = app.profile.config.attendance.attendancePageSelection
val pagerAdapter = FragmentLazyPagerAdapter( val pagerAdapter = FragmentLazyPagerAdapter(
parentFragmentManager, parentFragmentManager,
@ -114,7 +113,7 @@ class AttendanceFragment : Fragment(), CoroutineScope {
currentItem = pageSelection currentItem = pageSelection
addOnPageSelectedListener { addOnPageSelectedListener {
pageSelection = it pageSelection = it
app.config.forProfile().attendance.attendancePageSelection = it app.profile.config.attendance.attendancePageSelection = it
} }
b.tabLayout.setupWithViewPager(this) b.tabLayout.setupWithViewPager(this)
} }

View File

@ -129,8 +129,8 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
if (attendance.isEmpty()) if (attendance.isEmpty())
return mutableListOf() return mutableListOf()
val groupConsecutiveDays = app.config.forProfile().attendance.groupConsecutiveDays val groupConsecutiveDays = app.profile.config.attendance.groupConsecutiveDays
val showPresenceInMonth = app.config.forProfile().attendance.showPresenceInMonth val showPresenceInMonth = app.profile.config.attendance.showPresenceInMonth
if (viewType == AttendanceFragment.VIEW_DAYS) { if (viewType == AttendanceFragment.VIEW_DAYS) {
val items = attendance val items = attendance

View File

@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged
import androidx.sqlite.db.SimpleSQLiteQuery import androidx.sqlite.db.SimpleSQLiteQuery
import com.chuckerteam.chucker.api.Chucker import com.chuckerteam.chucker.api.Chucker
import com.chuckerteam.chucker.api.Chucker.SCREEN_HTTP import com.chuckerteam.chucker.api.Chucker.SCREEN_HTTP
@ -21,6 +22,8 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.config.Config import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
import pl.szczodrzynski.edziennik.data.db.entity.EventType.Companion.SOURCE_DEFAULT
import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment
@ -60,8 +63,10 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
b.last10unseen.isVisible = false b.last10unseen.isVisible = false
b.fullSync.isVisible = false b.fullSync.isVisible = false
b.clearProfile.isVisible = false b.clearProfile.isVisible = false
b.clearEndpointTimers.isVisible = false
b.rodo.isVisible = false b.rodo.isVisible = false
b.removeHomework.isVisible = false b.removeHomework.isVisible = false
b.resetEventTypes.isVisible = false
b.unarchive.isVisible = false b.unarchive.isVisible = false
b.profile.isVisible = false b.profile.isVisible = false
} }
@ -80,19 +85,28 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
app.db.teacherDao().query(SimpleSQLiteQuery("UPDATE teachers SET teacherSurname = \"\" WHERE profileId = ${App.profileId}")) app.db.teacherDao().query(SimpleSQLiteQuery("UPDATE teachers SET teacherSurname = \"\" WHERE profileId = ${App.profileId}"))
} }
b.fullSync.onClick { b.fullSync.onClick {
app.db.query(SimpleSQLiteQuery("UPDATE profiles SET empty = 1 WHERE profileId = ${App.profileId}")) app.profile.empty = true
app.db.query(SimpleSQLiteQuery("DELETE FROM endpointTimers WHERE profileId = ${App.profileId}")) app.profileSave()
} }
b.clearProfile.onClick { b.clearProfile.onClick {
ProfileRemoveDialog(activity, App.profileId, "FAKE", noProfileRemoval = true).show() ProfileRemoveDialog(activity, App.profileId, "FAKE", noProfileRemoval = true).show()
} }
b.clearEndpointTimers.onClick {
app.db.endpointTimerDao().clear(app.profileId)
}
b.removeHomework.onClick { b.removeHomework.onClick {
app.db.eventDao().getRawNow("UPDATE events SET homeworkBody = NULL WHERE profileId = ${App.profileId}") app.db.eventDao().getRawNow("UPDATE events SET homeworkBody = NULL WHERE profileId = ${App.profileId}")
} }
b.resetEventTypes.onClick {
app.db.eventTypeDao().clearBySource(App.profileId, SOURCE_DEFAULT)
app.db.eventTypeDao().getAllWithDefaults(App.profile)
}
b.chucker.isChecked = App.enableChucker b.chucker.isChecked = App.enableChucker
b.chucker.onChange { _, isChecked -> b.chucker.onChange { _, isChecked ->
app.config.enableChucker = isChecked app.config.enableChucker = isChecked
@ -141,6 +155,16 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
app.config.apiInvalidCert = null app.config.apiInvalidCert = null
} }
b.apiKey.setText(app.config.apiKeyCustom ?: SignatureInterceptor.API_KEY)
b.apiKey.doAfterTextChanged {
it?.toString()?.let { key ->
if (key == SignatureInterceptor.API_KEY)
app.config.apiKeyCustom = null
else
app.config.apiKeyCustom = key.takeValue()?.trim()
}
}
b.rebuildConfig.onClick { b.rebuildConfig.onClick {
App.config = Config(App.db) App.config = Config(App.db)
} }

View File

@ -12,11 +12,16 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.gson.* import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.google.gson.JsonPrimitive
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
@ -66,7 +71,7 @@ class LabProfileFragment : LazyFragment(), CoroutineScope {
"LoginStore" -> loginStore "LoginStore" -> loginStore
"LoginStore / data" -> loginStore?.data ?: JsonObject() "LoginStore / data" -> loginStore?.data ?: JsonObject()
"Config" -> app.config.values "Config" -> app.config.values
"Config (profile)" -> app.config.forProfile().values "Config (profile)" -> app.profile.config.values
else -> when (obj) { else -> when (obj) {
is JsonObject -> (obj as JsonObject).get(el) is JsonObject -> (obj as JsonObject).get(el)
is JsonArray -> (obj as JsonArray).get(el.toInt()) is JsonArray -> (obj as JsonArray).get(el.toInt())
@ -176,7 +181,7 @@ class LabProfileFragment : LazyFragment(), CoroutineScope {
json.add("LoginStore", app.gson.toJsonTree(loginStore)) json.add("LoginStore", app.gson.toJsonTree(loginStore))
json.add("LoginStore / data", loginStore?.data ?: JsonObject()) json.add("LoginStore / data", loginStore?.data ?: JsonObject())
json.add("Config", JsonParser.parseString(app.gson.toJson(app.config.values.toSortedMap()))) json.add("Config", JsonParser.parseString(app.gson.toJson(app.config.values.toSortedMap())))
json.add("Config (profile)", JsonParser.parseString(app.gson.toJson(app.config.forProfile().values.toSortedMap()))) json.add("Config (profile)", JsonParser.parseString(app.gson.toJson(app.profile.config.values.toSortedMap())))
} }
adapter.items = LabJsonAdapter.expand(json, 0) adapter.items = LabJsonAdapter.expand(json, 0)
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()

View File

@ -6,13 +6,11 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
import java.util.*
class AgendaConfigDialog( class AgendaConfigDialog(
activity: AppCompatActivity, activity: AppCompatActivity,
@ -32,48 +30,35 @@ class AgendaConfigDialog(
override fun inflate(layoutInflater: LayoutInflater) = override fun inflate(layoutInflater: LayoutInflater) =
DialogConfigAgendaBinding.inflate(layoutInflater) DialogConfigAgendaBinding.inflate(layoutInflater)
private val profileConfig by lazy { app.config.forProfile().ui }
override suspend fun loadConfig() { override suspend fun loadConfig() {
b.config = profileConfig b.config = app.profile.config
b.isAgendaMode = profileConfig.agendaViewType == Profile.AGENDA_DEFAULT b.isAgendaMode = app.profile.config.ui.agendaViewType == Profile.AGENDA_DEFAULT
b.eventSharingEnabled.isChecked = var calledFromListener = false
app.profile.enableSharedEvents && app.profile.canShare b.eventSharingEnabled.isChecked = app.profile.canShare
b.shareByDefault.isEnabled = app.profile.canShare
b.eventSharingEnabled.onChange { _, isChecked -> b.eventSharingEnabled.onChange { _, isChecked ->
if (isChecked && !app.profile.canShare) { if (calledFromListener) {
b.eventSharingEnabled.isChecked = false calledFromListener = false
val dialog = RegistrationConfigDialog(
activity,
app.profile,
onChangeListener = { enabled ->
b.eventSharingEnabled.isChecked = enabled
setEventSharingEnabled(enabled)
},
onShowListener,
onDismissListener,
)
dialog.showEnableDialog()
return@onChange return@onChange
} }
setEventSharingEnabled(isChecked) b.eventSharingEnabled.isChecked = !isChecked
val dialog = RegistrationConfigDialog(
activity,
app.profile,
onChangeListener = { enabled ->
calledFromListener = true
b.eventSharingEnabled.isChecked = enabled
b.shareByDefault.isEnabled = enabled
},
onShowListener,
onDismissListener,
)
if (isChecked)
dialog.showEnableDialog()
else
dialog.showDisableDialog()
return@onChange
} }
} }
private fun setEventSharingEnabled(enabled: Boolean) {
if (enabled == app.profile.enableSharedEvents)
return
app.profile.enableSharedEvents = enabled
app.profileSave()
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.event_sharing)
.setMessage(
if (enabled)
R.string.settings_register_shared_events_dialog_enabled_text
else
R.string.settings_register_shared_events_dialog_disabled_text
)
.setPositiveButton(R.string.ok, null)
.show()
}
} }

View File

@ -29,23 +29,21 @@ class AttendanceConfigDialog(
override fun inflate(layoutInflater: LayoutInflater) = override fun inflate(layoutInflater: LayoutInflater) =
AttendanceConfigDialogBinding.inflate(layoutInflater) AttendanceConfigDialogBinding.inflate(layoutInflater)
private val profileConfig by lazy { app.config.getFor(app.profileId).attendance }
override suspend fun loadConfig() { override suspend fun loadConfig() {
b.useSymbols.isChecked = profileConfig.useSymbols b.useSymbols.isChecked = app.profile.config.attendance.useSymbols
b.groupConsecutiveDays.isChecked = profileConfig.groupConsecutiveDays b.groupConsecutiveDays.isChecked = app.profile.config.attendance.groupConsecutiveDays
b.showPresenceInMonth.isChecked = profileConfig.showPresenceInMonth b.showPresenceInMonth.isChecked = app.profile.config.attendance.showPresenceInMonth
} }
override fun initView() { override fun initView() {
b.useSymbols.onChange { _, isChecked -> b.useSymbols.onChange { _, isChecked ->
profileConfig.useSymbols = isChecked app.profile.config.attendance.useSymbols = isChecked
} }
b.groupConsecutiveDays.onChange { _, isChecked -> b.groupConsecutiveDays.onChange { _, isChecked ->
profileConfig.groupConsecutiveDays = isChecked app.profile.config.attendance.groupConsecutiveDays = isChecked
} }
b.showPresenceInMonth.onChange { _, isChecked -> b.showPresenceInMonth.onChange { _, isChecked ->
profileConfig.showPresenceInMonth = isChecked app.profile.config.attendance.showPresenceInMonth = isChecked
} }
} }
} }

View File

@ -45,17 +45,15 @@ class GradesConfigDialog(
override fun inflate(layoutInflater: LayoutInflater) = override fun inflate(layoutInflater: LayoutInflater) =
DialogConfigGradesBinding.inflate(layoutInflater) DialogConfigGradesBinding.inflate(layoutInflater)
private val profileConfig by lazy { app.config.getFor(app.profileId).grades }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override suspend fun loadConfig() { override suspend fun loadConfig() {
b.customPlusCheckBox.isChecked = profileConfig.plusValue != null b.customPlusCheckBox.isChecked = app.profile.config.grades.plusValue != null
b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked
b.customMinusCheckBox.isChecked = profileConfig.minusValue != null b.customMinusCheckBox.isChecked = app.profile.config.grades.minusValue != null
b.customMinusValue.isVisible = b.customMinusCheckBox.isChecked b.customMinusValue.isVisible = b.customMinusCheckBox.isChecked
b.customPlusValue.progress = profileConfig.plusValue ?: 0.5f b.customPlusValue.progress = app.profile.config.grades.plusValue ?: 0.5f
b.customMinusValue.progress = profileConfig.minusValue ?: 0.25f b.customMinusValue.progress = app.profile.config.grades.minusValue ?: 0.25f
when (config.orderBy) { when (config.orderBy) {
ORDER_BY_DATE_DESC -> b.sortGradesByDateRadio ORDER_BY_DATE_DESC -> b.sortGradesByDateRadio
@ -63,13 +61,13 @@ class GradesConfigDialog(
else -> null else -> null
}?.isChecked = true }?.isChecked = true
when (profileConfig.colorMode) { when (app.profile.config.grades.colorMode) {
COLOR_MODE_DEFAULT -> b.gradeColorFromERegister COLOR_MODE_DEFAULT -> b.gradeColorFromERegister
COLOR_MODE_WEIGHTED -> b.gradeColorByValue COLOR_MODE_WEIGHTED -> b.gradeColorByValue
else -> null else -> null
}?.isChecked = true }?.isChecked = true
when (profileConfig.yearAverageMode) { when (app.profile.config.grades.yearAverageMode) {
YEAR_ALL_GRADES -> b.gradeAverageMode4 YEAR_ALL_GRADES -> b.gradeAverageMode4
YEAR_1_AVG_2_AVG -> b.gradeAverageMode0 YEAR_1_AVG_2_AVG -> b.gradeAverageMode0
YEAR_1_SEM_2_AVG -> b.gradeAverageMode1 YEAR_1_SEM_2_AVG -> b.gradeAverageMode1
@ -79,21 +77,21 @@ class GradesConfigDialog(
}?.isChecked = true }?.isChecked = true
b.dontCountGrades.isChecked = b.dontCountGrades.isChecked =
profileConfig.dontCountEnabled && profileConfig.dontCountGrades.isNotEmpty() app.profile.config.grades.dontCountEnabled && app.profile.config.grades.dontCountGrades.isNotEmpty()
b.hideImproved.isChecked = profileConfig.hideImproved b.hideImproved.isChecked = app.profile.config.grades.hideImproved
b.averageWithoutWeight.isChecked = profileConfig.averageWithoutWeight b.averageWithoutWeight.isChecked = app.profile.config.grades.averageWithoutWeight
if (profileConfig.dontCountGrades.isEmpty()) { if (app.profile.config.grades.dontCountGrades.isEmpty()) {
b.dontCountGradesText.setText("nb, 0, bz, bd") b.dontCountGradesText.setText("nb, 0, bz, bd")
} else { } else {
b.dontCountGradesText.setText(profileConfig.dontCountGrades.join(", ")) b.dontCountGradesText.setText(app.profile.config.grades.dontCountGrades.join(", "))
} }
} }
override suspend fun saveConfig() { override suspend fun saveConfig() {
profileConfig.plusValue = app.profile.config.grades.plusValue =
if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null
profileConfig.minusValue = app.profile.config.grades.minusValue =
if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null
b.dontCountGradesText.setText( b.dontCountGradesText.setText(
@ -103,8 +101,8 @@ class GradesConfigDialog(
?.lowercase() ?.lowercase()
?.replace(", ", ",") ?.replace(", ", ",")
) )
profileConfig.dontCountEnabled = b.dontCountGrades.isChecked app.profile.config.grades.dontCountEnabled = b.dontCountGrades.isChecked
profileConfig.dontCountGrades = b.dontCountGradesText.text app.profile.config.grades.dontCountGrades = b.dontCountGradesText.text
?.split(",") ?.split(",")
?.map { it.trim() } ?.map { it.trim() }
?: listOf() ?: listOf()
@ -121,39 +119,39 @@ class GradesConfigDialog(
// who the hell named those methods // who the hell named those methods
// THIS SHIT DOES NOT EVEN WORK // THIS SHIT DOES NOT EVEN WORK
b.customPlusValue.doOnStopTrackingTouch { b.customPlusValue.doOnStopTrackingTouch {
profileConfig.plusValue = it.progress app.profile.config.grades.plusValue = it.progress
} }
b.customMinusValue.doOnStopTrackingTouch { b.customMinusValue.doOnStopTrackingTouch {
profileConfig.minusValue = it.progress app.profile.config.grades.minusValue = it.progress
} }
b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ORDER_BY_DATE_DESC } b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ORDER_BY_DATE_DESC }
b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ORDER_BY_SUBJECT_ASC } b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ORDER_BY_SUBJECT_ASC }
b.gradeColorFromERegister.setOnSelectedListener { b.gradeColorFromERegister.setOnSelectedListener {
profileConfig.colorMode = COLOR_MODE_DEFAULT app.profile.config.grades.colorMode = COLOR_MODE_DEFAULT
} }
b.gradeColorByValue.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_WEIGHTED } b.gradeColorByValue.setOnSelectedListener { app.profile.config.grades.colorMode = COLOR_MODE_WEIGHTED }
b.gradeAverageMode4.setOnSelectedListener { b.gradeAverageMode4.setOnSelectedListener {
profileConfig.yearAverageMode = YEAR_ALL_GRADES app.profile.config.grades.yearAverageMode = YEAR_ALL_GRADES
} }
b.gradeAverageMode0.setOnSelectedListener { b.gradeAverageMode0.setOnSelectedListener {
profileConfig.yearAverageMode = YEAR_1_AVG_2_AVG app.profile.config.grades.yearAverageMode = YEAR_1_AVG_2_AVG
} }
b.gradeAverageMode1.setOnSelectedListener { b.gradeAverageMode1.setOnSelectedListener {
profileConfig.yearAverageMode = YEAR_1_SEM_2_AVG app.profile.config.grades.yearAverageMode = YEAR_1_SEM_2_AVG
} }
b.gradeAverageMode2.setOnSelectedListener { b.gradeAverageMode2.setOnSelectedListener {
profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM app.profile.config.grades.yearAverageMode = YEAR_1_AVG_2_SEM
} }
b.gradeAverageMode3.setOnSelectedListener { b.gradeAverageMode3.setOnSelectedListener {
profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM app.profile.config.grades.yearAverageMode = YEAR_1_SEM_2_SEM
} }
b.hideImproved.onChange { _, isChecked -> profileConfig.hideImproved = isChecked } b.hideImproved.onChange { _, isChecked -> app.profile.config.grades.hideImproved = isChecked }
b.averageWithoutWeight.onChange { _, isChecked -> b.averageWithoutWeight.onChange { _, isChecked ->
profileConfig.averageWithoutWeight = isChecked app.profile.config.grades.averageWithoutWeight = isChecked
} }
b.averageWithoutWeightHelp.onClick { b.averageWithoutWeightHelp.onClick {

View File

@ -28,13 +28,11 @@ class MessagesConfigDialog(
override fun inflate(layoutInflater: LayoutInflater) = override fun inflate(layoutInflater: LayoutInflater) =
MessagesConfigDialogBinding.inflate(layoutInflater) MessagesConfigDialogBinding.inflate(layoutInflater)
private val profileConfig by lazy { app.config.getFor(app.profileId).ui }
override suspend fun loadConfig() { override suspend fun loadConfig() {
b.config = profileConfig b.config = app.profile.config.ui
b.greetingText.setText( b.greetingText.setText(
profileConfig.messagesGreetingText app.profile.config.ui.messagesGreetingText
?: "\n\nZ poważaniem\n${app.profile.accountOwnerName}" ?: "\n\nZ poważaniem\n${app.profile.accountOwnerName}"
) )
} }
@ -42,8 +40,8 @@ class MessagesConfigDialog(
override suspend fun saveConfig() { override suspend fun saveConfig() {
val greetingText = b.greetingText.text?.toString()?.trim() val greetingText = b.greetingText.text?.toString()?.trim()
if (greetingText.isNullOrEmpty()) if (greetingText.isNullOrEmpty())
profileConfig.messagesGreetingText = null app.profile.config.ui.messagesGreetingText = null
else else
profileConfig.messagesGreetingText = "\n\n$greetingText" app.profile.config.ui.messagesGreetingText = "\n\n$greetingText"
} }
} }

View File

@ -30,7 +30,7 @@ class NotificationFilterDialog(
.associateBy { it.titleRes.resolveString(activity) as CharSequence } .associateBy { it.titleRes.resolveString(activity) as CharSequence }
override fun getDefaultSelectedItems() = NotificationType.values() override fun getDefaultSelectedItems() = NotificationType.values()
.filter { it.enabledByDefault != null && it !in app.config.forProfile().sync.notificationFilter } .filter { it.enabledByDefault != null && it !in app.profile.config.sync.notificationFilter }
.toSet() .toSet()
override suspend fun onShow() = Unit override suspend fun onShow() = Unit
@ -47,7 +47,7 @@ class NotificationFilterDialog(
.setTitle(R.string.are_you_sure) .setTitle(R.string.are_you_sure)
.setMessage(R.string.notification_filter_warning) .setMessage(R.string.notification_filter_warning)
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
app.config.forProfile().sync.notificationFilter = disabledTypes app.profile.config.sync.notificationFilter = disabledTypes
dismiss() dismiss()
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
@ -55,7 +55,7 @@ class NotificationFilterDialog(
return NO_DISMISS return NO_DISMISS
} }
app.config.forProfile().sync.notificationFilter = disabledTypes app.profile.config.sync.notificationFilter = disabledTypes
return DISMISS return DISMISS
} }

View File

@ -7,7 +7,11 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
@ -117,7 +121,7 @@ class RegistrationConfigDialog(
profile.registration = Profile.REGISTRATION_ENABLED profile.registration = Profile.REGISTRATION_ENABLED
// force full registration of the user // force full registration of the user
App.config.getFor(profile.id).hash = "" profile.config.hash = ""
SzkolnyApi(app).runCatching(activity) { SzkolnyApi(app).runCatching(activity) {
AppSync(app, mutableListOf(), listOf(profile), this).run( AppSync(app, mutableListOf(), listOf(profile), this).run(

View File

@ -30,14 +30,12 @@ class TimetableConfigDialog(
override fun inflate(layoutInflater: LayoutInflater) = override fun inflate(layoutInflater: LayoutInflater) =
TimetableConfigDialogBinding.inflate(layoutInflater) TimetableConfigDialogBinding.inflate(layoutInflater)
private val profileConfig by lazy { app.config.getFor(app.profileId).ui }
override fun initView() { override fun initView() {
b.features = app.profile.loginStoreType.features b.features = app.profile.loginStoreType.features
} }
override suspend fun loadConfig() { override suspend fun loadConfig() {
b.config = profileConfig b.config = app.profile.config.ui
} }
override suspend fun saveConfig() { override suspend fun saveConfig() {

View File

@ -43,11 +43,11 @@ class UpdateAvailableDialog(
override suspend fun onShow() = Unit override suspend fun onShow() = Unit
override suspend fun onPositiveClick(): Boolean { override suspend fun onPositiveClick(): Boolean {
if (update == null) if (update == null || update.isOnGooglePlay)
Utils.openGooglePlay(activity) Utils.openGooglePlay(activity)
else else
activity.startService(Intent(app, UpdateDownloaderService::class.java)) activity.startService(Intent(app, UpdateDownloaderService::class.java))
return NO_DISMISS return DISMISS
} }
override suspend fun onBeforeShow(): Boolean { override suspend fun onBeforeShow(): Boolean {

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-22.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import android.app.DownloadManager
import android.database.CursorIndexOutOfBoundsException
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import kotlinx.coroutines.Job
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.databinding.UpdateProgressDialogBinding
import pl.szczodrzynski.edziennik.ext.getInt
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.sync.UpdateStateEvent
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.utils.Utils
class UpdateProgressDialog(
activity: AppCompatActivity,
private val update: Update,
private val downloadId: Long,
onShowListener: ((tag: String) -> Unit)? = null,
onDismissListener: ((tag: String) -> Unit)? = null,
) : BindingDialog<UpdateProgressDialogBinding>(activity, onShowListener, onDismissListener) {
override val TAG = "UpdateProgressDialog"
override fun getTitleRes() = R.string.notification_downloading_update
override fun inflate(layoutInflater: LayoutInflater) =
UpdateProgressDialogBinding.inflate(layoutInflater)
override fun isCancelable() = false
override fun getNegativeButtonText() = R.string.cancel
private var timerJob: Job? = null
override suspend fun onShow() {
EventBus.getDefault().register(this)
b.update = update
b.progress.progress = 0
val downloadManager = app.getSystemService<DownloadManager>() ?: return
val query = DownloadManager.Query().setFilterById(downloadId)
timerJob?.cancel()
timerJob = startCoroutineTimer(repeatMillis = 100L) {
try {
val cursor = downloadManager.query(query)
cursor.moveToFirst()
val progress = cursor.getInt(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
?.toFloat() ?: return@startCoroutineTimer
b.downloadedSize.text = Utils.readableFileSize(progress.toLong())
val total = cursor.getInt(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
?.toFloat() ?: return@startCoroutineTimer
b.totalSize.text = Utils.readableFileSize(total.toLong())
b.progress.progress = (progress / total * 100.0f).toInt()
} catch (_: CursorIndexOutOfBoundsException) {}
}
}
override fun onDismiss() {
EventBus.getDefault().unregister(this)
timerJob?.cancel()
}
override suspend fun onNegativeClick(): Boolean {
val downloadManager = app.getSystemService<DownloadManager>() ?: return NO_DISMISS
downloadManager.remove(downloadId)
return DISMISS
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onUpdateStateEvent(event: UpdateStateEvent) {
if (event.downloadId != downloadId)
return
EventBus.getDefault().removeStickyEvent(event)
if (!event.running)
dismiss()
}
}

View File

@ -113,9 +113,20 @@ class EventDetailsDialog(
b.typeColor.background?.setTintColor(event.eventColor) b.typeColor.background?.setTintColor(event.eventColor)
b.details = mutableListOf( val agendaSubjectImportant = event.subjectLongName != null
&& App.config[event.profileId].ui.agendaSubjectImportant
b.name = if (agendaSubjectImportant)
event.subjectLongName
else
event.typeName
b.details = listOfNotNull(
if (agendaSubjectImportant)
event.typeName
else
event.subjectLongName, event.subjectLongName,
event.teamName?.asColoredSpannable(colorSecondary) event.teamName?.asColoredSpannable(colorSecondary)
).concat(bullet) ).concat(bullet)
b.addedBy.setText( b.addedBy.setText(

View File

@ -24,6 +24,7 @@ class EventListAdapter(
val showDate: Boolean = false, val showDate: Boolean = false,
val showColor: Boolean = true, val showColor: Boolean = true,
val showType: Boolean = true, val showType: Boolean = true,
val showTypeColor: Boolean = showType,
val showTime: Boolean = true, val showTime: Boolean = true,
val showSubject: Boolean = true, val showSubject: Boolean = true,
val markAsSeen: Boolean = true, val markAsSeen: Boolean = true,

View File

@ -13,23 +13,37 @@ import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jaredrummler.android.colorpicker.ColorPickerDialog import com.jaredrummler.android.colorpicker.ColorPickerDialog
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.config.AppData
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskFinishedEvent import pl.szczodrzynski.edziennik.data.api.events.ApiTaskFinishedEvent
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.db.entity.* import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Subject
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.LessonFull import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.JsonObject
import pl.szczodrzynski.edziennik.ext.appendView
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.removeFromParent
import pl.szczodrzynski.edziennik.ext.setText
import pl.szczodrzynski.edziennik.ext.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.views.TimeDropdown.Companion.DISPLAY_LESSONS import pl.szczodrzynski.edziennik.ui.views.TimeDropdown.Companion.DISPLAY_LESSONS
@ -107,8 +121,14 @@ class EventManualDialog(
} }
override suspend fun onShow() { override suspend fun onShow() {
b.shareSwitch.isChecked = editingShared val data = withContext(Dispatchers.IO) {
b.shareSwitch.isEnabled = !editingShared || (editingShared && editingOwn) val profile = app.db.profileDao().getByIdSuspend(profileId) ?: return@withContext null
AppData.get(profile.loginStoreType)
}
if (data?.uiConfig?.eventManualShowSubjectDropdown == true) {
b.subjectDropdownLayout.removeFromParent()
b.timeDropdownLayout.appendView(b.subjectDropdownLayout)
}
b.showMore.onClick { // TODO iconics is broken b.showMore.onClick { // TODO iconics is broken
it.apply { it.apply {
@ -137,6 +157,11 @@ class EventManualDialog(
} }
loadLists() loadLists()
val shareByDefault = app.profile.config.shareByDefault && profile.canShare
b.shareSwitch.isChecked = editingShared || editingEvent == null && shareByDefault
b.shareSwitch.isEnabled = !editingShared || editingOwn
} }
private fun updateShareText(checked: Boolean = b.shareSwitch.isChecked) { private fun updateShareText(checked: Boolean = b.shareSwitch.isChecked) {

View File

@ -113,7 +113,7 @@ class EventViewHolder(
b.attachmentIcon.isVisible = item.hasAttachments b.attachmentIcon.isVisible = item.hasAttachments
b.typeColor.background?.setTintColor(item.eventColor) b.typeColor.background?.setTintColor(item.eventColor)
b.typeColor.isVisible = adapter.showType && adapter.showColor b.typeColor.isVisible = adapter.showTypeColor
b.editButton.isVisible = !adapter.simpleMode b.editButton.isVisible = !adapter.simpleMode
&& item.addedManually && item.addedManually

View File

@ -81,7 +81,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
} }
val items = when { val items = when {
app.config.forProfile().grades.hideSticksFromOld && App.devMode -> grades.filter { it.value != 1.0f } app.profile.config.grades.hideSticksFromOld && App.devMode -> grades.filter { it.value != 1.0f }
else -> grades else -> grades
} }

View File

@ -12,7 +12,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding
import pl.szczodrzynski.edziennik.ext.getFloat import pl.szczodrzynski.edziennik.ext.getFloat
@ -37,7 +39,7 @@ class GradesEditorFragment : Fragment() {
private val navController: NavController by lazy { Navigation.findNavController(b.root) } private val navController: NavController by lazy { Navigation.findNavController(b.root) }
*/ */
private val config by lazy { app.config.getFor(App.profileId).grades } private val config by lazy { app.profile.config.grades }
private var subjectId: Long = -1 private var subjectId: Long = -1
private var semester: Int = 1 private var semester: Int = 1

View File

@ -4,8 +4,10 @@
package pl.szczodrzynski.edziennik.ui.home package pl.szczodrzynski.edziennik.ui.home
import android.graphics.BitmapFactory
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.jetradarmobile.snowfall.SnowfallView
import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp import com.mikepenz.iconics.utils.sizeDp
@ -20,6 +22,7 @@ import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ext.timeLeft import pl.szczodrzynski.edziennik.ext.timeLeft
import pl.szczodrzynski.edziennik.ext.timeTill import pl.szczodrzynski.edziennik.ext.timeTill
import pl.szczodrzynski.edziennik.ui.dialogs.BellSyncTimeChooseDialog import pl.szczodrzynski.edziennik.ui.dialogs.BellSyncTimeChooseDialog
import pl.szczodrzynski.edziennik.utils.BigNightUtil
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -61,6 +64,7 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
it.type != Lesson.TYPE_SHIFTED_SOURCE it.type != Lesson.TYPE_SHIFTED_SOURCE
}) })
} }
lessonList.onEach { it.filterNotes() }
} }
b.bellSync.setImageDrawable( b.bellSync.setImageDrawable(
@ -81,6 +85,27 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
counterJob = startCoroutineTimer(repeatMillis = 500) { counterJob = startCoroutineTimer(repeatMillis = 500) {
update() update()
} }
// IT'S WINTER MY DUDES
val today = Date.getToday()
if ((today.month / 3 % 4 == 0) && app.config.ui.snowfall) {
b.rootFrame.addView(layoutInflater.inflate(R.layout.snowfall, b.rootFrame, false))
} else if (app.config.ui.eggfall && BigNightUtil().isDataWielkanocyNearDzisiaj()) {
val eggfall = layoutInflater.inflate(
R.layout.eggfall,
b.rootFrame,
false
) as SnowfallView
eggfall.setSnowflakeBitmaps(listOf(
BitmapFactory.decodeResource(resources, R.drawable.egg1),
BitmapFactory.decodeResource(resources, R.drawable.egg2),
BitmapFactory.decodeResource(resources, R.drawable.egg3),
BitmapFactory.decodeResource(resources, R.drawable.egg4),
BitmapFactory.decodeResource(resources, R.drawable.egg5),
BitmapFactory.decodeResource(resources, R.drawable.egg6)
))
b.rootFrame.addView(eggfall)
}
}} }}
private fun update() { private fun update() {
@ -101,13 +126,15 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
when { when {
actual != null -> { actual != null -> {
b.lessonName.text = actual.displaySubjectName b.lessonName.text = actual.getNoteSubstituteText(showNotes = true)
?: actual.displaySubjectName
val left = actual.displayEndTime!! - now val left = actual.displayEndTime!! - now
b.timeLeft.text = timeLeft(left.toInt(), "\n", countInSeconds) b.timeLeft.text = timeLeft(left.toInt(), "\n", countInSeconds)
} }
next != null -> { next != null -> {
b.lessonName.text = next.displaySubjectName b.lessonName.text = next.getNoteSubstituteText(showNotes = true)
?: next.displaySubjectName
val till = next.displayStartTime!! - now val till = next.displayStartTime!! - now
b.timeLeft.text = timeTill(till.toInt(), "\n", countInSeconds) b.timeLeft.text = timeTill(till.toInt(), "\n", countInSeconds)

View File

@ -37,18 +37,17 @@ class HomeConfigDialog(
).mapKeys { (resId, _) -> activity.getString(resId) } ).mapKeys { (resId, _) -> activity.getString(resId) }
override fun getDefaultSelectedItems() = override fun getDefaultSelectedItems() =
profileConfig.homeCards app.profile.config.ui.homeCards
.filter { it.profileId == App.profileId } .filter { it.profileId == App.profileId }
.map { it.cardId } .map { it.cardId }
.toSet() .toSet()
override suspend fun onShow() = Unit override suspend fun onShow() = Unit
private val profileConfig by lazy { app.config.getFor(app.profileId).ui }
private var configChanged = false private var configChanged = false
override suspend fun onPositiveClick(): Boolean { override suspend fun onPositiveClick(): Boolean {
val homeCards = profileConfig.homeCards.toMutableList() val homeCards = app.profile.config.ui.homeCards.toMutableList()
homeCards.removeAll { it.profileId == App.profileId } homeCards.removeAll { it.profileId == App.profileId }
homeCards += getMultiSelection().mapNotNull { homeCards += getMultiSelection().mapNotNull {
HomeCardModel( HomeCardModel(
@ -56,7 +55,7 @@ class HomeConfigDialog(
cardId = it as? Int ?: return@mapNotNull null cardId = it as? Int ?: return@mapNotNull null
) )
} }
profileConfig.homeCards = homeCards app.profile.config.ui.homeCards = homeCards
return DISMISS return DISMISS
} }

View File

@ -50,18 +50,18 @@ class HomeFragment : Fragment(), CoroutineScope {
cardAdapter.items[toPosition] = fromCard cardAdapter.items[toPosition] = fromCard
cardAdapter.notifyItemMoved(fromPosition, toPosition) cardAdapter.notifyItemMoved(fromPosition, toPosition)
val homeCards = App.config.forProfile().ui.homeCards.toMutableList() val homeCards = App.profile.config.ui.homeCards.toMutableList()
val fromIndex = homeCards.indexOfFirst { it.cardId == fromCard.id } val fromIndex = homeCards.indexOfFirst { it.cardId == fromCard.id }
val toIndex = homeCards.indexOfFirst { it.cardId == toCard.id } val toIndex = homeCards.indexOfFirst { it.cardId == toCard.id }
val fromPair = homeCards[fromIndex] val fromPair = homeCards[fromIndex]
homeCards[fromIndex] = homeCards[toIndex] homeCards[fromIndex] = homeCards[toIndex]
homeCards[toIndex] = fromPair homeCards[toIndex] = fromPair
App.config.forProfile().ui.homeCards = homeCards App.profile.config.ui.homeCards = homeCards
return true return true
} }
fun removeCard(position: Int, cardAdapter: HomeCardAdapter) { fun removeCard(position: Int, cardAdapter: HomeCardAdapter) {
val homeCards = App.config.forProfile().ui.homeCards.toMutableList() val homeCards = App.profile.config.ui.homeCards.toMutableList()
if (position >= homeCards.size) if (position >= homeCards.size)
return return
val card = cardAdapter.items[position] val card = cardAdapter.items[position]
@ -71,7 +71,7 @@ class HomeFragment : Fragment(), CoroutineScope {
return return
} }
homeCards.removeAll { it.cardId == card.id } homeCards.removeAll { it.cardId == card.id }
App.config.forProfile().ui.homeCards = homeCards App.profile.config.ui.homeCards = homeCards
} }
} }
@ -144,7 +144,7 @@ class HomeFragment : Fragment(), CoroutineScope {
b.refreshLayout.isEnabled = scrollY == 0 b.refreshLayout.isEnabled = scrollY == 0
} }
val cards = app.config.forProfile().ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList() val cards = app.profile.config.ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList()
if (cards.isEmpty()) { if (cards.isEmpty()) {
cards += listOfNotNull( cards += listOfNotNull(
HomeCardModel(app.profile.id, HomeCard.CARD_LUCKY_NUMBER).takeIf { app.profile.hasUIFeature(FeatureType.LUCKY_NUMBER) }, HomeCardModel(app.profile.id, HomeCard.CARD_LUCKY_NUMBER).takeIf { app.profile.hasUIFeature(FeatureType.LUCKY_NUMBER) },
@ -153,7 +153,7 @@ class HomeFragment : Fragment(), CoroutineScope {
HomeCardModel(app.profile.id, HomeCard.CARD_GRADES).takeIf { app.profile.hasUIFeature(FeatureType.GRADES) }, HomeCardModel(app.profile.id, HomeCard.CARD_GRADES).takeIf { app.profile.hasUIFeature(FeatureType.GRADES) },
HomeCardModel(app.profile.id, HomeCard.CARD_NOTES), HomeCardModel(app.profile.id, HomeCard.CARD_NOTES),
) )
app.config.forProfile().ui.homeCards = app.config.forProfile().ui.homeCards.toMutableList().also { it.addAll(cards) } app.profile.config.ui.homeCards = app.profile.config.ui.homeCards.toMutableList().also { it.addAll(cards) }
} }
val items = mutableListOf<HomeCard>() val items = mutableListOf<HomeCard>()

View File

@ -15,7 +15,10 @@ import coil.load
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.CardHomeAvailabilityBinding import pl.szczodrzynski.edziennik.databinding.CardHomeAvailabilityBinding
import pl.szczodrzynski.edziennik.ext.Intent import pl.szczodrzynski.edziennik.ext.Intent
@ -28,6 +31,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.sync.UpdateAvailableDialog
import pl.szczodrzynski.edziennik.ui.home.HomeCard import pl.szczodrzynski.edziennik.ui.home.HomeCard
import pl.szczodrzynski.edziennik.ui.home.HomeCardAdapter import pl.szczodrzynski.edziennik.ui.home.HomeCardAdapter
import pl.szczodrzynski.edziennik.ui.home.HomeFragment import pl.szczodrzynski.edziennik.ui.home.HomeFragment
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.html.BetterHtml import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -79,7 +83,7 @@ class HomeAvailabilityCard(
else if (update != null && update.versionCode > BuildConfig.VERSION_CODE) { else if (update != null && update.versionCode > BuildConfig.VERSION_CODE) {
b.homeAvailabilityTitle.setText(R.string.home_availability_title) b.homeAvailabilityTitle.setText(R.string.home_availability_title)
b.homeAvailabilityText.setText(R.string.home_availability_text, update.versionName) b.homeAvailabilityText.setText(R.string.home_availability_text, update.versionName)
b.homeAvailabilityUpdate.isVisible = true b.homeAvailabilityUpdate.isVisible = !app.buildManager.isPlayRelease
b.homeAvailabilityIcon.setImageResource(R.drawable.ic_update) b.homeAvailabilityIcon.setImageResource(R.drawable.ic_update)
onInfoClick = { onInfoClick = {
UpdateAvailableDialog(activity, update).show() UpdateAvailableDialog(activity, update).show()
@ -92,7 +96,10 @@ class HomeAvailabilityCard(
b.homeAvailabilityUpdate.onClick { b.homeAvailabilityUpdate.onClick {
if (update == null) if (update == null)
return@onClick return@onClick
activity.startService(Intent(app, UpdateDownloaderService::class.java)) if (update.isOnGooglePlay)
Utils.openGooglePlay(activity)
else
activity.startService(Intent(app, UpdateDownloaderService::class.java))
} }
b.homeAvailabilityInfo.onClick(onInfoClick) b.homeAvailabilityInfo.onClick(onInfoClick)

Some files were not shown because too many files have changed in this diff Show More