mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-19 00:32:44 +02:00
Compare commits
81 Commits
v4.0-beta.
...
v4.0-rc.2
Author | SHA1 | Date | |
---|---|---|---|
30b6ac2a06 | |||
a7fa7cb5e4 | |||
f3e87f9016 | |||
a983af6c28 | |||
114c841f0c | |||
e271048577 | |||
b8c5925e82 | |||
9bda6c8869 | |||
d1608d308c | |||
b8e1e1d33a | |||
8099a037e7 | |||
af23c932a6 | |||
4edabbb186 | |||
37a5bea79b | |||
40cdc7d713 | |||
49825aca48 | |||
1d57c4e705 | |||
87ae5787ee | |||
20f16c25a3 | |||
6f1ec79d9b | |||
18c7eea89c | |||
f73060aeb6 | |||
2f653b83b6 | |||
445dec907d | |||
927316d24b | |||
3957453ed6 | |||
0296c704cb | |||
1e7fe972de | |||
c95bc656ea | |||
a082d95b04 | |||
6866dd4801 | |||
2186da416e | |||
22d859fcde | |||
39514b69b3 | |||
c384736840 | |||
507657f273 | |||
60641742ed | |||
0fc6f07986 | |||
1b2bdc0580 | |||
9bac239f77 | |||
371acb2d2a | |||
454f82e139 | |||
e8da249353 | |||
c7950c53da | |||
b5502478e4 | |||
4480a7e486 | |||
7c7dff743b | |||
c568cd3f2e | |||
6ec2bc6f21 | |||
af3b6f3a97 | |||
d855118610 | |||
c9992d9fe8 | |||
85fe2636cc | |||
35f4a31a76 | |||
1e494ebb70 | |||
ed93627505 | |||
b9b4b0036f | |||
4aa31424d6 | |||
8a825227cb | |||
cc1b581d7e | |||
9936d90ae2 | |||
df1a241b2b | |||
ae89b33fb7 | |||
e05b483f5c | |||
715f536b23 | |||
930813fb8a | |||
acd5e9b998 | |||
06011bf4ae | |||
30e15b813c | |||
fcd7a7f349 | |||
42ef40439e | |||
098beb14fe | |||
0b186a754a | |||
d00963b53d | |||
e282af0e80 | |||
630361849c | |||
88a1de50ca | |||
d8263d0b6a | |||
611ab0f100 | |||
70c307b796 | |||
054a233ad6 |
@ -128,6 +128,7 @@ dependencies {
|
|||||||
implementation "com.mikepenz:iconics-core:${versions.iconics}"
|
implementation "com.mikepenz:iconics-core:${versions.iconics}"
|
||||||
implementation "com.mikepenz:iconics-views:${versions.iconics}"
|
implementation "com.mikepenz:iconics-views:${versions.iconics}"
|
||||||
implementation "com.mikepenz:community-material-typeface:${versions.font_cmd}@aar"
|
implementation "com.mikepenz:community-material-typeface:${versions.font_cmd}@aar"
|
||||||
|
implementation "com.mikepenz:materialize:1.2.1"
|
||||||
|
|
||||||
implementation "com.github.kuba2k2:NavLib:${versions.navlib}"
|
implementation "com.github.kuba2k2:NavLib:${versions.navlib}"
|
||||||
|
|
||||||
|
@ -31,6 +31,12 @@
|
|||||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
|
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
|
||||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
|
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
|
||||||
|
|
||||||
|
-keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); }
|
||||||
|
-keepclassmembernames class androidx.appcompat.view.menu.StandardMenuPopup { private *; }
|
||||||
|
-keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); }
|
||||||
|
|
||||||
|
-keepclassmembernames class com.mikepenz.materialdrawer.widget.MiniDrawerSliderView { private *; }
|
||||||
|
|
||||||
-keep class .R
|
-keep class .R
|
||||||
-keep class **.R$* {
|
-keep class **.R$* {
|
||||||
<fields>;
|
<fields>;
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@style/AppTheme.NoDisplay">
|
android:theme="@style/AppTheme.Dark.NoDisplay">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@ -84,7 +84,7 @@
|
|||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@style/AppTheme.NoDisplay" />
|
android:theme="@style/AppTheme.Dark.NoDisplay" />
|
||||||
<!-- NOTIFICATIONS -->
|
<!-- NOTIFICATIONS -->
|
||||||
<receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider"
|
<receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider"
|
||||||
android:label="@string/widget_notifications_title">
|
android:label="@string/widget_notifications_title">
|
||||||
|
@ -1,35 +1,29 @@
|
|||||||
<h3>Wersja 4.0-beta.11, 2020-03-06</h3>
|
<h3>Wersja 4.0-rc.2, 2020-03-26</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
|
|
||||||
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
||||||
|
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkość oraz poprawność pobieranych danych</li>
|
||||||
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li>
|
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li>
|
||||||
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
|
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
|
||||||
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
|
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
|
||||||
|
<li>Nowe <b>Oceny</b> - z możliwością zmiany wartości plusów oraz minusów oraz wyłączenia niektórych ocen ze średniej</li>
|
||||||
|
<li>Opcja wyłączenia wybranych powiadomień z aplikacji</li>
|
||||||
|
<li>Znaczki nieprzeczytanych informacji na obrazkach profili.</li>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
|
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
|
||||||
<li>Nowe, przyjemniejsze powiadomienia</li>
|
<li>Nowe, przyjemniejsze powiadomienia</li>
|
||||||
<li>Łatwiejsze dodawanie własnych wydarzeń</li>
|
|
||||||
<li>Dużo poprawek w widoku <b>Wiadomości</b> oraz <b>Ogłoszeń</b></li>
|
<li>Dużo poprawek w widoku <b>Wiadomości</b> oraz <b>Ogłoszeń</b></li>
|
||||||
<li>Częściowa <b>Obsługa dziennika EduDziennik</b></li>
|
<li>Częściowa <b>Obsługa dziennika EduDziennik</b></li>
|
||||||
<li>Librus: opcja logowania w dziennikach <b>Jednostek Samorządu Terytorialnego</b> oraz <b>Oświata w Radomiu</b></li>
|
<li>Librus: opcja logowania w dziennikach <b>Jednostek Samorządu Terytorialnego</b> oraz <b>Oświata w Radomiu</b></li>
|
||||||
<li>Librus: <b>poprawione obliczanie frekwencji</b></li>
|
<li>Librus: <b>poprawione obliczanie frekwencji</b></li>
|
||||||
<li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</li>
|
<li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</li>
|
||||||
<li>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li>
|
<li>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li>
|
||||||
|
<li>Łatwiejsze dodawanie własnych wydarzeń</li>
|
||||||
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
|
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
|
||||||
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</li>
|
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</li>
|
||||||
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
|
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
|
||||||
<li><strike>Występują natomiast nowe błędy, dlatego proszę o ich zgłaszanie :)</strike></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
|
||||||
<b>Uwaga.</b> Ponieważ to wersja <i>beta</i>, niektóre funkcje mogą nie działać prawidłowo.<br>
|
|
||||||
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
|
|
||||||
<ul>
|
|
||||||
<li>Wysyłanie wiadomości może nie działać w pełni prawidłowo - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
|
|
||||||
<li>Cisza nocna w powiadomieniach jeszcze nie działa.</li>
|
|
||||||
</ul>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Dzięki za korzystanie ze Szkolnego!<br>
|
Dzięki za korzystanie ze Szkolnego!<br>
|
||||||
<i>© Kuba Szczodrzyński, Kacper Ziubryniewicz 2020</i>
|
<i>© Kuba Szczodrzyński, Kacper Ziubryniewicz 2020</i>
|
||||||
|
@ -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] = {
|
||||||
0x00, 0x37, 0x6a, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
0x30, 0xfe, 0xe2, 0x8d, 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);
|
||||||
|
|
||||||
|
@ -28,9 +28,6 @@ import com.hypertrack.hyperlog.HyperLog
|
|||||||
import com.mikepenz.iconics.Iconics
|
import com.mikepenz.iconics.Iconics
|
||||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||||
import im.wangchao.mhttp.MHttp
|
import im.wangchao.mhttp.MHttp
|
||||||
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar
|
|
||||||
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache
|
|
||||||
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import me.leolin.shortcutbadger.ShortcutBadger
|
import me.leolin.shortcutbadger.ShortcutBadger
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -41,12 +38,14 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
|||||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
import pl.szczodrzynski.edziennik.network.NetworkUtils
|
import pl.szczodrzynski.edziennik.network.NetworkUtils
|
||||||
|
import pl.szczodrzynski.edziennik.network.cookie.DumbCookieJar
|
||||||
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
|
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
|
||||||
import pl.szczodrzynski.edziennik.utils.*
|
import pl.szczodrzynski.edziennik.utils.*
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
|
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
|
||||||
|
import pl.szczodrzynski.edziennik.utils.managers.TimetableManager
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.UserActionManager
|
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
|
||||||
@ -67,6 +66,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
|||||||
val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
|
val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
|
||||||
val userActionManager by lazy { UserActionManager(this) }
|
val userActionManager by lazy { UserActionManager(this) }
|
||||||
val gradesManager by lazy { GradesManager(this) }
|
val gradesManager by lazy { GradesManager(this) }
|
||||||
|
val timetableManager by lazy { TimetableManager(this) }
|
||||||
|
|
||||||
val db
|
val db
|
||||||
get() = App.db
|
get() = App.db
|
||||||
@ -123,7 +123,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
|||||||
.followSslRedirects(false)
|
.followSslRedirects(false)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
val cookieJar by lazy { PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(this)) }
|
val cookieJar by lazy { DumbCookieJar(this) }
|
||||||
|
|
||||||
/* _____ _ _
|
/* _____ _ _
|
||||||
/ ____(_) | |
|
/ ____(_) | |
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pl.szczodrzynski.edziennik;
|
|
||||||
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.databinding.BindingAdapter;
|
|
||||||
|
|
||||||
public class Binding {
|
|
||||||
@BindingAdapter("strikeThrough")
|
|
||||||
public static void strikeThrough(TextView textView, Boolean strikeThrough) {
|
|
||||||
if (strikeThrough) {
|
|
||||||
textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
|
||||||
} else {
|
|
||||||
textView.setPaintFlags(textView.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
20
app/src/main/java/pl/szczodrzynski/edziennik/Binding.kt
Normal file
20
app/src/main/java/pl/szczodrzynski/edziennik/Binding.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||||
|
*/
|
||||||
|
package pl.szczodrzynski.edziennik
|
||||||
|
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.databinding.BindingAdapter
|
||||||
|
|
||||||
|
object Binding {
|
||||||
|
@JvmStatic
|
||||||
|
@BindingAdapter("strikeThrough")
|
||||||
|
fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
|
||||||
|
if (strikeThrough) {
|
||||||
|
textView.paintFlags = textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||||
|
} else {
|
||||||
|
textView.paintFlags = textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@ package pl.szczodrzynski.edziennik
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@ -10,6 +12,7 @@ import android.content.res.Resources
|
|||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.PorterDuffColorFilter
|
import android.graphics.PorterDuffColorFilter
|
||||||
|
import android.graphics.Rect
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@ -23,6 +26,7 @@ import android.util.Base64
|
|||||||
import android.util.Base64.NO_WRAP
|
import android.util.Base64.NO_WRAP
|
||||||
import android.util.Base64.encodeToString
|
import android.util.Base64.encodeToString
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
import android.widget.CheckBox
|
import android.widget.CheckBox
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
import android.widget.RadioButton
|
import android.widget.RadioButton
|
||||||
@ -51,6 +55,7 @@ import okhttp3.RequestBody
|
|||||||
import okhttp3.TlsVersion
|
import okhttp3.TlsVersion
|
||||||
import okio.Buffer
|
import okio.Buffer
|
||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApiException
|
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApiException
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||||
@ -1080,6 +1085,7 @@ fun Throwable.toErrorCode() = when (this) {
|
|||||||
private fun ApiResponse.Error.toErrorCode() = when (this.code) {
|
private fun ApiResponse.Error.toErrorCode() = when (this.code) {
|
||||||
else -> ERROR_API_EXCEPTION
|
else -> ERROR_API_EXCEPTION
|
||||||
}
|
}
|
||||||
|
fun Throwable.toApiError(tag: String) = ApiError.fromThrowable(tag, this)
|
||||||
|
|
||||||
inline fun <A, B, R> ifNotNull(a: A?, b: B?, code: (A, B) -> R): R? {
|
inline fun <A, B, R> ifNotNull(a: A?, b: B?, code: (A, B) -> R): R? {
|
||||||
if (a != null && b != null) {
|
if (a != null && b != null) {
|
||||||
@ -1092,3 +1098,71 @@ inline fun <A, B, R> ifNotNull(a: A?, b: B?, code: (A, B) -> R): R? {
|
|||||||
fun Iterable<Int>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
|
fun Iterable<Int>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
|
||||||
@kotlin.jvm.JvmName("averageOrNullOfFloat")
|
@kotlin.jvm.JvmName("averageOrNullOfFloat")
|
||||||
fun Iterable<Float>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
|
fun Iterable<Float>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
|
||||||
|
|
||||||
|
fun String.copyToClipboard(context: Context) {
|
||||||
|
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
val clipData = ClipData.newPlainText("Tekst", this)
|
||||||
|
clipboard.primaryClip = clipData
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TextView.getTextPosition(range: IntRange): Rect {
|
||||||
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
|
||||||
|
// Initialize global value
|
||||||
|
var parentTextViewRect = Rect()
|
||||||
|
|
||||||
|
// Initialize values for the computing of clickedText position
|
||||||
|
//val completeText = parentTextView.text as SpannableString
|
||||||
|
val textViewLayout = this.layout
|
||||||
|
|
||||||
|
val startOffsetOfClickedText = range.first//completeText.getSpanStart(clickedText)
|
||||||
|
val endOffsetOfClickedText = range.last//completeText.getSpanEnd(clickedText)
|
||||||
|
var startXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal(startOffsetOfClickedText)
|
||||||
|
var endXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal(endOffsetOfClickedText)
|
||||||
|
|
||||||
|
// Get the rectangle of the clicked text
|
||||||
|
val currentLineStartOffset = textViewLayout.getLineForOffset(startOffsetOfClickedText)
|
||||||
|
val currentLineEndOffset = textViewLayout.getLineForOffset(endOffsetOfClickedText)
|
||||||
|
val keywordIsInMultiLine = currentLineStartOffset != currentLineEndOffset
|
||||||
|
textViewLayout.getLineBounds(currentLineStartOffset, parentTextViewRect)
|
||||||
|
|
||||||
|
// Update the rectangle position to his real position on screen
|
||||||
|
val parentTextViewLocation = intArrayOf(0, 0)
|
||||||
|
this.getLocationOnScreen(parentTextViewLocation)
|
||||||
|
|
||||||
|
val parentTextViewTopAndBottomOffset = (parentTextViewLocation[1] - this.scrollY + this.compoundPaddingTop)
|
||||||
|
parentTextViewRect.top += parentTextViewTopAndBottomOffset
|
||||||
|
parentTextViewRect.bottom += parentTextViewTopAndBottomOffset
|
||||||
|
|
||||||
|
// In the case of multi line text, we have to choose what rectangle take
|
||||||
|
if (keywordIsInMultiLine) {
|
||||||
|
val screenHeight = windowManager.defaultDisplay.height
|
||||||
|
val dyTop = parentTextViewRect.top
|
||||||
|
val dyBottom = screenHeight - parentTextViewRect.bottom
|
||||||
|
val onTop = dyTop > dyBottom
|
||||||
|
|
||||||
|
if (onTop) {
|
||||||
|
endXCoordinatesOfClickedText = textViewLayout.getLineRight(currentLineStartOffset);
|
||||||
|
} else {
|
||||||
|
parentTextViewRect = Rect()
|
||||||
|
textViewLayout.getLineBounds(currentLineEndOffset, parentTextViewRect);
|
||||||
|
parentTextViewRect.top += parentTextViewTopAndBottomOffset;
|
||||||
|
parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;
|
||||||
|
startXCoordinatesOfClickedText = textViewLayout.getLineLeft(currentLineEndOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parentTextViewRect.left += (
|
||||||
|
parentTextViewLocation[0] +
|
||||||
|
startXCoordinatesOfClickedText +
|
||||||
|
this.compoundPaddingLeft -
|
||||||
|
this.scrollX
|
||||||
|
).toInt()
|
||||||
|
parentTextViewRect.right = (
|
||||||
|
parentTextViewRect.left +
|
||||||
|
endXCoordinatesOfClickedText -
|
||||||
|
startXCoordinatesOfClickedText
|
||||||
|
).toInt()
|
||||||
|
|
||||||
|
return parentTextViewRect
|
||||||
|
}
|
||||||
|
@ -12,7 +12,6 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -32,8 +31,8 @@ import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
|||||||
import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
||||||
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||||
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
||||||
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
|
import com.mikepenz.materialdrawer.model.interfaces.*
|
||||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile
|
import com.mikepenz.materialdrawer.model.utils.withIsHiddenInMiniDrawer
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
@ -42,7 +41,6 @@ import pl.droidsonroids.gif.GifDrawable
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.*
|
import pl.szczodrzynski.edziennik.data.api.events.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
|
||||||
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
|
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
|
||||||
@ -51,6 +49,7 @@ import pl.szczodrzynski.edziennik.sync.SyncWorker
|
|||||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
|
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
|
||||||
@ -91,7 +90,6 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
|||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||||
import pl.szczodrzynski.navlib.drawer.NavDrawer
|
import pl.szczodrzynski.navlib.drawer.NavDrawer
|
||||||
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
|
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
|
||||||
import pl.szczodrzynski.navlib.drawer.items.withAppTitle
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -286,8 +284,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
|
|
||||||
setContentView(b.root)
|
setContentView(b.root)
|
||||||
|
|
||||||
Log.d(TAG, Signing.appPassword)
|
|
||||||
|
|
||||||
mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||||
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||||
|
|
||||||
@ -364,7 +360,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
drawerProfileLongClickListener = { _, profile, _, view ->
|
drawerProfileLongClickListener = { _, profile, _, view ->
|
||||||
if (profile is ProfileDrawerItem) {
|
if (view != null && profile is ProfileDrawerItem) {
|
||||||
showProfileContextMenu(profile, view)
|
showProfileContextMenu(profile, view)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -448,6 +444,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
|
|
||||||
// WHAT'S NEW DIALOG
|
// WHAT'S NEW DIALOG
|
||||||
if (app.config.appVersion < BuildConfig.VERSION_CODE) {
|
if (app.config.appVersion < BuildConfig.VERSION_CODE) {
|
||||||
|
// force an AppSync after update
|
||||||
|
app.config.sync.lastAppSync = 0L
|
||||||
ChangelogDialog(this)
|
ChangelogDialog(this)
|
||||||
if (app.config.appVersion < 170) {
|
if (app.config.appVersion < 170) {
|
||||||
//Intent intent = new Intent(this, ChangelogIntroActivity.class);
|
//Intent intent = new Intent(this, ChangelogIntroActivity.class);
|
||||||
@ -725,6 +723,15 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
)
|
)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
"createManualEvent" -> {
|
||||||
|
val date = extras.getString("eventDate")?.let { Date.fromY_m_d(it) } ?: Date.getToday()
|
||||||
|
EventManualDialog(
|
||||||
|
this,
|
||||||
|
App.profileId,
|
||||||
|
defaultDate = date
|
||||||
|
)
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
if (handled && !navLoading) {
|
if (handled && !navLoading) {
|
||||||
@ -906,9 +913,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
bottomSheet.close()
|
bottomSheet.close()
|
||||||
bottomSheet.removeAllContextual()
|
bottomSheet.removeAllContextual()
|
||||||
bottomSheet.toggleGroupEnabled = false
|
bottomSheet.toggleGroupEnabled = false
|
||||||
bottomSheet.onCloseListener = null
|
|
||||||
drawer.close()
|
drawer.close()
|
||||||
drawer.setSelection(target.id, fireOnClick = false)
|
if (drawer.getSelection() != target.id)
|
||||||
|
drawer.setSelection(target.id, fireOnClick = false)
|
||||||
navView.toolbar.setTitle(target.title ?: target.name)
|
navView.toolbar.setTitle(target.title ?: target.name)
|
||||||
navView.bottomBar.fabEnable = false
|
navView.bottomBar.fabEnable = false
|
||||||
navView.bottomBar.fabExtended = false
|
navView.bottomBar.fabExtended = false
|
||||||
@ -1056,7 +1063,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
val item = DrawerPrimaryItem()
|
val item = DrawerPrimaryItem()
|
||||||
.withIdentifier(target.id.toLong())
|
.withIdentifier(target.id.toLong())
|
||||||
.withName(target.name)
|
.withName(target.name)
|
||||||
.withHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id))
|
.withIsHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id))
|
||||||
.also { if (target.description != null) it.withDescription(target.description!!) }
|
.also { if (target.description != null) it.withDescription(target.description!!) }
|
||||||
.also { if (target.icon != null) it.withIcon(target.icon!!) }
|
.also { if (target.icon != null) it.withIcon(target.icon!!) }
|
||||||
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
|
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
|
||||||
@ -1120,7 +1127,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
drawer.addProfileSettings(*drawerProfiles.toTypedArray())
|
drawer.addProfileSettings(*drawerProfiles.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showProfileContextMenu(profile: IProfile<*>, view: View) {
|
private fun showProfileContextMenu(profile: IProfile, view: View) {
|
||||||
val profileId = profile.identifier.toInt()
|
val profileId = profile.identifier.toInt()
|
||||||
val popupMenu = PopupMenu(this, view)
|
val popupMenu = PopupMenu(this, view)
|
||||||
popupMenu.menu.add(0, 1, 1, R.string.profile_menu_open_settings)
|
popupMenu.menu.add(0, 1, 1, R.string.profile_menu_open_settings)
|
||||||
@ -1133,7 +1140,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
loadTarget(DRAWER_ITEM_SETTINGS, null)
|
loadTarget(DRAWER_ITEM_SETTINGS, null)
|
||||||
} else if (item.itemId == 2) {
|
} else if (item.itemId == 2) {
|
||||||
ProfileRemoveDialog(this, profileId, profile.name?.getText(this)?.toString() ?: "?")
|
ProfileRemoveDialog(this, profileId, profile.name?.getText(this) ?: "?")
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -1144,7 +1151,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
private var targetHomeId: Int = -1
|
private var targetHomeId: Int = -1
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (!b.navView.onBackPressed()) {
|
if (!b.navView.onBackPressed()) {
|
||||||
if (App.config.ui.openDrawerOnBackPressed) {
|
if (App.config.ui.openDrawerOnBackPressed && ((navTarget.popTo == null && navTarget.popToHome)
|
||||||
|
|| navTarget.id == DRAWER_ITEM_HOME)) {
|
||||||
b.navView.drawer.toggle()
|
b.navView.drawer.toggle()
|
||||||
} else {
|
} else {
|
||||||
navigateUp()
|
navigateUp()
|
||||||
|
@ -22,7 +22,7 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
|
|
||||||
class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||||
companion object {
|
companion object {
|
||||||
const val DATA_VERSION = 10
|
const val DATA_VERSION = 11
|
||||||
}
|
}
|
||||||
|
|
||||||
private val job = Job()
|
private val job = Job()
|
||||||
@ -105,11 +105,6 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
|||||||
get() { mWidgetConfigs = mWidgetConfigs ?: values.get("widgetConfigs", JsonObject()); return mWidgetConfigs ?: JsonObject() }
|
get() { mWidgetConfigs = mWidgetConfigs ?: values.get("widgetConfigs", JsonObject()); return mWidgetConfigs ?: JsonObject() }
|
||||||
set(value) { set("widgetConfigs", value); mWidgetConfigs = value }
|
set(value) { set("widgetConfigs", value); mWidgetConfigs = value }
|
||||||
|
|
||||||
private var mLastAppSync: Long? = null
|
|
||||||
var lastAppSync: Long
|
|
||||||
get() { mLastAppSync = mLastAppSync ?: values.get("lastAppSync", 0L); return mLastAppSync ?: 0L }
|
|
||||||
set(value) { set("lastAppSync", value); mLastAppSync = value }
|
|
||||||
|
|
||||||
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
|
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
|
||||||
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
|
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
|
||||||
init {
|
init {
|
||||||
|
@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.config
|
|||||||
import pl.szczodrzynski.edziennik.config.utils.get
|
import pl.szczodrzynski.edziennik.config.utils.get
|
||||||
import pl.szczodrzynski.edziennik.config.utils.getIntList
|
import pl.szczodrzynski.edziennik.config.utils.getIntList
|
||||||
import pl.szczodrzynski.edziennik.config.utils.set
|
import pl.szczodrzynski.edziennik.config.utils.set
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
class ConfigSync(private val config: Config) {
|
class ConfigSync(private val config: Config) {
|
||||||
private var mDontShowAppManagerDialog: Boolean? = null
|
private var mDontShowAppManagerDialog: Boolean? = null
|
||||||
@ -19,6 +20,11 @@ class ConfigSync(private val config: Config) {
|
|||||||
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
|
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
|
||||||
set(value) { config.set("syncEnabled", value); mSyncEnabled = value }
|
set(value) { config.set("syncEnabled", value); mSyncEnabled = value }
|
||||||
|
|
||||||
|
private var mWebPushEnabled: Boolean? = null
|
||||||
|
var webPushEnabled: Boolean
|
||||||
|
get() { mWebPushEnabled = mWebPushEnabled ?: config.values.get("webPushEnabled", true); return mWebPushEnabled ?: true }
|
||||||
|
set(value) { config.set("webPushEnabled", value); mWebPushEnabled = value }
|
||||||
|
|
||||||
private var mSyncOnlyWifi: Boolean? = null
|
private var mSyncOnlyWifi: Boolean? = null
|
||||||
var onlyWifi: Boolean
|
var onlyWifi: Boolean
|
||||||
get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates }
|
get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates }
|
||||||
@ -34,20 +40,30 @@ class ConfigSync(private val config: Config) {
|
|||||||
get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true }
|
get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true }
|
||||||
set(value) { config.set("notifyAboutUpdates", value); mNotifyAboutUpdates = value }
|
set(value) { config.set("notifyAboutUpdates", value); mNotifyAboutUpdates = value }
|
||||||
|
|
||||||
|
private var mLastAppSync: Long? = null
|
||||||
|
var lastAppSync: Long
|
||||||
|
get() { mLastAppSync = mLastAppSync ?: config.values.get("lastAppSync", 0L); return mLastAppSync ?: 0L }
|
||||||
|
set(value) { config.set("lastAppSync", value); mLastAppSync = value }
|
||||||
|
|
||||||
/* ____ _ _ _
|
/* ____ _ _ _
|
||||||
/ __ \ (_) | | | |
|
/ __ \ (_) | | | |
|
||||||
| | | |_ _ _ ___| |_ | |__ ___ _ _ _ __ ___
|
| | | |_ _ _ ___| |_ | |__ ___ _ _ _ __ ___
|
||||||
| | | | | | | |/ _ \ __| | '_ \ / _ \| | | | '__/ __|
|
| | | | | | | |/ _ \ __| | '_ \ / _ \| | | | '__/ __|
|
||||||
| |__| | |_| | | __/ |_ | | | | (_) | |_| | | \__ \
|
| |__| | |_| | | __/ |_ | | | | (_) | |_| | | \__ \
|
||||||
\___\_\\__,_|_|\___|\__| |_| |_|\___/ \__,_|_| |__*/
|
\___\_\\__,_|_|\___|\__| |_| |_|\___/ \__,_|_| |__*/
|
||||||
private var mQuietHoursStart: Long? = null
|
private var mQuietHoursEnabled: Boolean? = null
|
||||||
var quietHoursStart: Long
|
var quietHoursEnabled: Boolean
|
||||||
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", 0L); return mQuietHoursStart ?: 0L }
|
get() { mQuietHoursEnabled = mQuietHoursEnabled ?: config.values.get("quietHoursEnabled", false); return mQuietHoursEnabled ?: false }
|
||||||
|
set(value) { config.set("quietHoursEnabled", value); mQuietHoursEnabled = value }
|
||||||
|
|
||||||
|
private var mQuietHoursStart: Time? = null
|
||||||
|
var quietHoursStart: Time?
|
||||||
|
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", null as Time?); return mQuietHoursStart }
|
||||||
set(value) { config.set("quietHoursStart", value); mQuietHoursStart = value }
|
set(value) { config.set("quietHoursStart", value); mQuietHoursStart = value }
|
||||||
|
|
||||||
private var mQuietHoursEnd: Long? = null
|
private var mQuietHoursEnd: Time? = null
|
||||||
var quietHoursEnd: Long
|
var quietHoursEnd: Time?
|
||||||
get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", 0L); return mQuietHoursEnd ?: 0L }
|
get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", null as Time?); return mQuietHoursEnd }
|
||||||
set(value) { config.set("quietHoursEnd", value); mQuietHoursEnd = value }
|
set(value) { config.set("quietHoursEnd", value); mQuietHoursEnd = value }
|
||||||
|
|
||||||
private var mQuietDuringLessons: Boolean? = null
|
private var mQuietDuringLessons: Boolean? = null
|
||||||
@ -90,4 +106,4 @@ class ConfigSync(private val config: Config) {
|
|||||||
var tokenVulcanList: List<Int>
|
var tokenVulcanList: List<Int>
|
||||||
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
|
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
|
||||||
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
|
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,15 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
|
|||||||
get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES }
|
get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES }
|
||||||
set(value) { config.set("yearAverageMode", value); mYearAverageMode = value }
|
set(value) { config.set("yearAverageMode", value); mYearAverageMode = value }
|
||||||
|
|
||||||
private var mCountZeroToAvg: Boolean? = null
|
private var mHideImproved: Boolean? = null
|
||||||
var countZeroToAvg: Boolean
|
var hideImproved: Boolean
|
||||||
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true }
|
get() { mHideImproved = mHideImproved ?: config.values.get("hideImproved", false); return mHideImproved ?: false }
|
||||||
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value }
|
set(value) { config.set("hideImproved", value); mHideImproved = value }
|
||||||
|
|
||||||
|
private var mAverageWithoutWeight: Boolean? = null
|
||||||
|
var averageWithoutWeight: Boolean
|
||||||
|
get() { mAverageWithoutWeight = mAverageWithoutWeight ?: config.values.get("averageWithoutWeight", true); return mAverageWithoutWeight ?: true }
|
||||||
|
set(value) { config.set("averageWithoutWeight", value); mAverageWithoutWeight = value }
|
||||||
|
|
||||||
private var mPlusValue: Float? = null
|
private var mPlusValue: Float? = null
|
||||||
var plusValue: Float?
|
var plusValue: Float?
|
||||||
@ -35,6 +40,11 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
|
|||||||
get() { mMinusValue = mMinusValue ?: config.values.getFloat("minusValue"); return mMinusValue }
|
get() { mMinusValue = mMinusValue ?: config.values.getFloat("minusValue"); return mMinusValue }
|
||||||
set(value) { config.set("minusValue", value); mMinusValue = value }
|
set(value) { config.set("minusValue", value); mMinusValue = value }
|
||||||
|
|
||||||
|
private var mDontCountEnabled: Boolean? = null
|
||||||
|
var dontCountEnabled: Boolean
|
||||||
|
get() { mDontCountEnabled = mDontCountEnabled ?: config.values.get("dontCountEnabled", false); return mDontCountEnabled ?: false }
|
||||||
|
set(value) { config.set("dontCountEnabled", value); mDontCountEnabled = value }
|
||||||
|
|
||||||
private var mDontCountGrades: List<String>? = null
|
private var mDontCountGrades: List<String>? = null
|
||||||
var dontCountGrades: List<String>
|
var dontCountGrades: List<String>
|
||||||
get() { mDontCountGrades = mDontCountGrades ?: config.values.get("dontCountGrades", listOf()); return mDontCountGrades ?: listOf() }
|
get() { mDontCountGrades = mDontCountGrades ?: config.values.get("dontCountGrades", listOf()); return mDontCountGrades ?: listOf() }
|
||||||
|
@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
|
|||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
||||||
init { config.apply {
|
init { config.apply {
|
||||||
@ -42,9 +43,7 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
|||||||
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
|
||||||
sync.quietHoursStart = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
|
|
||||||
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", null)?.toLongOrNull() ?: 0
|
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", null)?.toLongOrNull() ?: 0
|
||||||
sync.quietHoursEnd = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
|
|
||||||
timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false
|
timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false
|
||||||
ui.headerBackground = p.getString("$s.headerBackground", null).fix()
|
ui.headerBackground = p.getString("$s.headerBackground", null).fix()
|
||||||
ui.appBackground = p.getString("$s.appBackground", null).fix()
|
ui.appBackground = p.getString("$s.appBackground", null).fix()
|
||||||
@ -59,6 +58,22 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
|||||||
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
|
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
|
||||||
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
|
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
|
||||||
|
|
||||||
|
val startMillis = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
|
||||||
|
val endMillis = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
|
||||||
|
if (startMillis > 0) {
|
||||||
|
try {
|
||||||
|
sync.quietHoursStart = Time.fromMillis(abs(startMillis))
|
||||||
|
sync.quietHoursEnd = Time.fromMillis(abs(endMillis))
|
||||||
|
sync.quietHoursEnabled = true
|
||||||
|
}
|
||||||
|
catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sync.quietHoursEnabled = false
|
||||||
|
sync.quietHoursStart = null
|
||||||
|
sync.quietHoursEnd = null
|
||||||
|
}
|
||||||
|
|
||||||
sync.tokenMobidziennikList = listOf()
|
sync.tokenMobidziennikList = listOf()
|
||||||
sync.tokenVulcanList = listOf()
|
sync.tokenVulcanList = listOf()
|
||||||
sync.tokenLibrusList = listOf()
|
sync.tokenLibrusList = listOf()
|
||||||
@ -78,4 +93,4 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
|||||||
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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ import pl.szczodrzynski.edziennik.HOUR
|
|||||||
import pl.szczodrzynski.edziennik.MainActivity
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
import pl.szczodrzynski.edziennik.config.Config
|
import pl.szczodrzynski.edziennik.config.Config
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
class ConfigMigration(app: App, config: Config) {
|
class ConfigMigration(app: App, config: Config) {
|
||||||
init { config.apply {
|
init { config.apply {
|
||||||
@ -43,8 +45,9 @@ class ConfigMigration(app: App, config: Config) {
|
|||||||
sync.interval = 1*HOUR.toInt()
|
sync.interval = 1*HOUR.toInt()
|
||||||
sync.notifyAboutUpdates = true
|
sync.notifyAboutUpdates = true
|
||||||
sync.onlyWifi = false
|
sync.onlyWifi = false
|
||||||
sync.quietHoursStart = 0
|
sync.quietHoursEnabled = false
|
||||||
sync.quietHoursEnd = 0
|
sync.quietHoursStart = null
|
||||||
|
sync.quietHoursEnd = null
|
||||||
sync.quietDuringLessons = false
|
sync.quietDuringLessons = false
|
||||||
sync.tokenApp = null
|
sync.tokenApp = null
|
||||||
sync.tokenMobidziennik = null
|
sync.tokenMobidziennik = null
|
||||||
@ -69,5 +72,25 @@ class ConfigMigration(app: App, config: Config) {
|
|||||||
|
|
||||||
dataVersion = 10
|
dataVersion = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dataVersion < 11) {
|
||||||
|
val startMillis = config.values.get("quietHoursStart", 0L)
|
||||||
|
val endMillis = config.values.get("quietHoursEnd", 0L)
|
||||||
|
if (startMillis > 0) {
|
||||||
|
try {
|
||||||
|
sync.quietHoursStart = Time.fromMillis(abs(startMillis))
|
||||||
|
sync.quietHoursEnd = Time.fromMillis(abs(endMillis))
|
||||||
|
sync.quietHoursEnabled = true
|
||||||
|
}
|
||||||
|
catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sync.quietHoursEnabled = false
|
||||||
|
sync.quietHoursStart = null
|
||||||
|
sync.quietHoursEnd = null
|
||||||
|
}
|
||||||
|
|
||||||
|
dataVersion = 11
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class ProfileConfigMigration(config: ProfileConfig) {
|
|||||||
|
|
||||||
if (dataVersion < 1) {
|
if (dataVersion < 1) {
|
||||||
grades.colorMode = COLOR_MODE_WEIGHTED
|
grades.colorMode = COLOR_MODE_WEIGHTED
|
||||||
grades.countZeroToAvg = true
|
grades.dontCountEnabled = false
|
||||||
grades.yearAverageMode = YEAR_ALL_GRADES
|
grades.yearAverageMode = YEAR_ALL_GRADES
|
||||||
ui.agendaViewType = AGENDA_DEFAULT
|
ui.agendaViewType = AGENDA_DEFAULT
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import pl.szczodrzynski.edziennik.data.api.task.ErrorReportTask
|
|||||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||||
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
|
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
|
import pl.szczodrzynski.edziennik.toApiError
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@ -181,7 +182,7 @@ class ApiService : Service() {
|
|||||||
is SzkolnyTask -> task.run(taskCallback)
|
is SzkolnyTask -> task.run(taskCallback)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
|
taskCallback.onError(e.toApiError(TAG))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +124,8 @@ const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
|
|||||||
const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
|
const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
|
||||||
const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED = 184
|
const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED = 184
|
||||||
const val ERROR_LIBRUS_API_DEVICE_REGISTERED = 185
|
const val ERROR_LIBRUS_API_DEVICE_REGISTERED = 185
|
||||||
|
const val ERROR_LIBRUS_MESSAGES_NOT_FOUND = 186
|
||||||
|
const val ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST = 187
|
||||||
|
|
||||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
|
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
|
||||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
|
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
|
||||||
@ -140,6 +142,7 @@ const val ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE = 214
|
|||||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215
|
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215
|
||||||
const val ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN = 216
|
const val ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN = 216
|
||||||
const val ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER = 217
|
const val ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER = 217
|
||||||
|
const val ERROR_MOBIDZIENNIK_WEB_SERVER_PROBLEM = 218
|
||||||
|
|
||||||
const val ERROR_LOGIN_VULCAN_INVALID_SYMBOL = 301
|
const val ERROR_LOGIN_VULCAN_INVALID_SYMBOL = 301
|
||||||
const val ERROR_LOGIN_VULCAN_INVALID_TOKEN = 302
|
const val ERROR_LOGIN_VULCAN_INVALID_TOKEN = 302
|
||||||
|
@ -70,13 +70,13 @@ val librusLoginMethods = listOf(
|
|||||||
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java)
|
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java)
|
||||||
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
||||||
.withRequiredLoginMethod { profile, _ ->
|
.withRequiredLoginMethod { profile, _ ->
|
||||||
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
|
if (profile?.hasStudentData("accountPassword") == false || true) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
|
||||||
},
|
},
|
||||||
|
|
||||||
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java)
|
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java)
|
||||||
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
||||||
.withRequiredLoginMethod { profile, _ ->
|
.withRequiredLoginMethod { profile, _ ->
|
||||||
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
|
if (profile?.hasStudentData("accountPassword") == false || true) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.api
|
package pl.szczodrzynski.edziennik.data.api
|
||||||
|
|
||||||
import kotlin.text.RegexOption.DOT_MATCHES_ALL
|
import kotlin.text.RegexOption.DOT_MATCHES_ALL
|
||||||
|
import kotlin.text.RegexOption.IGNORE_CASE
|
||||||
|
|
||||||
object Regexes {
|
object Regexes {
|
||||||
val STYLE_CSS_COLOR by lazy {
|
val STYLE_CSS_COLOR by lazy {
|
||||||
@ -126,7 +127,7 @@ object Regexes {
|
|||||||
|
|
||||||
|
|
||||||
val LIBRUS_ATTACHMENT_KEY by lazy {
|
val LIBRUS_ATTACHMENT_KEY by lazy {
|
||||||
"""singleUseKey=([0-9A-f_]+)""".toRegex()
|
"""singleUseKey=([0-9A-z_]+)""".toRegex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -199,4 +200,20 @@ object Regexes {
|
|||||||
val EDUDZIENNIK_TEACHERS by lazy {
|
val EDUDZIENNIK_TEACHERS by lazy {
|
||||||
"""<div class="teacher">.*?<p>(.+?) (.+?)</p>""".toRegex(DOT_MATCHES_ALL)
|
"""<div class="teacher">.*?<p>(.+?) (.+?)</p>""".toRegex(DOT_MATCHES_ALL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val LINKIFY_DATE_YMD by lazy {
|
||||||
|
"""(1\d{3}|20\d{2})[\-./](1[0-2]|0?\d)[\-./]([1-2]\d|3[0-1]|0?\d)""".toRegex()
|
||||||
|
}
|
||||||
|
val LINKIFY_DATE_DMY by lazy {
|
||||||
|
"""(?<![\d\-./])([1-2]\d|3[0-1]|0?\d)[\-./](1[0-2]|0?\d)(?:[\-./](1\d{3}|2?0?\d{2}))?(?![\d\-/])""".toRegex()
|
||||||
|
}
|
||||||
|
val LINKIFY_DATE_ABSOLUTE by lazy {
|
||||||
|
"""([1-3][0-9]|[1-9])\s(sty|lut|mar|kwi|maj|cze|lip|sie|wrz|paź|lis|gru).*?\s(1[0-9]{3}|20[0-9]{2})?""".toRegex(IGNORE_CASE)
|
||||||
|
}
|
||||||
|
val LINKIFY_DATE_RELATIVE by lazy {
|
||||||
|
"""za\s([0-9]+)?\s?(dni|dzień|tydzień|tygodnie)""".toRegex(IGNORE_CASE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data
|
|||||||
import im.wangchao.mhttp.Request
|
import im.wangchao.mhttp.Request
|
||||||
import im.wangchao.mhttp.Response
|
import im.wangchao.mhttp.Response
|
||||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
import okhttp3.Cookie
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
@ -43,8 +42,8 @@ open class EdudziennikWeb(open val data: DataEdudziennik, open val lastSync: Lon
|
|||||||
|
|
||||||
if (semester == null && url.contains("start")) {
|
if (semester == null && url.contains("start")) {
|
||||||
profile?.also { profile ->
|
profile?.also { profile ->
|
||||||
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com")
|
val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
|
||||||
val semesterCookie = cookies.firstOrNull { it.name() == "semester" }?.value()?.toIntOrNull()
|
val semesterCookie = cookies["semester"]?.toIntOrNull()
|
||||||
|
|
||||||
semesterCookie?.let { data.currentSemester = it }
|
semesterCookie?.let { data.currentSemester = it }
|
||||||
|
|
||||||
@ -75,13 +74,7 @@ open class EdudziennikWeb(open val data: DataEdudziennik, open val lastSync: Lon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("dziennikel.appspot.com", "sessionid", data.webSessionId)
|
||||||
Cookie.Builder()
|
|
||||||
.name("sessionid")
|
|
||||||
.value(data.webSessionId!!)
|
|
||||||
.domain("dziennikel.appspot.com")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
|
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
|
@ -24,7 +24,7 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("dziennikel.appspot.com")
|
data.app.cookieJar.clear("dziennikel.appspot.com")
|
||||||
if (data.loginEmail.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
|
if (data.loginEmail.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
|
||||||
loginWithCredentials()
|
loginWithCredentials()
|
||||||
}
|
}
|
||||||
@ -59,8 +59,8 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com")
|
val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
|
||||||
val sessionId = cookies.firstOrNull { it.name() == "sessionid" }?.value()
|
val sessionId = cookies["sessionid"]
|
||||||
|
|
||||||
if (sessionId == null) {
|
if (sessionId == null) {
|
||||||
data.error(ApiError(TAG, ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID)
|
data.error(ApiError(TAG, ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID)
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
|
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
|
||||||
|
|
||||||
import androidx.core.util.set
|
import androidx.core.util.set
|
||||||
import okhttp3.Cookie
|
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_API
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_API
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_WEB
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_WEB
|
||||||
@ -24,18 +23,8 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
|
|||||||
loginMethods.clear()
|
loginMethods.clear()
|
||||||
if (isWebLoginValid()) {
|
if (isWebLoginValid()) {
|
||||||
loginMethods += LOGIN_METHOD_IDZIENNIK_WEB
|
loginMethods += LOGIN_METHOD_IDZIENNIK_WEB
|
||||||
app.cookieJar.saveFromResponse(null, listOf(
|
app.cookieJar.set("iuczniowie.progman.pl", "ASP.NET_SessionId_iDziennik", webSessionId)
|
||||||
Cookie.Builder()
|
app.cookieJar.set("iuczniowie.progman.pl", ".ASPXAUTH", webAuth)
|
||||||
.name("ASP.NET_SessionId_iDziennik")
|
|
||||||
.value(webSessionId!!)
|
|
||||||
.domain("iuczniowie.progman.pl")
|
|
||||||
.secure().httpOnly().build(),
|
|
||||||
Cookie.Builder()
|
|
||||||
.name(".ASPXAUTH")
|
|
||||||
.value(webAuth!!)
|
|
||||||
.domain("iuczniowie.progman.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
if (isApiLoginValid())
|
if (isApiLoginValid())
|
||||||
loginMethods += LOGIN_METHOD_IDZIENNIK_API
|
loginMethods += LOGIN_METHOD_IDZIENNIK_API
|
||||||
|
@ -16,6 +16,8 @@ import pl.szczodrzynski.edziennik.data.db.entity.Announcement
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||||
import pl.szczodrzynski.edziennik.getJsonObject
|
import pl.szczodrzynski.edziennik.getJsonObject
|
||||||
|
import pl.szczodrzynski.edziennik.getLong
|
||||||
|
import pl.szczodrzynski.edziennik.getString
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
|
||||||
class IdziennikWebAnnouncements(override val data: DataIdziennik,
|
class IdziennikWebAnnouncements(override val data: DataIdziennik,
|
||||||
@ -43,11 +45,11 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik,
|
|||||||
for (jAnnouncementEl in json.getAsJsonArray("ListK")) {
|
for (jAnnouncementEl in json.getAsJsonArray("ListK")) {
|
||||||
val jAnnouncement = jAnnouncementEl.asJsonObject
|
val jAnnouncement = jAnnouncementEl.asJsonObject
|
||||||
// jAnnouncement
|
// jAnnouncement
|
||||||
val announcementId = jAnnouncement.get("Id").asLong
|
val announcementId = jAnnouncement.getLong("Id") ?: -1
|
||||||
|
|
||||||
val rTeacher = data.getTeacherByFirstLast(jAnnouncement.get("Autor").asString)
|
val rTeacher = data.getTeacherByFirstLast(jAnnouncement.getString("Autor") ?: "")
|
||||||
val addedDate = java.lang.Long.parseLong(jAnnouncement.get("DataDodania").asString.replace("[^\\d]".toRegex(), ""))
|
val addedDate = jAnnouncement.getString("DataDodania")?.replace("[^\\d]".toRegex(), "")?.toLongOrNull() ?: System.currentTimeMillis()
|
||||||
val startDate = Date.fromMillis(java.lang.Long.parseLong(jAnnouncement.get("DataWydarzenia").asString.replace("[^\\d]".toRegex(), "")))
|
val startDate = jAnnouncement.getString("DataWydarzenia")?.replace("[^\\d]".toRegex(), "")?.toLongOrNull()?.let { Date.fromMillis(it) }
|
||||||
|
|
||||||
val announcementObject = Announcement(
|
val announcementObject = Announcement(
|
||||||
profileId,
|
profileId,
|
||||||
|
@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login
|
|||||||
import im.wangchao.mhttp.Request
|
import im.wangchao.mhttp.Request
|
||||||
import im.wangchao.mhttp.Response
|
import im.wangchao.mhttp.Response
|
||||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
import okhttp3.Cookie
|
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||||
@ -24,22 +23,12 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
|||||||
|
|
||||||
init { run {
|
init { run {
|
||||||
if (data.isWebLoginValid()) {
|
if (data.isWebLoginValid()) {
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("iuczniowie.progman.pl", "ASP.NET_SessionId_iDziennik", data.webSessionId)
|
||||||
Cookie.Builder()
|
data.app.cookieJar.set("iuczniowie.progman.pl", ".ASPXAUTH", data.webAuth)
|
||||||
.name("ASP.NET_SessionId_iDziennik")
|
|
||||||
.value(data.webSessionId!!)
|
|
||||||
.domain("iuczniowie.progman.pl")
|
|
||||||
.secure().httpOnly().build(),
|
|
||||||
Cookie.Builder()
|
|
||||||
.name(".ASPXAUTH")
|
|
||||||
.value(data.webAuth!!)
|
|
||||||
.domain("iuczniowie.progman.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("iuczniowie.progman.pl")
|
data.app.cookieJar.clear("iuczniowie.progman.pl")
|
||||||
if (data.webSchoolName != null && data.webUsername != null && data.webPassword != null) {
|
if (data.webSchoolName != null && data.webUsername != null && data.webPassword != null) {
|
||||||
loginWithCredentials()
|
loginWithCredentials()
|
||||||
}
|
}
|
||||||
@ -62,11 +51,11 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
|||||||
|
|
||||||
// login succeeded: there is a start page
|
// login succeeded: there is a start page
|
||||||
if (text.contains("czyWyswietlicDostepMobilny")) {
|
if (text.contains("czyWyswietlicDostepMobilny")) {
|
||||||
val cookies = data.app.cookieJar.getForDomain("iuczniowie.progman.pl")
|
val cookies = data.app.cookieJar.getAll("iuczniowie.progman.pl")
|
||||||
run {
|
run {
|
||||||
data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION
|
data.webSessionId = cookies["ASP.NET_SessionId_iDziennik"] ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION
|
||||||
data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH
|
data.webAuth = cookies[".ASPXAUTH"] ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH
|
||||||
data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER
|
data.apiBearer = cookies["Bearer"]?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER
|
||||||
data.loginExpiryTime = response.getUnixDate() + 30 * MINUTE /* after about 40 minutes the login didn't work already */
|
data.loginExpiryTime = response.getUnixDate() + 30 * MINUTE /* after about 40 minutes the login didn't work already */
|
||||||
data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */
|
data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.data.api.edziennik.librus
|
package pl.szczodrzynski.edziennik.data.api.edziennik.librus
|
||||||
|
|
||||||
import okhttp3.Cookie
|
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_API
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_API
|
||||||
@ -31,23 +30,11 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
|||||||
loginMethods += LOGIN_METHOD_LIBRUS_API
|
loginMethods += LOGIN_METHOD_LIBRUS_API
|
||||||
if (isSynergiaLoginValid()) {
|
if (isSynergiaLoginValid()) {
|
||||||
loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA
|
loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA
|
||||||
app.cookieJar.saveFromResponse(null, listOf(
|
app.cookieJar.set("synergia.librus.pl", "DZIENNIKSID", synergiaSessionId)
|
||||||
Cookie.Builder()
|
|
||||||
.name("DZIENNIKSID")
|
|
||||||
.value(synergiaSessionId!!)
|
|
||||||
.domain("synergia.librus.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
if (isMessagesLoginValid()) {
|
if (isMessagesLoginValid()) {
|
||||||
loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES
|
loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES
|
||||||
app.cookieJar.saveFromResponse(null, listOf(
|
app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", messagesSessionId)
|
||||||
Cookie.Builder()
|
|
||||||
.name("DZIENNIKSID")
|
|
||||||
.value(messagesSessionId!!)
|
|
||||||
.domain("wiadomosci.librus.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ val LibrusFeatures = listOf(
|
|||||||
Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf(
|
Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf(
|
||||||
ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API
|
ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API
|
||||||
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
|
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
|
||||||
!data.app.config.sync.tokenLibrusList.contains(data.profileId)
|
(data as DataLibrus).isPremium && !data.app.config.sync.tokenLibrusList.contains(data.profileId)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@ -116,11 +116,11 @@ val LibrusFeatures = listOf(
|
|||||||
* Homework - using API.
|
* Homework - using API.
|
||||||
* Sync only if account has premium access.
|
* Sync only if account has premium access.
|
||||||
*/
|
*/
|
||||||
Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
|
/*Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
|
||||||
ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API
|
ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API
|
||||||
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
|
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
|
||||||
(data as DataLibrus).isPremium
|
(data as DataLibrus).isPremium
|
||||||
},
|
},*/
|
||||||
/**
|
/**
|
||||||
* Behaviour - using API.
|
* Behaviour - using API.
|
||||||
*/
|
*/
|
||||||
@ -227,9 +227,9 @@ val LibrusFeatures = listOf(
|
|||||||
*/
|
*/
|
||||||
Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
|
Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
|
||||||
ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK to LOGIN_METHOD_LIBRUS_SYNERGIA
|
ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK to LOGIN_METHOD_LIBRUS_SYNERGIA
|
||||||
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)).withShouldSync { data ->
|
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA))/*.withShouldSync { data ->
|
||||||
!(data as DataLibrus).isPremium
|
!(data as DataLibrus).isPremium
|
||||||
},
|
}*/,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Messages inbox - using messages website.
|
* Messages inbox - using messages website.
|
||||||
|
@ -49,6 +49,7 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
|||||||
val error = if (response?.code() == 200) null else
|
val error = if (response?.code() == 200) null else
|
||||||
json.getString("Code") ?:
|
json.getString("Code") ?:
|
||||||
json.getString("Message") ?:
|
json.getString("Message") ?:
|
||||||
|
json.getString("Status") ?:
|
||||||
response?.parserErrorBody
|
response?.parserErrorBody
|
||||||
error?.let { code ->
|
error?.let { code ->
|
||||||
when (code) {
|
when (code) {
|
||||||
@ -67,6 +68,7 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
|||||||
"Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT
|
"Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT
|
||||||
"NoticeboardProblem" -> ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
|
"NoticeboardProblem" -> ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
|
||||||
"DeviceRegistered" -> ERROR_LIBRUS_API_DEVICE_REGISTERED
|
"DeviceRegistered" -> ERROR_LIBRUS_API_DEVICE_REGISTERED
|
||||||
|
"Maintenance" -> ERROR_LIBRUS_API_MAINTENANCE
|
||||||
else -> ERROR_LIBRUS_API_OTHER
|
else -> ERROR_LIBRUS_API_OTHER
|
||||||
}.let { errorCode ->
|
}.let { errorCode ->
|
||||||
if (errorCode !in ignoreErrors) {
|
if (errorCode !in ignoreErrors) {
|
||||||
@ -119,6 +121,7 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
|
|||||||
.allowErrorCode(HTTP_UNAUTHORIZED)
|
.allowErrorCode(HTTP_UNAUTHORIZED)
|
||||||
.allowErrorCode(HTTP_UNAVAILABLE)
|
.allowErrorCode(HTTP_UNAVAILABLE)
|
||||||
.allowErrorCode(HTTP_NOT_FOUND)
|
.allowErrorCode(HTTP_NOT_FOUND)
|
||||||
|
.allowErrorCode(503)
|
||||||
.callback(callback)
|
.callback(callback)
|
||||||
.build()
|
.build()
|
||||||
.enqueue()
|
.enqueue()
|
||||||
|
@ -12,7 +12,6 @@ import im.wangchao.mhttp.body.MediaTypeUtils
|
|||||||
import im.wangchao.mhttp.callback.FileCallbackHandler
|
import im.wangchao.mhttp.callback.FileCallbackHandler
|
||||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
import okhttp3.Cookie
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.json.XML
|
import org.json.XML
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
@ -55,13 +54,20 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
|
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN
|
||||||
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
|
text.contains("<message>Nie odnaleziono wiadomości.</message>") -> ERROR_LIBRUS_MESSAGES_NOT_FOUND
|
||||||
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED
|
||||||
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
|
text.contains("eAccessDeny") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
|
||||||
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
|
text.contains("OffLine") -> ERROR_LIBRUS_MESSAGES_MAINTENANCE
|
||||||
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
text.contains("<status>error</status>") -> ERROR_LIBRUS_MESSAGES_ERROR
|
||||||
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
|
text.contains("<type>eVarWhitThisNameNotExists</type>") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
|
||||||
|
text.contains("<error>") -> ERROR_LIBRUS_MESSAGES_OTHER
|
||||||
|
else -> null
|
||||||
|
}?.let { errorCode ->
|
||||||
|
data.error(ApiError(tag, errorCode)
|
||||||
|
.withApiResponse(text)
|
||||||
|
.withResponse(response))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -82,14 +88,7 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", data.messagesSessionId)
|
||||||
Cookie.Builder()
|
|
||||||
.name("DZIENNIKSID")
|
|
||||||
.value(data.messagesSessionId!!)
|
|
||||||
.domain("wiadomosci.librus.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||||
val doc = docBuilder.newDocument()
|
val doc = docBuilder.newDocument()
|
||||||
@ -139,13 +138,20 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
|
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN
|
||||||
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
|
text.contains("<message>Nie odnaleziono wiadomości.</message>") -> ERROR_LIBRUS_MESSAGES_NOT_FOUND
|
||||||
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED
|
||||||
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
|
text.contains("eAccessDeny") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
|
||||||
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
|
text.contains("OffLine") -> ERROR_LIBRUS_MESSAGES_MAINTENANCE
|
||||||
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
text.contains("<status>error</status>") -> ERROR_LIBRUS_MESSAGES_ERROR
|
||||||
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
|
text.contains("<type>eVarWhitThisNameNotExists</type>") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
|
||||||
|
text.contains("<error>") -> ERROR_LIBRUS_MESSAGES_OTHER
|
||||||
|
else -> null
|
||||||
|
}?.let { errorCode ->
|
||||||
|
data.error(ApiError(tag, errorCode)
|
||||||
|
.withApiResponse(text)
|
||||||
|
.withResponse(response))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -166,14 +172,7 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", data.messagesSessionId)
|
||||||
Cookie.Builder()
|
|
||||||
.name("DZIENNIKSID")
|
|
||||||
.value(data.messagesSessionId!!)
|
|
||||||
.domain("wiadomosci.librus.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||||
val doc = docBuilder.newDocument()
|
val doc = docBuilder.newDocument()
|
||||||
|
@ -44,6 +44,7 @@ open class LibrusPortal(open val data: DataLibrus) {
|
|||||||
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
|
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
|
||||||
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
|
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
|
||||||
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
|
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
|
||||||
|
"Unable to refresh the account" -> ERROR_LIBRUS_PORTAL_MAINTENANCE
|
||||||
else -> when (json.getString("hint")) {
|
else -> when (json.getString("hint")) {
|
||||||
"Error while decoding to JSON" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
|
"Error while decoding to JSON" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
|
||||||
else -> ERROR_LIBRUS_PORTAL_OTHER
|
else -> ERROR_LIBRUS_PORTAL_OTHER
|
||||||
@ -97,6 +98,7 @@ open class LibrusPortal(open val data: DataLibrus) {
|
|||||||
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
|
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
|
||||||
.allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
|
.allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
|
||||||
.allowErrorCode(HttpURLConnection.HTTP_GONE)
|
.allowErrorCode(HttpURLConnection.HTTP_GONE)
|
||||||
|
.allowErrorCode(424)
|
||||||
.callback(callback)
|
.callback(callback)
|
||||||
.build()
|
.build()
|
||||||
.enqueue()
|
.enqueue()
|
||||||
|
@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
class LibrusApiAttendances(override val data: DataLibrus,
|
class LibrusApiAttendances(override val data: DataLibrus,
|
||||||
override val lastSync: Long?,
|
override val lastSync: Long?,
|
||||||
@ -42,10 +43,10 @@ class LibrusApiAttendances(override val data: DataLibrus,
|
|||||||
val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id")
|
val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id")
|
||||||
val semester = attendance.getInt("Semester") ?: return@forEach
|
val semester = attendance.getInt("Semester") ?: return@forEach
|
||||||
val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
|
val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
|
||||||
val typeObject = data.attendanceTypes.get(type)
|
val typeObject = data.attendanceTypes[type] ?: null
|
||||||
val topic = typeObject?.name ?: ""
|
val topic = typeObject?.name ?: ""
|
||||||
|
|
||||||
val startTime = data.lessonRanges.get(lessonNo).startTime
|
val startTime = data.lessonRanges.get(lessonNo)?.startTime
|
||||||
|
|
||||||
val lesson = if (lessonId != -1L)
|
val lesson = if (lessonId != -1L)
|
||||||
data.librusLessons.singleOrNull { it.lessonId == lessonId }
|
data.librusLessons.singleOrNull { it.lessonId == lessonId }
|
||||||
@ -59,14 +60,14 @@ class LibrusApiAttendances(override val data: DataLibrus,
|
|||||||
semester,
|
semester,
|
||||||
topic,
|
topic,
|
||||||
lessonDate,
|
lessonDate,
|
||||||
startTime,
|
startTime ?: Time(0, 0, 0),
|
||||||
typeObject.type
|
typeObject?.type ?: Attendance.TYPE_CUSTOM
|
||||||
)
|
)
|
||||||
|
|
||||||
val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach)
|
val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach)
|
||||||
|
|
||||||
data.attendanceList.add(attendanceObject)
|
data.attendanceList.add(attendanceObject)
|
||||||
if(typeObject.type != Attendance.TYPE_PRESENT) {
|
if(typeObject?.type != Attendance.TYPE_PRESENT) {
|
||||||
data.metadataList.add(Metadata(
|
data.metadataList.add(Metadata(
|
||||||
profileId,
|
profileId,
|
||||||
Metadata.TYPE_ATTENDANCE,
|
Metadata.TYPE_ATTENDANCE,
|
||||||
|
@ -27,6 +27,17 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
|||||||
|
|
||||||
private val nameFormat by lazy { DecimalFormat("#.##") }
|
private val nameFormat by lazy { DecimalFormat("#.##") }
|
||||||
|
|
||||||
|
private val types by lazy {
|
||||||
|
mapOf(
|
||||||
|
1 to ("wz" to "wzorowe"),
|
||||||
|
2 to ("bdb" to "bardzo dobre"),
|
||||||
|
3 to ("db" to "dobre"),
|
||||||
|
4 to ("popr" to "poprawne"),
|
||||||
|
5 to ("ndp" to "nieodpowiednie"),
|
||||||
|
6 to ("ng" to "naganne")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
init { data.profile?.also { profile ->
|
init { data.profile?.also { profile ->
|
||||||
apiGet(TAG, "BehaviourGrades/Points") { json ->
|
apiGet(TAG, "BehaviourGrades/Points") { json ->
|
||||||
|
|
||||||
@ -95,8 +106,12 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
|||||||
val addedDate = grade.getString("AddDate")?.let { Date.fromIso(it) }
|
val addedDate = grade.getString("AddDate")?.let { Date.fromIso(it) }
|
||||||
?: System.currentTimeMillis()
|
?: System.currentTimeMillis()
|
||||||
|
|
||||||
|
val text = grade.getString("Text")
|
||||||
|
val type = grade.getJsonObject("BehaviourGrade")?.getInt("Id")?.let { types[it] }
|
||||||
|
|
||||||
val name = when {
|
val name = when {
|
||||||
value != null -> (if (value >= 0) "+" else "") + nameFormat.format(value)
|
type != null -> type.first
|
||||||
|
value != null -> (if (value > 0) "+" else "") + nameFormat.format(value)
|
||||||
shortName != null -> shortName
|
shortName != null -> shortName
|
||||||
else -> return@forEach
|
else -> return@forEach
|
||||||
}
|
}
|
||||||
@ -115,14 +130,14 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
|||||||
|
|
||||||
val categoryName = category?.text ?: ""
|
val categoryName = category?.text ?: ""
|
||||||
|
|
||||||
val description = grade.getJsonArray("Comments")?.asJsonObjectList()?.let { comments ->
|
val comments = grade.getJsonArray("Comments")
|
||||||
if (comments.isNotEmpty()) {
|
?.asJsonObjectList()
|
||||||
data.gradeCategories.singleOrNull {
|
?.mapNotNull { comment ->
|
||||||
it.type == GradeCategory.TYPE_BEHAVIOUR_COMMENT
|
val cId = comment.getLong("Id") ?: return@mapNotNull null
|
||||||
&& it.categoryId == comments[0].asJsonObject.getLong("Id")
|
data.gradeCategories[cId]?.text
|
||||||
}?.text
|
} ?: listOf()
|
||||||
} else null
|
|
||||||
} ?: ""
|
val description = listOfNotNull(type?.second) + comments
|
||||||
|
|
||||||
val valueFrom = value ?: category?.valueFrom ?: 0f
|
val valueFrom = value ?: category?.valueFrom ?: 0f
|
||||||
val valueTo = category?.valueTo ?: 0f
|
val valueTo = category?.valueTo ?: 0f
|
||||||
@ -136,8 +151,8 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
|
|||||||
weight = -1f,
|
weight = -1f,
|
||||||
color = color,
|
color = color,
|
||||||
category = categoryName,
|
category = categoryName,
|
||||||
description = description,
|
description = text ?: description.join(" - "),
|
||||||
comment = null,
|
comment = if (text != null) description.join(" - ") else null,
|
||||||
semester = semester,
|
semester = semester,
|
||||||
teacherId = teacherId,
|
teacherId = teacherId,
|
||||||
subjectId = 1
|
subjectId = 1
|
||||||
|
@ -4,15 +4,12 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
|
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
|
||||||
|
|
||||||
import pl.szczodrzynski.edziennik.DAY
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
|
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.getInt
|
|
||||||
import pl.szczodrzynski.edziennik.getJsonObject
|
|
||||||
import pl.szczodrzynski.edziennik.getString
|
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
@ -41,9 +38,10 @@ class LibrusApiLuckyNumber(override val data: DataLibrus,
|
|||||||
luckyNumber
|
luckyNumber
|
||||||
)
|
)
|
||||||
|
|
||||||
//if (luckyNumberDate > Date.getToday()) {
|
if (luckyNumberDate >= Date.getToday())
|
||||||
nextSync = luckyNumberDate.combineWith(Time(15, 0, 0))
|
nextSync = luckyNumberDate.combineWith(Time(15, 0, 0))
|
||||||
//}
|
else
|
||||||
|
nextSync = System.currentTimeMillis() + 6*HOUR*1000
|
||||||
|
|
||||||
data.luckyNumberList.add(luckyNumberObject)
|
data.luckyNumberList.add(luckyNumberObject)
|
||||||
data.metadataList.add(
|
data.metadataList.add(
|
||||||
|
@ -21,6 +21,14 @@ class LibrusApiPushConfig(override val data: DataLibrus,
|
|||||||
}
|
}
|
||||||
|
|
||||||
init { data.app.config.sync.tokenLibrus?.also { tokenLibrus ->
|
init { data.app.config.sync.tokenLibrus?.also { tokenLibrus ->
|
||||||
|
if(tokenLibrus.isEmpty()) {
|
||||||
|
data.setSyncNext(ENDPOINT_LIBRUS_API_PUSH_CONFIG, SYNC_ALWAYS)
|
||||||
|
data.app.config.sync.tokenLibrusList =
|
||||||
|
data.app.config.sync.tokenLibrusList + profileId
|
||||||
|
onSuccess(ENDPOINT_LIBRUS_API_PUSH_CONFIG)
|
||||||
|
return@also
|
||||||
|
}
|
||||||
|
|
||||||
apiGet(TAG, "ChangeRegister", payload = JsonObject(
|
apiGet(TAG, "ChangeRegister", payload = JsonObject(
|
||||||
"provider" to "FCM",
|
"provider" to "FCM",
|
||||||
"device" to tokenLibrus,
|
"device" to tokenLibrus,
|
||||||
|
@ -57,7 +57,7 @@ class LibrusApiTextGrades(override val data: DataLibrus,
|
|||||||
color = category?.color ?: -1,
|
color = category?.color ?: -1,
|
||||||
category = category?.text ?: "",
|
category = category?.text ?: "",
|
||||||
description = description,
|
description = description,
|
||||||
comment = null,
|
comment = grade.getString("Phrase") /* whatever it is */,
|
||||||
semester = semester,
|
semester = semester,
|
||||||
teacherId = teacherId,
|
teacherId = teacherId,
|
||||||
subjectId = subjectId
|
subjectId = subjectId
|
||||||
|
@ -111,7 +111,7 @@ class LibrusMessagesGetList(override val data: DataLibrus,
|
|||||||
|
|
||||||
data.messageIgnoreList.add(messageObject)
|
data.messageIgnoreList.add(messageObject)
|
||||||
data.messageRecipientList.add(messageRecipientObject)
|
data.messageRecipientList.add(messageRecipientObject)
|
||||||
data.metadataList.add(Metadata(
|
data.setSeenMetadataList.add(Metadata(
|
||||||
profileId,
|
profileId,
|
||||||
Metadata.TYPE_MESSAGE,
|
Metadata.TYPE_MESSAGE,
|
||||||
id,
|
id,
|
||||||
|
@ -29,7 +29,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
|||||||
init { data.profile?.also { profile ->
|
init { data.profile?.also { profile ->
|
||||||
synergiaGet(TAG, "moje_zadania", method = POST, parameters = mapOf(
|
synergiaGet(TAG, "moje_zadania", method = POST, parameters = mapOf(
|
||||||
"dataOd" to
|
"dataOd" to
|
||||||
if (!data.profile.empty)
|
if (profile.empty)
|
||||||
profile.getSemesterStart(1).stringY_m_d
|
profile.getSemesterStart(1).stringY_m_d
|
||||||
else
|
else
|
||||||
Date.getToday().stringY_m_d,
|
Date.getToday().stringY_m_d,
|
||||||
@ -54,7 +54,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
|||||||
val teacherId = data.teacherList.singleOrNull { teacherName == it.fullName }?.id
|
val teacherId = data.teacherList.singleOrNull { teacherName == it.fullName }?.id
|
||||||
?: -1
|
?: -1
|
||||||
val topic = elements[2].text().trim()
|
val topic = elements[2].text().trim()
|
||||||
val addedDate = Date.fromY_m_d(elements[4].text().trim()).inMillis
|
val addedDate = Date.fromY_m_d(elements[4].text().trim())
|
||||||
val eventDate = Date.fromY_m_d(elements[6].text().trim())
|
val eventDate = Date.fromY_m_d(elements[6].text().trim())
|
||||||
val id = "/podglad/([0-9]+)'".toRegex().find(
|
val id = "/podglad/([0-9]+)'".toRegex().find(
|
||||||
elements[9].select("input").attr("onclick")
|
elements[9].select("input").attr("onclick")
|
||||||
@ -63,10 +63,25 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
|||||||
val lessons = data.db.timetableDao().getForDateNow(profileId, eventDate)
|
val lessons = data.db.timetableDao().getForDateNow(profileId, eventDate)
|
||||||
val startTime = lessons.firstOrNull { it.subjectId == subjectId }?.startTime
|
val startTime = lessons.firstOrNull { it.subjectId == subjectId }?.startTime
|
||||||
|
|
||||||
val moreInfo = graphElements[2 * i + 1].select("td[title]")
|
/*val moreInfo = graphElements[2 * i + 1].select("td[title]")
|
||||||
.attr("title").trim()
|
.attr("title").trim()*/
|
||||||
val description = "Treść: (.*)".toRegex(RegexOption.DOT_MATCHES_ALL).find(moreInfo)
|
|
||||||
?.get(1)?.replace("<br.*/>".toRegex(), "\n")?.trim()
|
var description = ""
|
||||||
|
|
||||||
|
graphElements.forEach { graphEl ->
|
||||||
|
graphEl.select("td[title]")?.also {
|
||||||
|
val title = it.attr("title")
|
||||||
|
val r = "Temat: (.*?)<br.?/>Data udostępnienia: (.*?)<br.?/>Termin wykonania: (.*?)<br.?/>Treść: (.*)"
|
||||||
|
.toRegex(RegexOption.DOT_MATCHES_ALL).find(title) ?: return@forEach
|
||||||
|
val gTopic = r[1].trim()
|
||||||
|
val gAddedDate = Date.fromY_m_d(r[2].trim())
|
||||||
|
val gEventDate = Date.fromY_m_d(r[3].trim())
|
||||||
|
if (gTopic == topic && gAddedDate == addedDate && gEventDate == eventDate) {
|
||||||
|
description = r[4].replace("<br.?/>".toRegex(), "\n").trim()
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val seen = when (profile.empty) {
|
val seen = when (profile.empty) {
|
||||||
true -> true
|
true -> true
|
||||||
@ -94,7 +109,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
|
|||||||
id,
|
id,
|
||||||
seen,
|
seen,
|
||||||
seen,
|
seen,
|
||||||
addedDate
|
addedDate.inMillis
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,8 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
return@portalGet
|
return@portalGet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isParent = account.getString("group") == "parent"
|
||||||
|
|
||||||
val id = account.getInt("id") ?: continue
|
val id = account.getInt("id") ?: continue
|
||||||
val login = account.getString("login") ?: continue
|
val login = account.getString("login") ?: continue
|
||||||
val token = account.getString("accessToken") ?: continue
|
val token = account.getString("accessToken") ?: continue
|
||||||
@ -69,7 +71,7 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
data.portalEmail,
|
data.portalEmail,
|
||||||
studentNameLong,
|
studentNameLong,
|
||||||
studentNameShort,
|
studentNameShort,
|
||||||
null
|
if (isParent) studentNameLong else null /* temporarily - there is no parent name provided, only the type */
|
||||||
).apply {
|
).apply {
|
||||||
studentData["accountId"] = id
|
studentData["accountId"] = id
|
||||||
studentData["accountLogin"] = login
|
studentData["accountLogin"] = login
|
||||||
|
@ -137,6 +137,7 @@ class LibrusLoginApi {
|
|||||||
"librus_change_password_error" -> ERROR_LOGIN_LIBRUS_API_CHANGE_PASSWORD_ERROR
|
"librus_change_password_error" -> ERROR_LOGIN_LIBRUS_API_CHANGE_PASSWORD_ERROR
|
||||||
"librus_password_change_required" -> ERROR_LOGIN_LIBRUS_API_PASSWORD_CHANGE_REQUIRED
|
"librus_password_change_required" -> ERROR_LOGIN_LIBRUS_API_PASSWORD_CHANGE_REQUIRED
|
||||||
"invalid_grant" -> ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN
|
"invalid_grant" -> ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN
|
||||||
|
"invalid_request" -> ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST
|
||||||
else -> ERROR_LOGIN_LIBRUS_API_OTHER
|
else -> ERROR_LOGIN_LIBRUS_API_OTHER
|
||||||
}.let { errorCode ->
|
}.let { errorCode ->
|
||||||
data.error(ApiError(TAG, errorCode)
|
data.error(ApiError(TAG, errorCode)
|
||||||
|
@ -8,7 +8,6 @@ import im.wangchao.mhttp.Request
|
|||||||
import im.wangchao.mhttp.Response
|
import im.wangchao.mhttp.Response
|
||||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
import okhttp3.Cookie
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
@ -64,21 +63,15 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.isMessagesLoginValid()) {
|
if (data.isMessagesLoginValid()) {
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", data.messagesSessionId)
|
||||||
Cookie.Builder()
|
|
||||||
.name("DZIENNIKSID")
|
|
||||||
.value(data.messagesSessionId!!)
|
|
||||||
.domain("wiadomosci.librus.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("wiadomosci.librus.pl")
|
data.app.cookieJar.clear("wiadomosci.librus.pl")
|
||||||
if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) {
|
if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) {
|
||||||
loginWithSynergia()
|
loginWithSynergia()
|
||||||
}
|
}
|
||||||
else if (data.apiLogin != null && data.apiPassword != null) {
|
else if (data.apiLogin != null && data.apiPassword != null && false) {
|
||||||
loginWithCredentials()
|
loginWithCredentials()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -148,7 +141,7 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun saveSessionId(response: Response?, text: String?) {
|
private fun saveSessionId(response: Response?, text: String?) {
|
||||||
var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID")
|
var sessionId = data.app.cookieJar.get("wiadomosci.librus.pl", "DZIENNIKSID")
|
||||||
sessionId = sessionId?.replace("-MAINT", "") // dunno what's this
|
sessionId = sessionId?.replace("-MAINT", "") // dunno what's this
|
||||||
sessionId = sessionId?.replace("MAINT", "") // dunno what's this
|
sessionId = sessionId?.replace("MAINT", "") // dunno what's this
|
||||||
if (sessionId == null) {
|
if (sessionId == null) {
|
||||||
|
@ -37,19 +37,19 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
else if (data.portalRefreshToken != null) {
|
else if (data.portalRefreshToken != null) {
|
||||||
if (data.fakeLogin) {
|
if (data.fakeLogin) {
|
||||||
data.app.cookieJar.clearForDomain("librus.szkolny.eu")
|
data.app.cookieJar.clear("librus.szkolny.eu")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
data.app.cookieJar.clear("portal.librus.pl")
|
||||||
}
|
}
|
||||||
accessToken(null, data.portalRefreshToken)
|
accessToken(null, data.portalRefreshToken)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (data.fakeLogin) {
|
if (data.fakeLogin) {
|
||||||
data.app.cookieJar.clearForDomain("librus.szkolny.eu")
|
data.app.cookieJar.clear("librus.szkolny.eu")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
data.app.cookieJar.clear("portal.librus.pl")
|
||||||
}
|
}
|
||||||
authorize(if (data.fakeLogin) FAKE_LIBRUS_AUTHORIZE else LIBRUS_AUTHORIZE_URL)
|
authorize(if (data.fakeLogin) FAKE_LIBRUS_AUTHORIZE else LIBRUS_AUTHORIZE_URL)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import com.google.gson.JsonObject
|
|||||||
import im.wangchao.mhttp.Request
|
import im.wangchao.mhttp.Request
|
||||||
import im.wangchao.mhttp.Response
|
import im.wangchao.mhttp.Response
|
||||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
import okhttp3.Cookie
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||||
@ -30,17 +29,11 @@ class LibrusLoginSynergia(override val data: DataLibrus, val onSuccess: () -> Un
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.isSynergiaLoginValid()) {
|
if (data.isSynergiaLoginValid()) {
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("synergia.librus.pl", "DZIENNIKSID", data.synergiaSessionId)
|
||||||
Cookie.Builder()
|
|
||||||
.name("DZIENNIKSID")
|
|
||||||
.value(data.synergiaSessionId!!)
|
|
||||||
.domain("synergia.librus.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("synergia.librus.pl")
|
data.app.cookieJar.clear("synergia.librus.pl")
|
||||||
if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_API)) {
|
if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_API)) {
|
||||||
loginWithApi()
|
loginWithApi()
|
||||||
}
|
}
|
||||||
@ -92,7 +85,7 @@ class LibrusLoginSynergia(override val data: DataLibrus, val onSuccess: () -> Un
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (location?.endsWith("centrum_powiadomien") == true) {
|
if (location?.endsWith("centrum_powiadomien") == true) {
|
||||||
val sessionId = data.app.cookieJar.getCookie("synergia.librus.pl", "DZIENNIKSID")
|
val sessionId = data.app.cookieJar.get("synergia.librus.pl", "DZIENNIKSID")
|
||||||
if (sessionId == null) {
|
if (sessionId == null) {
|
||||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID)
|
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID)
|
||||||
.withResponse(response)
|
.withResponse(response)
|
||||||
@ -117,7 +110,7 @@ class LibrusLoginSynergia(override val data: DataLibrus, val onSuccess: () -> Un
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.app.cookieJar.clearForDomain("synergia.librus.pl")
|
data.app.cookieJar.clear("synergia.librus.pl")
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url(LIBRUS_SYNERGIA_TOKEN_LOGIN_URL.replace("TOKEN", token) + "/uczen/widok/centrum_powiadomien")
|
.url(LIBRUS_SYNERGIA_TOKEN_LOGIN_URL.replace("TOKEN", token) + "/uczen/widok/centrum_powiadomien")
|
||||||
.userAgent(LIBRUS_USER_AGENT)
|
.userAgent(LIBRUS_USER_AGENT)
|
||||||
|
@ -8,7 +8,6 @@ import im.wangchao.mhttp.Request
|
|||||||
import im.wangchao.mhttp.Response
|
import im.wangchao.mhttp.Response
|
||||||
import im.wangchao.mhttp.callback.FileCallbackHandler
|
import im.wangchao.mhttp.callback.FileCallbackHandler
|
||||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
import okhttp3.Cookie
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
@ -26,6 +25,23 @@ open class MobidziennikWeb(open val data: DataMobidziennik, open val lastSync: L
|
|||||||
val profile
|
val profile
|
||||||
get() = data.profile
|
get() = data.profile
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
add error handling:
|
||||||
|
|
||||||
|
<!-- CONTENT -->
|
||||||
|
<div id="content">
|
||||||
|
<a name="tresc"></a>
|
||||||
|
<h1>Ważna informacja!</h1>
|
||||||
|
|
||||||
|
<div class="okienko_informacyjne">
|
||||||
|
Korzystasz z hasła, które zostało wygenerowane automatycznie.<hr/>
|
||||||
|
Pamiętaj, aby je zmienić oraz uzupełnić dane w swoim profilu.<br/><br/>
|
||||||
|
Obie te operacje można wykonać uruchamiając opcję
|
||||||
|
<a href="https://poznan.mobidziennik.pl/dziennik/edytujprofil" title="Edycja profilu, możliwość zmiany hasła">Moje konto->Edycja profilu</a>.
|
||||||
|
</div> </div>
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
fun webGet(
|
fun webGet(
|
||||||
tag: String,
|
tag: String,
|
||||||
endpoint: String,
|
endpoint: String,
|
||||||
@ -65,6 +81,12 @@ open class MobidziennikWeb(open val data: DataMobidziennik, open val lastSync: L
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (text.contains("<h2>Problemy z wydajnością</h2>")) {
|
||||||
|
data.error(ApiError(TAG, ERROR_MOBIDZIENNIK_WEB_SERVER_PROBLEM)
|
||||||
|
.withResponse(response))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
onSuccess(text)
|
onSuccess(text)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -82,18 +104,8 @@ open class MobidziennikWeb(open val data: DataMobidziennik, open val lastSync: L
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("${data.loginServerName}.mobidziennik.pl", data.webSessionKey, data.webSessionValue)
|
||||||
Cookie.Builder()
|
data.app.cookieJar.set("${data.loginServerName}.mobidziennik.pl", "SERVERID", data.webServerId)
|
||||||
.name(data.webSessionKey!!)
|
|
||||||
.value(data.webSessionValue!!)
|
|
||||||
.domain("${data.loginServerName}.mobidziennik.pl")
|
|
||||||
.secure().httpOnly().build(),
|
|
||||||
Cookie.Builder()
|
|
||||||
.name("SERVERID")
|
|
||||||
.value(data.webServerId!!)
|
|
||||||
.domain("${data.loginServerName}.mobidziennik.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
|
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
@ -164,18 +176,8 @@ open class MobidziennikWeb(open val data: DataMobidziennik, open val lastSync: L
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("${data.loginServerName}.mobidziennik.pl", data.webSessionKey, data.webSessionValue)
|
||||||
Cookie.Builder()
|
data.app.cookieJar.set("${data.loginServerName}.mobidziennik.pl", "SERVERID", data.webServerId)
|
||||||
.name(data.webSessionKey!!)
|
|
||||||
.value(data.webSessionValue!!)
|
|
||||||
.domain("${data.loginServerName}.mobidziennik.pl")
|
|
||||||
.secure().httpOnly().build(),
|
|
||||||
Cookie.Builder()
|
|
||||||
.name("SERVERID")
|
|
||||||
.value(data.webServerId!!)
|
|
||||||
.domain("${data.loginServerName}.mobidziennik.pl")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
|
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
|
@ -118,7 +118,7 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
|
|||||||
|
|
||||||
// this needs to be at the end
|
// this needs to be at the end
|
||||||
message.apply {
|
message.apply {
|
||||||
this.body = body.html().replace("\n", "<br>")
|
this.body = body.html()
|
||||||
|
|
||||||
clearAttachments()
|
clearAttachments()
|
||||||
content.select("ul li").map { it.select("a").first() }.forEach {
|
content.select("ul li").map { it.select("a").first() }.forEach {
|
||||||
|
@ -32,15 +32,15 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
|
|||||||
|
|
||||||
val doc = Jsoup.parse(text)
|
val doc = Jsoup.parse(text)
|
||||||
|
|
||||||
val listElement = doc.getElementsByClass("spis").first()
|
val listElement = doc.getElementsByClass("spis")?.first()
|
||||||
if (listElement == null) {
|
if (listElement == null) {
|
||||||
data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, 7*DAY)
|
data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, 7*DAY)
|
||||||
onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL)
|
onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL)
|
||||||
return@webGet
|
return@webGet
|
||||||
}
|
}
|
||||||
val list = listElement.getElementsByClass("podswietl")
|
val list = listElement.getElementsByClass("podswietl")
|
||||||
for (item in list) {
|
list?.forEach { item ->
|
||||||
val id = item.attr("rel").replace("[^\\d]".toRegex(), "").toLongOrNull() ?: continue
|
val id = item.attr("rel").replace("[^\\d]".toRegex(), "").toLongOrNull() ?: return@forEach
|
||||||
|
|
||||||
val subjectEl = item.select("td:eq(0) div").first()
|
val subjectEl = item.select("td:eq(0) div").first()
|
||||||
val subject = subjectEl.text()
|
val subject = subjectEl.text()
|
||||||
|
@ -36,9 +36,9 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
|
|||||||
|
|
||||||
val doc = Jsoup.parse(text)
|
val doc = Jsoup.parse(text)
|
||||||
|
|
||||||
val list = doc.getElementsByClass("spis").first().getElementsByClass("podswietl")
|
val list = doc.getElementsByClass("spis")?.first()?.getElementsByClass("podswietl")
|
||||||
for (item in list) {
|
list?.forEach { item ->
|
||||||
val id = item.attr("rel").toLongOrNull() ?: continue
|
val id = item.attr("rel").toLongOrNull() ?: return@forEach
|
||||||
|
|
||||||
val subjectEl = item.select("td:eq(0)").first()
|
val subjectEl = item.select("td:eq(0)").first()
|
||||||
var hasAttachments = false
|
var hasAttachments = false
|
||||||
|
@ -40,9 +40,9 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
|
|||||||
|
|
||||||
val doc = Jsoup.parse(text)
|
val doc = Jsoup.parse(text)
|
||||||
|
|
||||||
val list = doc.getElementsByClass("spis").first().getElementsByClass("podswietl")
|
val list = doc.getElementsByClass("spis")?.first()?.getElementsByClass("podswietl")
|
||||||
for (item in list) {
|
list?.forEach { item ->
|
||||||
val id = item.attr("rel").toLongOrNull() ?: continue
|
val id = item.attr("rel").toLongOrNull() ?: return@forEach
|
||||||
|
|
||||||
val subjectEl = item.select("td:eq(0)").first()
|
val subjectEl = item.select("td:eq(0)").first()
|
||||||
var hasAttachments = false
|
var hasAttachments = false
|
||||||
|
@ -26,7 +26,7 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (data.loginServerName.isNotNullNorEmpty() && data.loginUsername.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
|
if (data.loginServerName.isNotNullNorEmpty() && data.loginUsername.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
|
||||||
data.app.cookieJar.clearForDomain(data.loginServerName + ".mobidziennik.pl")
|
data.app.cookieJar.clear("${data.loginServerName}.mobidziennik.pl")
|
||||||
loginWithCredentials()
|
loginWithCredentials()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -58,10 +58,10 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cookies = data.app.cookieJar.getForDomain("${data.loginServerName}.mobidziennik.pl")
|
val cookies = data.app.cookieJar.getAll("${data.loginServerName}.mobidziennik.pl")
|
||||||
val cookie = cookies.singleOrNull { it.name().length > 32 }
|
val cookie = cookies.entries.firstOrNull { it.key.length > 32 }
|
||||||
val sessionKey = cookie?.name()
|
val sessionKey = cookie?.key
|
||||||
val sessionId = cookie?.value()
|
val sessionId = cookie?.value
|
||||||
if (sessionId == null) {
|
if (sessionId == null) {
|
||||||
data.error(ApiError(TAG, ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID)
|
data.error(ApiError(TAG, ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID)
|
||||||
.withResponse(response)
|
.withResponse(response)
|
||||||
@ -71,7 +71,7 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit
|
|||||||
|
|
||||||
data.webSessionKey = sessionKey
|
data.webSessionKey = sessionKey
|
||||||
data.webSessionValue = sessionId
|
data.webSessionValue = sessionId
|
||||||
data.webServerId = data.app.cookieJar.getCookie("${data.loginServerName}.mobidziennik.pl", "SERVERID")
|
data.webServerId = data.app.cookieJar.get("${data.loginServerName}.mobidziennik.pl", "SERVERID")
|
||||||
data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
|
data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.data.api.edziennik.template
|
package pl.szczodrzynski.edziennik.data.api.edziennik.template
|
||||||
|
|
||||||
import okhttp3.Cookie
|
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_TEMPLATE_API
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_TEMPLATE_API
|
||||||
@ -28,13 +27,7 @@ class DataTemplate(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
|
|||||||
loginMethods.clear()
|
loginMethods.clear()
|
||||||
if (isWebLoginValid()) {
|
if (isWebLoginValid()) {
|
||||||
loginMethods += LOGIN_METHOD_TEMPLATE_WEB
|
loginMethods += LOGIN_METHOD_TEMPLATE_WEB
|
||||||
app.cookieJar.saveFromResponse(null, listOf(
|
app.cookieJar.set("eregister.example.com", "AuthCookie", webCookie)
|
||||||
Cookie.Builder()
|
|
||||||
.name("AuthCookie")
|
|
||||||
.value(webCookie!!)
|
|
||||||
.domain("eregister.example.com")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
if (isApiLoginValid())
|
if (isApiLoginValid())
|
||||||
loginMethods += LOGIN_METHOD_TEMPLATE_API
|
loginMethods += LOGIN_METHOD_TEMPLATE_API
|
||||||
|
@ -4,12 +4,11 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.data.api.edziennik.template.login
|
package pl.szczodrzynski.edziennik.data.api.edziennik.template.login
|
||||||
|
|
||||||
import okhttp3.Cookie
|
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||||
import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_DATA_MISSING
|
import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_DATA_MISSING
|
||||||
import pl.szczodrzynski.edziennik.data.api.ERROR_PROFILE_MISSING
|
import pl.szczodrzynski.edziennik.data.api.ERROR_PROFILE_MISSING
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.DataTemplate
|
import pl.szczodrzynski.edziennik.data.api.edziennik.template.DataTemplate
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
|
||||||
|
|
||||||
class TemplateLoginWeb(val data: DataTemplate, val onSuccess: () -> Unit) {
|
class TemplateLoginWeb(val data: DataTemplate, val onSuccess: () -> Unit) {
|
||||||
companion object {
|
companion object {
|
||||||
@ -23,17 +22,11 @@ class TemplateLoginWeb(val data: DataTemplate, val onSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.isWebLoginValid()) {
|
if (data.isWebLoginValid()) {
|
||||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
data.app.cookieJar.set("eregister.example.com", "AuthCookie", data.webCookie)
|
||||||
Cookie.Builder()
|
|
||||||
.name("AuthCookie")
|
|
||||||
.value(data.webCookie!!)
|
|
||||||
.domain("eregister.example.com")
|
|
||||||
.secure().httpOnly().build()
|
|
||||||
))
|
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("eregister.example.com")
|
data.app.cookieJar.clear("eregister.example.com")
|
||||||
if (/*data.webLogin != null && data.webPassword != null && */true) {
|
if (/*data.webLogin != null && data.webPassword != null && */true) {
|
||||||
loginWithCredentials()
|
loginWithCredentials()
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,10 @@ import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_API
|
|||||||
import pl.szczodrzynski.edziennik.data.api.models.Data
|
import pl.szczodrzynski.edziennik.data.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.entity.Team
|
||||||
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Utils
|
||||||
|
import pl.szczodrzynski.edziennik.values
|
||||||
|
|
||||||
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||||
|
|
||||||
@ -26,6 +29,25 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
// during the first sync `profile.studentClassName` is already set
|
||||||
|
if (teamList.values().none { it.type == Team.TYPE_CLASS }) {
|
||||||
|
profile?.studentClassName?.also { name ->
|
||||||
|
val id = Utils.crc16(name.toByteArray()).toLong()
|
||||||
|
|
||||||
|
val teamObject = Team(
|
||||||
|
profileId,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
Team.TYPE_CLASS,
|
||||||
|
"$schoolName:$name",
|
||||||
|
-1
|
||||||
|
)
|
||||||
|
teamList.put(id, teamObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun generateUserCode() = "$schoolName:$studentId"
|
override fun generateUserCode() = "$schoolName:$studentId"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,11 +210,11 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
|||||||
"SZ9" -> "http://hack.szkolny.eu"
|
"SZ9" -> "http://hack.szkolny.eu"
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
return if (url != null) "$url/$symbol" else loginStore.getLoginData("apiUrl", null)
|
return if (url != null) "$url/$symbol/" else loginStore.getLoginData("apiUrl", null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val fullApiUrl: String?
|
val fullApiUrl: String?
|
||||||
get() {
|
get() {
|
||||||
return "$apiUrl/$schoolSymbol"
|
return "$apiUrl$schoolSymbol/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ import io.github.wulkanowy.signer.android.signContent
|
|||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Team
|
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils
|
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -38,26 +36,10 @@ open class VulcanApi(open val data: DataVulcan, open val lastSync: Long?) {
|
|||||||
baseUrl: Boolean = false,
|
baseUrl: Boolean = false,
|
||||||
onSuccess: (json: JsonObject, response: Response?) -> Unit
|
onSuccess: (json: JsonObject, response: Response?) -> Unit
|
||||||
) {
|
) {
|
||||||
val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}/$endpoint"
|
val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}$endpoint"
|
||||||
|
|
||||||
d(tag, "Request: Vulcan/Api - $url")
|
d(tag, "Request: Vulcan/Api - $url")
|
||||||
|
|
||||||
if (data.teamList.size() == 0) {
|
|
||||||
data.profile?.studentClassName?.also { name ->
|
|
||||||
val id = Utils.crc16(name.toByteArray()).toLong()
|
|
||||||
|
|
||||||
val teamObject = Team(
|
|
||||||
profileId,
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
Team.TYPE_CLASS,
|
|
||||||
"${data.schoolName}:$name",
|
|
||||||
-1
|
|
||||||
)
|
|
||||||
data.teamList.put(id, teamObject)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val finalPayload = JsonObject()
|
val finalPayload = JsonObject()
|
||||||
parameters.map { (name, value) ->
|
parameters.map { (name, value) ->
|
||||||
when (value) {
|
when (value) {
|
||||||
|
@ -86,31 +86,36 @@ class VulcanApiTimetable(override val data: DataVulcan,
|
|||||||
data.teamList[id] = team
|
data.teamList[id] = team
|
||||||
}
|
}
|
||||||
team.id
|
team.id
|
||||||
} ?: data.studentClassId.toLong()
|
} ?: data.teamClass?.id ?: -1
|
||||||
|
|
||||||
val subjectId = lesson.getLong("IdPrzedmiot")?.let {
|
val subjectId = lesson.getLong("IdPrzedmiot").let { id ->
|
||||||
when (it) {
|
// get the specified subject name
|
||||||
0L -> {
|
val subjectName = lesson.getString("PrzedmiotNazwa") ?: ""
|
||||||
val subjectName = lesson.getString("PrzedmiotNazwa") ?: ""
|
|
||||||
|
|
||||||
data.subjectList.singleOrNull { subject -> subject.longName == subjectName }?.id
|
val condition = when (id) {
|
||||||
?: {
|
// "special" subject - e.g. one time classes, a trip, etc.
|
||||||
/**
|
0L -> { subject: Subject -> subject.longName == subjectName }
|
||||||
* CREATE A NEW SUBJECT IF IT DOESN'T EXIST
|
// normal subject, check if it exists
|
||||||
*/
|
else -> { subject: Subject -> subject.id == id }
|
||||||
|
|
||||||
val subjectObject = Subject(
|
|
||||||
profileId,
|
|
||||||
-1 * crc16(subjectName.toByteArray()).toLong(),
|
|
||||||
subjectName,
|
|
||||||
subjectName
|
|
||||||
)
|
|
||||||
data.subjectList.put(subjectObject.id, subjectObject)
|
|
||||||
subjectObject.id
|
|
||||||
}.invoke()
|
|
||||||
}
|
|
||||||
else -> it
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.subjectList.singleOrNull(condition)?.id ?: {
|
||||||
|
/**
|
||||||
|
* CREATE A NEW SUBJECT IF IT DOESN'T EXIST
|
||||||
|
*/
|
||||||
|
|
||||||
|
val subjectObject = Subject(
|
||||||
|
profileId,
|
||||||
|
if (id == null || id == 0L)
|
||||||
|
-1 * crc16(subjectName.toByteArray()).toLong()
|
||||||
|
else
|
||||||
|
id,
|
||||||
|
subjectName,
|
||||||
|
subjectName
|
||||||
|
)
|
||||||
|
data.subjectList.put(subjectObject.id, subjectObject)
|
||||||
|
subjectObject.id
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
val lessonObject = Lesson(profileId, -1).apply {
|
val lessonObject = Lesson(profileId, -1).apply {
|
||||||
|
@ -137,7 +137,7 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url("${data.apiUrl}/$VULCAN_API_ENDPOINT_CERTIFICATE")
|
.url("${data.apiUrl}$VULCAN_API_ENDPOINT_CERTIFICATE")
|
||||||
.userAgent(VULCAN_API_USER_AGENT)
|
.userAgent(VULCAN_API_USER_AGENT)
|
||||||
.addHeader("RequestMobileType", "RegisterDevice")
|
.addHeader("RequestMobileType", "RegisterDevice")
|
||||||
.addParameter("PIN", data.apiPin)
|
.addParameter("PIN", data.apiPin)
|
||||||
|
@ -12,10 +12,7 @@ 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 pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.BuildConfig
|
|
||||||
import pl.szczodrzynski.edziennik.R
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.DateAdapter
|
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.DateAdapter
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.TimeAdapter
|
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.TimeAdapter
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
|
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
|
||||||
@ -28,7 +25,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
import pl.szczodrzynski.edziennik.md5
|
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDetailsDialog
|
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDetailsDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
@ -80,7 +76,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
withContext(Dispatchers.Default) { block() }
|
withContext(Dispatchers.Default) { block() }
|
||||||
}
|
}
|
||||||
catch (e: Exception) {
|
catch (e: Exception) {
|
||||||
errorSnackbar.addError(ApiError.fromThrowable(TAG, e)).show()
|
errorSnackbar.addError(e.toApiError(TAG)).show()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +87,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
catch (e: Exception) {
|
catch (e: Exception) {
|
||||||
ErrorDetailsDialog(
|
ErrorDetailsDialog(
|
||||||
activity,
|
activity,
|
||||||
listOf(ApiError.fromThrowable(TAG, e)),
|
listOf(e.toApiError(TAG)),
|
||||||
R.string.error_occured
|
R.string.error_occured
|
||||||
)
|
)
|
||||||
null
|
null
|
||||||
@ -160,7 +156,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun getEvents(profiles: List<Profile>, notifications: List<Notification>, blacklistedIds: List<Long>): List<EventFull> {
|
fun getEvents(profiles: List<Profile>, notifications: List<Notification>, blacklistedIds: List<Long>, lastSyncTime: Long): List<EventFull> {
|
||||||
val teams = app.db.teamDao().allNow
|
val teams = app.db.teamDao().allNow
|
||||||
|
|
||||||
val response = api.serverSync(ServerSyncRequest(
|
val response = api.serverSync(ServerSyncRequest(
|
||||||
@ -185,19 +181,26 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
lastSync = lastSyncTime,
|
||||||
notifications = notifications.map { ServerSyncRequest.Notification(it.profileName ?: "", it.type, it.text) }
|
notifications = notifications.map { ServerSyncRequest.Notification(it.profileName ?: "", it.type, it.text) }
|
||||||
)).execute()
|
)).execute()
|
||||||
parseResponse(response)
|
val (events, hasBrowsers) = parseResponse(response)
|
||||||
|
|
||||||
val events = mutableListOf<EventFull>()
|
hasBrowsers?.let {
|
||||||
|
app.config.sync.webPushEnabled = it
|
||||||
|
}
|
||||||
|
|
||||||
response.body()?.data?.events?.forEach { event ->
|
val eventList = mutableListOf<EventFull>()
|
||||||
|
|
||||||
|
events.forEach { event ->
|
||||||
|
// skip blacklisted events
|
||||||
if (event.id in blacklistedIds)
|
if (event.id in blacklistedIds)
|
||||||
return@forEach
|
return@forEach
|
||||||
|
// create the event for every matching team and profile
|
||||||
teams.filter { it.code == event.teamCode }.onEach { team ->
|
teams.filter { it.code == event.teamCode }.onEach { team ->
|
||||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@onEach
|
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@onEach
|
||||||
|
|
||||||
events.add(EventFull(event).apply {
|
eventList += EventFull(event).apply {
|
||||||
profileId = team.profileId
|
profileId = team.profileId
|
||||||
teamId = team.id
|
teamId = team.id
|
||||||
addedManually = true
|
addedManually = true
|
||||||
@ -205,11 +208,11 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
notified = profile.empty
|
notified = profile.empty
|
||||||
|
|
||||||
if (profile.userCode == event.sharedBy) sharedBy = "self"
|
if (profile.userCode == event.sharedBy) sharedBy = "self"
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return events
|
return eventList
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
@ -253,9 +256,8 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
browserId = browserId,
|
browserId = browserId,
|
||||||
pairToken = pairToken
|
pairToken = pairToken
|
||||||
)).execute()
|
)).execute()
|
||||||
parseResponse(response)
|
|
||||||
|
|
||||||
return response.body()?.data?.browsers ?: emptyList()
|
return parseResponse(response).browsers
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
@ -265,9 +267,8 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
device = getDevice(),
|
device = getDevice(),
|
||||||
action = "listBrowsers"
|
action = "listBrowsers"
|
||||||
)).execute()
|
)).execute()
|
||||||
parseResponse(response)
|
|
||||||
|
|
||||||
return response.body()?.data?.browsers ?: emptyList()
|
return parseResponse(response).browsers
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
@ -278,9 +279,8 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
action = "unpairBrowser",
|
action = "unpairBrowser",
|
||||||
browserId = browserId
|
browserId = browserId
|
||||||
)).execute()
|
)).execute()
|
||||||
parseResponse(response)
|
|
||||||
|
|
||||||
return response.body()?.data?.browsers ?: emptyList()
|
return parseResponse(response).browsers
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
@ -307,9 +307,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun getUpdate(channel: String): List<Update> {
|
fun getUpdate(channel: String): List<Update> {
|
||||||
val response = api.updates(channel).execute()
|
val response = api.updates(channel).execute()
|
||||||
parseResponse(response)
|
return parseResponse(response)
|
||||||
|
|
||||||
return response.body()?.data ?: emptyList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
@ -321,8 +319,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
targetDeviceId = targetDeviceId,
|
targetDeviceId = targetDeviceId,
|
||||||
text = text
|
text = text
|
||||||
)).execute()
|
)).execute()
|
||||||
val data = parseResponse(response)
|
|
||||||
|
|
||||||
return data.message
|
return parseResponse(response).message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,6 @@ object Signing {
|
|||||||
|
|
||||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||||
return "$param1.MTIzNDU2Nzg5MDAtDsP5Si===.$param2".sha256()
|
return "$param1.MTIzNDU2Nzg5MDmD46bIpY===.$param2".sha256()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ data class ServerSyncRequest(
|
|||||||
val userCodes: List<String>,
|
val userCodes: List<String>,
|
||||||
val users: List<User>? = null,
|
val users: List<User>? = null,
|
||||||
|
|
||||||
|
val lastSync: Long,
|
||||||
|
|
||||||
val notifications: List<Notification>? = null
|
val notifications: List<Notification>? = null
|
||||||
) {
|
) {
|
||||||
data class User(
|
data class User(
|
||||||
|
@ -6,4 +6,7 @@ package pl.szczodrzynski.edziennik.data.api.szkolny.response
|
|||||||
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
|
|
||||||
data class ServerSyncResponse(val events: List<EventFull>)
|
data class ServerSyncResponse(
|
||||||
|
val events: List<EventFull>,
|
||||||
|
val hasBrowsers: Boolean? = null
|
||||||
|
)
|
||||||
|
@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
|
||||||
class AppSync(val app: App, val notifications: MutableList<Notification>, val profiles: List<Profile>, val api: SzkolnyApi) {
|
class AppSync(val app: App, val notifications: MutableList<Notification>, val profiles: List<Profile>, val api: SzkolnyApi) {
|
||||||
companion object {
|
companion object {
|
||||||
@ -24,26 +25,27 @@ class AppSync(val app: App, val notifications: MutableList<Notification>, val pr
|
|||||||
*
|
*
|
||||||
* @return a number of events inserted to DB, possibly needing a notification
|
* @return a number of events inserted to DB, possibly needing a notification
|
||||||
*/
|
*/
|
||||||
fun run(): Int {
|
fun run(lastSyncTime: Long, markAsSeen: Boolean = false): Int {
|
||||||
val profiles = profiles.filter { it.registration == Profile.REGISTRATION_ENABLED && !it.archived }
|
val blacklistedIds = app.db.eventDao().blacklistedIds
|
||||||
if (profiles.isNotEmpty()) {
|
val events = api.getEvents(profiles, notifications, blacklistedIds, lastSyncTime)
|
||||||
val blacklistedIds = app.db.eventDao().blacklistedIds;
|
|
||||||
val events = api.getEvents(profiles, notifications, blacklistedIds)
|
|
||||||
|
|
||||||
if (events.isNotEmpty()) {
|
app.config.sync.lastAppSync = System.currentTimeMillis()
|
||||||
app.db.metadataDao().addAllIgnore(events.map { event ->
|
|
||||||
Metadata(
|
if (events.isNotEmpty()) {
|
||||||
event.profileId,
|
val today = Date.getToday()
|
||||||
Metadata.TYPE_EVENT,
|
app.db.metadataDao().addAllIgnore(events.map { event ->
|
||||||
event.id,
|
val isPast = event.eventDate < today
|
||||||
event.seen,
|
Metadata(
|
||||||
event.notified,
|
event.profileId,
|
||||||
event.addedDate
|
Metadata.TYPE_EVENT,
|
||||||
)
|
event.id,
|
||||||
})
|
isPast || markAsSeen || event.seen,
|
||||||
return app.db.eventDao().addAll(events).size
|
isPast || markAsSeen || event.notified,
|
||||||
}
|
event.addedDate
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return app.db.eventDao().addAll(events).size
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import androidx.core.util.forEach
|
|||||||
import androidx.core.util.set
|
import androidx.core.util.set
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_SERVER_MESSAGE
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_SERVER_MESSAGE
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification as AppNotification
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification as AppNotification
|
||||||
|
|
||||||
class PostNotifications(val app: App, nList: List<AppNotification>) {
|
class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||||
@ -17,22 +18,34 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
private const val TAG = "PostNotifications"
|
private const val TAG = "PostNotifications"
|
||||||
}
|
}
|
||||||
|
|
||||||
/*public boolean shouldBeQuiet() {
|
private val quiet by lazy { shouldBeQuiet() }
|
||||||
long now = Time.getNow().getInMillis();
|
fun shouldBeQuiet(): Boolean {
|
||||||
long start = app.config.getSync().getQuietHoursStart();
|
if (!app.config.sync.quietHoursEnabled)
|
||||||
long end = app.config.getSync().getQuietHoursEnd();
|
return false
|
||||||
|
val now = Time.getNow().value
|
||||||
|
val start = app.config.sync.quietHoursStart?.value ?: return false
|
||||||
|
var end = app.config.sync.quietHoursEnd?.value ?: return false
|
||||||
if (start > end) {
|
if (start > end) {
|
||||||
end += 1000 * 60 * 60 * 24;
|
// the range spans between two days
|
||||||
//Log.d(TAG, "Night passing");
|
end += 240000
|
||||||
}
|
}
|
||||||
if (start > now) {
|
return now in start..end || now+240000 in start..end
|
||||||
now += 1000 * 60 * 60 * 24;
|
}
|
||||||
//Log.d(TAG, "Now is smaller");
|
|
||||||
}
|
private fun NotificationCompat.Builder.addDefaults(): NotificationCompat.Builder {
|
||||||
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
|
return this.setColor(0xff2196f3.toInt())
|
||||||
return start > 0 && now >= start && now <= end;
|
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||||
}*/
|
.setPriority(if (quiet) NotificationCompat.PRIORITY_LOW else NotificationCompat.PRIORITY_MAX)
|
||||||
fun shouldBeQuiet() = false
|
.also {
|
||||||
|
if (quiet) {
|
||||||
|
it.setSound(null)
|
||||||
|
it.setVibrate(longArrayOf())
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||||
|
}
|
||||||
|
.setGroup(if (quiet) app.notificationChannelsManager.dataQuiet.key else app.notificationChannelsManager.data.key)
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildSummaryText(summaryCounts: SparseIntArray): CharSequence {
|
private fun buildSummaryText(summaryCounts: SparseIntArray): CharSequence {
|
||||||
val summaryTexts = mutableListOf<String>()
|
val summaryTexts = mutableListOf<String>()
|
||||||
@ -108,11 +121,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
it.addLine(line)
|
it.addLine(line)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setColor(0xff2196f3.toInt())
|
.addDefaults()
|
||||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
|
||||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
||||||
.setGroup(app.notificationChannelsManager.data.key)
|
|
||||||
.setContentIntent(summaryIntent)
|
.setContentIntent(summaryIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.build()
|
.build()
|
||||||
@ -131,11 +140,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
.setStyle(NotificationCompat.BigTextStyle()
|
.setStyle(NotificationCompat.BigTextStyle()
|
||||||
.bigText(it.text))
|
.bigText(it.text))
|
||||||
.setWhen(it.addedDate)
|
.setWhen(it.addedDate)
|
||||||
.setColor(0xff2196f3.toInt())
|
.addDefaults()
|
||||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
|
||||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
||||||
.setGroup(app.notificationChannelsManager.data.key)
|
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||||
.setContentIntent(it.getPendingIntent(app))
|
.setContentIntent(it.getPendingIntent(app))
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
@ -155,11 +160,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
.setContentText(buildSummaryText(summaryCounts))
|
.setContentText(buildSummaryText(summaryCounts))
|
||||||
.setTicker(newNotificationsText)
|
.setTicker(newNotificationsText)
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
.setColor(0xff2196f3.toInt())
|
.addDefaults()
|
||||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
|
||||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
||||||
.setGroup(app.notificationChannelsManager.data.key)
|
|
||||||
.setGroupSummary(true)
|
.setGroupSummary(true)
|
||||||
.setContentIntent(summaryIntent)
|
.setContentIntent(summaryIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
|
@ -31,16 +31,23 @@ class SzkolnyTask(val app: App, val syncingProfiles: List<Profile>) : IApiTask(-
|
|||||||
val notifications = Notifications(app, notificationList, profiles)
|
val notifications = Notifications(app, notificationList, profiles)
|
||||||
notifications.run()
|
notifications.run()
|
||||||
|
|
||||||
val shouldAppSync = notificationList.isNotEmpty() || (System.currentTimeMillis() - app.config.lastAppSync > 24*HOUR*1000)
|
val appSyncProfiles = profiles.filter { it.registration == Profile.REGISTRATION_ENABLED && !it.archived }
|
||||||
// do an AppSync every 24 hours, or if WebPush has a notification
|
// App Sync conditions:
|
||||||
|
// - every 24 hours && any profile is registered
|
||||||
|
// - if there are new notifications && any browser is paired
|
||||||
|
val shouldAppSync =
|
||||||
|
System.currentTimeMillis() - app.config.sync.lastAppSync > 24*HOUR*1000
|
||||||
|
&& appSyncProfiles.isNotEmpty()
|
||||||
|
|| notificationList.isNotEmpty()
|
||||||
|
&& app.config.sync.webPushEnabled
|
||||||
|
|
||||||
if (shouldAppSync) {
|
if (shouldAppSync) {
|
||||||
// send notifications to web push, get shared events
|
// send notifications to web push, get shared events
|
||||||
val addedEvents = AppSync(app, notificationList, profiles, api).run()
|
val addedEvents = AppSync(app, notificationList, appSyncProfiles, api).run(app.config.sync.lastAppSync)
|
||||||
if (addedEvents > 0) {
|
if (addedEvents > 0) {
|
||||||
// create notifications for shared events (not present before app sync)
|
// create notifications for shared events (not present before app sync)
|
||||||
notifications.sharedEventNotifications()
|
notifications.sharedEventNotifications()
|
||||||
}
|
}
|
||||||
app.config.lastAppSync = System.currentTimeMillis()
|
|
||||||
}
|
}
|
||||||
d(TAG, "Created ${notificationList.count()} notifications.")
|
d(TAG, "Created ${notificationList.count()} notifications.")
|
||||||
|
|
||||||
|
@ -41,8 +41,9 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
|
|||||||
Lesson::class,
|
Lesson::class,
|
||||||
ConfigEntry::class,
|
ConfigEntry::class,
|
||||||
LibrusLesson::class,
|
LibrusLesson::class,
|
||||||
|
TimetableManual::class,
|
||||||
Metadata::class
|
Metadata::class
|
||||||
], version = 78)
|
], version = 79)
|
||||||
@TypeConverters(
|
@TypeConverters(
|
||||||
ConverterTime::class,
|
ConverterTime::class,
|
||||||
ConverterDate::class,
|
ConverterDate::class,
|
||||||
@ -80,6 +81,7 @@ abstract class AppDb : RoomDatabase() {
|
|||||||
abstract fun timetableDao(): TimetableDao
|
abstract fun timetableDao(): TimetableDao
|
||||||
abstract fun configDao(): ConfigDao
|
abstract fun configDao(): ConfigDao
|
||||||
abstract fun librusLessonDao(): LibrusLessonDao
|
abstract fun librusLessonDao(): LibrusLessonDao
|
||||||
|
abstract fun timetableManualDao(): TimetableManualDao
|
||||||
abstract fun metadataDao(): MetadataDao
|
abstract fun metadataDao(): MetadataDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -161,7 +163,8 @@ abstract class AppDb : RoomDatabase() {
|
|||||||
Migration75(),
|
Migration75(),
|
||||||
Migration76(),
|
Migration76(),
|
||||||
Migration77(),
|
Migration77(),
|
||||||
Migration78()
|
Migration78(),
|
||||||
|
Migration79()
|
||||||
).allowMainThreadQueries().build()
|
).allowMainThreadQueries().build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date
|
|||||||
|
|
||||||
class ConverterDateInt {
|
class ConverterDateInt {
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun toDate(value: Int): Date = Date.fromValue(value)
|
fun toDate(value: Int): Date? = if (value == 0) null else Date.fromValue(value)
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun toInt(date: Date?): Int = date?.value ?: 0
|
fun toInt(date: Date?): Int = date?.value ?: 0
|
||||||
|
@ -41,7 +41,7 @@ interface ProfileDao {
|
|||||||
fun getIdsByLoginStoreIdNow(loginStoreId: Int): List<Int>
|
fun getIdsByLoginStoreIdNow(loginStoreId: Int): List<Int>
|
||||||
|
|
||||||
@get:Query("SELECT * FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
|
@get:Query("SELECT * FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
|
||||||
val profilesForSyncNow: List<Profile>
|
val profilesForFirebaseNow: List<Profile>
|
||||||
|
|
||||||
@get:Query("SELECT profileId FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
|
@get:Query("SELECT profileId FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
|
||||||
val idsForSyncNow: List<Int>
|
val idsForSyncNow: List<Int>
|
||||||
|
@ -5,10 +5,9 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.db.dao
|
package pl.szczodrzynski.edziennik.data.db.dao
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.room.Dao
|
import androidx.room.*
|
||||||
import androidx.room.Insert
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.sqlite.db.SupportSQLiteQuery
|
||||||
import androidx.room.Query
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||||
@ -53,6 +52,9 @@ interface TimetableDao {
|
|||||||
@Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo))")
|
@Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo))")
|
||||||
fun clearBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date)
|
fun clearBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date)
|
||||||
|
|
||||||
|
@RawQuery(observedEntities = [Lesson::class])
|
||||||
|
fun getRaw(query: SupportSQLiteQuery): LiveData<List<LessonFull>>
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
$QUERY
|
$QUERY
|
||||||
WHERE timetable.profileId = :profileId AND type != -1 AND type != 0
|
WHERE timetable.profileId = :profileId AND type != -1 AND type != 0
|
||||||
@ -67,12 +69,11 @@ interface TimetableDao {
|
|||||||
""")
|
""")
|
||||||
fun getChangesForDateNow(profileId: Int, date: Date): List<LessonFull>
|
fun getChangesForDateNow(profileId: Int, date: Date): List<LessonFull>
|
||||||
|
|
||||||
@Query("""
|
fun getForDate(profileId: Int, date: Date) = getRaw(SimpleSQLiteQuery("""
|
||||||
$QUERY
|
$QUERY
|
||||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
|
WHERE timetable.profileId = $profileId AND ((type != 3 AND date = "${date.stringY_m_d}") OR ((type = 3 OR type = 1) AND oldDate = "${date.stringY_m_d}"))
|
||||||
ORDER BY id, type
|
ORDER BY id, type
|
||||||
""")
|
"""))
|
||||||
fun getForDate(profileId: Int, date: Date): LiveData<List<LessonFull>>
|
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
$QUERY
|
$QUERY
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-22.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.db.dao
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.room.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.TimetableManual
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface TimetableManualDao {
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun add(timetableManual: TimetableManual)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun addAll(timetableManualList: List<TimetableManual>)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM timetableManual WHERE profileId = :profileId")
|
||||||
|
fun getAll(profileId: Int): LiveData<List<TimetableManual>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM timetableManual WHERE profileId = :profileId AND date >= :dateFrom AND date <= :dateTo")
|
||||||
|
fun getAllByDateRange(profileId: Int, dateFrom: Date, dateTo: Date): LiveData<List<TimetableManual>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM timetableManual WHERE profileId = :profileId AND (date IS NULL OR date = 0 OR date >= :dateFrom)")
|
||||||
|
fun getAllToDisplay(profileId: Int, dateFrom: Date): LiveData<List<TimetableManual>>
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(timetableManual: TimetableManual)
|
||||||
|
|
||||||
|
@Query("DELETE FROM timetableManual WHERE profileId = :profileId")
|
||||||
|
fun clear(profileId: Int)
|
||||||
|
}
|
@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.data.db.entity
|
|||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Ignore
|
||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
|
|
||||||
/*public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) {
|
/*public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) {
|
||||||
@ -81,5 +82,11 @@ open class Grade(
|
|||||||
*/
|
*/
|
||||||
@ColumnInfo(name = "gradeIsImprovement")
|
@ColumnInfo(name = "gradeIsImprovement")
|
||||||
var isImprovement = false
|
var isImprovement = false
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
var showAsUnseen = false
|
||||||
|
|
||||||
|
val isImproved
|
||||||
|
get() = parentId ?: -1L != -1L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.db.entity
|
package pl.szczodrzynski.edziennik.data.db.entity
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Ignore
|
||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
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
|
||||||
@ -66,6 +67,9 @@ open class Lesson(val profileId: Int, var id: Long) {
|
|||||||
|
|
||||||
fun buildId(): Long = (displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L + (hashCode() and 0xFFFF)
|
fun buildId(): Long = (displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L + (hashCode() and 0xFFFF)
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
var showAsUnseen = false
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Lesson(profileId=$profileId, " +
|
return "Lesson(profileId=$profileId, " +
|
||||||
"id=$id, " +
|
"id=$id, " +
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-22.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.db.entity
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
|
@Entity(tableName = "timetableManual",
|
||||||
|
indices = [
|
||||||
|
Index(value = ["profileId", "date"]),
|
||||||
|
Index(value = ["profileId", "weekDay"])
|
||||||
|
])
|
||||||
|
class TimetableManual(
|
||||||
|
val profileId: Int,
|
||||||
|
var type: Int,
|
||||||
|
var repeatBy: Int,
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Int = 0
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
const val TYPE_NORMAL = 0
|
||||||
|
const val TYPE_CANCELLED = 1
|
||||||
|
const val TYPE_CHANGE = 2
|
||||||
|
const val TYPE_SHIFTED_SOURCE = 3
|
||||||
|
const val TYPE_SHIFTED_TARGET = 4
|
||||||
|
const val TYPE_REMOVED = 5
|
||||||
|
const val TYPE_CLASSROOM = 6
|
||||||
|
const val REPEAT_WEEKLY = 0
|
||||||
|
const val REPEAT_ONCE = 1
|
||||||
|
const val REPEAT_BY_SUBJECT = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// `date` for one time lesson
|
||||||
|
@ColumnInfo(typeAffinity = ColumnInfo.INTEGER)
|
||||||
|
var date: Date? = null
|
||||||
|
// `weekDay` for repeating lesson (every week)
|
||||||
|
var weekDay: Int? = null
|
||||||
|
|
||||||
|
var lessonNumber: Int? = null
|
||||||
|
var startTime: Time? = null
|
||||||
|
var endTime: Time? = null
|
||||||
|
|
||||||
|
var subjectId: Long? = null
|
||||||
|
var teacherId: Long? = null
|
||||||
|
var teamId: Long? = null
|
||||||
|
var classroom: String? = null
|
||||||
|
|
||||||
|
fun verifyParams(): Boolean {
|
||||||
|
return when (repeatBy) {
|
||||||
|
REPEAT_WEEKLY -> date == null && weekDay != null
|
||||||
|
REPEAT_ONCE -> date != null && weekDay == null
|
||||||
|
REPEAT_BY_SUBJECT -> date == null && weekDay == null && subjectId != null
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.data.db.migration
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration79 : Migration(78, 79) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
// manual timetable implementation
|
||||||
|
database.execSQL("""CREATE TABLE timetableManual (
|
||||||
|
profileId INTEGER NOT NULL,
|
||||||
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
|
type INTEGER NOT NULL,
|
||||||
|
repeatBy INTEGER NOT NULL DEFAULT 0,
|
||||||
|
date INTEGER DEFAULT NULL,
|
||||||
|
weekDay INTEGER DEFAULT NULL,
|
||||||
|
lessonNumber INTEGER DEFAULT NULL,
|
||||||
|
startTime TEXT DEFAULT NULL,
|
||||||
|
endTime TEXT DEFAULT NULL,
|
||||||
|
subjectId INTEGER DEFAULT NULL,
|
||||||
|
teacherId INTEGER DEFAULT NULL,
|
||||||
|
teamId INTEGER DEFAULT NULL,
|
||||||
|
classroom TEXT DEFAULT NULL
|
||||||
|
)""")
|
||||||
|
database.execSQL("CREATE INDEX index_timetableManual_profileId_date ON timetableManual (profileId, date)")
|
||||||
|
database.execSQL("CREATE INDEX index_timetableManual_profileId_weekDay ON timetableManual (profileId, weekDay)")
|
||||||
|
}
|
||||||
|
}
|
@ -9,8 +9,8 @@ import android.app.PendingIntent
|
|||||||
import android.app.PendingIntent.CanceledException
|
import android.app.PendingIntent.CanceledException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.firebase.iid.zzaq
|
import com.google.firebase.iid.zzad
|
||||||
import com.google.firebase.iid.zzv
|
import com.google.firebase.iid.zzaz
|
||||||
import com.google.firebase.messaging.zzc
|
import com.google.firebase.messaging.zzc
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
@ -36,7 +36,7 @@ open class FirebaseService : zzc() {
|
|||||||
|
|
||||||
// apparently this gets the correct intent from some
|
// apparently this gets the correct intent from some
|
||||||
// kind of queue inside Firebase's InstanceID Receiver
|
// kind of queue inside Firebase's InstanceID Receiver
|
||||||
final override fun zza(intent: Intent?) = zzaq.zza()?.zzb()
|
final override fun zza(intent: Intent?) = zzaz.zza()?.zzb()
|
||||||
final override fun zzb(intent: Intent?): Boolean {
|
final override fun zzb(intent: Intent?): Boolean {
|
||||||
val action = intent?.action
|
val action = intent?.action
|
||||||
if (action == "com.google.firebase.messaging.NOTIFICATION_OPEN") {
|
if (action == "com.google.firebase.messaging.NOTIFICATION_OPEN") {
|
||||||
@ -80,7 +80,7 @@ open class FirebaseService : zzc() {
|
|||||||
val ackBundle = Bundle(
|
val ackBundle = Bundle(
|
||||||
"google.message_id" to messageId
|
"google.message_id" to messageId
|
||||||
)
|
)
|
||||||
zzv.zza(this).zza(2, ackBundle)
|
zzad.zza(this).zza(2, ackBundle)
|
||||||
}
|
}
|
||||||
// check for duplicate message
|
// check for duplicate message
|
||||||
// and add it to queue
|
// and add it to queue
|
||||||
|
@ -36,7 +36,7 @@ class MyFirebaseService : FirebaseService(), CoroutineScope {
|
|||||||
putString(System.currentTimeMillis().toString(), message.toString())
|
putString(System.currentTimeMillis().toString(), message.toString())
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
val profiles = app.db.profileDao().profilesForSyncNow
|
val profiles = app.db.profileDao().profilesForFirebaseNow
|
||||||
when (message.from) {
|
when (message.from) {
|
||||||
"640759989760" -> SzkolnyAppFirebase(app, profiles, message)
|
"640759989760" -> SzkolnyAppFirebase(app, profiles, message)
|
||||||
"747285019373" -> SzkolnyMobidziennikFirebase(app, profiles, message)
|
"747285019373" -> SzkolnyMobidziennikFirebase(app, profiles, message)
|
||||||
|
@ -101,7 +101,9 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
val notificationList = mutableListOf<Notification>()
|
val notificationList = mutableListOf<Notification>()
|
||||||
|
|
||||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||||
val profile = profiles.firstOrNull { it.id == team.profileId }
|
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||||
|
if (profile.registration != Profile.REGISTRATION_ENABLED)
|
||||||
|
return@forEach
|
||||||
val event = Event(
|
val event = Event(
|
||||||
team.profileId,
|
team.profileId,
|
||||||
json.getLong("id") ?: return,
|
json.getLong("id") ?: return,
|
||||||
@ -116,12 +118,9 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
team.id
|
team.id
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO? i guess - this comment is here for like a year
|
|
||||||
//val oldEvent: Event? = app.db.eventDao().getByIdNow(profile?.id ?: -1, event.id)
|
|
||||||
|
|
||||||
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) event.sharedBy = "self"
|
if (profile.userCode == event.sharedBy) event.sharedBy = "self"
|
||||||
|
|
||||||
val metadata = Metadata(
|
val metadata = Metadata(
|
||||||
event.profileId,
|
event.profileId,
|
||||||
@ -132,18 +131,6 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
json.getLong("addedDate") ?: System.currentTimeMillis()
|
json.getLong("addedDate") ?: System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
|
|
||||||
//val eventType = eventTypes.firstOrNull { it.profileId == profile?.id && it.id == event.type }
|
|
||||||
|
|
||||||
/*val text = app.getString(
|
|
||||||
if (oldEvent == null)
|
|
||||||
R.string.notification_shared_event_format
|
|
||||||
else
|
|
||||||
R.string.notification_shared_event_modified_format,
|
|
||||||
event.sharedByName,
|
|
||||||
eventType?.name ?: "wydarzenie",
|
|
||||||
event.eventDate.formattedString,
|
|
||||||
event.topic
|
|
||||||
)*/
|
|
||||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
|
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
|
||||||
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter
|
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter
|
||||||
|
|
||||||
@ -153,8 +140,8 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
title = app.getNotificationTitle(type),
|
title = app.getNotificationTitle(type),
|
||||||
text = message,
|
text = message,
|
||||||
type = type,
|
type = type,
|
||||||
profileId = profile?.id,
|
profileId = profile.id,
|
||||||
profileName = profile?.name,
|
profileName = profile.name,
|
||||||
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
||||||
addedDate = metadata.addedDate
|
addedDate = metadata.addedDate
|
||||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||||
@ -177,18 +164,20 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
val notificationList = mutableListOf<Notification>()
|
val notificationList = mutableListOf<Notification>()
|
||||||
|
|
||||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||||
val profile = profiles.firstOrNull { it.id == team.profileId }
|
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||||
|
if (profile.registration != Profile.REGISTRATION_ENABLED)
|
||||||
|
return@forEach
|
||||||
val notificationFilter = app.config.getFor(team.profileId).sync.notificationFilter
|
val notificationFilter = app.config.getFor(team.profileId).sync.notificationFilter
|
||||||
|
|
||||||
if (!notificationFilter.contains(Notification.TYPE_REMOVED_SHARED_EVENT)) {
|
if (!notificationFilter.contains(Notification.TYPE_REMOVED_SHARED_EVENT)) {
|
||||||
val notification = Notification(
|
val notification = Notification(
|
||||||
id = Notification.buildId(profile?.id
|
id = Notification.buildId(profile.id
|
||||||
?: 0, Notification.TYPE_REMOVED_SHARED_EVENT, eventId),
|
?: 0, Notification.TYPE_REMOVED_SHARED_EVENT, eventId),
|
||||||
title = app.getNotificationTitle(Notification.TYPE_REMOVED_SHARED_EVENT),
|
title = app.getNotificationTitle(Notification.TYPE_REMOVED_SHARED_EVENT),
|
||||||
text = message,
|
text = message,
|
||||||
type = Notification.TYPE_REMOVED_SHARED_EVENT,
|
type = Notification.TYPE_REMOVED_SHARED_EVENT,
|
||||||
profileId = profile?.id,
|
profileId = profile.id,
|
||||||
profileName = profile?.name,
|
profileName = profile.name,
|
||||||
viewId = MainActivity.DRAWER_ITEM_AGENDA
|
viewId = MainActivity.DRAWER_ITEM_AGENDA
|
||||||
)
|
)
|
||||||
notificationList += notification
|
notificationList += notification
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-24.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.network.cookie
|
||||||
|
|
||||||
|
import okhttp3.Cookie
|
||||||
|
|
||||||
|
class DumbCookie(var cookie: Cookie) {
|
||||||
|
|
||||||
|
constructor(domain: String, name: String, value: String, expiresAt: Long? = null) : this(
|
||||||
|
Cookie.Builder()
|
||||||
|
.name(name)
|
||||||
|
.value(value)
|
||||||
|
.also { if (expiresAt != null) it.expiresAt(expiresAt) }
|
||||||
|
.domain(domain)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
cookie = Cookie.Builder()
|
||||||
|
.name(cookie.name())
|
||||||
|
.value(cookie.value())
|
||||||
|
.expiresAt(cookie.expiresAt())
|
||||||
|
.domain(cookie.domain())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun domainMatches(host: String): Boolean {
|
||||||
|
val domain = cookie.domain()
|
||||||
|
return host == domain || host.endsWith(".$domain")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is DumbCookie) return false
|
||||||
|
if (this.cookie === other.cookie) return true
|
||||||
|
|
||||||
|
return cookie.name() == other.cookie.name()
|
||||||
|
&& cookie.domain() == other.cookie.domain()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var hash = 17
|
||||||
|
hash = 31 * hash + cookie.name().hashCode()
|
||||||
|
hash = 31 * hash + cookie.domain().hashCode()
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-24.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.network.cookie
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import okhttp3.Cookie
|
||||||
|
import okhttp3.CookieJar
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple cookie jar that does not care about the [Cookie.secure], [Cookie.hostOnly],
|
||||||
|
* [Cookie.httpOnly] and [Cookie.path] attributes.
|
||||||
|
*/
|
||||||
|
class DumbCookieJar(
|
||||||
|
/**
|
||||||
|
* A context to create the shared prefs file.
|
||||||
|
*/
|
||||||
|
context: Context,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to persist session cookies as well, when [Cookie.persistent] is false.
|
||||||
|
*/
|
||||||
|
private val persistAll: Boolean = false
|
||||||
|
) : CookieJar {
|
||||||
|
|
||||||
|
private val prefs = context.getSharedPreferences("cookies", Context.MODE_PRIVATE)
|
||||||
|
private val sessionCookies = mutableSetOf<DumbCookie>()
|
||||||
|
private val savedCookies = mutableSetOf<DumbCookie>()
|
||||||
|
private fun save(dc: DumbCookie) {
|
||||||
|
sessionCookies.remove(dc)
|
||||||
|
sessionCookies.add(dc)
|
||||||
|
if (dc.cookie.persistent() || persistAll) {
|
||||||
|
savedCookies.remove(dc)
|
||||||
|
savedCookies.add(dc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun delete(vararg toRemove: DumbCookie) {
|
||||||
|
sessionCookies.removeAll(toRemove)
|
||||||
|
savedCookies.removeAll(toRemove)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveFromResponse(url: HttpUrl?, cookies: List<Cookie>) {
|
||||||
|
for (cookie in cookies) {
|
||||||
|
val dc = DumbCookie(cookie)
|
||||||
|
save(dc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||||
|
return sessionCookies.filter {
|
||||||
|
it.cookie.matches(url)
|
||||||
|
}.map { it.cookie }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(domain: String, name: String): String? {
|
||||||
|
return sessionCookies.firstOrNull {
|
||||||
|
it.domainMatches(domain) && it.cookie.name() == name
|
||||||
|
}?.cookie?.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun set(domain: String, name: String, value: String?, isSession: Boolean) = set(
|
||||||
|
domain, name, value,
|
||||||
|
if (isSession) null
|
||||||
|
else System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000L
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a cookie to the cache.
|
||||||
|
* By default a session cookie is added. If [expiresAt] is set, the cookie is
|
||||||
|
* additionally persisted.
|
||||||
|
*/
|
||||||
|
fun set(domain: String, name: String?, value: String?, expiresAt: Long? = null) {
|
||||||
|
name ?: return
|
||||||
|
if (value == null) {
|
||||||
|
remove(domain, name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val dc = DumbCookie(domain, name, value, expiresAt)
|
||||||
|
save(dc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAll(domain: String): Map<String, String> {
|
||||||
|
return sessionCookies.filter {
|
||||||
|
it.domainMatches(domain)
|
||||||
|
}.map { it.cookie.name() to it.cookie.value() }.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(domain: String, name: String) {
|
||||||
|
val toRemove = sessionCookies.filter {
|
||||||
|
it.domainMatches(domain) && it.cookie.name() == name
|
||||||
|
}
|
||||||
|
delete(*toRemove.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear(domain: String) {
|
||||||
|
val toRemove = sessionCookies.filter {
|
||||||
|
it.domainMatches(domain)
|
||||||
|
}
|
||||||
|
delete(*toRemove.toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ class QrScannerDialog(
|
|||||||
onShowListener?.invoke(TAG)
|
onShowListener?.invoke(TAG)
|
||||||
app = activity.applicationContext as App
|
app = activity.applicationContext as App
|
||||||
scannerView = ZXingScannerView(activity)
|
scannerView = ZXingScannerView(activity)
|
||||||
scannerView.setPadding(0, 16.dp, 0, 0)
|
scannerView.setPadding(0, 16.dp, 2.dp, 0)
|
||||||
dialog = MaterialAlertDialogBuilder(activity)
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
.setTitle(R.string.qr_scanner_dialog_title)
|
.setTitle(R.string.qr_scanner_dialog_title)
|
||||||
.setView(scannerView)
|
.setView(scannerView)
|
||||||
@ -59,4 +59,4 @@ class QrScannerDialog(
|
|||||||
}
|
}
|
||||||
scannerView.startCamera()
|
scannerView.startCamera()
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ class BellSyncDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
counterJob = startCoroutineTimer(repeatMillis = 1000) {
|
counterJob = startCoroutineTimer(repeatMillis = 500) {
|
||||||
val (bellDiff, multiplier) = actualBellDiff
|
val (bellDiff, multiplier) = actualBellDiff
|
||||||
val bellDiffText = (if (multiplier == -1) '-' else '+') + bellDiff.stringHMS
|
val bellDiffText = (if (multiplier == -1) '-' else '+') + bellDiff.stringHMS
|
||||||
b.bellSyncHowto.text = app.getString(R.string.bell_sync_howto, bellTime.stringHM, bellDiffText)
|
b.bellSyncHowto.text = app.getString(R.string.bell_sync_howto, bellTime.stringHM, bellDiffText)
|
||||||
|
@ -19,6 +19,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
|||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding
|
import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||||
|
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@ -47,6 +48,8 @@ class EventDetailsDialog(
|
|||||||
SzkolnyApi(app)
|
SzkolnyApi(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var progressDialog: AlertDialog? = null
|
||||||
|
|
||||||
init { run {
|
init { run {
|
||||||
if (activity.isFinishing)
|
if (activity.isFinishing)
|
||||||
return@run
|
return@run
|
||||||
@ -64,6 +67,7 @@ class EventDetailsDialog(
|
|||||||
}
|
}
|
||||||
.setOnDismissListener {
|
.setOnDismissListener {
|
||||||
onDismissListener?.invoke(TAG)
|
onDismissListener?.invoke(TAG)
|
||||||
|
progressDialog?.dismiss()
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
|
|
||||||
@ -156,6 +160,23 @@ class EventDetailsDialog(
|
|||||||
Toast.makeText(activity, R.string.hint_edit_event, Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, R.string.hint_edit_event, Toast.LENGTH_SHORT).show()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.topic.text = event.topic
|
||||||
|
BetterLink.attach(b.topic) {
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showRemovingProgressDialog() {
|
||||||
|
if (progressDialog != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.please_wait)
|
||||||
|
.setMessage(R.string.event_removing_text)
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showRemoveEventDialog() {
|
private fun showRemoveEventDialog() {
|
||||||
@ -186,11 +207,14 @@ class EventDetailsDialog(
|
|||||||
launch {
|
launch {
|
||||||
if (eventShared && eventOwn) {
|
if (eventShared && eventOwn) {
|
||||||
// unshare + remove own event
|
// unshare + remove own event
|
||||||
Toast.makeText(activity, R.string.event_manual_unshare_remove, Toast.LENGTH_SHORT).show()
|
showRemovingProgressDialog()
|
||||||
|
|
||||||
api.runCatching(activity) {
|
api.runCatching(activity) {
|
||||||
unshareEvent(event)
|
unshareEvent(event)
|
||||||
} ?: return@launch
|
} ?: run {
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
finishRemoving()
|
finishRemoving()
|
||||||
} else if (eventShared && !eventOwn) {
|
} else if (eventShared && !eventOwn) {
|
||||||
@ -202,6 +226,7 @@ class EventDetailsDialog(
|
|||||||
Toast.makeText(activity, R.string.event_manual_remove, Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, R.string.event_manual_remove, Toast.LENGTH_SHORT).show()
|
||||||
finishRemoving()
|
finishRemoving()
|
||||||
}
|
}
|
||||||
|
progressDialog?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,24 +12,33 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL
|
import androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL
|
||||||
import androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE
|
import androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import com.google.android.material.datepicker.MaterialDatePicker
|
|
||||||
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.*
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.greenrobot.eventbus.Subscribe
|
||||||
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
|
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
|
||||||
|
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.EventType
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.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.ui.dialogs.sync.RegistrationEnableDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.views.TimeDropdown.Companion.DISPLAY_LESSONS
|
||||||
import pl.szczodrzynski.edziennik.utils.Anim
|
import pl.szczodrzynski.edziennik.utils.Anim
|
||||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||||
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 pl.szczodrzynski.edziennik.utils.models.Week
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class EventManualDialog(
|
class EventManualDialog(
|
||||||
@ -48,30 +57,34 @@ class EventManualDialog(
|
|||||||
private const val TAG = "EventManualDialog"
|
private const val TAG = "EventManualDialog"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var job: Job
|
private val job: Job = Job()
|
||||||
override val coroutineContext: CoroutineContext
|
override val coroutineContext: CoroutineContext
|
||||||
get() = job + Dispatchers.Main
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
private val app by lazy { activity.application as App }
|
private val app by lazy { activity.application as App }
|
||||||
private lateinit var b: DialogEventManualV2Binding
|
private lateinit var b: DialogEventManualV2Binding
|
||||||
private lateinit var dialog: AlertDialog
|
private lateinit var dialog: AlertDialog
|
||||||
|
private var profile: Profile? = null
|
||||||
|
|
||||||
private lateinit var event: Event
|
|
||||||
private var customColor: Int? = null
|
private var customColor: Int? = null
|
||||||
private val editingShared = editingEvent?.sharedBy != null
|
private val editingShared = editingEvent?.sharedBy != null
|
||||||
private val editingOwn = editingEvent?.sharedBy == "self"
|
private val editingOwn = editingEvent?.sharedBy == "self"
|
||||||
private var removeEventDialog: AlertDialog? = null
|
private var removeEventDialog: AlertDialog? = null
|
||||||
private var defaultLoaded = false
|
|
||||||
|
|
||||||
private val api by lazy {
|
private val api by lazy {
|
||||||
SzkolnyApi(app)
|
SzkolnyApi(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var enqueuedWeekDialog: AlertDialog? = null
|
||||||
|
private var enqueuedWeekStart = Date.getToday()
|
||||||
|
|
||||||
|
private var progressDialog: AlertDialog? = null
|
||||||
|
|
||||||
init { run {
|
init { run {
|
||||||
if (activity.isFinishing)
|
if (activity.isFinishing)
|
||||||
return@run
|
return@run
|
||||||
job = Job()
|
|
||||||
onShowListener?.invoke(TAG)
|
onShowListener?.invoke(TAG)
|
||||||
|
EventBus.getDefault().register(this)
|
||||||
b = DialogEventManualV2Binding.inflate(activity.layoutInflater)
|
b = DialogEventManualV2Binding.inflate(activity.layoutInflater)
|
||||||
dialog = MaterialAlertDialogBuilder(activity)
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
.setTitle(R.string.dialog_event_manual_title)
|
.setTitle(R.string.dialog_event_manual_title)
|
||||||
@ -85,6 +98,9 @@ class EventManualDialog(
|
|||||||
}
|
}
|
||||||
.setOnDismissListener {
|
.setOnDismissListener {
|
||||||
onDismissListener?.invoke(TAG)
|
onDismissListener?.invoke(TAG)
|
||||||
|
EventBus.getDefault().unregister(this@EventManualDialog)
|
||||||
|
enqueuedWeekDialog?.dismiss()
|
||||||
|
progressDialog?.dismiss()
|
||||||
}
|
}
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.create()
|
.create()
|
||||||
@ -104,12 +120,6 @@ class EventManualDialog(
|
|||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
|
|
||||||
event = editingEvent?.clone() ?: Event().also { event ->
|
|
||||||
event.profileId = profileId
|
|
||||||
defaultType?.let {
|
|
||||||
event.type = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.shareSwitch.isChecked = editingShared
|
b.shareSwitch.isChecked = editingShared
|
||||||
b.shareSwitch.isEnabled = !editingShared || (editingShared && editingOwn)
|
b.shareSwitch.isEnabled = !editingShared || (editingShared && editingOwn)
|
||||||
|
|
||||||
@ -144,41 +154,170 @@ class EventManualDialog(
|
|||||||
else -> R.string.dialog_event_manual_share_first_notice
|
else -> R.string.dialog_event_manual_share_first_notice
|
||||||
}
|
}
|
||||||
|
|
||||||
b.shareDetails.setText(text, event.sharedByName ?: "")
|
b.shareDetails.setText(text, editingEvent?.sharedByName ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun syncTimetable(date: Date) {
|
||||||
|
if (enqueuedWeekDialog != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (app.profile.getStudentData("timetableNotPublic", false)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val weekStart = date.weekStart
|
||||||
|
enqueuedWeekDialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.please_wait)
|
||||||
|
.setMessage(R.string.timetable_syncing_text)
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
|
|
||||||
|
enqueuedWeekStart = weekStart
|
||||||
|
|
||||||
|
EdziennikTask.syncProfile(
|
||||||
|
profileId = profileId,
|
||||||
|
viewIds = listOf(
|
||||||
|
MainActivity.DRAWER_ITEM_TIMETABLE to 0
|
||||||
|
),
|
||||||
|
arguments = JsonObject(
|
||||||
|
"weekStart" to weekStart.stringY_m_d
|
||||||
|
)
|
||||||
|
).enqueue(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSharingProgressDialog() {
|
||||||
|
if (progressDialog != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.please_wait)
|
||||||
|
.setMessage(R.string.event_sharing_text)
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showRemovingProgressDialog() {
|
||||||
|
if (progressDialog != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.please_wait)
|
||||||
|
.setMessage(R.string.event_removing_text)
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||||
|
if (event.profileId == profileId) {
|
||||||
|
enqueuedWeekDialog?.dismiss()
|
||||||
|
enqueuedWeekDialog = null
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
launch {
|
||||||
|
b.timeDropdown.loadItems()
|
||||||
|
b.timeDropdown.selectDefault(editingEvent?.startTime)
|
||||||
|
b.timeDropdown.selectDefault(defaultLesson?.displayStartTime ?: defaultTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||||
|
enqueuedWeekDialog?.dismiss()
|
||||||
|
enqueuedWeekDialog = null
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
|
||||||
|
enqueuedWeekDialog?.dismiss()
|
||||||
|
enqueuedWeekDialog = null
|
||||||
|
progressDialog?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadLists() { launch {
|
private fun loadLists() { launch {
|
||||||
|
profile = withContext(Dispatchers.Default) { app.db.profileDao().getByIdNow(profileId) }
|
||||||
|
|
||||||
|
with (b.dateDropdown) {
|
||||||
|
db = app.db
|
||||||
|
profileId = this@EventManualDialog.profileId
|
||||||
|
showWeekDays = false
|
||||||
|
showDays = true
|
||||||
|
showOtherDate = true
|
||||||
|
defaultLesson?.let {
|
||||||
|
nextLessonSubjectId = it.displaySubjectId
|
||||||
|
nextLessonSubjectName = it.displaySubjectName
|
||||||
|
nextLessonTeamId = it.displayTeamId
|
||||||
|
}
|
||||||
|
loadItems()
|
||||||
|
selectDefault(editingEvent?.eventDate)
|
||||||
|
selectDefault(defaultLesson?.displayDate ?: defaultDate)
|
||||||
|
onDateSelected = { date, lesson ->
|
||||||
|
b.timeDropdown.deselect()
|
||||||
|
b.timeDropdown.lessonsDate = date
|
||||||
|
this@EventManualDialog.launch {
|
||||||
|
if (!b.timeDropdown.loadItems())
|
||||||
|
syncTimetable(date)
|
||||||
|
lesson?.displayStartTime?.let { b.timeDropdown.selectTime(it) }
|
||||||
|
lesson?.displaySubjectId?.let { b.subjectDropdown.selectSubject(it) } ?: b.subjectDropdown.deselect()
|
||||||
|
lesson?.displayTeacherId?.let { b.teacherDropdown.selectTeacher(it) } ?: b.teacherDropdown.deselect()
|
||||||
|
lesson?.displayTeamId?.let { b.teamDropdown.selectTeam(it) } ?: b.teamDropdown.selectTeamClass()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with (b.timeDropdown) {
|
||||||
|
db = app.db
|
||||||
|
profileId = this@EventManualDialog.profileId
|
||||||
|
showAllDay = true
|
||||||
|
showCustomTime = true
|
||||||
|
lessonsDate = b.dateDropdown.getSelected() as? Date ?: Date.getToday()
|
||||||
|
displayMode = DISPLAY_LESSONS
|
||||||
|
if (!loadItems())
|
||||||
|
syncTimetable(lessonsDate ?: Date.getToday())
|
||||||
|
selectDefault(editingEvent?.startTime)
|
||||||
|
if (editingEvent != null && editingEvent.startTime == null)
|
||||||
|
select(0L)
|
||||||
|
selectDefault(defaultLesson?.displayStartTime ?: defaultTime)
|
||||||
|
onLessonSelected = { lesson ->
|
||||||
|
lesson.displaySubjectId?.let { b.subjectDropdown.selectSubject(it) } ?: b.subjectDropdown.deselect()
|
||||||
|
lesson.displayTeacherId?.let { b.teacherDropdown.selectTeacher(it) } ?: b.teacherDropdown.deselect()
|
||||||
|
lesson.displayTeamId?.let { b.teamDropdown.selectTeam(it) } ?: b.teamDropdown.selectTeamClass()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with (b.teamDropdown) {
|
||||||
|
db = app.db
|
||||||
|
profileId = this@EventManualDialog.profileId
|
||||||
|
showNoTeam = true
|
||||||
|
loadItems()
|
||||||
|
selectTeamClass()
|
||||||
|
selectDefault(editingEvent?.teamId)
|
||||||
|
selectDefault(defaultLesson?.displayTeamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
with (b.subjectDropdown) {
|
||||||
|
db = app.db
|
||||||
|
profileId = this@EventManualDialog.profileId
|
||||||
|
showNoSubject = true
|
||||||
|
showCustomSubject = false
|
||||||
|
loadItems()
|
||||||
|
selectDefault(editingEvent?.subjectId)
|
||||||
|
selectDefault(defaultLesson?.displaySubjectId)
|
||||||
|
}
|
||||||
|
|
||||||
|
with (b.teacherDropdown) {
|
||||||
|
db = app.db
|
||||||
|
profileId = this@EventManualDialog.profileId
|
||||||
|
showNoTeacher = true
|
||||||
|
loadItems()
|
||||||
|
selectDefault(editingEvent?.teacherId)
|
||||||
|
selectDefault(defaultLesson?.displayTeacherId)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val deferred = async(Dispatchers.Default) {
|
val deferred = async(Dispatchers.Default) {
|
||||||
// get the team list
|
|
||||||
val teams = app.db.teamDao().getAllNow(profileId)
|
|
||||||
b.teamDropdown.clear()
|
|
||||||
b.teamDropdown += TextInputDropDown.Item(
|
|
||||||
-1,
|
|
||||||
activity.getString(R.string.dialog_event_manual_no_team),
|
|
||||||
""
|
|
||||||
)
|
|
||||||
b.teamDropdown += teams.map { TextInputDropDown.Item(it.id, it.name, tag = it) }
|
|
||||||
|
|
||||||
// get the subject list
|
|
||||||
val subjects = app.db.subjectDao().getAllNow(profileId)
|
|
||||||
b.subjectDropdown.clear()
|
|
||||||
b.subjectDropdown += TextInputDropDown.Item(
|
|
||||||
-1,
|
|
||||||
activity.getString(R.string.dialog_event_manual_no_subject),
|
|
||||||
""
|
|
||||||
)
|
|
||||||
b.subjectDropdown += subjects.map { TextInputDropDown.Item(it.id, it.longName, tag = it) }
|
|
||||||
|
|
||||||
// get the teacher list
|
|
||||||
val teachers = app.db.teacherDao().getAllNow(profileId)
|
|
||||||
b.teacherDropdown.clear()
|
|
||||||
b.teacherDropdown += TextInputDropDown.Item(
|
|
||||||
-1,
|
|
||||||
activity.getString(R.string.dialog_event_manual_no_teacher),
|
|
||||||
""
|
|
||||||
)
|
|
||||||
b.teacherDropdown += teachers.map { TextInputDropDown.Item(it.id, it.fullName, tag = it) }
|
|
||||||
|
|
||||||
// get the event type list
|
// get the event type list
|
||||||
val eventTypes = app.db.eventTypeDao().getAllNow(profileId)
|
val eventTypes = app.db.eventTypeDao().getAllNow(profileId)
|
||||||
b.typeDropdown.clear()
|
b.typeDropdown.clear()
|
||||||
@ -186,13 +325,10 @@ class EventManualDialog(
|
|||||||
}
|
}
|
||||||
deferred.await()
|
deferred.await()
|
||||||
|
|
||||||
b.teamDropdown.isEnabled = true
|
|
||||||
b.subjectDropdown.isEnabled = true
|
|
||||||
b.teacherDropdown.isEnabled = true
|
|
||||||
b.typeDropdown.isEnabled = true
|
b.typeDropdown.isEnabled = true
|
||||||
|
|
||||||
defaultType?.let {
|
defaultType?.let {
|
||||||
b.typeDropdown.select(it.toLong())
|
b.typeDropdown.select(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.typeDropdown.selected?.let { item ->
|
b.typeDropdown.selected?.let { item ->
|
||||||
@ -201,9 +337,6 @@ class EventManualDialog(
|
|||||||
|
|
||||||
// copy IDs from event being edited
|
// copy IDs from event being edited
|
||||||
editingEvent?.let {
|
editingEvent?.let {
|
||||||
b.teamDropdown.select(it.teamId)
|
|
||||||
b.subjectDropdown.select(it.subjectId)
|
|
||||||
b.teacherDropdown.select(it.teacherId)
|
|
||||||
b.topic.setText(it.topic)
|
b.topic.setText(it.topic)
|
||||||
b.typeDropdown.select(it.type.toLong())?.let { item ->
|
b.typeDropdown.select(it.type.toLong())?.let { item ->
|
||||||
customColor = (item.tag as EventType).color
|
customColor = (item.tag as EventType).color
|
||||||
@ -215,8 +348,6 @@ class EventManualDialog(
|
|||||||
// copy IDs from the LessonFull
|
// copy IDs from the LessonFull
|
||||||
defaultLesson?.let {
|
defaultLesson?.let {
|
||||||
b.teamDropdown.select(it.displayTeamId)
|
b.teamDropdown.select(it.displayTeamId)
|
||||||
b.subjectDropdown.select(it.displaySubjectId)
|
|
||||||
b.teacherDropdown.select(it.displayTeacherId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b.typeDropdown.setOnChangeListener {
|
b.typeDropdown.setOnChangeListener {
|
||||||
@ -230,7 +361,7 @@ class EventManualDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.typeColor.onClick {
|
b.typeColor.onClick {
|
||||||
val currentColor = (b.typeDropdown?.selected?.tag as EventType?)?.color ?: Event.COLOR_DEFAULT
|
val currentColor = (b.typeDropdown.selected?.tag as EventType?)?.color ?: Event.COLOR_DEFAULT
|
||||||
val colorPickerDialog = ColorPickerDialog.newBuilder()
|
val colorPickerDialog = ColorPickerDialog.newBuilder()
|
||||||
.setColor(currentColor)
|
.setColor(currentColor)
|
||||||
.create()
|
.create()
|
||||||
@ -244,278 +375,8 @@ class EventManualDialog(
|
|||||||
})
|
})
|
||||||
colorPickerDialog.show(activity.fragmentManager, "color-picker-dialog")
|
colorPickerDialog.show(activity.fragmentManager, "color-picker-dialog")
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDates()
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
private fun loadDates() { launch {
|
|
||||||
val date = Date.getToday()
|
|
||||||
val today = date.value
|
|
||||||
var weekDay = date.weekDay
|
|
||||||
|
|
||||||
val deferred = async(Dispatchers.Default) {
|
|
||||||
val dates = mutableListOf<TextInputDropDown.Item>()
|
|
||||||
// item choosing the next lesson of specific subject
|
|
||||||
b.subjectDropdown.selected?.let {
|
|
||||||
if (it.tag is Subject) {
|
|
||||||
dates += TextInputDropDown.Item(
|
|
||||||
-it.id,
|
|
||||||
activity.getString(R.string.dialog_event_manual_date_next_lesson, it.tag.longName)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODAY
|
|
||||||
dates += TextInputDropDown.Item(
|
|
||||||
date.value.toLong(),
|
|
||||||
activity.getString(R.string.dialog_event_manual_date_today, date.formattedString),
|
|
||||||
tag = date.clone()
|
|
||||||
)
|
|
||||||
|
|
||||||
// TOMORROW
|
|
||||||
if (weekDay < 4) {
|
|
||||||
date.stepForward(0, 0, 1)
|
|
||||||
weekDay++
|
|
||||||
dates += TextInputDropDown.Item(
|
|
||||||
date.value.toLong(),
|
|
||||||
activity.getString(R.string.dialog_event_manual_date_tomorrow, date.formattedString),
|
|
||||||
tag = date.clone()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// REMAINING SCHOOL DAYS OF THE CURRENT WEEK
|
|
||||||
while (weekDay < 4) {
|
|
||||||
date.stepForward(0, 0, 1) // step one day forward
|
|
||||||
weekDay++
|
|
||||||
dates += TextInputDropDown.Item(
|
|
||||||
date.value.toLong(),
|
|
||||||
activity.getString(R.string.dialog_event_manual_date_this_week, Week.getFullDayName(weekDay), date.formattedString),
|
|
||||||
tag = date.clone()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// go to next week Monday
|
|
||||||
date.stepForward(0, 0, -weekDay + 7)
|
|
||||||
weekDay = 0
|
|
||||||
// ALL SCHOOL DAYS OF THE NEXT WEEK
|
|
||||||
while (weekDay < 4) {
|
|
||||||
dates += TextInputDropDown.Item(
|
|
||||||
date.value.toLong(),
|
|
||||||
activity.getString(R.string.dialog_event_manual_date_next_week, Week.getFullDayName(weekDay), date.formattedString),
|
|
||||||
tag = date.clone()
|
|
||||||
)
|
|
||||||
date.stepForward(0, 0, 1) // step one day forward
|
|
||||||
weekDay++
|
|
||||||
}
|
|
||||||
dates += TextInputDropDown.Item(
|
|
||||||
-1L,
|
|
||||||
activity.getString(R.string.dialog_event_manual_date_other)
|
|
||||||
)
|
|
||||||
dates
|
|
||||||
}
|
|
||||||
|
|
||||||
val dates = deferred.await()
|
|
||||||
b.dateDropdown.clear().append(dates)
|
|
||||||
|
|
||||||
defaultDate?.let {
|
|
||||||
event.eventDate = it
|
|
||||||
if (b.dateDropdown.select(it) == null)
|
|
||||||
b.dateDropdown.select(TextInputDropDown.Item(
|
|
||||||
it.value.toLong(),
|
|
||||||
it.formattedString,
|
|
||||||
tag = it
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
editingEvent?.eventDate?.let {
|
|
||||||
b.dateDropdown.select(TextInputDropDown.Item(
|
|
||||||
it.value.toLong(),
|
|
||||||
it.formattedString,
|
|
||||||
tag = it
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultLesson?.displayDate?.let {
|
|
||||||
b.dateDropdown.select(TextInputDropDown.Item(
|
|
||||||
it.value.toLong(),
|
|
||||||
it.formattedString,
|
|
||||||
tag = it
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b.dateDropdown.selected == null) {
|
|
||||||
b.dateDropdown.select(today.toLong())
|
|
||||||
}
|
|
||||||
|
|
||||||
b.dateDropdown.isEnabled = true
|
|
||||||
|
|
||||||
b.dateDropdown.setOnChangeListener { item ->
|
|
||||||
when {
|
|
||||||
// next lesson with specified subject
|
|
||||||
item.id < -1 -> {
|
|
||||||
val teamId = defaultLesson?.teamId ?: -1
|
|
||||||
val selectedLessonDate = defaultLesson?.date ?: Date.getToday()
|
|
||||||
|
|
||||||
when (teamId) {
|
|
||||||
-1L -> app.db.timetableDao().getNextWithSubject(profileId, selectedLessonDate, -item.id)
|
|
||||||
else -> app.db.timetableDao().getNextWithSubjectAndTeam(profileId, selectedLessonDate, -item.id, teamId)
|
|
||||||
}.observeOnce(activity, Observer {
|
|
||||||
val lessonDate = it?.displayDate ?: return@Observer
|
|
||||||
b.dateDropdown.select(TextInputDropDown.Item(
|
|
||||||
lessonDate.value.toLong(),
|
|
||||||
lessonDate.formattedString,
|
|
||||||
tag = lessonDate
|
|
||||||
))
|
|
||||||
b.teamDropdown.select(it.displayTeamId)
|
|
||||||
b.subjectDropdown.select(it.displaySubjectId)
|
|
||||||
b.teacherDropdown.select(it.displayTeacherId)
|
|
||||||
defaultLoaded = false
|
|
||||||
loadHours(it.displayStartTime)
|
|
||||||
})
|
|
||||||
return@setOnChangeListener false
|
|
||||||
}
|
|
||||||
// custom date
|
|
||||||
item.id == -1L -> {
|
|
||||||
MaterialDatePicker.Builder
|
|
||||||
.datePicker()
|
|
||||||
.setSelection((b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) }
|
|
||||||
?: Date.getToday()).inMillis)
|
|
||||||
.build()
|
|
||||||
.apply {
|
|
||||||
addOnPositiveButtonClickListener {
|
|
||||||
val dateSelected = Date.fromMillis(it)
|
|
||||||
b.dateDropdown.select(TextInputDropDown.Item(
|
|
||||||
dateSelected.value.toLong(),
|
|
||||||
dateSelected.formattedString,
|
|
||||||
tag = dateSelected
|
|
||||||
))
|
|
||||||
loadHours()
|
|
||||||
}
|
|
||||||
show(this@EventManualDialog.activity.supportFragmentManager, "MaterialDatePicker")
|
|
||||||
}
|
|
||||||
|
|
||||||
return@setOnChangeListener false
|
|
||||||
}
|
|
||||||
// a specific date
|
|
||||||
else -> {
|
|
||||||
b.dateDropdown.select(item)
|
|
||||||
loadHours()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@setOnChangeListener true
|
|
||||||
}
|
|
||||||
|
|
||||||
loadHours()
|
|
||||||
}}
|
|
||||||
|
|
||||||
private fun loadHours(defaultHour: Time? = null) {
|
|
||||||
b.timeDropdown.isEnabled = false
|
|
||||||
// get the selected date
|
|
||||||
val date = b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) } ?: return
|
|
||||||
// get all lessons for selected date
|
|
||||||
app.db.timetableDao().getForDate(profileId, date).observeOnce(activity, Observer { lessons ->
|
|
||||||
val hours = mutableListOf<TextInputDropDown.Item>()
|
|
||||||
// add All day time choice
|
|
||||||
hours += TextInputDropDown.Item(
|
|
||||||
0L,
|
|
||||||
activity.getString(R.string.dialog_event_manual_all_day)
|
|
||||||
)
|
|
||||||
lessons.forEach { lesson ->
|
|
||||||
if (lesson.type == Lesson.TYPE_NO_LESSONS) {
|
|
||||||
// indicate there are no lessons this day
|
|
||||||
hours += TextInputDropDown.Item(
|
|
||||||
-2L,
|
|
||||||
activity.getString(R.string.dialog_event_manual_no_lessons)
|
|
||||||
)
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
// create the lesson caption
|
|
||||||
val text = listOfNotEmpty(
|
|
||||||
lesson.displayStartTime?.stringHM ?: "",
|
|
||||||
lesson.displaySubjectName?.let {
|
|
||||||
when {
|
|
||||||
lesson.type == Lesson.TYPE_CANCELLED
|
|
||||||
|| lesson.type == Lesson.TYPE_SHIFTED_SOURCE -> it.asStrikethroughSpannable()
|
|
||||||
lesson.type != Lesson.TYPE_NORMAL -> it.asItalicSpannable()
|
|
||||||
else -> it
|
|
||||||
}
|
|
||||||
} ?: ""
|
|
||||||
)
|
|
||||||
// add an item with LessonFull as the tag
|
|
||||||
hours += TextInputDropDown.Item(
|
|
||||||
lesson.displayStartTime?.value?.toLong() ?: -1,
|
|
||||||
text.concat(" "),
|
|
||||||
tag = lesson
|
|
||||||
)
|
|
||||||
}
|
|
||||||
b.timeDropdown.clear().append(hours)
|
|
||||||
|
|
||||||
if (defaultLoaded) {
|
|
||||||
b.timeDropdown.deselect()
|
|
||||||
// select the TEAM_CLASS if possible
|
|
||||||
b.teamDropdown.items.singleOrNull {
|
|
||||||
it.tag is Team && it.tag.type == Team.TYPE_CLASS
|
|
||||||
}?.let {
|
|
||||||
b.teamDropdown.select(it)
|
|
||||||
} ?: b.teamDropdown.deselect()
|
|
||||||
|
|
||||||
// clear subject, teacher selection
|
|
||||||
b.subjectDropdown.deselect()
|
|
||||||
b.teacherDropdown.deselect()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val setTime: (Time) -> Unit = {
|
|
||||||
event.startTime = it
|
|
||||||
if (b.timeDropdown.select(it) == null)
|
|
||||||
b.timeDropdown.select(TextInputDropDown.Item(
|
|
||||||
it.value.toLong(),
|
|
||||||
it.stringHM,
|
|
||||||
tag = it
|
|
||||||
))
|
|
||||||
}
|
|
||||||
defaultTime?.let(setTime)
|
|
||||||
editingEvent?.startTime?.let(setTime)
|
|
||||||
defaultLesson?.displayStartTime?.let(setTime)
|
|
||||||
defaultHour?.let(setTime)
|
|
||||||
}
|
|
||||||
defaultLoaded = true
|
|
||||||
b.timeDropdown.isEnabled = true
|
|
||||||
|
|
||||||
// attach a listener to time dropdown
|
|
||||||
b.timeDropdown.setOnChangeListener { item ->
|
|
||||||
when (item.id) {
|
|
||||||
// no lessons this day
|
|
||||||
-2L -> {
|
|
||||||
b.timeDropdown.deselect()
|
|
||||||
return@setOnChangeListener false
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom start hour
|
|
||||||
-1L -> return@setOnChangeListener false
|
|
||||||
|
|
||||||
// selected a specific lesson
|
|
||||||
else -> {
|
|
||||||
if (item.tag is LessonFull) {
|
|
||||||
// update team, subject, teacher dropdowns,
|
|
||||||
// using the LessonFull from item tag
|
|
||||||
b.teamDropdown.deselect()
|
|
||||||
b.subjectDropdown.deselect()
|
|
||||||
b.teacherDropdown.deselect()
|
|
||||||
item.tag.displayTeamId?.let {
|
|
||||||
b.teamDropdown.select(it)
|
|
||||||
}
|
|
||||||
item.tag.displaySubjectId?.let {
|
|
||||||
b.subjectDropdown.select(it)
|
|
||||||
}
|
|
||||||
item.tag.displayTeacherId?.let {
|
|
||||||
b.teacherDropdown.select(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@setOnChangeListener true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showRemoveEventDialog() {
|
private fun showRemoveEventDialog() {
|
||||||
val shareNotice = when {
|
val shareNotice = when {
|
||||||
editingShared && editingOwn -> "\n\n"+activity.getString(R.string.dialog_event_manual_remove_shared_self)
|
editingShared && editingOwn -> "\n\n"+activity.getString(R.string.dialog_event_manual_remove_shared_self)
|
||||||
@ -541,37 +402,67 @@ class EventManualDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun saveEvent() {
|
private fun saveEvent() {
|
||||||
val date = b.dateDropdown.selected?.tag.instanceOfOrNull<Date>()
|
val date = b.dateDropdown.getSelected() as? Date
|
||||||
val startTime = b.timeDropdown.selected?.tag.instanceOfOrNull<Time>()
|
val timeSelected = b.timeDropdown.getSelected()
|
||||||
val teamId = b.teamDropdown.selected?.id
|
val teamId = b.teamDropdown.getSelected() as? Long
|
||||||
val type = b.typeDropdown.selected?.id
|
val type = b.typeDropdown.selected?.id
|
||||||
val topic = b.topic.text?.toString()
|
val topic = b.topic.text?.toString()
|
||||||
val subjectId = b.subjectDropdown.selected?.id
|
val subjectId = b.subjectDropdown.getSelected() as? Long
|
||||||
val teacherId = b.teacherDropdown.selected?.id
|
val teacherId = b.teacherDropdown.getSelected()
|
||||||
|
|
||||||
val share = b.shareSwitch.isChecked
|
val share = b.shareSwitch.isChecked
|
||||||
|
|
||||||
|
if (share && profile?.registration != Profile.REGISTRATION_ENABLED) {
|
||||||
|
RegistrationEnableDialog(activity, profileId).showEventShareDialog {
|
||||||
|
if (it != null)
|
||||||
|
profile = it
|
||||||
|
saveEvent()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.dateDropdown.error = null
|
||||||
b.teamDropdown.error = null
|
b.teamDropdown.error = null
|
||||||
b.typeDropdown.error = null
|
b.typeDropdown.error = null
|
||||||
b.topic.error = null
|
b.topic.error = null
|
||||||
|
|
||||||
var isError = false
|
var isError = false
|
||||||
|
|
||||||
|
if (date == null) {
|
||||||
|
b.dateDropdown.error = app.getString(R.string.dialog_event_manual_date_choose)
|
||||||
|
b.dateDropdown.requestFocus()
|
||||||
|
isError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeSelected !is Pair<*, *> && timeSelected != 0L) {
|
||||||
|
b.timeDropdown.error = app.getString(R.string.dialog_event_manual_time_choose)
|
||||||
|
if (!isError) b.timeDropdown.parent.requestChildFocus(b.timeDropdown, b.timeDropdown)
|
||||||
|
isError = true
|
||||||
|
}
|
||||||
|
|
||||||
if (share && teamId == null) {
|
if (share && teamId == null) {
|
||||||
b.teamDropdown.error = app.getString(R.string.dialog_event_manual_team_choose)
|
b.teamDropdown.error = app.getString(R.string.dialog_event_manual_team_choose)
|
||||||
|
if (!isError) b.teamDropdown.parent.requestChildFocus(b.teamDropdown, b.teamDropdown)
|
||||||
isError = true
|
isError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
b.typeDropdown.error = app.getString(R.string.dialog_event_manual_type_choose)
|
b.typeDropdown.error = app.getString(R.string.dialog_event_manual_type_choose)
|
||||||
|
if (!isError) b.typeDropdown.requestFocus()
|
||||||
isError = true
|
isError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topic.isNullOrBlank()) {
|
if (topic.isNullOrBlank()) {
|
||||||
b.topic.error = app.getString(R.string.dialog_event_manual_topic_choose)
|
b.topic.error = app.getString(R.string.dialog_event_manual_topic_choose)
|
||||||
|
if (!isError) b.topic.requestFocus()
|
||||||
isError = true
|
isError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val startTime = if (timeSelected == 0L)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
(timeSelected as? Pair<*, *>)?.first as? Time
|
||||||
|
|
||||||
if (isError) return
|
if (isError) return
|
||||||
|
|
||||||
val id = System.currentTimeMillis()
|
val id = System.currentTimeMillis()
|
||||||
@ -606,7 +497,7 @@ class EventManualDialog(
|
|||||||
val profile = app.db.profileDao().getByIdNow(profileId)
|
val profile = app.db.profileDao().getByIdNow(profileId)
|
||||||
|
|
||||||
if (!share && !editingShared) {
|
if (!share && !editingShared) {
|
||||||
Toast.makeText(activity, R.string.event_manual_saving, Toast.LENGTH_SHORT).show()
|
//Toast.makeText(activity, R.string.event_manual_saving, Toast.LENGTH_SHORT).show()
|
||||||
finishAdding(eventObject, metadataObject)
|
finishAdding(eventObject, metadataObject)
|
||||||
}
|
}
|
||||||
else if (editingShared && !editingOwn) {
|
else if (editingShared && !editingOwn) {
|
||||||
@ -614,7 +505,7 @@ class EventManualDialog(
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
else if (!share && editingShared) {
|
else if (!share && editingShared) {
|
||||||
Toast.makeText(activity, R.string.event_manual_unshare, Toast.LENGTH_SHORT).show()
|
showSharingProgressDialog()
|
||||||
|
|
||||||
eventObject.apply {
|
eventObject.apply {
|
||||||
sharedBy = null
|
sharedBy = null
|
||||||
@ -623,13 +514,16 @@ class EventManualDialog(
|
|||||||
|
|
||||||
api.runCatching(activity) {
|
api.runCatching(activity) {
|
||||||
unshareEvent(eventObject)
|
unshareEvent(eventObject)
|
||||||
} ?: return@launch
|
} ?: run {
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
eventObject.sharedByName = null
|
eventObject.sharedByName = null
|
||||||
finishAdding(eventObject, metadataObject)
|
finishAdding(eventObject, metadataObject)
|
||||||
}
|
}
|
||||||
else if (share) {
|
else if (share) {
|
||||||
Toast.makeText(activity, R.string.event_manual_share, Toast.LENGTH_SHORT).show()
|
showSharingProgressDialog()
|
||||||
|
|
||||||
eventObject.apply {
|
eventObject.apply {
|
||||||
sharedBy = profile?.userCode
|
sharedBy = profile?.userCode
|
||||||
@ -640,7 +534,10 @@ class EventManualDialog(
|
|||||||
|
|
||||||
api.runCatching(activity) {
|
api.runCatching(activity) {
|
||||||
shareEvent(eventObject.withMetadata(metadataObject))
|
shareEvent(eventObject.withMetadata(metadataObject))
|
||||||
} ?: return@launch
|
} ?: run {
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
eventObject.sharedBy = "self"
|
eventObject.sharedBy = "self"
|
||||||
finishAdding(eventObject, metadataObject)
|
finishAdding(eventObject, metadataObject)
|
||||||
@ -648,6 +545,7 @@ class EventManualDialog(
|
|||||||
else {
|
else {
|
||||||
Toast.makeText(activity, "Unknown action :(", Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, "Unknown action :(", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
progressDialog?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,11 +553,14 @@ class EventManualDialog(
|
|||||||
launch {
|
launch {
|
||||||
if (editingShared && editingOwn) {
|
if (editingShared && editingOwn) {
|
||||||
// unshare + remove own event
|
// unshare + remove own event
|
||||||
Toast.makeText(activity, R.string.event_manual_unshare_remove, Toast.LENGTH_SHORT).show()
|
showRemovingProgressDialog()
|
||||||
|
|
||||||
api.runCatching(activity) {
|
api.runCatching(activity) {
|
||||||
unshareEvent(editingEvent!!)
|
unshareEvent(editingEvent!!)
|
||||||
} ?: return@launch
|
} ?: run {
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
finishRemoving()
|
finishRemoving()
|
||||||
} else if (editingShared && !editingOwn) {
|
} else if (editingShared && !editingOwn) {
|
||||||
@ -668,9 +569,10 @@ class EventManualDialog(
|
|||||||
// TODO
|
// TODO
|
||||||
} else {
|
} else {
|
||||||
// remove event
|
// remove event
|
||||||
Toast.makeText(activity, R.string.event_manual_remove, Toast.LENGTH_SHORT).show()
|
//Toast.makeText(activity, R.string.event_manual_remove, Toast.LENGTH_SHORT).show()
|
||||||
finishRemoving()
|
finishRemoving()
|
||||||
}
|
}
|
||||||
|
progressDialog?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.grade
|
|||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
|
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 kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@ -10,7 +11,9 @@ import pl.szczodrzynski.edziennik.App
|
|||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||||
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
|
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
|
||||||
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
import pl.szczodrzynski.edziennik.setTintColor
|
import pl.szczodrzynski.edziennik.setTintColor
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
@ -50,16 +53,22 @@ class GradeDetailsDialog(
|
|||||||
.show()
|
.show()
|
||||||
val manager = app.gradesManager
|
val manager = app.gradesManager
|
||||||
|
|
||||||
val gradeColor = manager.getColor(grade)
|
val gradeColor = manager.getGradeColor(grade)
|
||||||
b.grade = grade
|
b.grade = grade
|
||||||
b.weightText = manager.getWeightString(app, grade)
|
b.weightText = manager.getWeightString(app, grade)
|
||||||
b.commentVisible = false
|
b.commentVisible = false
|
||||||
b.devMode = App.debugMode
|
b.devMode = App.debugMode
|
||||||
b.gradeName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.3) -0x1000000 else -0x1)
|
b.gradeName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.3) 0xaa000000.toInt() else 0xccffffff.toInt())
|
||||||
b.gradeName.background.setTintColor(gradeColor)
|
b.gradeName.background.setTintColor(gradeColor)
|
||||||
|
|
||||||
b.gradeValue = if (grade.weight == 0f || grade.value < 0f) -1f else manager.getGradeValue(grade)
|
b.gradeValue = if (grade.weight == 0f || grade.value < 0f) -1f else manager.getGradeValue(grade)
|
||||||
|
|
||||||
|
b.customValueDivider.isVisible = manager.plusValue != null || manager.minusValue != null
|
||||||
|
b.customValueLayout.isVisible = b.customValueDivider.isVisible
|
||||||
|
b.customValueButton.onClick {
|
||||||
|
GradesConfigDialog(activity, reloadOnDismiss = true)
|
||||||
|
}
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
val historyList = withContext(Dispatchers.Default) {
|
val historyList = withContext(Dispatchers.Default) {
|
||||||
app.db.gradeDao().getAllWithParentIdNow(App.profileId, grade.id)
|
app.db.gradeDao().getAllWithParentIdNow(App.profileId, grade.id)
|
||||||
|
@ -20,6 +20,7 @@ import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_
|
|||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_SEM
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_SEM
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class GradesConfigDialog(
|
class GradesConfigDialog(
|
||||||
val activity: AppCompatActivity,
|
val activity: AppCompatActivity,
|
||||||
@ -88,12 +89,34 @@ class GradesConfigDialog(
|
|||||||
else -> null
|
else -> null
|
||||||
}?.isChecked = true
|
}?.isChecked = true
|
||||||
|
|
||||||
b.dontCountZeroToAverage.isChecked = !profileConfig.countZeroToAvg
|
b.dontCountGrades.isChecked = profileConfig.dontCountEnabled && profileConfig.dontCountGrades.isNotEmpty()
|
||||||
|
b.hideImproved.isChecked = profileConfig.hideImproved
|
||||||
|
b.averageWithoutWeight.isChecked = profileConfig.averageWithoutWeight
|
||||||
|
|
||||||
|
if (profileConfig.dontCountGrades.isEmpty()) {
|
||||||
|
b.dontCountGradesText.setText("nb, 0, bz, bd")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
b.dontCountGradesText.setText(profileConfig.dontCountGrades.join(", "))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveConfig() {
|
private fun saveConfig() {
|
||||||
profileConfig.plusValue = if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null
|
profileConfig.plusValue = if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null
|
||||||
profileConfig.minusValue = if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null
|
profileConfig.minusValue = if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null
|
||||||
|
|
||||||
|
b.dontCountGradesText.setText(
|
||||||
|
b.dontCountGradesText
|
||||||
|
.text
|
||||||
|
?.toString()
|
||||||
|
?.toLowerCase(Locale.getDefault())
|
||||||
|
?.replace(", ", ",")
|
||||||
|
)
|
||||||
|
profileConfig.dontCountEnabled = b.dontCountGrades.isChecked
|
||||||
|
profileConfig.dontCountGrades = b.dontCountGradesText.text
|
||||||
|
?.split(",")
|
||||||
|
?.map { it.trim() }
|
||||||
|
?: listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
@ -125,6 +148,15 @@ class GradesConfigDialog(
|
|||||||
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM }
|
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM }
|
||||||
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM }
|
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM }
|
||||||
|
|
||||||
b.dontCountZeroToAverage.setOnCheckedChangeListener { _, isChecked -> profileConfig.countZeroToAvg = !isChecked }
|
b.hideImproved.onChange { _, isChecked -> profileConfig.hideImproved = isChecked }
|
||||||
|
b.averageWithoutWeight.onChange { _, isChecked -> profileConfig.averageWithoutWeight = isChecked }
|
||||||
|
|
||||||
|
b.averageWithoutWeightHelp.onClick {
|
||||||
|
MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.grades_config_average_without_weight)
|
||||||
|
.setMessage(R.string.grades_config_average_without_weight_message)
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-3-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.dialogs.sync
|
||||||
|
|
||||||
|
import android.text.Html
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.task.AppSync
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class RegistrationEnableDialog(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val profileId: Int
|
||||||
|
) : CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "RegistrationEnableDialog"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
// local variables go here
|
||||||
|
private var progressDialog: AlertDialog? = null
|
||||||
|
|
||||||
|
init { run {
|
||||||
|
if (activity.isFinishing)
|
||||||
|
return@run
|
||||||
|
app = activity.applicationContext as App
|
||||||
|
}}
|
||||||
|
|
||||||
|
fun showEventShareDialog(onSuccess: (profile: Profile?) -> Unit) {
|
||||||
|
MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.event_manual_need_registration_title)
|
||||||
|
.setMessage(R.string.event_manual_need_registration_text)
|
||||||
|
.setPositiveButton(R.string.ok) { dialog, which ->
|
||||||
|
enableRegistration(onSuccess)
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showEnableDialog(onSuccess: (profile: Profile?) -> Unit) {
|
||||||
|
MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.registration_enable_dialog_title)
|
||||||
|
.setMessage(Html.fromHtml(app.getString(R.string.registration_enable_dialog_text)))
|
||||||
|
.setPositiveButton(R.string.ok) { dialog, which ->
|
||||||
|
enableRegistration(onSuccess)
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun enableRegistration(onSuccess: (profile: Profile?) -> Unit) { launch {
|
||||||
|
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.please_wait)
|
||||||
|
.setMessage(R.string.registration_enable_progress_text)
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
|
|
||||||
|
val profile = withContext(Dispatchers.Default) {
|
||||||
|
val profile = app.db.profileDao().getByIdNow(profileId) ?: return@withContext null
|
||||||
|
profile.registration = Profile.REGISTRATION_ENABLED
|
||||||
|
|
||||||
|
// force full registration of the user
|
||||||
|
App.config.getFor(profile.id).hash = ""
|
||||||
|
|
||||||
|
AppSync(app, mutableListOf(), listOf(profile), SzkolnyApi(app)).run(0L, markAsSeen = true)
|
||||||
|
app.db.profileDao().add(profile)
|
||||||
|
if (profile.id == App.profileId) {
|
||||||
|
App.profile.registration = profile.registration
|
||||||
|
}
|
||||||
|
return@withContext profile
|
||||||
|
}
|
||||||
|
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
onSuccess(profile)
|
||||||
|
}}
|
||||||
|
}
|
@ -18,10 +18,17 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import com.google.android.material.datepicker.MaterialDatePicker
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.greenrobot.eventbus.Subscribe
|
||||||
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskFinishedEvent
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||||
import pl.szczodrzynski.edziennik.databinding.DialogGenerateBlockTimetableBinding
|
import pl.szczodrzynski.edziennik.databinding.DialogGenerateBlockTimetableBinding
|
||||||
@ -53,7 +60,7 @@ class GenerateBlockTimetableDialog(
|
|||||||
|
|
||||||
private val app by lazy { activity.application as App }
|
private val app by lazy { activity.application as App }
|
||||||
|
|
||||||
private lateinit var job: Job
|
private val job: Job = Job()
|
||||||
override val coroutineContext: CoroutineContext
|
override val coroutineContext: CoroutineContext
|
||||||
get() = job + Dispatchers.Main
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
@ -64,11 +71,15 @@ class GenerateBlockTimetableDialog(
|
|||||||
private var showTeachersNames: Boolean = true
|
private var showTeachersNames: Boolean = true
|
||||||
private var noColors: Boolean = false
|
private var noColors: Boolean = false
|
||||||
|
|
||||||
|
private var enqueuedWeekDialog: AlertDialog? = null
|
||||||
|
private var enqueuedWeekStart = Date.getToday()
|
||||||
|
private var enqueuedWeekEnd = Date.getToday()
|
||||||
|
|
||||||
init { run {
|
init { run {
|
||||||
if (activity.isFinishing)
|
if (activity.isFinishing)
|
||||||
return@run
|
return@run
|
||||||
job = Job()
|
|
||||||
onShowListener?.invoke(TAG)
|
onShowListener?.invoke(TAG)
|
||||||
|
EventBus.getDefault().register(this)
|
||||||
|
|
||||||
val weekCurrentStart = Week.getWeekStart()
|
val weekCurrentStart = Week.getWeekStart()
|
||||||
val weekCurrentEnd = Week.getWeekEnd()
|
val weekCurrentEnd = Week.getWeekEnd()
|
||||||
@ -88,39 +99,55 @@ class GenerateBlockTimetableDialog(
|
|||||||
.setTitle(R.string.timetable_generate_range)
|
.setTitle(R.string.timetable_generate_range)
|
||||||
.setView(b.root)
|
.setView(b.root)
|
||||||
.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
||||||
.setPositiveButton(R.string.save) { dialog, _ ->
|
.setPositiveButton(R.string.save, null)
|
||||||
dialog.dismiss()
|
.setOnDismissListener {
|
||||||
when (b.weekSelectionRadioGroup.checkedRadioButtonId) {
|
onDismissListener?.invoke(TAG)
|
||||||
R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd)
|
EventBus.getDefault().unregister(this@GenerateBlockTimetableDialog)
|
||||||
R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd)
|
|
||||||
R.id.forSelectedWeekRadio -> selectDate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.setOnDismissListener { onDismissListener?.invoke(TAG) }
|
|
||||||
.show()
|
.show()
|
||||||
|
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
|
||||||
|
when (b.weekSelectionRadioGroup.checkedRadioButtonId) {
|
||||||
|
R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd)
|
||||||
|
R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd)
|
||||||
|
R.id.forSelectedWeekRadio -> selectDate()
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
private fun selectDate() {
|
private fun selectDate() {
|
||||||
MaterialDatePicker.Builder
|
val date = Date.getToday()
|
||||||
.datePicker()
|
DatePickerDialog
|
||||||
.setSelection(Date.getToday().inMillis)
|
.newInstance({ _, year, monthOfYear, dayOfMonth ->
|
||||||
.build()
|
val dateSelected = Date(year, monthOfYear, dayOfMonth)
|
||||||
|
generateBlockTimetable(dateSelected.weekStart, dateSelected.weekEnd)
|
||||||
|
}, date.year, date.month, date.day)
|
||||||
.apply {
|
.apply {
|
||||||
addOnPositiveButtonClickListener { dateInMillis ->
|
accentColor = R.attr.colorPrimary.resolveAttr(this@GenerateBlockTimetableDialog.activity)
|
||||||
dismiss()
|
show(this@GenerateBlockTimetableDialog.activity.supportFragmentManager, "DatePickerDialog")
|
||||||
val selectedDate = Date.fromMillis(dateInMillis)
|
|
||||||
generateBlockTimetable(selectedDate.weekStart, selectedDate.weekEnd)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.show(activity.supportFragmentManager, "MaterialDatePicker")
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||||
|
if (event.profileId == App.profileId) {
|
||||||
|
enqueuedWeekDialog?.dismiss()
|
||||||
|
generateBlockTimetable(enqueuedWeekStart, enqueuedWeekEnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||||
|
enqueuedWeekDialog?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
|
||||||
|
dialog.dismiss()
|
||||||
|
enqueuedWeekDialog?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateBlockTimetable(weekStart: Date, weekEnd: Date) { launch {
|
private fun generateBlockTimetable(weekStart: Date, weekEnd: Date) { launch {
|
||||||
val progressDialog = MaterialAlertDialogBuilder(activity)
|
|
||||||
.setTitle(R.string.timetable_generate_progress_title)
|
|
||||||
.setMessage(R.string.timetable_generate_progress_text)
|
|
||||||
.show()
|
|
||||||
|
|
||||||
val weekDays = mutableListOf<MutableList<Lesson>>()
|
val weekDays = mutableListOf<MutableList<Lesson>>()
|
||||||
for (i in weekStart.weekDay..weekEnd.weekDay) {
|
for (i in weekStart.weekDay..weekEnd.weekDay) {
|
||||||
weekDays.add(mutableListOf())
|
weekDays.add(mutableListOf())
|
||||||
@ -157,174 +184,212 @@ class GenerateBlockTimetableDialog(
|
|||||||
return@mapNotNull lesson
|
return@mapNotNull lesson
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lessons.isEmpty()) {
|
||||||
|
if (enqueuedWeekDialog != null) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
enqueuedWeekDialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.please_wait)
|
||||||
|
.setMessage(R.string.timetable_syncing_text)
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
|
|
||||||
|
enqueuedWeekStart = weekStart
|
||||||
|
enqueuedWeekEnd = weekEnd
|
||||||
|
|
||||||
|
EdziennikTask.syncProfile(
|
||||||
|
profileId = App.profileId,
|
||||||
|
viewIds = listOf(
|
||||||
|
MainActivity.DRAWER_ITEM_TIMETABLE to 0
|
||||||
|
),
|
||||||
|
arguments = JsonObject(
|
||||||
|
"weekStart" to weekStart.stringY_m_d
|
||||||
|
)
|
||||||
|
).enqueue(activity)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val progressDialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.timetable_generate_progress_title)
|
||||||
|
.setMessage(R.string.timetable_generate_progress_text)
|
||||||
|
.show()
|
||||||
|
|
||||||
if (minTime == null) {
|
if (minTime == null) {
|
||||||
progressDialog.dismiss()
|
progressDialog.dismiss()
|
||||||
// TODO: Toast
|
// TODO: Toast
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
val diff = Time.diff(maxTime, minTime)
|
dialog.dismiss()
|
||||||
|
|
||||||
val imageWidth = WIDTH_CONSTANT + maxWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING) - WIDTH_SPACING
|
val uri = withContext(Dispatchers.Default) {
|
||||||
val imageHeight = heightProfileName + HEIGHT_CONSTANT + diff.inMinutes * HEIGHT_MINUTE + HEIGHT_FOOTER
|
|
||||||
val bitmap = Bitmap.createBitmap(imageWidth + 20, imageHeight + 30, Bitmap.Config.ARGB_8888)
|
|
||||||
val canvas = Canvas(bitmap)
|
|
||||||
|
|
||||||
if (noColors) canvas.drawARGB(255, 255, 255, 255)
|
val diff = Time.diff(maxTime, minTime)
|
||||||
else canvas.drawARGB(255, 225, 225, 225)
|
|
||||||
|
|
||||||
val paint = Paint().apply {
|
val imageWidth = WIDTH_CONSTANT + maxWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING) - WIDTH_SPACING
|
||||||
isAntiAlias = true
|
val imageHeight = heightProfileName + HEIGHT_CONSTANT + diff.inMinutes * HEIGHT_MINUTE + HEIGHT_FOOTER
|
||||||
isFilterBitmap = true
|
val bitmap = Bitmap.createBitmap(imageWidth + 20, imageHeight + 30, Bitmap.Config.ARGB_8888)
|
||||||
isDither = true
|
val canvas = Canvas(bitmap)
|
||||||
}
|
|
||||||
|
|
||||||
lessons.forEach { lesson ->
|
if (noColors) canvas.drawARGB(255, 255, 255, 255)
|
||||||
val lessonLength = Time.diff(lesson.displayEndTime, lesson.displayStartTime)
|
else canvas.drawARGB(255, 225, 225, 225)
|
||||||
val firstOffset = Time.diff(lesson.displayStartTime, minTime)
|
|
||||||
val lessonWeekDay = lesson.displayDate!!.weekDay
|
|
||||||
|
|
||||||
val left = WIDTH_CONSTANT + lessonWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING)
|
val paint = Paint().apply {
|
||||||
val top = heightProfileName + HEIGHT_CONSTANT + firstOffset.inMinutes * HEIGHT_MINUTE
|
isAntiAlias = true
|
||||||
|
isFilterBitmap = true
|
||||||
val blockWidth = WIDTH_WEEKDAY
|
isDither = true
|
||||||
val blockHeight = lessonLength.inMinutes * HEIGHT_MINUTE
|
|
||||||
|
|
||||||
val viewWidth = 380.dp
|
|
||||||
val viewHeight = lessonLength.inMinutes * 4.dp
|
|
||||||
|
|
||||||
val layout = activity.layoutInflater.inflate(R.layout.row_timetable_block_item, null) as LinearLayout
|
|
||||||
|
|
||||||
val item: LinearLayout = layout.findViewById(R.id.timetableItemLayout)
|
|
||||||
val card: CardView = layout.findViewById(R.id.timetableItemCard)
|
|
||||||
val subjectName: TextView = layout.findViewById(R.id.timetableItemSubjectName)
|
|
||||||
val classroomName: TextView = layout.findViewById(R.id.timetableItemClassroomName)
|
|
||||||
val teacherName: TextView = layout.findViewById(R.id.timetableItemTeacherName)
|
|
||||||
val teamName: TextView = layout.findViewById(R.id.timetableItemTeamName)
|
|
||||||
|
|
||||||
if (noColors) {
|
|
||||||
card.setCardBackgroundColor(Color.WHITE)
|
|
||||||
card.cardElevation = 0f
|
|
||||||
item.setBackgroundResource(R.drawable.bg_rounded_16dp_outline)
|
|
||||||
subjectName.setTextColor(Color.BLACK)
|
|
||||||
classroomName.setTextColor(0xffaaaaaa.toInt())
|
|
||||||
teacherName.setTextColor(0xffaaaaaa.toInt())
|
|
||||||
teamName.setTextColor(0xffaaaaaa.toInt())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subjectName.text = lesson.displaySubjectName ?: ""
|
lessons.forEach { lesson ->
|
||||||
classroomName.text = lesson.displayClassroom ?: ""
|
val lessonLength = Time.diff(lesson.displayEndTime, lesson.displayStartTime)
|
||||||
teacherName.text = lesson.displayTeacherName ?: ""
|
val firstOffset = Time.diff(lesson.displayStartTime, minTime)
|
||||||
teamName.text = lesson.displayTeamName ?: ""
|
val lessonWeekDay = lesson.displayDate!!.weekDay
|
||||||
|
|
||||||
if (!showTeachersNames) teacherName.visibility = View.GONE
|
val left = WIDTH_CONSTANT + lessonWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING)
|
||||||
|
val top = heightProfileName + HEIGHT_CONSTANT + firstOffset.inMinutes * HEIGHT_MINUTE
|
||||||
|
|
||||||
when (lesson.type) {
|
val blockWidth = WIDTH_WEEKDAY
|
||||||
Lesson.TYPE_NORMAL -> {}
|
val blockHeight = lessonLength.inMinutes * HEIGHT_MINUTE
|
||||||
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
|
|
||||||
card.setCardBackgroundColor(Color.BLACK)
|
val viewWidth = 380.dp
|
||||||
subjectName.setTextColor(Color.WHITE)
|
val viewHeight = lessonLength.inMinutes * 4.dp
|
||||||
subjectName.text = lesson.displaySubjectName?.asStrikethroughSpannable() ?: ""
|
|
||||||
|
val layout = activity.layoutInflater.inflate(R.layout.row_timetable_block_item, null) as LinearLayout
|
||||||
|
|
||||||
|
val item: LinearLayout = layout.findViewById(R.id.timetableItemLayout)
|
||||||
|
val card: CardView = layout.findViewById(R.id.timetableItemCard)
|
||||||
|
val subjectName: TextView = layout.findViewById(R.id.timetableItemSubjectName)
|
||||||
|
val classroomName: TextView = layout.findViewById(R.id.timetableItemClassroomName)
|
||||||
|
val teacherName: TextView = layout.findViewById(R.id.timetableItemTeacherName)
|
||||||
|
val teamName: TextView = layout.findViewById(R.id.timetableItemTeamName)
|
||||||
|
|
||||||
|
if (noColors) {
|
||||||
|
card.setCardBackgroundColor(Color.WHITE)
|
||||||
|
card.cardElevation = 0f
|
||||||
|
item.setBackgroundResource(R.drawable.bg_rounded_16dp_outline)
|
||||||
|
subjectName.setTextColor(Color.BLACK)
|
||||||
|
classroomName.setTextColor(0xffaaaaaa.toInt())
|
||||||
|
teacherName.setTextColor(0xffaaaaaa.toInt())
|
||||||
|
teamName.setTextColor(0xffaaaaaa.toInt())
|
||||||
}
|
}
|
||||||
else -> {
|
|
||||||
card.setCardBackgroundColor(0xff234158.toInt())
|
subjectName.text = lesson.displaySubjectName ?: ""
|
||||||
subjectName.setTextColor(Color.WHITE)
|
classroomName.text = lesson.displayClassroom ?: ""
|
||||||
subjectName.setTypeface(null, Typeface.BOLD_ITALIC)
|
teacherName.text = lesson.displayTeacherName ?: ""
|
||||||
|
teamName.text = lesson.displayTeamName ?: ""
|
||||||
|
|
||||||
|
if (!showTeachersNames) teacherName.visibility = View.GONE
|
||||||
|
|
||||||
|
when (lesson.type) {
|
||||||
|
Lesson.TYPE_NORMAL -> {
|
||||||
|
}
|
||||||
|
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||||
|
card.setCardBackgroundColor(Color.BLACK)
|
||||||
|
subjectName.setTextColor(Color.WHITE)
|
||||||
|
subjectName.text = lesson.displaySubjectName?.asStrikethroughSpannable()
|
||||||
|
?: ""
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
card.setCardBackgroundColor(0xff234158.toInt())
|
||||||
|
subjectName.setTextColor(Color.WHITE)
|
||||||
|
subjectName.setTypeface(null, Typeface.BOLD_ITALIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.isDrawingCacheEnabled = true
|
||||||
|
layout.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY))
|
||||||
|
layout.layout(0, 0, layout.measuredWidth, layout.measuredHeight)
|
||||||
|
layout.buildDrawingCache(true)
|
||||||
|
|
||||||
|
val itemBitmap = layout.drawingCache
|
||||||
|
canvas.drawBitmap(itemBitmap, null, Rect(left, top, left + blockWidth, top + blockHeight), paint)
|
||||||
|
}
|
||||||
|
|
||||||
|
val textPaint = Paint().apply {
|
||||||
|
setARGB(255, 0, 0, 0)
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
textSize = 30f
|
||||||
|
isAntiAlias = true
|
||||||
|
isFilterBitmap = true
|
||||||
|
isDither = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (w in 0..maxWeekDay) {
|
||||||
|
val x = WIDTH_CONSTANT + w * WIDTH_WEEKDAY + w * WIDTH_SPACING
|
||||||
|
canvas.drawText(Week.getFullDayName(w), x + (WIDTH_WEEKDAY / 2f), heightProfileName + HEIGHT_CONSTANT / 2 + 10f, textPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showProfileName) {
|
||||||
|
textPaint.textSize = 50f
|
||||||
|
canvas.drawText("${app.profile.name} - plan lekcji, ${weekStart.formattedStringShort} - ${weekEnd.formattedStringShort}", (imageWidth + 20) / 2f, 80f, textPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
textPaint.apply {
|
||||||
|
setARGB(128, 0, 0, 0)
|
||||||
|
textAlign = Paint.Align.RIGHT
|
||||||
|
textSize = 26f
|
||||||
|
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
|
||||||
|
}
|
||||||
|
|
||||||
|
val footerTextPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
||||||
|
canvas.drawText("Wygenerowano w aplikacji Szkolny.eu", imageWidth - 10f, imageHeight - footerTextPaintCenter - 10f, textPaint)
|
||||||
|
|
||||||
|
textPaint.apply {
|
||||||
|
setARGB(255, 127, 127, 127)
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
textSize = 16f
|
||||||
|
typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
val textPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
||||||
|
|
||||||
|
val linePaint = Paint().apply {
|
||||||
|
setARGB(255, 100, 100, 100)
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
pathEffect = DashPathEffect(floatArrayOf(10f, 10f), 0f)
|
||||||
|
isAntiAlias = true
|
||||||
|
isFilterBitmap = true
|
||||||
|
isDither = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val minTimeInt = ((minTime!!.value / 10000) * 60) + ((minTime!!.value / 100) % 100)
|
||||||
|
|
||||||
|
lessonRanges.forEach { (startTime, endTime) ->
|
||||||
|
listOf(startTime, endTime).forEach { value ->
|
||||||
|
val hour = value / 10000
|
||||||
|
val minute = (value / 100) % 100
|
||||||
|
val time = Time(hour, minute, 0)
|
||||||
|
|
||||||
|
val firstOffset = time.inMinutes - minTimeInt // offset in minutes
|
||||||
|
val top = (heightProfileName + HEIGHT_CONSTANT + firstOffset * HEIGHT_MINUTE).toFloat()
|
||||||
|
|
||||||
|
canvas.drawText(time.stringHM, WIDTH_CONSTANT / 2f, top - textPaintCenter, textPaint)
|
||||||
|
canvas.drawLine(WIDTH_CONSTANT.toFloat(), top, imageWidth.toFloat(), top, linePaint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.isDrawingCacheEnabled = true
|
val today = Date.getToday().stringY_m_d
|
||||||
layout.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
|
val now = Time.getNow().stringH_M_S
|
||||||
MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY))
|
|
||||||
layout.layout(0, 0, layout.measuredWidth, layout.measuredHeight)
|
|
||||||
layout.buildDrawingCache(true)
|
|
||||||
|
|
||||||
val itemBitmap = layout.drawingCache
|
val outputDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu").apply { mkdirs() }
|
||||||
canvas.drawBitmap(itemBitmap, null, Rect(left, top, left + blockWidth, top + blockHeight), paint)
|
val outputFile = File(outputDir, "plan_lekcji_${app.profile.name}_${today}_${now}.png")
|
||||||
}
|
|
||||||
|
|
||||||
val textPaint = Paint().apply {
|
try {
|
||||||
setARGB(255, 0, 0, 0)
|
val fos = FileOutputStream(outputFile)
|
||||||
textAlign = Paint.Align.CENTER
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
|
||||||
textSize = 30f
|
fos.close()
|
||||||
isAntiAlias = true
|
} catch (e: Exception) {
|
||||||
isFilterBitmap = true
|
Log.e("SAVE_IMAGE", e.message, e)
|
||||||
isDither = true
|
return@withContext null
|
||||||
}
|
|
||||||
|
|
||||||
for (w in 0..maxWeekDay) {
|
|
||||||
val x = WIDTH_CONSTANT + w * WIDTH_WEEKDAY + w * WIDTH_SPACING
|
|
||||||
canvas.drawText(Week.getFullDayName(w), x + (WIDTH_WEEKDAY / 2f), heightProfileName + HEIGHT_CONSTANT / 2 + 10f, textPaint)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showProfileName) {
|
|
||||||
textPaint.textSize = 50f
|
|
||||||
canvas.drawText("${app.profile.name} - plan lekcji, ${weekStart.formattedStringShort} - ${weekEnd.formattedStringShort}", (imageWidth + 20) / 2f, 80f, textPaint)
|
|
||||||
}
|
|
||||||
|
|
||||||
textPaint.apply {
|
|
||||||
setARGB(128, 0, 0, 0)
|
|
||||||
textAlign = Paint.Align.RIGHT
|
|
||||||
textSize = 26f
|
|
||||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
|
|
||||||
}
|
|
||||||
|
|
||||||
val footerTextPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
|
||||||
canvas.drawText("Wygenerowano w aplikacji Szkolny.eu", imageWidth - 10f, imageHeight - footerTextPaintCenter - 10f, textPaint)
|
|
||||||
|
|
||||||
textPaint.apply {
|
|
||||||
setARGB(255, 127, 127, 127)
|
|
||||||
textAlign = Paint.Align.CENTER
|
|
||||||
textSize = 16f
|
|
||||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
val textPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
|
||||||
|
|
||||||
val linePaint = Paint().apply {
|
|
||||||
setARGB(255, 100, 100, 100)
|
|
||||||
style = Paint.Style.STROKE
|
|
||||||
pathEffect = DashPathEffect(floatArrayOf(10f, 10f), 0f)
|
|
||||||
isAntiAlias = true
|
|
||||||
isFilterBitmap = true
|
|
||||||
isDither = true
|
|
||||||
}
|
|
||||||
|
|
||||||
val minTimeInt = ((minTime!!.value / 10000) * 60) + ((minTime!!.value / 100) % 100)
|
|
||||||
|
|
||||||
lessonRanges.forEach { (startTime, endTime) ->
|
|
||||||
listOf(startTime, endTime).forEach { value ->
|
|
||||||
val hour = value / 10000
|
|
||||||
val minute = (value / 100) % 100
|
|
||||||
val time = Time(hour, minute, 0)
|
|
||||||
|
|
||||||
val firstOffset = time.inMinutes - minTimeInt // offset in minutes
|
|
||||||
val top = (heightProfileName + HEIGHT_CONSTANT + firstOffset * HEIGHT_MINUTE).toFloat()
|
|
||||||
|
|
||||||
canvas.drawText(time.stringHM, WIDTH_CONSTANT / 2f, top - textPaintCenter, textPaint)
|
|
||||||
canvas.drawLine(WIDTH_CONSTANT.toFloat(), top, imageWidth.toFloat(), top, linePaint)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val today = Date.getToday().stringY_m_d
|
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
val now = Time.getNow().stringH_M_S
|
FileProvider.getUriForFile(activity, app.packageName + ".provider", outputFile)
|
||||||
|
} else {
|
||||||
val outputDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu").apply { mkdirs() }
|
Uri.parse("file://" + outputFile.absolutePath)
|
||||||
val outputFile = File(outputDir, "plan_lekcji_${app.profile.name}_${today}_${now}.png")
|
}
|
||||||
|
uri
|
||||||
try {
|
|
||||||
val fos = FileOutputStream(outputFile)
|
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
|
|
||||||
fos.close()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("SAVE_IMAGE", e.message, e)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
FileProvider.getUriForFile(activity, app.packageName + ".provider", outputFile)
|
|
||||||
} else {
|
|
||||||
Uri.parse("file://" + outputFile.absolutePath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progressDialog.dismiss()
|
progressDialog.dismiss()
|
||||||
|
@ -25,7 +25,6 @@ import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
|||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableUtils
|
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||||
@ -50,7 +49,7 @@ class LessonDetailsDialog(
|
|||||||
get() = job + Dispatchers.Main
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
private lateinit var adapter: EventListAdapter
|
private lateinit var adapter: EventListAdapter
|
||||||
private val utils by lazy { TimetableUtils() }
|
private val manager by lazy { app.timetableManager }
|
||||||
|
|
||||||
init { run {
|
init { run {
|
||||||
if (activity.isFinishing)
|
if (activity.isFinishing)
|
||||||
@ -91,7 +90,7 @@ class LessonDetailsDialog(
|
|||||||
val lessonTime = lesson.displayStartTime ?: return
|
val lessonTime = lesson.displayStartTime ?: return
|
||||||
b.lessonDate.text = Week.getFullDayName(lessonDate.weekDay) + ", " + lessonDate.formattedString
|
b.lessonDate.text = Week.getFullDayName(lessonDate.weekDay) + ", " + lessonDate.formattedString
|
||||||
|
|
||||||
b.annotationVisible = utils.getAnnotation(activity, lesson, b.annotation)
|
b.annotationVisible = manager.getAnnotation(activity, lesson, b.annotation)
|
||||||
|
|
||||||
if (lesson.type >= Lesson.TYPE_SHIFTED_SOURCE) {
|
if (lesson.type >= Lesson.TYPE_SHIFTED_SOURCE) {
|
||||||
b.shiftedLayout.visibility = View.VISIBLE
|
b.shiftedLayout.visibility = View.VISIBLE
|
||||||
|
@ -126,6 +126,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createDefaultAgendaView() { (b as? FragmentAgendaDefaultBinding)?.let { b -> launch {
|
private fun createDefaultAgendaView() { (b as? FragmentAgendaDefaultBinding)?.let { b -> launch {
|
||||||
|
if (!isAdded)
|
||||||
|
return@launch
|
||||||
delay(500)
|
delay(500)
|
||||||
|
|
||||||
val eventList = mutableListOf<CalendarEvent>()
|
val eventList = mutableListOf<CalendarEvent>()
|
||||||
@ -139,6 +141,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
|||||||
/**
|
/**
|
||||||
* LESSON CHANGES
|
* LESSON CHANGES
|
||||||
*/
|
*/
|
||||||
|
if (!isAdded)
|
||||||
|
return@launch
|
||||||
|
|
||||||
val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getAllChangesNow(app.profileId) }
|
val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getAllChangesNow(app.profileId) }
|
||||||
val lessonChangeCounters = mutableListOf<LessonChangeCounter>()
|
val lessonChangeCounters = mutableListOf<LessonChangeCounter>()
|
||||||
@ -170,6 +174,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
|||||||
/**
|
/**
|
||||||
* TEACHER ABSENCES
|
* TEACHER ABSENCES
|
||||||
*/
|
*/
|
||||||
|
if (!isAdded)
|
||||||
|
return@launch
|
||||||
|
|
||||||
val showTeacherAbsences = app.profile.getStudentData("showTeacherAbsences", true)
|
val showTeacherAbsences = app.profile.getStudentData("showTeacherAbsences", true)
|
||||||
|
|
||||||
@ -208,6 +214,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
|||||||
/**
|
/**
|
||||||
* EVENTS
|
* EVENTS
|
||||||
*/
|
*/
|
||||||
|
if (!isAdded)
|
||||||
|
return@launch
|
||||||
|
|
||||||
val events = withContext(Dispatchers.Default) { app.db.eventDao().getAllNow(app.profileId) }
|
val events = withContext(Dispatchers.Default) { app.db.eventDao().getAllNow(app.profileId) }
|
||||||
val unreadEventDates = mutableSetOf<Int>()
|
val unreadEventDates = mutableSetOf<Int>()
|
||||||
|
@ -33,6 +33,7 @@ import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration;
|
|||||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
||||||
|
|
||||||
|
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||||
import static pl.szczodrzynski.edziennik.data.db.entity.LoginStore.LOGIN_TYPE_LIBRUS;
|
import static pl.szczodrzynski.edziennik.data.db.entity.LoginStore.LOGIN_TYPE_LIBRUS;
|
||||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ANNOUNCEMENT;
|
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ANNOUNCEMENT;
|
||||||
|
|
||||||
@ -90,6 +91,18 @@ public class AnnouncementsFragment extends Fragment {
|
|||||||
recyclerView.setLayoutManager(linearLayoutManager);
|
recyclerView.setLayoutManager(linearLayoutManager);
|
||||||
recyclerView.addItemDecoration(new SimpleDividerItemDecoration(view.getContext()));
|
recyclerView.addItemDecoration(new SimpleDividerItemDecoration(view.getContext()));
|
||||||
|
|
||||||
|
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
||||||
|
if (recyclerView.canScrollVertically(-1)) {
|
||||||
|
b.refreshLayout.setEnabled(false);
|
||||||
|
}
|
||||||
|
if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
|
||||||
|
b.refreshLayout.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.db.announcementDao().getAll(App.Companion.getProfileId()).observe(this, announcements -> {
|
app.db.announcementDao().getAll(App.Companion.getProfileId()).observe(this, announcements -> {
|
||||||
if (app == null || activity == null || b == null || !isAdded())
|
if (app == null || activity == null || b == null || !isAdded())
|
||||||
return;
|
return;
|
||||||
|
@ -21,6 +21,7 @@ import androidx.core.graphics.ColorUtils;
|
|||||||
import androidx.databinding.DataBindingUtil;
|
import androidx.databinding.DataBindingUtil;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration;
|
|||||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
||||||
|
|
||||||
|
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||||
import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT;
|
import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT;
|
||||||
import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT_EXCUSED;
|
import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT_EXCUSED;
|
||||||
import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_BELATED;
|
import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_BELATED;
|
||||||
@ -181,6 +183,18 @@ public class AttendanceFragment extends Fragment {
|
|||||||
b.attendanceView.setLayoutManager(linearLayoutManager);
|
b.attendanceView.setLayoutManager(linearLayoutManager);
|
||||||
b.attendanceView.addItemDecoration(new SimpleDividerItemDecoration(getContext()));
|
b.attendanceView.addItemDecoration(new SimpleDividerItemDecoration(getContext()));
|
||||||
|
|
||||||
|
b.attendanceView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
||||||
|
if (recyclerView.canScrollVertically(-1)) {
|
||||||
|
b.refreshLayout.setEnabled(false);
|
||||||
|
}
|
||||||
|
if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
|
||||||
|
b.refreshLayout.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
App.db.attendanceDao().getAll(App.Companion.getProfileId()).observe(this, attendance -> {
|
App.db.attendanceDao().getAll(App.Companion.getProfileId()).observe(this, attendance -> {
|
||||||
if (app == null || activity == null || b == null || !isAdded())
|
if (app == null || activity == null || b == null || !isAdded())
|
||||||
return;
|
return;
|
||||||
|
@ -40,6 +40,7 @@ class MainSnackbar(val activity: AppCompatActivity) {
|
|||||||
setAction(actionText) {
|
setAction(actionText) {
|
||||||
onClick?.invoke()
|
onClick?.invoke()
|
||||||
}
|
}
|
||||||
|
duration = 7000
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-22.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.base
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
|
||||||
|
abstract class PagerFragment : Fragment() {
|
||||||
|
private var isPageCreated = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the page is first shown, or if previous
|
||||||
|
* [onPageCreated] returned false
|
||||||
|
*
|
||||||
|
* @return true if the view is set up
|
||||||
|
* @return false if the setup failed. The method may be then called
|
||||||
|
* again, when page becomes visible.
|
||||||
|
*/
|
||||||
|
abstract fun onPageCreated(): Boolean
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
if (!isPageCreated) {
|
||||||
|
isPageCreated = onPageCreated()
|
||||||
|
}
|
||||||
|
super.onResume()
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import androidx.appcompat.widget.PopupMenu;
|
|||||||
import androidx.databinding.DataBindingUtil;
|
import androidx.databinding.DataBindingUtil;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ import pl.szczodrzynski.edziennik.databinding.FragmentBehaviourBinding;
|
|||||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
||||||
|
|
||||||
|
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_NOTICE;
|
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_NOTICE;
|
||||||
|
|
||||||
public class BehaviourFragment extends Fragment {
|
public class BehaviourFragment extends Fragment {
|
||||||
@ -97,6 +99,18 @@ public class BehaviourFragment extends Fragment {
|
|||||||
b.noticesView.setHasFixedSize(true);
|
b.noticesView.setHasFixedSize(true);
|
||||||
b.noticesView.setLayoutManager(linearLayoutManager);
|
b.noticesView.setLayoutManager(linearLayoutManager);
|
||||||
|
|
||||||
|
b.noticesView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
||||||
|
if (recyclerView.canScrollVertically(-1)) {
|
||||||
|
b.refreshLayout.setEnabled(false);
|
||||||
|
}
|
||||||
|
if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
|
||||||
|
b.refreshLayout.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.db.noticeDao().getAll(App.Companion.getProfileId()).observe(this, notices -> {
|
app.db.noticeDao().getAll(App.Companion.getProfileId()).observe(this, notices -> {
|
||||||
if (app == null || activity == null || b == null || !isAdded())
|
if (app == null || activity == null || b == null || !isAdded())
|
||||||
return;
|
return;
|
||||||
|
@ -48,6 +48,7 @@ class ErrorSnackbar(val activity: AppCompatActivity) : CoroutineScope {
|
|||||||
fun addError(apiError: ApiError): ErrorSnackbar {
|
fun addError(apiError: ApiError): ErrorSnackbar {
|
||||||
errors.add(apiError)
|
errors.add(apiError)
|
||||||
snackbar?.setText(apiError.getStringReason(activity))
|
snackbar?.setText(apiError.getStringReason(activity))
|
||||||
|
snackbar?.duration = 15000
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
package pl.szczodrzynski.edziennik.ui.modules.feedback;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.databinding.DataBindingUtil;
|
|
||||||
|
|
||||||
import pl.szczodrzynski.edziennik.App;
|
|
||||||
import pl.szczodrzynski.edziennik.R;
|
|
||||||
import pl.szczodrzynski.edziennik.databinding.ActivityFeedbackBinding;
|
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
|
||||||
|
|
||||||
public class FeedbackActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
private static final String TAG = "FeedbackActivity";
|
|
||||||
private App app;
|
|
||||||
private ActivityFeedbackBinding b;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setTheme(Themes.INSTANCE.getAppTheme());
|
|
||||||
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_feedback, null, false);
|
|
||||||
setContentView(b.getRoot());
|
|
||||||
app = (App) getApplication();
|
|
||||||
|
|
||||||
setSupportActionBar(b.toolbar);
|
|
||||||
if (getSupportActionBar() != null)
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == android.R.id.home) // Press Back Icon
|
|
||||||
{
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,25 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.ui.modules.feedback
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Themes.appTheme
|
||||||
|
|
||||||
|
class FeedbackActivity : AppCompatActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setTheme(appTheme)
|
||||||
|
setContentView(R.layout.activity_feedback)
|
||||||
|
|
||||||
|
val transaction = supportFragmentManager.beginTransaction()
|
||||||
|
transaction.replace(R.id.feedbackFragment, FeedbackFragment())
|
||||||
|
transaction.commitAllowingStateLoss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == android.R.id.home)
|
||||||
|
finish()
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import coil.Coil
|
import coil.Coil
|
||||||
import coil.api.load
|
import coil.api.load
|
||||||
@ -22,15 +23,26 @@ import kotlinx.coroutines.*
|
|||||||
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.crc16
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.FeedbackMessageEvent
|
import pl.szczodrzynski.edziennik.data.api.events.FeedbackMessageEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage
|
import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentFeedbackBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentFeedbackBinding
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils
|
import pl.szczodrzynski.edziennik.utils.Utils
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.openUrl
|
import pl.szczodrzynski.edziennik.utils.Utils.openUrl
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.List
|
||||||
|
import kotlin.collections.any
|
||||||
|
import kotlin.collections.filter
|
||||||
|
import kotlin.collections.firstOrNull
|
||||||
|
import kotlin.collections.forEach
|
||||||
|
import kotlin.collections.forEachIndexed
|
||||||
|
import kotlin.collections.isNotEmpty
|
||||||
|
import kotlin.collections.mutableMapOf
|
||||||
|
import kotlin.collections.set
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class FeedbackFragment : Fragment(), CoroutineScope {
|
class FeedbackFragment : Fragment(), CoroutineScope {
|
||||||
@ -39,7 +51,7 @@ class FeedbackFragment : Fragment(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var app: App
|
private lateinit var app: App
|
||||||
private lateinit var activity: MainActivity
|
private lateinit var activity: AppCompatActivity
|
||||||
private lateinit var b: FragmentFeedbackBinding
|
private lateinit var b: FragmentFeedbackBinding
|
||||||
|
|
||||||
private val job: Job = Job()
|
private val job: Job = Job()
|
||||||
@ -54,11 +66,10 @@ class FeedbackFragment : Fragment(), CoroutineScope {
|
|||||||
private var receiver: BroadcastReceiver? = null
|
private var receiver: BroadcastReceiver? = null
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
activity = (getActivity() as MainActivity?) ?: return null
|
activity = (getActivity() as AppCompatActivity?) ?: return null
|
||||||
if (context == null)
|
if (context == null)
|
||||||
return null
|
return null
|
||||||
app = activity.application as App
|
app = activity.application as App
|
||||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
|
||||||
// activity, context and profile is valid
|
// activity, context and profile is valid
|
||||||
b = FragmentFeedbackBinding.inflate(inflater)
|
b = FragmentFeedbackBinding.inflate(inflater)
|
||||||
// prevent doubled received messages on enter
|
// prevent doubled received messages on enter
|
||||||
@ -239,7 +250,7 @@ class FeedbackFragment : Fragment(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
val message = api.runCatching(activity.errorSnackbar) {
|
val message = api.runCatching(activity) {
|
||||||
val message = api.sendFeedbackMessage(
|
val message = api.sendFeedbackMessage(
|
||||||
senderName = App.profile.accountName ?: App.profile.studentNameLong,
|
senderName = App.profile.accountName ?: App.profile.studentNameLong,
|
||||||
targetDeviceId = if (isDev) currentDeviceId else null,
|
targetDeviceId = if (isDev) currentDeviceId else null,
|
||||||
|
@ -6,7 +6,6 @@ package pl.szczodrzynski.edziennik.ui.modules.grades
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.TypedValue.COMPLEX_UNIT_SP
|
import android.util.TypedValue.COMPLEX_UNIT_SP
|
||||||
@ -48,7 +47,7 @@ class GradeView : AppCompatTextView {
|
|||||||
|
|
||||||
val gradeName = grade.name
|
val gradeName = grade.name
|
||||||
|
|
||||||
val gradeColor = manager.getColor(grade)
|
val gradeColor = manager.getGradeColor(grade)
|
||||||
|
|
||||||
text = if (periodGradesTextual)
|
text = if (periodGradesTextual)
|
||||||
when (grade.type) {
|
when (grade.type) {
|
||||||
@ -78,12 +77,12 @@ class GradeView : AppCompatTextView {
|
|||||||
TYPE_SEMESTER2_PROPOSED,
|
TYPE_SEMESTER2_PROPOSED,
|
||||||
TYPE_YEAR_PROPOSED -> android.R.attr.textColorPrimary.resolveAttr(context)
|
TYPE_YEAR_PROPOSED -> android.R.attr.textColorPrimary.resolveAttr(context)
|
||||||
else -> if (ColorUtils.calculateLuminance(gradeColor) > 0.3)
|
else -> if (ColorUtils.calculateLuminance(gradeColor) > 0.3)
|
||||||
0x99000000.toInt()
|
0xaa000000.toInt()
|
||||||
else
|
else
|
||||||
0x99ffffff.toInt()
|
0xccffffff.toInt()
|
||||||
})
|
})
|
||||||
|
|
||||||
typeface = Typeface.create("serif-monospace", Typeface.BOLD)
|
//typeface = Typeface.create("sans-serif-light", Typeface.NORMAL)
|
||||||
setBackgroundResource(when (grade.type) {
|
setBackgroundResource(when (grade.type) {
|
||||||
TYPE_SEMESTER1_PROPOSED,
|
TYPE_SEMESTER1_PROPOSED,
|
||||||
TYPE_SEMESTER2_PROPOSED,
|
TYPE_SEMESTER2_PROPOSED,
|
||||||
@ -104,7 +103,7 @@ class GradeView : AppCompatTextView {
|
|||||||
setPadding(2.dp, 2.dp, 2.dp, 2.dp)
|
setPadding(2.dp, 2.dp, 2.dp, 2.dp)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setTextSize(COMPLEX_UNIT_SP, 16f)
|
setTextSize(COMPLEX_UNIT_SP, 14f)
|
||||||
setPadding(5.dp, 0, 5.dp, 0)
|
setPadding(5.dp, 0, 5.dp, 0)
|
||||||
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
||||||
setMargins(0, 0, 5.dp, 0)
|
setMargins(0, 0, 5.dp, 0)
|
||||||
|
@ -10,19 +10,24 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
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.db.entity.Grade
|
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||||
import pl.szczodrzynski.edziennik.onClick
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
|
import pl.szczodrzynski.edziennik.startCoroutineTimer
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.*
|
import pl.szczodrzynski.edziennik.ui.modules.grades.models.*
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.*
|
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.*
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class GradesAdapter(
|
class GradesAdapter(
|
||||||
val activity: AppCompatActivity,
|
val activity: AppCompatActivity,
|
||||||
var onGradeClick: ((item: GradeFull) -> Unit)? = null,
|
var onGradeClick: ((item: GradeFull) -> Unit)? = null,
|
||||||
var onGradesEditorClick: ((subject: GradesSubject, semester: GradesSemester) -> Unit)? = null
|
var onGradesEditorClick: ((subject: GradesSubject, semester: GradesSemester) -> Unit)? = null
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), CoroutineScope {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "GradesAdapter"
|
private const val TAG = "GradesAdapter"
|
||||||
private const val ITEM_TYPE_SUBJECT = 0
|
private const val ITEM_TYPE_SUBJECT = 0
|
||||||
@ -34,6 +39,13 @@ class GradesAdapter(
|
|||||||
const val STATE_OPENED = 1
|
const val STATE_OPENED = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val app = activity.applicationContext as App
|
||||||
|
private val manager = app.gradesManager
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
var items = mutableListOf<Any>()
|
var items = mutableListOf<Any>()
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
@ -67,56 +79,63 @@ class GradesAdapter(
|
|||||||
}
|
}
|
||||||
if (model !is ExpandableItemModel<*>)
|
if (model !is ExpandableItemModel<*>)
|
||||||
return@OnClickListener
|
return@OnClickListener
|
||||||
|
expandModel(model, view)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expandModel(model: ExpandableItemModel<*>?, view: View?, notifyAdapter: Boolean = true) {
|
||||||
|
model ?: return
|
||||||
val position = items.indexOf(model)
|
val position = items.indexOf(model)
|
||||||
if (position == -1)
|
if (position == -1)
|
||||||
return@OnClickListener
|
return
|
||||||
//val position = it.getTag(R.string.tag_key_position) as? Int ?: return@OnClickListener
|
|
||||||
|
|
||||||
if (model is GradesSubject || model is GradesSemester) {
|
if (model is GradesSubject || model is GradesSemester) {
|
||||||
view.findViewById<View>(R.id.dropdownIcon)?.let { dropdownIcon ->
|
view?.findViewById<View>(R.id.dropdownIcon)?.let { dropdownIcon ->
|
||||||
ObjectAnimator.ofFloat(
|
ObjectAnimator.ofFloat(
|
||||||
dropdownIcon,
|
dropdownIcon,
|
||||||
View.ROTATION,
|
View.ROTATION,
|
||||||
if (model.state == STATE_CLOSED) 0f else 180f,
|
if (model.state == STATE_CLOSED) 0f else 180f,
|
||||||
if (model.state == STATE_CLOSED) 180f else 0f
|
if (model.state == STATE_CLOSED) 180f else 0f
|
||||||
).setDuration(200).start();
|
).setDuration(200).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (model is GradesSubject) {
|
if (model is GradesSubject) {
|
||||||
val preview = view.findViewById<View>(R.id.previewContainer)
|
val preview = view?.findViewById<View>(R.id.previewContainer)
|
||||||
val summary = view.findViewById<View>(R.id.yearSummary)
|
val summary = view?.findViewById<View>(R.id.yearSummary)
|
||||||
preview?.visibility = if (model.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE
|
preview?.visibility = if (model.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE
|
||||||
summary?.visibility = if (model.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE
|
summary?.visibility = if (model.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.state == STATE_CLOSED) {
|
if (model.state == STATE_CLOSED) {
|
||||||
|
|
||||||
val subItems = if (model is GradesSemester && model.grades.isEmpty())
|
val subItems = when {
|
||||||
listOf(GradesEmpty())
|
model is GradesSemester && model.grades.isEmpty() ->
|
||||||
else
|
listOf(GradesEmpty())
|
||||||
model.items
|
model is GradesSemester && manager.hideImproved ->
|
||||||
|
model.items.filter { !it.seen || !it.isImproved }
|
||||||
|
else -> model.items
|
||||||
|
}
|
||||||
|
|
||||||
model.state = STATE_OPENED
|
model.state = STATE_OPENED
|
||||||
items.addAll(position + 1, subItems.filterNotNull())
|
items.addAll(position + 1, subItems.filterNotNull())
|
||||||
notifyItemRangeInserted(position + 1, subItems.size)
|
if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size)
|
||||||
/*notifyItemRangeChanged(
|
|
||||||
position + subItems.size,
|
|
||||||
items.size - (position + subItems.size)
|
|
||||||
)*/
|
|
||||||
//notifyItemRangeChanged(position, items.size - position)
|
|
||||||
|
|
||||||
if (model is GradesSubject) {
|
if (model is GradesSubject) {
|
||||||
// auto expand first semester
|
// auto expand first semester
|
||||||
if (model.semesters.isNotEmpty()) {
|
if (model.semesters.isNotEmpty()) {
|
||||||
val semester = model.semesters.firstOrNull { it.grades.isNotEmpty() } ?: model.semesters.first()
|
val semester = model.semesters.firstOrNull { it.grades.isNotEmpty() } ?: model.semesters.first()
|
||||||
val semesterIndex = model.semesters.indexOf(semester)
|
val semesterIndex = model.semesters.indexOf(semester)
|
||||||
val grades = if (semester.grades.isEmpty())
|
|
||||||
listOf(GradesEmpty())
|
val grades = when {
|
||||||
else
|
semester.grades.isEmpty() ->
|
||||||
semester.grades
|
listOf(GradesEmpty())
|
||||||
|
manager.hideImproved ->
|
||||||
|
semester.grades.filter { !it.seen || !it.isImproved }
|
||||||
|
else -> semester.grades
|
||||||
|
}
|
||||||
|
|
||||||
semester.state = STATE_OPENED
|
semester.state = STATE_OPENED
|
||||||
items.addAll(position + 2 + semesterIndex, grades)
|
items.addAll(position + 2 + semesterIndex, grades)
|
||||||
notifyItemRangeInserted(position + 2 + semesterIndex, grades.size)
|
if (notifyAdapter) notifyItemRangeInserted(position + 2 + semesterIndex, grades.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,9 +157,7 @@ class GradesAdapter(
|
|||||||
|
|
||||||
if (end != -1) {
|
if (end != -1) {
|
||||||
items.subList(start, end).clear()
|
items.subList(start, end).clear()
|
||||||
notifyItemRangeRemoved(start, end - start)
|
if (notifyAdapter) notifyItemRangeRemoved(start, end - start)
|
||||||
//notifyItemRangeChanged(start, end - start)
|
|
||||||
//notifyItemRangeChanged(position, items.size - position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model.state = STATE_CLOSED
|
model.state = STATE_CLOSED
|
||||||
@ -152,8 +169,6 @@ class GradesAdapter(
|
|||||||
if (holder !is BindableViewHolder<*>)
|
if (holder !is BindableViewHolder<*>)
|
||||||
return
|
return
|
||||||
|
|
||||||
val app = activity.applicationContext as App
|
|
||||||
|
|
||||||
val viewType = when (holder) {
|
val viewType = when (holder) {
|
||||||
is SubjectViewHolder -> ITEM_TYPE_SUBJECT
|
is SubjectViewHolder -> ITEM_TYPE_SUBJECT
|
||||||
is SemesterViewHolder -> ITEM_TYPE_SEMESTER
|
is SemesterViewHolder -> ITEM_TYPE_SEMESTER
|
||||||
@ -167,11 +182,11 @@ class GradesAdapter(
|
|||||||
holder.itemView.setTag(R.string.tag_key_model, item)
|
holder.itemView.setTag(R.string.tag_key_model, item)
|
||||||
|
|
||||||
when {
|
when {
|
||||||
holder is SubjectViewHolder && item is GradesSubject -> holder.onBind(activity, app, item, position)
|
holder is SubjectViewHolder && item is GradesSubject -> holder.onBind(activity, app, item, position, this)
|
||||||
holder is SemesterViewHolder && item is GradesSemester -> holder.onBind(activity, app, item, position)
|
holder is SemesterViewHolder && item is GradesSemester -> holder.onBind(activity, app, item, position, this)
|
||||||
holder is EmptyViewHolder && item is GradesEmpty -> holder.onBind(activity, app, item, position)
|
holder is EmptyViewHolder && item is GradesEmpty -> holder.onBind(activity, app, item, position, this)
|
||||||
holder is GradeViewHolder && item is GradeFull -> holder.onBind(activity, app, item, position)
|
holder is GradeViewHolder && item is GradeFull -> holder.onBind(activity, app, item, position, this)
|
||||||
holder is StatsViewHolder && item is GradesStats -> holder.onBind(activity, app, item, position)
|
holder is StatsViewHolder && item is GradesStats -> holder.onBind(activity, app, item, position, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (holder is SemesterViewHolder && item is GradesSemester) {
|
if (holder is SemesterViewHolder && item is GradesSemester) {
|
||||||
@ -184,5 +199,23 @@ class GradesAdapter(
|
|||||||
holder.itemView.setOnClickListener(onClickListener)
|
holder.itemView.setOnClickListener(onClickListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun notifyItemChanged(model: Any) {
|
||||||
|
startCoroutineTimer(1000L, 0L) {
|
||||||
|
val index = items.indexOf(model)
|
||||||
|
if (index != -1)
|
||||||
|
notifyItemChanged(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeItem(model: Any) {
|
||||||
|
startCoroutineTimer(2000L, 0L) {
|
||||||
|
val index = items.indexOf(model)
|
||||||
|
if (index != -1) {
|
||||||
|
items.removeAt(index)
|
||||||
|
notifyItemRemoved(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getItemCount() = items.size
|
override fun getItemCount() = items.size
|
||||||
}
|
}
|
||||||
|
@ -4,31 +4,39 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.grades
|
package pl.szczodrzynski.edziennik.ui.modules.grades
|
||||||
|
|
||||||
|
import android.os.AsyncTask
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
|
||||||
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon2
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.Bundle
|
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
|
||||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_GRADES_EDITOR
|
import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_GRADES_EDITOR
|
||||||
import pl.szczodrzynski.edziennik.averageOrNull
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||||
import pl.szczodrzynski.edziennik.databinding.GradesFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.GradesFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
|
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
|
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats
|
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
|
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
|
|
||||||
class GradesFragment : Fragment(), CoroutineScope {
|
class GradesFragment : Fragment(), CoroutineScope {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "GradesFragment"
|
private const val TAG = "GradesFragment"
|
||||||
@ -47,7 +55,9 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
GradesAdapter(activity)
|
GradesAdapter(activity)
|
||||||
}
|
}
|
||||||
private val manager by lazy { app.gradesManager }
|
private val manager by lazy { app.gradesManager }
|
||||||
|
private val dontCountEnabled by lazy { manager.dontCountEnabled }
|
||||||
private val dontCountGrades by lazy { manager.dontCountGrades }
|
private val dontCountGrades by lazy { manager.dontCountGrades }
|
||||||
|
private var expandSubjectId = 0L
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
activity = (getActivity() as MainActivity?) ?: return null
|
activity = (getActivity() as MainActivity?) ?: return null
|
||||||
@ -62,6 +72,8 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
if (!isAdded)
|
if (!isAdded)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
|
||||||
|
|
||||||
app.db.gradeDao()
|
app.db.gradeDao()
|
||||||
.getAllOrderBy(App.profileId, app.gradesManager.getOrderByString())
|
.getAllOrderBy(App.profileId, app.gradesManager.getOrderByString())
|
||||||
.observe(this, Observer { grades ->
|
.observe(this, Observer { grades ->
|
||||||
@ -71,6 +83,16 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
//addItemDecoration(SimpleDividerItemDecoration(context))
|
//addItemDecoration(SimpleDividerItemDecoration(context))
|
||||||
|
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
|
if (recyclerView.canScrollVertically(-1)) {
|
||||||
|
b.refreshLayout.isEnabled = false
|
||||||
|
}
|
||||||
|
if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) {
|
||||||
|
b.refreshLayout.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,8 +133,29 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
"finalOtherSemester" to otherSemester?.finalGrade?.value
|
"finalOtherSemester" to otherSemester?.finalGrade?.value
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activity.bottomSheet.prependItems(
|
||||||
|
BottomSheetPrimaryItem(true)
|
||||||
|
.withTitle(R.string.menu_grades_config)
|
||||||
|
.withIcon(Icon2.cmd_settings_outline)
|
||||||
|
.withOnClickListener(View.OnClickListener {
|
||||||
|
activity.bottomSheet.close()
|
||||||
|
GradesConfigDialog(activity, true, null, null)
|
||||||
|
}),
|
||||||
|
BottomSheetSeparatorItem(true),
|
||||||
|
BottomSheetPrimaryItem(true)
|
||||||
|
.withTitle(R.string.menu_mark_as_read)
|
||||||
|
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||||
|
.withOnClickListener(View.OnClickListener {
|
||||||
|
activity.bottomSheet.close()
|
||||||
|
AsyncTask.execute { App.db.metadataDao().setAllSeen(App.profileId, TYPE_GRADE, true) }
|
||||||
|
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
activity.gainAttention()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("SuspendFunctionOnCoroutineScope")
|
||||||
private suspend fun processGrades(grades: List<GradeFull>) {
|
private suspend fun processGrades(grades: List<GradeFull>) {
|
||||||
val items = mutableListOf<GradesSubject>()
|
val items = mutableListOf<GradesSubject>()
|
||||||
|
|
||||||
@ -121,6 +164,8 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
var subject = GradesSubject(subjectId, "")
|
var subject = GradesSubject(subjectId, "")
|
||||||
var semester = GradesSemester(0, 1)
|
var semester = GradesSemester(0, 1)
|
||||||
|
|
||||||
|
val hideImproved = manager.hideImproved
|
||||||
|
|
||||||
// grades returned by the query are ordered
|
// grades returned by the query are ordered
|
||||||
// by the subject ID, so it's easier and probably
|
// by the subject ID, so it's easier and probably
|
||||||
// a bit faster to build all the models
|
// a bit faster to build all the models
|
||||||
@ -144,6 +189,11 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
?: GradesSemester(subject.subjectId, grade.semester).also { subject.semesters += it }
|
?: GradesSemester(subject.subjectId, grade.semester).also { subject.semesters += it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
grade.showAsUnseen = !grade.seen
|
||||||
|
if (!grade.seen) {
|
||||||
|
semester.hasUnseen = true
|
||||||
|
}
|
||||||
|
|
||||||
when (grade.type) {
|
when (grade.type) {
|
||||||
Grade.TYPE_SEMESTER1_PROPOSED,
|
Grade.TYPE_SEMESTER1_PROPOSED,
|
||||||
Grade.TYPE_SEMESTER2_PROPOSED -> semester.proposedGrade = grade
|
Grade.TYPE_SEMESTER2_PROPOSED -> semester.proposedGrade = grade
|
||||||
@ -237,18 +287,41 @@ class GradesFragment : Fragment(), CoroutineScope {
|
|||||||
adapter.items = items.toMutableList()
|
adapter.items = items.toMutableList()
|
||||||
adapter.items.add(stats)
|
adapter.items.add(stats)
|
||||||
|
|
||||||
|
var expandSubjectModel: GradesSubject? = null
|
||||||
|
if (expandSubjectId != 0L) {
|
||||||
|
expandSubjectModel = items.firstOrNull { it.subjectId == expandSubjectId }
|
||||||
|
adapter.expandModel(
|
||||||
|
model = expandSubjectModel,
|
||||||
|
view = null,
|
||||||
|
notifyAdapter = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startCoroutineTimer(500L, 0L) {
|
||||||
|
if (expandSubjectModel != null) {
|
||||||
|
b.gradesRecyclerView.smoothScrollToPosition(
|
||||||
|
items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun countGrade(grade: Grade, averages: GradesAverages) {
|
private fun countGrade(grade: Grade, averages: GradesAverages) {
|
||||||
val value = manager.getGradeValue(grade)
|
val value = manager.getGradeValue(grade)
|
||||||
val weight = manager.getGradeWeight(dontCountGrades, grade)
|
val weight = manager.getGradeWeight(dontCountEnabled, dontCountGrades, grade)
|
||||||
when (grade.type) {
|
when (grade.type) {
|
||||||
Grade.TYPE_NORMAL -> {
|
Grade.TYPE_NORMAL -> {
|
||||||
averages.normalSum += value
|
if (grade.value > 0f) {
|
||||||
averages.normalCount ++
|
// count to the arithmetic average
|
||||||
|
// only if value more than 0
|
||||||
|
// to exclude "+", "-", "np" etc.
|
||||||
|
averages.normalSum += value
|
||||||
|
averages.normalCount++
|
||||||
|
}
|
||||||
averages.normalWeightedSum += value * weight
|
averages.normalWeightedSum += value * weight
|
||||||
averages.normalWeightedCount += weight
|
averages.normalWeightedCount += weight
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.editor
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.PorterDuffColorFilter
|
import android.graphics.PorterDuffColorFilter
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -49,8 +48,7 @@ class GradesEditorAdapter(
|
|||||||
|
|
||||||
holder.gradesListName.text = editorGrade.name
|
holder.gradesListName.text = editorGrade.name
|
||||||
holder.gradesListName.isSelected = true
|
holder.gradesListName.isSelected = true
|
||||||
holder.gradesListName.setTypeface(null, Typeface.BOLD)
|
holder.gradesListName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.25) 0xaa000000.toInt() else 0xccffffff.toInt())
|
||||||
holder.gradesListName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.25) -0x1000000 else -0x1)
|
|
||||||
holder.gradesListName.background.colorFilter = PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY)
|
holder.gradesListName.background.colorFilter = PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY)
|
||||||
holder.gradesListCategory.text = editorGrade.category
|
holder.gradesListCategory.text = editorGrade.category
|
||||||
if (editorGrade.weight < 0) {
|
if (editorGrade.weight < 0) {
|
||||||
|
@ -16,7 +16,6 @@ import pl.szczodrzynski.edziennik.*
|
|||||||
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.utils.Colors
|
import pl.szczodrzynski.edziennik.utils.Colors
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_AVG
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_AVG
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_SEM
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_SEM
|
||||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
|
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
|
||||||
@ -60,16 +59,13 @@ class GradesEditorFragment : Fragment() {
|
|||||||
if (context == null)
|
if (context == null)
|
||||||
return null
|
return null
|
||||||
app = activity.application as App
|
app = activity.application as App
|
||||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
|
||||||
if (app.profile == null)
|
|
||||||
return inflater.inflate(R.layout.fragment_loading, container, false)
|
|
||||||
// activity, context and profile is valid
|
// activity, context and profile is valid
|
||||||
b = FragmentGradesEditorBinding.inflate(inflater)
|
b = FragmentGradesEditorBinding.inflate(inflater)
|
||||||
return b.root
|
return b.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
if (app.profile == null || !isAdded)
|
if (!isAdded)
|
||||||
return
|
return
|
||||||
|
|
||||||
subjectId = arguments.getLong("subjectId", -1)
|
subjectId = arguments.getLong("subjectId", -1)
|
||||||
@ -110,7 +106,7 @@ class GradesEditorFragment : Fragment() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var weight = editorGrade.weight
|
var weight = editorGrade.weight
|
||||||
if (!config.countZeroToAvg && editorGrade.name == "0") {
|
if (config.dontCountEnabled && config.dontCountGrades.contains(editorGrade.name.toLowerCase().trim())) {
|
||||||
weight = 0f
|
weight = 0f
|
||||||
}
|
}
|
||||||
val value = editorGrade.value * weight
|
val value = editorGrade.value * weight
|
||||||
@ -175,7 +171,7 @@ class GradesEditorFragment : Fragment() {
|
|||||||
averageSemester = 0f
|
averageSemester = 0f
|
||||||
for (editorGrade in editorGrades) {
|
for (editorGrade in editorGrades) {
|
||||||
var weight = editorGrade.weight
|
var weight = editorGrade.weight
|
||||||
if (!config.countZeroToAvg && editorGrade.name == "0") {
|
if (config.dontCountEnabled && config.dontCountGrades.contains(editorGrade.name.toLowerCase().trim())) {
|
||||||
weight = 0f
|
weight = 0f
|
||||||
}
|
}
|
||||||
val value = editorGrade.value * weight
|
val value = editorGrade.value * weight
|
||||||
@ -218,7 +214,7 @@ class GradesEditorFragment : Fragment() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var weight = grade.weight
|
var weight = grade.weight
|
||||||
if (!config.countZeroToAvg && grade.name == "0") {
|
if (config.dontCountEnabled && config.dontCountGrades.contains(grade.name.toLowerCase().trim())) {
|
||||||
weight = 0f
|
weight = 0f
|
||||||
}
|
}
|
||||||
val value = grade.value * weight
|
val value = grade.value * weight
|
||||||
|
@ -4,16 +4,17 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
package pl.szczodrzynski.edziennik.ui.modules.grades.models
|
||||||
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||||
|
|
||||||
data class GradesSemester(
|
data class GradesSemester(
|
||||||
val subjectId: Long,
|
val subjectId: Long,
|
||||||
val number: Int,
|
val number: Int,
|
||||||
val grades: MutableList<Grade> = mutableListOf()
|
val grades: MutableList<GradeFull> = mutableListOf()
|
||||||
) : ExpandableItemModel<Grade>(grades) {
|
) : ExpandableItemModel<GradeFull>(grades) {
|
||||||
override var level = 2
|
override var level = 2
|
||||||
|
|
||||||
|
var hasUnseen = false
|
||||||
|
|
||||||
val averages = GradesAverages()
|
val averages = GradesAverages()
|
||||||
var proposedGrade: GradeFull? = null
|
var proposedGrade: GradeFull? = null
|
||||||
var finalGrade: GradeFull? = null
|
var finalGrade: GradeFull? = null
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user