Compare commits

...

57 Commits

Author SHA1 Message Date
dc9e6081c5 [4.0] Update build.gradle, signing and changelog. 2020-04-19 22:06:19 +02:00
26f8c03570 [UI] Show class name and school year in subname. Fix setting language. 2020-04-19 22:01:29 +02:00
97e0f36f09 [UI] Fix semester grades overlapping when scrolling. 2020-04-19 21:37:39 +02:00
9b13552b73 [API/Mobidziennik] Fix downloading attachments. 2020-04-16 09:45:28 +02:00
d8559637a5 [Strings] Fix HTML tags missing in translations. 2020-04-13 22:43:26 +02:00
00a90a14dc [API] Fix downloading attachments with different name. Handle UTF encoded download names. 2020-04-07 13:57:12 +02:00
d56afb034b [API] Add Vulcan OneDrive attachment downloading. Add asking for permissions on demand. 2020-04-07 12:16:48 +02:00
0327ba37f1 [Strings] Update English translation. 2020-04-07 09:10:38 +02:00
12a54e58b5 [API] Add Vulcan message & homework attachments. Fix Mobidziennik homework attachments. 2020-04-06 22:50:00 +02:00
238250e8c9 [API/Mobidziennik] Fix homework attachment downloading. 2020-04-06 19:28:04 +02:00
041bfc6cc0 [4.0-rc.5] Update build.gradle, signing and changelog. 2020-04-05 23:59:13 +02:00
8a4866cb62 [Messages] Add new attachments view. Allow replying to deleted messages. 2020-04-05 23:56:56 +02:00
0e4d609bbf [Messages] Fix search in sent messages. Implement removing messages. 2020-04-05 23:39:32 +02:00
f07b12bd87 [Grades] Fix marking yearly grades as seen. 2020-04-05 22:04:23 +02:00
0413dbffa2 [Messages] Implement saving list position on message open. 2020-04-05 21:40:12 +02:00
c214b48409 [Messages] Add a search bar. Fix Grades not loading. 2020-04-05 20:06:35 +02:00
91a6366548 [Messages] Fix opening messages. 2020-04-04 23:14:40 +02:00
03c9932b8c [API/Edudziennik] Add getting whole homework body. 2020-04-04 22:24:15 +02:00
ea4919a25d [API/Librus] Add handling missing attachment error. 2020-04-04 22:23:31 +02:00
f98b174857 [UI] Refactor Messages fragment. 2020-04-04 21:31:14 +02:00
c0aeb0d2f3 [UI] Add showing unseen events. Add Lab fragment. 2020-04-03 20:58:51 +02:00
fcb627aac6 [Messages/Compose] Fix underlined word under the caret. 2020-04-02 15:38:16 +02:00
7f0aea29cd [API/Librus] Fix HTML to string conversion in homework. Add force download homework button in debug mode. 2020-04-02 12:56:24 +02:00
f6dcbb6594 [API/Librus] Disable Messages login when downloading homework attachment. 2020-04-02 11:19:44 +02:00
b790421693 [API/Idziennik] Correct showing proposed descriptive grades. Clarify some connection errors. 2020-04-02 11:17:47 +02:00
f5a7799924 [API/Librus] Fix getting homework without attachments. 2020-04-02 09:37:22 +02:00
b052b5bd66 [API/Librus] Add getting homework body and downloading homework attachments. 2020-04-01 22:55:20 +02:00
12d8de1def [API] Implement Idziennik homework attachment downloading. Change attachment events to sticky. 2020-04-01 21:49:05 +02:00
9303483470 [API] Implement Mobidziennik homework attachment downloading. Modify the interface a bit. Show attachments in UI. 2020-04-01 17:07:50 +02:00
f8adc86a0e [Events] Fix for duplicated events when metadata is doubled. 2020-04-01 17:04:06 +02:00
db57c258c5 [API] Add trimming whitespaces from events' titles. 2020-04-01 16:56:47 +02:00
ddb2760c16 [Events] Add Mobidziennik event attachment listing. 2020-03-31 20:04:32 +02:00
14d267a95a [API] Fix task cancelling with the notification. [UI] Add event downloading progress bar. 2020-03-31 18:20:24 +02:00
a6c4053896 [API] Add interface method to get event details. 2020-03-31 15:18:34 +02:00
949a68ec1d [Homework] Add mark as done confirmation dialog. Refactor code a bit. 2020-03-31 09:06:32 +02:00
93333a8c48 [Homework] Fix showing done homework on every profile. 2020-03-31 08:34:08 +02:00
da48c059ec [4.0-rc.4] Update build.gradle, signing and changelog. 2020-03-30 23:29:34 +02:00
ee5566d1ef [Events] Add toast hint to mark as done button. 2020-03-30 23:28:50 +02:00
b794b30346 [UI] Fix disabling pull to refresh when changing page using tab layout. 2020-03-30 23:16:35 +02:00
0db6393bb0 [Events] Add showing green check when event is done. Hide done events from homework current list. 2020-03-30 23:02:19 +02:00
fcc3c55110 [Events] Fix preserving isDone value. Improve DataRemoveModel method of keeping items. 2020-03-30 22:37:48 +02:00
328c07eaf4 [Messages] Fix Librus attachment downloading. Add option to force (re)download an attachment. 2020-03-30 19:48:56 +02:00
b004ec048e [UI] Refactor Grades, Notifications, Homework fragments to better match unified templates. 2020-03-30 18:55:28 +02:00
b9f83875a0 [Event] Add isDone attribute and marking events as done. 2020-03-30 18:19:19 +02:00
8c869d082b [UI] Add pager fragment templates. Move all templates to 'template' module. Fix swipe to refresh with pager fragments. 2020-03-30 12:50:21 +02:00
043f8210ba [UI] Add lazy loading to fragments with view pager. 2020-03-29 23:11:17 +02:00
41a79caf83 [API/Mobidziennik] Change data remove model to include only possible types. 2020-03-29 21:06:39 +02:00
0427fa6087 [Events] Add support for selective updates and upserting. 2020-03-29 18:05:56 +02:00
2f3c912dbe [Config] Disable teacher absence notifications by default. Add missing migration values. 2020-03-29 16:27:05 +02:00
219a7443c0 [4.0-rc.3] Update build.gradle, signing and changelog. 2020-03-29 15:31:49 +02:00
6deb408d80 [API/Librus] Fix attachment downloading, once again. 2020-03-29 15:30:30 +02:00
c6e1ff2164 [Events] Fix event sorting. Fix showing event teacher name. 2020-03-29 15:26:48 +02:00
bc0918a115 [API/Librus] Fix attachment downloading. 2020-03-29 15:16:35 +02:00
55ff9173be [API/Liburs] Fix unseen teacher absence metadata and add notifications for new teacher absences. 2020-03-28 17:08:36 +01:00
d4d548846f [Refactor] Refactor EventDao class. 2020-03-28 11:17:39 +01:00
ef4527f140 [Refactor] Rewrite events to Kotlin. 2020-03-27 18:51:56 +01:00
0b1e7242bb [API/Mobidziennik] Fix some errors. 2020-03-27 14:05:03 +01:00
227 changed files with 7196 additions and 3252 deletions

3
.gitignore vendored
View File

@ -86,4 +86,5 @@ app/schemas/
signatures/ signatures/
app/.cxx app/.cxx
/i18n/

View File

@ -41,5 +41,15 @@
<option name="name" value="MavenRepo" /> <option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" /> <option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository> </remote-repository>
<remote-repository>
<option name="id" value="maven4" />
<option name="name" value="maven4" />
<option name="url" value="https://dl.bintray.com/undervoid/Powerpermission" />
</remote-repository>
<remote-repository>
<option name="id" value="maven4" />
<option name="name" value="maven4" />
<option name="url" value="https://dl.bintray.com/undervoid/PowerPermission" />
</remote-repository>
</component> </component>
</project> </project>

1
annotation/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

29
annotation/build.gradle Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
*/
apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
sourceCompatibility = "7"
targetCompatibility = "7"
repositories {
mavenCentral()
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
*/
package pl.szczodrzynski.edziennik.annotation
import kotlin.reflect.KClass
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class SelectiveDao(
val db: KClass<*>
)

View File

@ -0,0 +1,13 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
*/
package pl.szczodrzynski.edziennik.annotation
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class UpdateSelective(
val primaryKeys: Array<String>,
val skippedColumns: Array<String> = []
)

View File

@ -193,6 +193,14 @@ dependencies {
implementation "io.coil-kt:coil:0.9.2" implementation "io.coil-kt:coil:0.9.2"
implementation 'com.github.kuba2k2:NumberSlidingPicker:2921225f76' implementation 'com.github.kuba2k2:NumberSlidingPicker:2921225f76'
implementation project(":annotation")
kapt project(":codegen")
implementation 'com.google.android:flexbox:2.0.1'
implementation 'com.qifan.powerpermission:powerpermission:1.0.0'
implementation 'com.qifan.powerpermission:powerpermission-coroutines:1.0.0'
} }
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -14,6 +14,9 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- PowerPermission uses minSdk 21, it's safe to override as it is used only in >= 23 -->
<uses-sdk tools:overrideLibrary="com.qifan.powerpermission.coroutines, com.qifan.powerpermission.core" />
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"

View File

@ -1,8 +1,10 @@
<h3>Wersja 4.0-rc.2, 2020-03-26</h3> <h3>Wersja 4.0, 2020-04-19</h3>
<ul> <ul>
<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 &#x1F44F;</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 &#x1F44F;</li>
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkość oraz poprawność pobieranych danych</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>Wyszukiwarka wiadomości, pozwalająca na łatwe znalezienie potrzebnej konwersacji.</li>
<li>Możliwość pobierania załączników do zadań domowych oraz wiadomości w każdym dzienniku.</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>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>
@ -10,6 +12,7 @@
<li>Znaczki nieprzeczytanych informacji na obrazkach profili.</li> <li>Znaczki nieprzeczytanych informacji na obrazkach profili.</li>
<br> <br>
<br> <br>
<li>Udoskonalone tłumaczenie na j.angielski (dzięki @Predator)</li>
<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>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>

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/ /*secret password - removed for source code publication*/
static toys AES_IV[16] = { static toys AES_IV[16] = {
0x30, 0xfe, 0xe2, 0x8d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 0x38, 0xd4, 0x73, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat); unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -43,10 +43,7 @@ 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.*
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
import pl.szczodrzynski.edziennik.utils.managers.TimetableManager
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 +64,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
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 timetableManager by lazy { TimetableManager(this) }
val eventManager by lazy { EventManager(this) }
val permissionManager by lazy { PermissionManager(this) }
val db val db
get() = App.db get() = App.db
@ -168,7 +167,11 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
db.profileDao().firstId?.let { profileLoadById(it) } db.profileDao().firstId?.let { profileLoadById(it) }
} }
devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG config.ui.language?.let {
setLanguage(it)
}
devMode = BuildConfig.DEBUG
Signing.getCert(this) Signing.getCert(this)

View File

@ -27,10 +27,7 @@ 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.view.WindowManager
import android.widget.CheckBox import android.widget.*
import android.widget.CompoundButton
import android.widget.RadioButton
import android.widget.TextView
import androidx.annotation.* import androidx.annotation.*
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.database.getIntOrNull import androidx.core.database.getIntOrNull
@ -40,7 +37,11 @@ import androidx.core.util.forEach
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.viewpager.widget.ViewPager
import com.google.android.gms.security.ProviderInstaller import com.google.android.gms.security.ProviderInstaller
import com.google.android.material.button.MaterialButton
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
@ -141,6 +142,10 @@ fun CharSequence?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty() return this != null && this.isNotEmpty()
} }
fun <T> Collection<T>?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
fun CharSequence?.isNotNullNorBlank(): Boolean { fun CharSequence?.isNotNullNorBlank(): Boolean {
return this != null && this.isNotBlank() return this != null && this.isNotBlank()
} }
@ -160,6 +165,13 @@ fun Bundle?.getString(key: String, defaultValue: String): String {
return this?.getString(key, defaultValue) ?: defaultValue return this?.getString(key, defaultValue) ?: defaultValue
} }
fun Bundle?.getIntOrNull(key: String): Int? {
return this?.get(key) as? Int
}
fun <T : Any> Bundle?.get(key: String): T? {
return this?.get(key) as? T?
}
/** /**
* ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. ` * ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. `
* *
@ -442,7 +454,7 @@ operator fun MatchResult.get(group: Int): String {
return groupValues[group] return groupValues[group]
} }
fun Activity.setLanguage(language: String) { fun Context.setLanguage(language: String) {
val locale = Locale(language.toLowerCase(Locale.ROOT)) val locale = Locale(language.toLowerCase(Locale.ROOT))
val configuration = resources.configuration val configuration = resources.configuration
Locale.setDefault(locale) Locale.setDefault(locale)
@ -451,7 +463,6 @@ fun Activity.setLanguage(language: String) {
} }
configuration.locale = locale configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics) resources.updateConfiguration(configuration, resources.displayMetrics)
baseContext.resources.updateConfiguration(configuration, baseContext.resources.displayMetrics)
} }
/* /*
@ -560,7 +571,7 @@ fun CharSequence?.asBoldSpannable(): Spannable {
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spannable return spannable
} }
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false): Spannable { fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false, ignoreDiacritics: Boolean = false): Spannable {
val spannable = SpannableString(this) val spannable = SpannableString(this)
if (substring == null) { if (substring == null) {
spans.forEach { spans.forEach {
@ -568,17 +579,44 @@ fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignor
} }
} }
else if (substring.isNotEmpty()) { else if (substring.isNotEmpty()) {
var index = indexOf(substring, ignoreCase = ignoreCase) val string =
if (ignoreDiacritics)
this.cleanDiacritics()
else this
var index = string.indexOf(substring, ignoreCase = ignoreCase)
.takeIf { it != -1 } ?: indexOf(substring, ignoreCase = ignoreCase)
while (index >= 0) { while (index >= 0) {
spans.forEach { spans.forEach {
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
} }
index = indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase) index = string.indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
.takeIf { it != -1 } ?: indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
} }
} }
return spannable return spannable
} }
fun CharSequence.cleanDiacritics(): String {
val nameClean = StringBuilder()
forEach {
val ch = when (it) {
'ż' -> 'z'
'ó' -> 'o'
'ł' -> 'l'
'ć' -> 'c'
'ę' -> 'e'
'ś' -> 's'
'ą' -> 'a'
'ź' -> 'z'
'ń' -> 'n'
else -> it
}
nameClean.append(ch)
}
return nameClean.toString()
}
/** /**
* Returns a new read-only list only of those given elements, that are not empty. * Returns a new read-only list only of those given elements, that are not empty.
* Applies for CharSequence and descendants. * Applies for CharSequence and descendants.
@ -722,6 +760,13 @@ inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
} }
} }
@Suppress("UNCHECKED_CAST")
inline fun <T : View> T.onLongClick(crossinline onLongClickListener: (v: T) -> Boolean) {
setOnLongClickListener { v: View ->
onLongClickListener(v as T)
}
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) { inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
setOnCheckedChangeListener { buttonView, isChecked -> setOnCheckedChangeListener { buttonView, isChecked ->
@ -729,6 +774,19 @@ inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T,
} }
} }
@Suppress("UNCHECKED_CAST")
inline fun <T : MaterialButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
clearOnCheckedChangeListeners()
addOnCheckedChangeListener { buttonView, isChecked ->
onChangeListener(buttonView as T, isChecked)
}
}
fun View.attachToastHint(stringRes: Int) = onLongClick {
Toast.makeText(it.context, stringRes, Toast.LENGTH_SHORT).show()
true
}
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) { fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> { observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) { override fun onChanged(t: T?) {
@ -784,7 +842,7 @@ fun View.findParentById(targetId: Int): View? {
return null return null
} }
fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: () -> Unit) = launch { fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: suspend CoroutineScope.() -> Unit) = launch {
delay(delayMillis) delay(delayMillis)
if (repeatMillis > 0) { if (repeatMillis > 0) {
while (true) { while (true) {
@ -1008,6 +1066,7 @@ fun Context.getNotificationTitle(type: Int): String {
Notification.TYPE_FEEDBACK_MESSAGE -> R.string.notification_type_feedback_message Notification.TYPE_FEEDBACK_MESSAGE -> R.string.notification_type_feedback_message
Notification.TYPE_NEW_ANNOUNCEMENT -> R.string.notification_type_new_announcement Notification.TYPE_NEW_ANNOUNCEMENT -> R.string.notification_type_new_announcement
Notification.TYPE_AUTO_ARCHIVING -> R.string.notification_type_auto_archiving Notification.TYPE_AUTO_ARCHIVING -> R.string.notification_type_auto_archiving
Notification.TYPE_TEACHER_ABSENCE -> R.string.notification_type_new_teacher_absence
Notification.TYPE_GENERAL -> R.string.notification_type_general Notification.TYPE_GENERAL -> R.string.notification_type_general
else -> R.string.notification_type_general else -> R.string.notification_type_general
}) })
@ -1166,3 +1225,23 @@ fun TextView.getTextPosition(range: IntRange): Rect {
return parentTextViewRect return parentTextViewRect
} }
inline fun ViewPager.addOnPageSelectedListener(crossinline block: (position: Int) -> Unit) = addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) { block(position) }
})
val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener
get() = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (recyclerView.canScrollVertically(-1))
this@onScrollListener.isEnabled = false
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE)
this@onScrollListener.isEnabled = true
}
}
operator fun <K, V> Iterable<Pair<K, V>>.get(key: K): V? {
return firstOrNull { it.first == key }?.second
}

View File

@ -20,7 +20,6 @@ import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.navigation.NavOptions import androidx.navigation.NavOptions
import androidx.recyclerview.widget.RecyclerView
import com.danimahardhika.cafebar.CafeBar import com.danimahardhika.cafebar.CafeBar
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.IconicsColor import com.mikepenz.iconics.IconicsColor
@ -55,22 +54,22 @@ import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment
import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment
import pl.szczodrzynski.edziennik.ui.modules.base.MainSnackbar import pl.szczodrzynski.edziennik.ui.modules.base.MainSnackbar
import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment
import pl.szczodrzynski.edziennik.ui.modules.debug.DebugFragment
import pl.szczodrzynski.edziennik.ui.modules.debug.LabFragment
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment import pl.szczodrzynski.edziennik.ui.modules.messages.compose.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
@ -129,6 +128,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
const val TARGET_MESSAGES_DETAILS = 503 const val TARGET_MESSAGES_DETAILS = 503
const val TARGET_MESSAGES_COMPOSE = 504 const val TARGET_MESSAGES_COMPOSE = 504
const val TARGET_WEB_PUSH = 140 const val TARGET_WEB_PUSH = 140
const val TARGET_LAB = 1000
const val HOME_ID = DRAWER_ITEM_HOME const val HOME_ID = DRAWER_ITEM_HOME
@ -153,7 +153,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.withBadgeTypeId(TYPE_EVENT) .withBadgeTypeId(TYPE_EVENT)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class) list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesListFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline) .withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
.withBadgeTypeId(TYPE_GRADE) .withBadgeTypeId(TYPE_GRADE)
.isInDrawer(true) .isInDrawer(true)
@ -185,7 +185,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
// static drawer items // static drawer items
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class) list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsListFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline) .withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
.isInDrawer(true) .isInDrawer(true)
.isStatic(true) .isStatic(true)
@ -226,7 +226,14 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class).withPopTo(DRAWER_ITEM_MESSAGES) list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class).withPopTo(DRAWER_ITEM_MESSAGES)
list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class) list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class)
list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class) list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class)
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class) if (App.debugMode) {
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
list += NavTarget(TARGET_LAB, R.string.menu_lab, LabFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_flask_outline)
.isInDrawer(true)
.isBelowSeparator(true)
.isStatic(true)
}
list list
} }
@ -407,8 +414,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
R.color.md_green_500 R.color.md_green_500
) )
isStoragePermissionGranted()
SyncWorker.scheduleNext(app) SyncWorker.scheduleNext(app)
UpdateWorker.scheduleNext(app) UpdateWorker.scheduleNext(app)
@ -509,7 +514,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.withIcon(CommunityMaterial.Icon2.cmd_help_circle_outline) .withIcon(CommunityMaterial.Icon2.cmd_help_circle_outline)
.withOnClickListener(View.OnClickListener { loadTarget(TARGET_FEEDBACK) }) .withOnClickListener(View.OnClickListener { loadTarget(TARGET_FEEDBACK) })
) )
if (App.devMode) { if (App.debugMode) {
bottomSheet += BottomSheetPrimaryItem(false) bottomSheet += BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_debug) .withTitle(R.string.menu_debug)
.withIcon(CommunityMaterial.Icon.cmd_android_studio) .withIcon(CommunityMaterial.Icon.cmd_android_studio)
@ -880,9 +885,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
app.profileLoad(id) { app.profileLoad(id) {
MessagesFragment.pageSelection = -1 MessagesFragment.pageSelection = -1
MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
setDrawerItems() setDrawerItems()
// the drawer profile is updated automatically when the drawer item is clicked // the drawer profile is updated automatically when the drawer item is clicked
@ -907,9 +909,10 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
loadTarget(target, arguments) loadTarget(target, arguments)
} }
} }
private fun loadTarget(target: NavTarget, arguments: Bundle? = null) { private fun loadTarget(target: NavTarget, args: Bundle? = null) {
d("NavDebug", "loadTarget(target = $target, arguments = $arguments)") d("NavDebug", "loadTarget(target = $target, args = $args)")
val arguments = args ?: navBackStack.firstOrNull { it.first.id == target.id }?.second ?: Bundle()
bottomSheet.close() bottomSheet.close()
bottomSheet.removeAllContextual() bottomSheet.removeAllContextual()
bottomSheet.toggleGroupEnabled = false bottomSheet.toggleGroupEnabled = false
@ -957,6 +960,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
navBackStack.removeAt(navBackStack.lastIndex) navBackStack.removeAt(navBackStack.lastIndex)
} }
navTarget = target navTarget = target
navArguments = arguments
return@let null return@let null
}?.let { }?.let {
@ -966,7 +970,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
R.anim.task_open_enter, R.anim.task_open_enter,
R.anim.task_open_exit R.anim.task_open_exit
) )
navBackStack.add(navTarget to arguments) navBackStack.add(navTarget to navArguments)
navTarget = target navTarget = target
navArguments = arguments navArguments = arguments
} }
@ -1068,6 +1072,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.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!!)) }
.also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)} .also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)}
.withSelectedBackgroundAnimated(false)
if (target.badgeTypeId != null) if (target.badgeTypeId != null)
drawer.addUnreadCounterType(target.badgeTypeId!!, target.id) drawer.addUnreadCounterType(target.badgeTypeId!!, target.id)

View File

@ -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 = 11 const val DATA_VERSION = 12
} }
private val job = Job() private val job = Job()

View File

@ -18,7 +18,7 @@ import kotlin.coroutines.CoroutineContext
class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig { class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig {
companion object { companion object {
const val DATA_VERSION = 1 const val DATA_VERSION = 2
} }
private val job = Job() private val job = Job()

View File

@ -64,11 +64,25 @@ class ConfigMigration(app: App, config: Config) {
dataVersion = 2 dataVersion = 2
} }
if (dataVersion < 3) {
update = null
privacyPolicyAccepted = false
debugMode = false
devModePassword = null
appInstalledTime = 0L
appRateSnackbarTime = 0L
dataVersion = 3
}
if (dataVersion < 10) { if (dataVersion < 10) {
ui.openDrawerOnBackPressed = false ui.openDrawerOnBackPressed = false
ui.snowfall = false ui.snowfall = false
ui.bottomSheetOpened = false ui.bottomSheetOpened = false
sync.dontShowAppManagerDialog = false sync.dontShowAppManagerDialog = false
sync.webPushEnabled = true
sync.lastAppSync = 0L
dataVersion = 10 dataVersion = 10
} }

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.config.utils package pl.szczodrzynski.edziennik.config.utils
import pl.szczodrzynski.edziennik.config.ProfileConfig import pl.szczodrzynski.edziennik.config.ProfileConfig
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
@ -14,11 +15,23 @@ class ProfileConfigMigration(config: ProfileConfig) {
if (dataVersion < 1) { if (dataVersion < 1) {
grades.colorMode = COLOR_MODE_WEIGHTED grades.colorMode = COLOR_MODE_WEIGHTED
grades.dontCountEnabled = false
grades.yearAverageMode = YEAR_ALL_GRADES grades.yearAverageMode = YEAR_ALL_GRADES
grades.hideImproved = false
grades.averageWithoutWeight = true
grades.plusValue = null
grades.minusValue = null
grades.dontCountEnabled = false
grades.dontCountGrades = listOf()
ui.agendaViewType = AGENDA_DEFAULT ui.agendaViewType = AGENDA_DEFAULT
// no migration for ui.homeCards
dataVersion = 1 dataVersion = 1
} }
if (dataVersion < 2) {
sync.notificationFilter = sync.notificationFilter + Notification.TYPE_TEACHER_ABSENCE
dataVersion = 2
}
}} }}
} }

View File

@ -38,6 +38,9 @@ class ApiService : Service() {
context.startService(Intent(context, ApiService::class.java)) context.startService(Intent(context, ApiService::class.java))
EventBus.getDefault().postSticky(request) EventBus.getDefault().postSticky(request)
} }
var lastEventTime = System.currentTimeMillis()
var taskCancelTries = 0
} }
private val app by lazy { applicationContext as App } private val app by lazy { applicationContext as App }
@ -64,9 +67,6 @@ class ApiService : Service() {
private val notification by lazy { EdziennikNotification(app) } private val notification by lazy { EdziennikNotification(app) }
private var lastEventTime = System.currentTimeMillis()
private var taskCancelTries = 0
/* ______ _ _ _ _ _____ _ _ _ _ /* ______ _ _ _ _ _____ _ _ _ _
| ____| | | (_) (_) | / ____| | | | | | | | ____| | | (_) (_) | / ____| | | | | | |
| |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __ | |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __

View File

@ -56,6 +56,8 @@ const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/to
const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module" const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module"
const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action=" const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action="
const val LIBRUS_SYNERGIA_HOMEWORK_ATTACHMENT_URL = "https://synergia.librus.pl/homework/downloadFile"
const val IDZIENNIK_USER_AGENT = SYNERGIA_USER_AGENT const val IDZIENNIK_USER_AGENT = SYNERGIA_USER_AGENT
const val IDZIENNIK_WEB_URL = "https://iuczniowie.progman.pl/idziennik" const val IDZIENNIK_WEB_URL = "https://iuczniowie.progman.pl/idziennik"
const val IDZIENNIK_WEB_LOGIN = "login.aspx" const val IDZIENNIK_WEB_LOGIN = "login.aspx"
@ -74,6 +76,8 @@ const val IDZIENNIK_WEB_GET_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/Pobier
const val IDZIENNIK_WEB_GET_RECIPIENT_LIST = "mod_komunikator/WS_wiadomosci.asmx/pobierzListeOdbiorcowPanelRodzic" const val IDZIENNIK_WEB_GET_RECIPIENT_LIST = "mod_komunikator/WS_wiadomosci.asmx/pobierzListeOdbiorcowPanelRodzic"
const val IDZIENNIK_WEB_SEND_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/WyslijWiadomosc" const val IDZIENNIK_WEB_SEND_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/WyslijWiadomosc"
const val IDZIENNIK_WEB_GET_ATTACHMENT = "mod_komunikator/Download.ashx" const val IDZIENNIK_WEB_GET_ATTACHMENT = "mod_komunikator/Download.ashx"
const val IDZIENNIK_WEB_GET_HOMEWORK = "mod_panelRodzica/pracaDomowa/WS_pracaDomowa.asmx/pobierzJednaPraceDomowa"
const val IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT = "mod_panelRodzica/pracaDomowa.aspx"
val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT
const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api" const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api"
@ -107,5 +111,7 @@ const val VULCAN_API_ENDPOINT_MESSAGES_SENT = "mobile-api/Uczen.v3.Uczen/Wiadomo
const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci" const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci"
const val VULCAN_API_ENDPOINT_MESSAGES_ADD = "mobile-api/Uczen.v3.Uczen/DodajWiadomosc" const val VULCAN_API_ENDPOINT_MESSAGES_ADD = "mobile-api/Uczen.v3.Uczen/DodajWiadomosc"
const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken" const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken"
const val VULCAN_API_ENDPOINT_MESSAGES_ATTACHMENTS = "mobile-api/Uczen.v3.Uczen/WiadomosciZalacznik"
const val VULCAN_API_ENDPOINT_HOMEWORK_ATTACHMENTS = "mobile-api/Uczen.v3.Uczen/ZadaniaDomoweZalacznik"
const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}" const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}"

View File

@ -8,11 +8,12 @@ import android.app.Notification
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.PRIORITY_MIN import androidx.core.app.NotificationCompat.PRIORITY_MIN
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.Bundle
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.receivers.SzkolnyReceiver
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -35,16 +36,18 @@ class EdziennikNotification(val app: App) {
var serviceClosed = false var serviceClosed = false
private fun cancelPendingIntent(taskId: Int): PendingIntent { private fun cancelPendingIntent(taskId: Int): PendingIntent {
val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN") val intent = SzkolnyReceiver.getIntent(app, Bundle(
intent.putExtra("task", "TaskCancelRequest") "task" to "TaskCancelRequest",
intent.putExtra("taskId", taskId) "taskId" to taskId
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent ))
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) as PendingIntent
} }
private val closePendingIntent: PendingIntent private val closePendingIntent: PendingIntent
get() { get() {
val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN") val intent = SzkolnyReceiver.getIntent(app, Bundle(
intent.putExtra("task", "ServiceCloseRequest") "task" to "ServiceCloseRequest"
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent ))
return PendingIntent.getBroadcast(app, 0, intent, 0) as PendingIntent
} }
private fun errorCountText(): String? { private fun errorCountText(): String? {

View File

@ -126,6 +126,7 @@ 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_LIBRUS_MESSAGES_NOT_FOUND = 186
const val ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST = 187 const val ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST = 187
const val ERROR_LIBRUS_MESSAGES_ATTACHMENT_NOT_FOUND = 188
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
@ -157,6 +158,7 @@ const val ERROR_LOGIN_VULCAN_NO_PUPILS = 331
const val ERROR_VULCAN_API_MAINTENANCE = 340 const val ERROR_VULCAN_API_MAINTENANCE = 340
const val ERROR_VULCAN_API_BAD_REQUEST = 341 const val ERROR_VULCAN_API_BAD_REQUEST = 341
const val ERROR_VULCAN_API_OTHER = 342 const val ERROR_VULCAN_API_OTHER = 342
const val ERROR_VULCAN_ATTACHMENT_DOWNLOAD = 343
const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN = 401 const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN = 401
const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402 const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402
@ -206,5 +208,6 @@ const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913
const val EXCEPTION_IDZIENNIK_API_REQUEST = 914 const val EXCEPTION_IDZIENNIK_API_REQUEST = 914
const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920 const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920
const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921 const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921
const val ERROR_ONEDRIVE_DOWNLOAD = 930
const val LOGIN_NO_ARGUMENTS = 1201 const val LOGIN_NO_ARGUMENTS = 1201

View File

@ -13,8 +13,8 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
internal const val FEATURE_TIMETABLE = 1 internal const val FEATURE_TIMETABLE = 1
internal const val FEATURE_AGENDA = 2 internal const val FEATURE_AGENDA = 2

View File

@ -84,6 +84,22 @@ object Regexes {
"""<strong>(.+?) - (.*?)</strong>.+?<small>.+?\((.+?), .+?(.+?)\)""".toRegex(DOT_MATCHES_ALL) """<strong>(.+?) - (.*?)</strong>.+?<small>.+?\((.+?), .+?(.+?)\)""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_HOMEWORK_ROW by lazy {
"""class="rowRolling">(.+?</div>\s*</td>)""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_ITEM by lazy {
"""<p><b>(.+?):</b>\s*(.+?)\s*</p>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_BODY by lazy {
"""Treść:</b>(.+?)<p><b>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_ID by lazy {
"""zadanieFormularz\(([0-9]+),""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_ATTACHMENT by lazy {
"""zalacznik(_zadania)?=([0-9]+)'.+?word-break">(.+?)</td>""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy { val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
@ -155,6 +171,9 @@ object Regexes {
val EDUDZIENNIK_ANNOUNCEMENT_DESCRIPTION by lazy { val EDUDZIENNIK_ANNOUNCEMENT_DESCRIPTION by lazy {
"""<div class="desc">.*?<p>(.*?)</p>""".toRegex(DOT_MATCHES_ALL) """<div class="desc">.*?<p>(.*?)</p>""".toRegex(DOT_MATCHES_ALL)
} }
val EDUDZIENNIK_HOMEWORK_DESCRIPTION by lazy {
"""<div class="desc">(.*?)</div>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_SUBJECT_ID by lazy { val EDUDZIENNIK_SUBJECT_ID by lazy {
"""/Courses/([\w-_]+?)/""".toRegex() """/Courses/([\w-_]+?)/""".toRegex()

View File

@ -19,9 +19,9 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) { open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
@ -36,8 +36,9 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text)) fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text))
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest()) fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement)) fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement))
fun attachmentGet(profileId: Int, message: Message, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(message, attachmentId, attachmentName)) fun attachmentGet(profileId: Int, owner: Any, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(owner, attachmentId, attachmentName))
fun recipientListGet(profileId: Int) = EdziennikTask(profileId, RecipientListGetRequest()) fun recipientListGet(profileId: Int) = EdziennikTask(profileId, RecipientListGetRequest())
fun eventGet(profileId: Int, event: EventFull) = EdziennikTask(profileId, EventGetRequest(event))
} }
private lateinit var loginStore: LoginStore private lateinit var loginStore: LoginStore
@ -92,8 +93,9 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
is FirstLoginRequest -> edziennikInterface?.firstLogin() is FirstLoginRequest -> edziennikInterface?.firstLogin()
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
is AnnouncementGetRequest -> edziennikInterface?.getAnnouncement(request.announcement) is AnnouncementGetRequest -> edziennikInterface?.getAnnouncement(request.announcement)
is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.message, request.attachmentId, request.attachmentName) is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.owner, request.attachmentId, request.attachmentName)
is RecipientListGetRequest -> edziennikInterface?.getRecipientList() is RecipientListGetRequest -> edziennikInterface?.getRecipientList()
is EventGetRequest -> edziennikInterface?.getEvent(request.event)
} }
} }
@ -113,6 +115,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String) data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String)
class AnnouncementsReadRequest class AnnouncementsReadRequest
data class AnnouncementGetRequest(val announcement: AnnouncementFull) data class AnnouncementGetRequest(val announcement: AnnouncementFull)
data class AttachmentGetRequest(val message: Message, val attachmentId: Long, val attachmentName: String) data class AttachmentGetRequest(val owner: Any, val attachmentId: Long, val attachmentName: String)
class RecipientListGetRequest class RecipientListGetRequest
data class EventGetRequest(val event: EventFull)
} }

View File

@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikData import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetAnnouncement import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetAnnouncement
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetHomework
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.firstlogin.EdudziennikFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.firstlogin.EdudziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLogin import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb
@ -16,10 +17,10 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -94,13 +95,22 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
} }
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {} override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {}
override fun getRecipientList() {} override fun getRecipientList() {}
override fun getEvent(eventFull: EventFull) {
EdudziennikLoginWeb(data) {
EdudziennikWebGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { EdudziennikFirstLogin(data) { completed() } } override fun firstLogin() { EdudziennikFirstLogin(data) { completed() } }
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")
data.cancel() data.cancel()
callback.onCompleted()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {

View File

@ -39,17 +39,16 @@ class EdudziennikWebEvents(override val data: DataEdudziennik,
?: return@forEach ?: return@forEach
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
date, date = date,
null, time = null,
title, topic = title,
-1, color = null,
Event.TYPE_CLASS_EVENT, type = Event.TYPE_CLASS_EVENT,
false, teacherId = -1,
-1, subjectId = -1,
-1, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -56,17 +56,16 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
val eventType = data.getEventType(eventTypeId, eventTypeName) val eventType = data.getEventType(eventTypeId, eventTypeName)
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
date, date = date,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
eventType.id, type = eventType.id,
false, teacherId = -1,
-1, subjectId = subject.id,
subject.id, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -0,0 +1,45 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import android.text.Html
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
class EdudziennikWebGetHomework(
override val data: DataEdudziennik,
val event: EventFull,
val onSuccess: () -> Unit
) : EdudziennikWeb(data, null) {
companion object {
const val TAG = "EdudziennikWebGetHomework"
}
init {
if (event.attachmentNames.isNotNullNorEmpty()) {
val id = event.attachmentNames!![0]
webGet(TAG, "Homework/$id") { text ->
val description = Regexes.EDUDZIENNIK_HOMEWORK_DESCRIPTION.find(text)?.get(1)?.trim()
if (description != null) event.topic = Html.fromHtml(description).toString()
event.homeworkBody = ""
event.attachmentNames = null
data.eventList += event
data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
} else {
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
}
}

View File

@ -33,8 +33,8 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
if (doc.getElementsByClass("message").text().trim() != "Brak prac domowych") { if (doc.getElementsByClass("message").text().trim() != "Brak prac domowych") {
doc.getElementsByTag("tr").forEach { homeworkElement -> doc.getElementsByTag("tr").forEach { homeworkElement ->
val dateElement = homeworkElement.getElementsByClass("date").first().child(0) val dateElement = homeworkElement.getElementsByClass("date").first().child(0)
val id = EDUDZIENNIK_HOMEWORK_ID.find(dateElement.attr("href"))?.get(1)?.crc32() val idStr = EDUDZIENNIK_HOMEWORK_ID.find(dateElement.attr("href"))?.get(1) ?: return@forEach
?: return@forEach val id = idStr.crc32()
val date = Date.fromY_m_d(dateElement.text()) val date = Date.fromY_m_d(dateElement.text())
val subjectElement = homeworkElement.child(1).child(0) val subjectElement = homeworkElement.child(1).child(0)
@ -49,22 +49,23 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
val teacherName = homeworkElement.child(2).text() val teacherName = homeworkElement.child(2).text()
val teacher = data.getTeacherByFirstLast(teacherName) val teacher = data.getTeacherByFirstLast(teacherName)
val topic = homeworkElement.child(4).text() val topic = homeworkElement.child(4).text()?.trim()
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
date, date = date,
startTime, time = startTime,
topic, topic = topic ?: "",
-1, color = null,
Event.TYPE_HOMEWORK, type = Event.TYPE_HOMEWORK,
false, teacherId = teacher.id,
teacher.id, subjectId = subject.id,
subject.id, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
eventObject.attachmentNames = mutableListOf(idStr)
data.eventList.add(eventObject) data.eventList.add(eventObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-7.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.helper
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.FileCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.ERROR_ONEDRIVE_DOWNLOAD
import pl.szczodrzynski.edziennik.data.api.ERROR_REQUEST_FAILURE
import pl.szczodrzynski.edziennik.data.api.SYSTEM_USER_AGENT
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class OneDriveDownloadAttachment(
app: App,
fileUrl: String,
val onSuccess: (file: File) -> Unit,
val onProgress: (written: Long, total: Long) -> Unit,
val onError: (apiError: ApiError) -> Unit
) {
companion object {
private const val TAG = "OneDriveDownloadAttachment"
}
init {
Request.builder()
.url(fileUrl)
.userAgent(SYSTEM_USER_AGENT)
.withClient(app.httpLazy)
.callback(object : TextCallbackHandler() {
override fun onSuccess(text: String, response: Response) {
val location = response.headers().get("Location")
// https://onedrive.live.com/redir?resid=D75496A2EB87531C!706&authkey=!ABjZeh3pHMqj11Q
if (location?.contains("onedrive.live.com/redir?resid=") != true) {
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
.withApiResponse(text)
.withResponse(response))
return
}
val url = location
.replace("onedrive.live.com/redir?resid=", "storage.live.com/items/")
.replace("?", "&")
.replaceFirst("&", "?")
downloadFile(url)
}
override fun onFailure(response: Response, throwable: Throwable) {
onError(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
})
.build()
.enqueue()
}
private fun downloadFile(url: String) {
val targetFile = Utils.getStorageDir()
val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) {
if (file == null) {
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
.withResponse(response))
return
}
try {
onSuccess(file)
} catch (e: Exception) {
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
.withResponse(response)
.withThrowable(e))
}
}
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
try {
this@OneDriveDownloadAttachment.onProgress(bytesWritten, bytesTotal)
} catch (e: Exception) {
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
.withThrowable(e))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
onError(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url(url)
.userAgent(SYSTEM_USER_AGENT)
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -8,20 +8,15 @@ import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetAttachment import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetRecipientList
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin.IdziennikFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin.IdziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLogin import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLogin
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -103,10 +98,17 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
override fun markAllAnnouncementsAsRead() {} override fun markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {} override fun getAnnouncement(announcement: AnnouncementFull) {}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
login(LOGIN_METHOD_IDZIENNIK_WEB) { login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetAttachment(data, message, attachmentId, attachmentName) { if (owner is Message) {
completed() IdziennikWebGetAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
}
else if (owner is Event) {
IdziennikWebGetHomeworkAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
} }
} }
} }
@ -119,10 +121,19 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
} }
} }
override fun getEvent(eventFull: EventFull) {
login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { IdziennikFirstLogin(data) { completed() } } override fun firstLogin() { IdziennikFirstLogin(data) { completed() } }
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")
data.cancel() data.cancel()
callback.onCompleted()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {

View File

@ -229,6 +229,7 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
.apply { .apply {
parameters.forEach { (k, v) -> addParameter(k, v) } parameters.forEach { (k, v) -> addParameter(k, v) }
} }
.contentType("application/x-www-form-urlencoded")
.post() .post()
.callback(callback) .callback(callback)
.build() .build()

View File

@ -11,8 +11,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.entity.* import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_DELETED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.getBoolean import pl.szczodrzynski.edziennik.getBoolean
import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.crc32 import pl.szczodrzynski.edziennik.utils.Utils.crc32
@ -33,11 +33,11 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
return@apiGet return@apiGet
} }
json.asJsonObjectList()?.forEach { jMessage -> json.asJsonObjectList().forEach { jMessage ->
val subject = jMessage.getString("tytul") val subject = jMessage.getString("tytul") ?: ""
if (subject?.contains("(") == true && subject.startsWith("iDziennik - ")) if (subject.contains("(") && subject.startsWith("iDziennik - "))
return@forEach return@forEach
if (subject?.startsWith("Uwaga dla ucznia (klasa:") == true) if (subject.startsWith("Uwaga dla ucznia (klasa:"))
return@forEach return@forEach
val messageIdStr = jMessage.getString("id") val messageIdStr = jMessage.getString("id")
@ -64,13 +64,12 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
rTeacher.setTeacherType(Teacher.TYPE_OTHER) rTeacher.setTeacherType(Teacher.TYPE_OTHER)
val message = Message( val message = Message(
profileId, profileId = profileId,
messageId, id = messageId,
subject, type = if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED,
body, subject = subject,
if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED, body = body,
rTeacher.id, senderId = rTeacher.id
-1
) )
val messageRecipient = MessageRecipient( val messageRecipient = MessageRecipient(
@ -81,7 +80,7 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
/*messageId*/ messageId /*messageId*/ messageId
) )
data.messageIgnoreList.add(message) data.messageList.add(message)
data.messageRecipientList.add(messageRecipient) data.messageRecipientList.add(messageRecipient)
data.setSeenMetadataList.add(Metadata( data.setSeenMetadataList.add(Metadata(
profileId, profileId,

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_SENT import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.Utils.crc32 import pl.szczodrzynski.edziennik.utils.Utils.crc32
@ -46,13 +46,12 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
val sentDate = Date.fromIso(jMessage.get("dataWyslania").asString) val sentDate = Date.fromIso(jMessage.get("dataWyslania").asString)
val message = Message( val message = Message(
profileId, profileId = profileId,
messageId, id = messageId,
subject, type = TYPE_SENT,
body, subject = subject,
TYPE_SENT, body = body,
-1, senderId = null
-1
) )
for (recipientEl in jMessage.getAsJsonArray("odbiorcy")) { for (recipientEl in jMessage.getAsJsonArray("odbiorcy")) {
@ -76,7 +75,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
data.messageRecipientIgnoreList.add(messageRecipient) data.messageRecipientIgnoreList.add(messageRecipient)
} }
data.messageIgnoreList.add(message) data.messageList.add(message)
data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, sentDate)) data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, sentDate))
} }

View File

@ -66,7 +66,7 @@ class IdziennikWebExams(override val data: DataIdziennik,
val subjectId = data.getSubject(subjectName, null, subjectName).id val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = exam.getString("wpisal") ?: return@forEach val teacherName = exam.getString("wpisal") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id val teacherId = data.getTeacherByLastFirst(teacherName).id
val topic = exam.getString("zakres") ?: "" val topic = exam.getString("zakres")?.trim() ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate) val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
@ -80,17 +80,16 @@ class IdziennikWebExams(override val data: DataIdziennik,
} }
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
examDate, date = examDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
eventType, type = eventType,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File import java.io.File
class IdziennikWebGetAttachment(override val data: DataIdziennik, class IdziennikWebGetAttachment(override val data: DataIdziennik,
val message: Message, val owner: Any,
val attachmentId: Long, val attachmentId: Long,
val attachmentName: String, val attachmentName: String,
val onSuccess: () -> Unit val onSuccess: () -> Unit
@ -25,6 +25,8 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
} }
init { init {
val message = owner as Message
val messageId = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex().find(message.body ?: "")?.get(2) ?: -1 val messageId = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex().find(message.body ?: "")?.get(2) ?: -1
val targetFile = File(Utils.getStorageDir(), attachmentName) val targetFile = File(Utils.getStorageDir(), attachmentName)
@ -34,29 +36,29 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
), { file -> ), { file ->
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_FINISHED, AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath file.absolutePath
) )
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}") val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName) Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event) EventBus.getDefault().postSticky(event)
onSuccess() onSuccess()
}) { written, _ -> }) { written, _ ->
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_PROGRESS, AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written bytesWritten = written
) )
EventBus.getDefault().post(event) EventBus.getDefault().postSticky(event)
} }
} }
} }

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-1.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.getBoolean
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
class IdziennikWebGetHomework(override val data: DataIdziennik,
val event: EventFull,
val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object {
private const val TAG = "IdziennikWebGetHomework"
}
init {
webApiGet(TAG, IDZIENNIK_WEB_GET_HOMEWORK, mapOf(
"idP" to data.registerId,
"idPD" to event.id
)) { result ->
val json = result.getJsonObject("d") ?: run {
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
.withApiResponse(result))
return@webApiGet
}
val homework = json.getJsonObject("praca") ?: return@webApiGet
if (homework.getBoolean("zalacznik", false)) {
event.attachmentIds = mutableListOf(event.id)
event.attachmentNames = mutableListOf("Załącznik do zadania")
}
else {
event.attachmentIds = mutableListOf()
event.attachmentNames = mutableListOf()
}
event.homeworkBody = homework.getString("tresc")
data.eventList.add(event)
data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-1.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import com.google.gson.JsonObject
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.set
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class IdziennikWebGetHomeworkAttachment(override val data: DataIdziennik,
val owner: Any,
val attachmentId: Long,
val attachmentName: String,
val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object {
const val TAG = "IdziennikWebGetHomeworkAttachment"
}
init {
val homework = owner as Event
/*val request = Request.Builder()
.url("")
.build()
data.app.http.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withThrowable(e))
}
override fun onResponse(call: Call, response: Response) {
val filename = response.header("content-disposition")?.substringAfter("\"")?.substringBeforeLast("\"")
val file: File = File(Utils.getStorageDir(), filename)
val sink = file.sink().buffer()
response.body()?.source()?.let {
sink.writeAll(it)
}
sink.close()
}
})*/
webGet(TAG, IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT) { text ->
val hiddenFields = JsonObject()
Regexes.IDZIENNIK_LOGIN_HIDDEN_FIELDS.findAll(text).forEach {
hiddenFields[it[1]] = it[2]
}
webGetFile(TAG, IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT, Utils.getStorageDir(), mapOf(
"__VIEWSTATE" to hiddenFields.getString("__VIEWSTATE", ""),
"__VIEWSTATEGENERATOR" to hiddenFields.getString("__VIEWSTATEGENERATOR", ""),
"__EVENTVALIDATION" to hiddenFields.getString("__EVENTVALIDATION", ""),
"__EVENTTARGET" to "ctl00\$cphContent\$bt_pobraniePliku",
"ctl00\$dxComboUczniowie" to data.registerId,
"ctl00\$cphContent\$idPracyDomowej" to attachmentId
), { file ->
val event = AttachmentGetEvent(
profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
homework.attachmentNames = mutableListOf(file.name)
data.eventList.add(homework)
data.eventListReplace = true
EventBus.getDefault().postSticky(event)
onSuccess()
}) { written, _ ->
val event = AttachmentGetEvent(
profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().postSticky(event)
}
}
}
}

View File

@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_MESSAGE
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
@ -50,7 +50,11 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
message.recipients?.clear() message.recipients?.clear()
when (message.type) { when (message.type) {
TYPE_RECEIVED -> { TYPE_RECEIVED -> {
val recipientObject = MessageRecipientFull(profileId, -1, message.id) val recipientObject = MessageRecipientFull(
profileId = profileId,
id = -1,
messageId = message.id
)
val readDateString = it.getString("DataOdczytania") val readDateString = it.getString("DataOdczytania")
recipientObject.readDate = if (readDateString.isNullOrBlank()) System.currentTimeMillis() recipientObject.readDate = if (readDateString.isNullOrBlank()) System.currentTimeMillis()
@ -67,7 +71,11 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
val recipientName = recipient.getString("NazwaOdbiorcy") ?: return@forEach val recipientName = recipient.getString("NazwaOdbiorcy") ?: return@forEach
val teacher = data.getTeacherByLastFirst(recipientName) val teacher = data.getTeacherByLastFirst(recipientName)
val recipientObject = MessageRecipientFull(profileId, teacher.id, message.id) val recipientObject = MessageRecipientFull(
profileId = profileId,
id = teacher.id,
messageId = message.id
)
recipientObject.readDate = recipient.getLong("Status") ?: return@forEach recipientObject.readDate = recipient.getLong("Status") ?: return@forEach
recipientObject.fullName = teacher.fullName recipientObject.fullName = teacher.fullName
@ -91,9 +99,10 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
)) ))
} }
EventBus.getDefault().postSticky(MessageGetEvent(message))
data.messageList.add(message) data.messageList.add(message)
data.messageListReplace = true
EventBus.getDefault().postSticky(MessageGetEvent(message))
onSuccess() onSuccess()
} }
} }

View File

@ -52,13 +52,14 @@ class IdziennikWebHomework(override val data: DataIdziennik,
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { homework -> json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { homework ->
val id = homework.getLong("_recordId") ?: return@forEach val id = homework.getLong("_recordId") ?: return@forEach
val eventDate = Date.fromY_m_d(homework.getString("dataO") ?: return@forEach) val eventDate = Date.fromY_m_d(homework.getString("dataO") ?: return@forEach)
val addedDate = Date.fromY_m_d(homework.getString("dataZ") ?: return@forEach)
val subjectName = homework.getString("przed") ?: return@forEach val subjectName = homework.getString("przed") ?: return@forEach
val subjectId = data.getSubject(subjectName, null, subjectName).id val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = homework.getString("usr") ?: return@forEach val teacherName = homework.getString("usr") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id val teacherId = data.getTeacherByLastFirst(teacherName).id
val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate) val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime
val topic = homework.getString("tytul") ?: "" val topic = homework.getString("tytul")?.trim() ?: ""
val seen = when (profile?.empty) { val seen = when (profile?.empty) {
true -> true true -> true
@ -67,17 +68,16 @@ class IdziennikWebHomework(override val data: DataIdziennik,
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
Event.TYPE_HOMEWORK, type = Event.TYPE_HOMEWORK,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)
@ -87,7 +87,7 @@ class IdziennikWebHomework(override val data: DataIdziennik,
eventObject.id, eventObject.id,
seen, seen,
seen, seen,
System.currentTimeMillis() addedDate.inMillis
)) ))
} }

View File

@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_DESCRIPTIVE
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
@ -20,7 +21,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getJsonArray import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.getWordGradeValue
class IdziennikWebProposedGrades(override val data: DataIdziennik, class IdziennikWebProposedGrades(override val data: DataIdziennik,
override val lastSync: Long?, override val lastSync: Long?,
@ -39,36 +39,64 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
.withApiResponse(result)) .withApiResponse(result))
return@webApiGet return@webApiGet
} }
val manager = data.app.gradesManager
json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { subject -> json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { subject ->
val subjectName = subject.getString("Przedmiot") ?: return@forEach val subjectName = subject.getString("Przedmiot") ?: return@forEach
val subjectObject = data.getSubject(subjectName, null, subjectName) val subjectObject = data.getSubject(subjectName, null, subjectName)
val semester1Proposed = subject.getString("OcenaSem1") ?: "" val semester1Proposed = subject.getString("OcenaSem1") ?: ""
val semester1Value = getWordGradeValue(semester1Proposed) val semester1Value = manager.getGradeValue(semester1Proposed)
val semester1Id = subjectObject.id * (-100) - 1 val semester1Id = subjectObject.id * (-100) - 1
val semester1Type =
if (semester1Value == 0f) TYPE_DESCRIPTIVE
else TYPE_SEMESTER1_PROPOSED
val semester1Name = when {
semester1Value == 0f -> " "
semester1Value % 1.0f == 0f -> semester1Value.toInt().toString()
else -> semester1Value.toString()
}
val semester1Color =
if (semester1Value == 0f) 0xff536dfe.toInt()
else -1
val semester2Proposed = subject.getString("OcenaSem2") ?: "" val semester2Proposed = subject.getString("OcenaSem2") ?: ""
val semester2Value = getWordGradeValue(semester2Proposed) val semester2Value = manager.getGradeValue(semester2Proposed)
val semester2Id = subjectObject.id * (-100) - 2 val semester2Id = subjectObject.id * (-100) - 2
val semester2Type =
if (semester2Value == 0f) TYPE_DESCRIPTIVE
else TYPE_YEAR_PROPOSED
val semester2Name = when {
semester2Value == 0f -> " "
semester2Value % 1.0f == 0f -> semester2Value.toInt().toString()
else -> semester2Value.toString()
}
val semester2Color =
if (semester2Value == 0f) 0xffff4081.toInt()
else -1
if (semester1Proposed != "") { if (semester1Proposed != "") {
val gradeObject = Grade( val gradeObject = Grade(
profileId = profileId, profileId = profileId,
id = semester1Id, id = semester1Id,
name = semester1Value.toString(), name = semester1Name,
type = TYPE_SEMESTER1_PROPOSED, type = semester1Type,
value = semester1Value.toFloat(), value = semester1Value,
weight = 0f, weight = 0f,
color = -1, color = semester1Color,
category = null, category = if (semester1Value == 0f) "Ocena opisowa semestralna" else null,
description = null, description = if (semester1Value == 0f) semester1Proposed else null,
comment = null, comment = null,
semester = 1, semester = 1,
teacherId = -1, teacherId = -1,
subjectId = subjectObject.id subjectId = subjectObject.id
) )
val addedDate = if (data.profile.empty)
data.profile.dateSemester1Start.inMillis
else
System.currentTimeMillis()
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
@ -76,7 +104,7 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
gradeObject.id, gradeObject.id,
profile.empty, profile.empty,
profile.empty, profile.empty,
System.currentTimeMillis() addedDate
)) ))
} }
@ -84,13 +112,13 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
val gradeObject = Grade( val gradeObject = Grade(
profileId = profileId, profileId = profileId,
id = semester2Id, id = semester2Id,
name = semester2Value.toString(), name = semester2Name,
type = TYPE_YEAR_PROPOSED, type = semester2Type,
value = semester2Value.toFloat(), value = semester2Value,
weight = 0f, weight = 0f,
color = -1, color = semester2Color,
category = null, category = if (semester2Value == 0f) "Ocena opisowa końcoworoczna" else null,
description = null, description = if (semester2Value == 0f) semester2Proposed else null,
comment = null, comment = null,
semester = 2, semester = 2,
teacherId = -1, teacherId = -1,
@ -98,7 +126,7 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
) )
val addedDate = if (data.profile.empty) val addedDate = if (data.profile.empty)
data.profile.dateSemester1Start.inMillis data.profile.dateSemester2Start.inMillis
else else
System.currentTimeMillis() System.currentTimeMillis()

View File

@ -57,7 +57,7 @@ class IdziennikWebSendMessage(override val data: DataIdziennik,
} }
IdziennikApiMessagesSent(data, null) { IdziennikApiMessagesSent(data, null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject } val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)

View File

@ -13,6 +13,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.Librus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetMessage import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetRecipientList import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetRecipientList
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesSendMessage import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaGetHomework
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaHomeworkGetAttachment
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin
@ -24,6 +26,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -118,11 +121,23 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
login(LOGIN_METHOD_LIBRUS_MESSAGES) { when (owner) {
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) { is Message -> {
completed() login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
}
} }
is EventFull -> {
login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
LibrusSynergiaHomeworkGetAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
}
}
else -> completed()
} }
} }
@ -134,10 +149,19 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
} }
override fun getEvent(eventFull: EventFull) {
login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
LibrusSynergiaGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { LibrusFirstLogin(data) { completed() } } override fun firstLogin() { LibrusFirstLogin(data) { completed() } }
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")
data.cancel() data.cancel()
callback.onCompleted()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {

View File

@ -252,10 +252,11 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
.enqueue() .enqueue()
} }
fun sandboxGetFile(tag: String, action: String, targetFile: File, onSuccess: (file: File) -> Unit, fun sandboxGetFile(tag: String, url: String, targetFile: File, onSuccess: (file: File) -> Unit,
method: Int = GET,
onProgress: (written: Long, total: Long) -> Unit) { onProgress: (written: Long, total: Long) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action") d(tag, "Request: Librus/Messages - $url")
val callback = object : FileCallbackHandler(targetFile) { val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) { override fun onSuccess(file: File?, response: Response?) {
@ -291,9 +292,14 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
} }
Request.builder() Request.builder()
.url("$LIBRUS_SANDBOX_URL$action") .url(url)
.userAgent(SYNERGIA_USER_AGENT) .userAgent(SYNERGIA_USER_AGENT)
.post() .also {
when (method) {
POST -> it.post()
else -> it.get()
}
}
.callback(callback) .callback(callback)
.build() .build()
.enqueue() .enqueue()

View File

@ -35,7 +35,7 @@ open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) {
return return
} }
if (!text.contains("jesteś zalogowany")) { if (!text.contains("jesteś zalogowany") && !text.contains("Podgląd zadania")) {
when { when {
text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED
text.contains("Przerwa techniczna") -> ERROR_LIBRUS_SYNERGIA_MAINTENANCE text.contains("Przerwa techniczna") -> ERROR_LIBRUS_SYNERGIA_MAINTENANCE
@ -48,7 +48,6 @@ open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) {
} }
} }
try { try {
onSuccess(text) onSuccess(text)
} catch (e: Exception) { } catch (e: Exception) {
@ -90,4 +89,42 @@ open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) {
.build() .build()
.enqueue() .enqueue()
} }
fun redirectUrlGet(tag: String, url: String, onSuccess: (url: String) -> Unit) {
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response) {
val redirectUrl = response.headers().get("Location")
if (redirectUrl != null) {
try {
onSuccess(redirectUrl)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_SYNERGIA_REQUEST)
.withResponse(response)
.withThrowable(e)
.withApiResponse(text))
}
} else {
data.error(ApiError(tag, ERROR_LIBRUS_SYNERGIA_OTHER)
.withResponse(response)
.withApiResponse(text))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url(url)
.userAgent(LIBRUS_USER_AGENT)
.withClient(data.app.httpLazy)
.get()
.callback(callback)
.build()
.enqueue()
}
} }

View File

@ -35,7 +35,7 @@ class LibrusApiEvents(override val data: DataLibrus,
events?.forEach { event -> events?.forEach { event ->
val id = event.getLong("Id") ?: return@forEach val id = event.getLong("Id") ?: return@forEach
val eventDate = Date.fromY_m_d(event.getString("Date")) val eventDate = Date.fromY_m_d(event.getString("Date"))
val topic = event.getString("Content") ?: "" val topic = event.getString("Content")?.trim() ?: ""
val type = event.getJsonObject("Category")?.getLong("Id") ?: -1 val type = event.getJsonObject("Category")?.getLong("Id") ?: -1
val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1 val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1
val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1 val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1
@ -47,17 +47,16 @@ class LibrusApiEvents(override val data: DataLibrus,
val addedDate = Date.fromIso(event.getString("AddDate")) val addedDate = Date.fromIso(event.getString("AddDate"))
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
type, type = type,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = teamId
teamId
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -34,17 +34,16 @@ class LibrusApiHomework(override val data: DataLibrus,
val addedDate = Date.fromY_m_d(homework.getString("Date")) val addedDate = Date.fromY_m_d(homework.getString("Date"))
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
null, time = null,
topic, topic = topic,
-1, color = null,
-1, type = -1,
false, teacherId = teacherId,
teacherId, subjectId = -1,
-1, teamId = -1
-1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -39,17 +39,16 @@ class LibrusApiPtMeetings(override val data: DataLibrus,
} }
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
Event.TYPE_PT_MEETING, type = Event.TYPE_PT_MEETING,
false, teacherId = teacherId,
teacherId, subjectId = -1,
-1, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -59,7 +59,7 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus,
profileId, profileId,
Metadata.TYPE_TEACHER_ABSENCE, Metadata.TYPE_TEACHER_ABSENCE,
id, id,
profile?.empty ?: false, true,
profile?.empty ?: false, profile?.empty ?: false,
System.currentTimeMillis() System.currentTimeMillis()
)) ))

View File

@ -4,22 +4,12 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import org.greenrobot.eventbus.EventBus import kotlinx.coroutines.Dispatchers
import pl.szczodrzynski.edziennik.data.api.ERROR_FILE_DOWNLOAD import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.data.api.EXCEPTION_LIBRUS_MESSAGES_REQUEST
import pl.szczodrzynski.edziennik.data.api.Regexes
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.LibrusMessages import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent.Companion.TYPE_FINISHED
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent.Companion.TYPE_PROGRESS
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class LibrusMessagesGetAttachment(override val data: DataLibrus, class LibrusMessagesGetAttachment(override val data: DataLibrus,
@ -37,8 +27,6 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default get() = job + Dispatchers.Default
private var getAttachmentCheckKeyTries = 0
init { init {
messagesGet(TAG, "GetFileDownloadLink", parameters = mapOf( messagesGet(TAG, "GetFileDownloadLink", parameters = mapOf(
"fileId" to attachmentId, "fileId" to attachmentId,
@ -46,81 +34,8 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
"archive" to 0 "archive" to 0
)) { doc -> )) { doc ->
val downloadLink = doc.select("response GetFileDownloadLink downloadLink").text() val downloadLink = doc.select("response GetFileDownloadLink downloadLink").text()
val keyMatcher = Regexes.LIBRUS_ATTACHMENT_KEY.find(downloadLink)
if (keyMatcher != null) { LibrusSandboxDownloadAttachment(data, downloadLink, message, attachmentId, attachmentName, onSuccess)
getAttachmentCheckKeyTries = 0
val attachmentKey = keyMatcher[1]
getAttachmentCheckKey(attachmentKey) {
downloadAttachment(attachmentKey)
}
} else {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withApiResponse(doc.toString()))
}
}
}
private fun getAttachmentCheckKey(attachmentKey: String, callback: () -> Unit) {
sandboxGet(TAG, "CSCheckKey",
parameters = mapOf("singleUseKey" to attachmentKey)) { json ->
when (json.getString("status")) {
"not_downloaded_yet" -> {
if (getAttachmentCheckKeyTries++ > 5) {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withApiResponse(json))
return@sandboxGet
}
launch {
delay(2000)
getAttachmentCheckKey(attachmentKey, callback)
}
}
"ready" -> {
launch { callback() }
}
else -> {
data.error(ApiError(TAG, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
.withApiResponse(json))
}
}
}
}
private fun downloadAttachment(attachmentKey: String) {
val targetFile = File(Utils.getStorageDir(), attachmentName)
sandboxGetFile(TAG, "CSDownload&singleUseKey=$attachmentKey", targetFile, { file ->
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event)
onSuccess()
}) { written, _ ->
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().post(event)
} }
} }
} }

View File

@ -12,7 +12,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_MESS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_MESSAGES_SENT import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
import pl.szczodrzynski.edziennik.data.db.entity.* import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.fixName import pl.szczodrzynski.edziennik.fixName
import pl.szczodrzynski.edziennik.singleOrNull import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
@ -78,7 +78,7 @@ class LibrusMessagesGetList(override val data: DataLibrus,
val senderId = when (type) { val senderId = when (type) {
TYPE_RECEIVED -> recipientId TYPE_RECEIVED -> recipientId
else -> -1 else -> null
} }
val receiverId = when (type) { val receiverId = when (type) {
@ -92,13 +92,12 @@ class LibrusMessagesGetList(override val data: DataLibrus,
} }
val messageObject = Message( val messageObject = Message(
profileId, profileId = profileId,
id, id = id,
subject, type = type,
null, subject = subject,
type, body = null,
senderId, senderId = senderId
-1
) )
val messageRecipientObject = MessageRecipient( val messageRecipientObject = MessageRecipient(
@ -109,7 +108,12 @@ class LibrusMessagesGetList(override val data: DataLibrus,
id id
) )
data.messageIgnoreList.add(messageObject) element.select("isAnyFileAttached")?.text()?.let {
if (it == "1")
messageObject.hasAttachments = true
}
data.messageList.add(messageObject)
data.messageRecipientList.add(messageRecipientObject) data.messageRecipientList.add(messageRecipientObject)
data.setSeenMetadataList.add(Metadata( data.setSeenMetadataList.add(Metadata(
profileId, profileId,

View File

@ -9,8 +9,8 @@ import org.greenrobot.eventbus.EventBus
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.LibrusMessages import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
@ -102,11 +102,10 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
} }
val messageRecipientObject = MessageRecipientFull( val messageRecipientObject = MessageRecipientFull(
profileId, profileId = profileId,
-1, id = -1,
-1, messageId = messageObject.id,
readDate, readDate = readDate
messageObject.id
) )
messageRecipientObject.fullName = profile.accountName ?: profile.studentNameLong ?: "" messageRecipientObject.fullName = profile.accountName ?: profile.studentNameLong ?: ""
@ -132,11 +131,10 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
} }
val messageRecipientObject = MessageRecipientFull( val messageRecipientObject = MessageRecipientFull(
profileId, profileId = profileId,
receiverId, id = receiverId,
-1, messageId = messageObject.id,
readDate, readDate = readDate
messageObject.id
) )
messageRecipientObject.fullName = "$receiverFirstName $receiverLastName" messageRecipientObject.fullName = "$receiverFirstName $receiverLastName"
@ -159,7 +157,9 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
messageObject.recipients = messageRecipientList messageObject.recipients = messageRecipientList
data.messageRecipientList.addAll(messageRecipientList) data.messageRecipientList.addAll(messageRecipientList)
data.messageList.add(messageObject) data.messageList.add(messageObject)
data.messageListReplace = true
EventBus.getDefault().postSticky(MessageGetEvent(messageObject)) EventBus.getDefault().postSticky(MessageGetEvent(messageObject))
onSuccess() onSuccess()

View File

@ -48,7 +48,7 @@ class LibrusMessagesSendMessage(override val data: DataLibrus,
} }
LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = null) { LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.id == id } val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.id == id }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)

View File

@ -0,0 +1,117 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
import kotlin.coroutines.CoroutineContext
class LibrusSandboxDownloadAttachment(override val data: DataLibrus,
downloadLink: String,
val owner: Any,
val attachmentId: Long,
val attachmentName: String,
val onSuccess: () -> Unit
) : LibrusMessages(data, null), CoroutineScope {
companion object {
const val TAG = "LibrusSandboxDownloadAttachment"
}
private var job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
private var getAttachmentCheckKeyTries = 0
init {
val keyMatcher = Regexes.LIBRUS_ATTACHMENT_KEY.find(downloadLink)
when {
downloadLink.contains("CSDownloadFailed") -> {
data.error(ApiError(TAG, ERROR_LIBRUS_MESSAGES_ATTACHMENT_NOT_FOUND))
onSuccess()
}
keyMatcher != null -> {
getAttachmentCheckKeyTries = 0
val attachmentKey = keyMatcher[1]
getAttachmentCheckKey(attachmentKey) {
downloadAttachment("${LIBRUS_SANDBOX_URL}CSDownload&singleUseKey=$attachmentKey", method = POST)
}
}
else -> {
downloadAttachment("$downloadLink/get", method = GET)
}
}
}
private fun getAttachmentCheckKey(attachmentKey: String, callback: () -> Unit) {
sandboxGet(TAG, "CSCheckKey",
parameters = mapOf("singleUseKey" to attachmentKey)) { json ->
when (json.getString("status")) {
"not_downloaded_yet" -> {
if (getAttachmentCheckKeyTries++ > 5) {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withApiResponse(json))
return@sandboxGet
}
launch {
delay(2000)
getAttachmentCheckKey(attachmentKey, callback)
}
}
"ready" -> {
launch { callback() }
}
else -> {
data.error(ApiError(TAG, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
.withApiResponse(json))
}
}
}
}
private fun downloadAttachment(url: String, method: Int = GET) {
val targetFile = File(Utils.getStorageDir(), attachmentName)
sandboxGetFile(TAG, url, targetFile, { file ->
val event = AttachmentGetEvent(
profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().postSticky(event)
onSuccess()
}) { written, _ ->
val event = AttachmentGetEvent(
profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().postSticky(event)
}
}
}

View File

@ -0,0 +1,48 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia
import android.text.Html
import org.greenrobot.eventbus.EventBus
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusSynergia
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.db.full.EventFull
class LibrusSynergiaGetHomework(override val data: DataLibrus,
val event: EventFull,
val onSuccess: () -> Unit
) : LibrusSynergia(data, null) {
companion object {
const val TAG = "LibrusSynergiaGetHomework"
}
init {
synergiaGet(TAG, "moje_zadania/podglad/${event.id}") { text ->
val doc = Jsoup.parse(text)
val table = doc.select("table.decorated tbody > tr")
event.topic = table[1].select("td")[1].text()
event.homeworkBody = Html.fromHtml(table[5].select("td")[1].html()).toString()
event.attachmentIds = mutableListOf()
event.attachmentNames = mutableListOf()
if (table.size > 6) {
table[6].select("a").forEach { a ->
val attachmentId = a.attr("href").split('/')
.last().toLongOrNull() ?: return@forEach
val filename = a.text()
event.attachmentIds?.add(attachmentId)
event.attachmentNames?.add(filename)
}
}
data.eventList.add(event)
data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
}
}

View File

@ -42,8 +42,6 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
doc.select("table.myHomeworkTable > tbody").firstOrNull()?.also { homeworkTable -> doc.select("table.myHomeworkTable > tbody").firstOrNull()?.also { homeworkTable ->
val homeworkElements = homeworkTable.children() val homeworkElements = homeworkTable.children()
val graphElements = doc.select("table[border].center td[align=left] tbody").first().children()
homeworkElements.forEachIndexed { i, el -> homeworkElements.forEachIndexed { i, el ->
val elements = el.children() val elements = el.children()
@ -63,43 +61,22 @@ 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]")
.attr("title").trim()*/
var description = ""
graphElements.forEach { graphEl ->
graphEl.select("td[title]")?.also {
val title = it.attr("title")
val r = "Temat: (.*?)<br.?/>Data udostępnienia: (.*?)<br.?/>Termin wykonania: (.*?)<br.?/>Treść: (.*)"
.toRegex(RegexOption.DOT_MATCHES_ALL).find(title) ?: return@forEach
val gTopic = r[1].trim()
val gAddedDate = Date.fromY_m_d(r[2].trim())
val gEventDate = Date.fromY_m_d(r[3].trim())
if (gTopic == topic && gAddedDate == addedDate && gEventDate == eventDate) {
description = r[4].replace("<br.?/>".toRegex(), "\n").trim()
return@forEach
}
}
}
val seen = when (profile.empty) { val seen = when (profile.empty) {
true -> true true -> true
else -> eventDate < Date.getToday() else -> eventDate < Date.getToday()
} }
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
"$topic\n$description", topic = topic,
-1, color = null,
Event.TYPE_HOMEWORK, type = Event.TYPE_HOMEWORK,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -0,0 +1,25 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia
import pl.szczodrzynski.edziennik.data.api.LIBRUS_SYNERGIA_HOMEWORK_ATTACHMENT_URL
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusSynergia
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusSandboxDownloadAttachment
import pl.szczodrzynski.edziennik.data.db.full.EventFull
class LibrusSynergiaHomeworkGetAttachment(
override val data: DataLibrus,
val event: EventFull,
val attachmentId: Long,
val attachmentName: String,
val onSuccess: () -> Unit
) : LibrusSynergia(data, null) {
companion object {
const val TAG = "LibrusSynergiaHomeworkGetAttachment"
}
init {
redirectUrlGet(TAG, "$LIBRUS_SYNERGIA_HOMEWORK_ATTACHMENT_URL/$attachmentId") { url ->
LibrusSandboxDownloadAttachment(data, url, event, attachmentId, attachmentName, onSuccess)
}
}
}

View File

@ -8,20 +8,17 @@ import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikData import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebGetAttachment import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.*
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebGetMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebGetRecipientList
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.firstlogin.MobidziennikFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.firstlogin.MobidziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLogin import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLogin
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -105,9 +102,9 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
override fun markAllAnnouncementsAsRead() {} override fun markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {} override fun getAnnouncement(announcement: AnnouncementFull) {}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
login(LOGIN_METHOD_MOBIDZIENNIK_WEB) { login(LOGIN_METHOD_MOBIDZIENNIK_WEB) {
MobidziennikWebGetAttachment(data, message, attachmentId, attachmentName) { MobidziennikWebGetAttachment(data, owner, attachmentId, attachmentName) {
completed() completed()
} }
} }
@ -121,10 +118,19 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
} }
} }
override fun getEvent(eventFull: EventFull) {
login(LOGIN_METHOD_MOBIDZIENNIK_WEB) {
MobidziennikWebGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { MobidziennikFirstLogin(data) { completed() } } override fun firstLogin() { MobidziennikFirstLogin(data) { completed() } }
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")
data.cancel() data.cancel()
callback.onCompleted()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {

View File

@ -17,6 +17,7 @@ const val ENDPOINT_MOBIDZIENNIK_WEB_NOTICES = 2040
const val ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE = 2050 const val ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE = 2050
const val ENDPOINT_MOBIDZIENNIK_WEB_MANUALS = 2100 const val ENDPOINT_MOBIDZIENNIK_WEB_MANUALS = 2100
const val ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL = 2200 const val ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL = 2200
const val ENDPOINT_MOBIDZIENNIK_WEB_HOMEWORK = 2300 // not used as an endpoint
const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000 const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000
val MobidziennikFeatures = listOf( val MobidziennikFeatures = listOf(

View File

@ -30,7 +30,7 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List<String>) {
val teacherId = cols[1].toLong() val teacherId = cols[1].toLong()
val subjectId = cols[3].toLong() val subjectId = cols[3].toLong()
var type = Event.TYPE_DEFAULT var type = Event.TYPE_DEFAULT
var topic = cols[5] var topic = cols[5].trim()
Regexes.MOBIDZIENNIK_EVENT_TYPE.find(topic)?.let { Regexes.MOBIDZIENNIK_EVENT_TYPE.find(topic)?.let {
val typeText = it.groupValues[1] val typeText = it.groupValues[1]
when (typeText) { when (typeText) {
@ -51,17 +51,16 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List<String>) {
val eventObject = Event( val eventObject = Event(
data.profileId, profileId = data.profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
type, type = type,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = teamId)
teamId)
data.eventList.add(eventObject) data.eventList.add(eventObject)
data.metadataList.add( data.metadataList.add(
@ -76,6 +75,8 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List<String>) {
} }
} }
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK)) data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_DEFAULT))
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_EXAM))
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_SHORT_QUIZ))
} }
} }

View File

@ -4,6 +4,7 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api
import android.text.Html
import androidx.core.util.contains import androidx.core.util.contains
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.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
@ -25,22 +26,21 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List<String>) {
val id = cols[0].toLong() val id = cols[0].toLong()
val teacherId = cols[7].toLong() val teacherId = cols[7].toLong()
val subjectId = cols[6].toLong() val subjectId = cols[6].toLong()
val topic = cols[1] val topic = Html.fromHtml(cols[1])?.toString()?.trim() ?: ""
val eventDate = Date.fromYmd(cols[2]) val eventDate = Date.fromYmd(cols[2])
val startTime = Time.fromYmdHm(cols[3]) val startTime = Time.fromYmdHm(cols[3])
val eventObject = Event( val eventObject = Event(
data.profileId, profileId = data.profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
Event.TYPE_HOMEWORK, type = Event.TYPE_HOMEWORK,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = teamId)
teamId)
data.eventList.add(eventObject) data.eventList.add(eventObject)
data.metadataList.add( data.metadataList.add(

View File

@ -44,7 +44,7 @@ class MobidziennikApiTeams(val data: DataMobidziennik, tableTeams: List<String>?
val studentId = cols[1].toInt() val studentId = cols[1].toInt()
val teamId = cols[2].toLong() val teamId = cols[2].toLong()
val studentNumber = cols[4].toInt() val studentNumber = cols[4].toIntOrNull() ?: -1
if (studentId != data.studentId) if (studentId != data.studentId)
continue continue

View File

@ -61,26 +61,25 @@ class MobidziennikWebCalendar(override val data: DataMobidziennik,
val title = event.getString("title") val title = event.getString("title")
val comment = event.getString("comment") val comment = event.getString("comment")
var topic = title var topic = title ?: ""
if (title != comment) { if (title != comment) {
topic += "\n" + comment topic += "\n" + comment
} }
if (id == -1L) { if (id == -1L) {
id = crc16(topic?.toByteArray()).toLong() id = crc16(topic.toByteArray()).toLong()
} }
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, null, date = eventDate, time = null,
topic, topic = topic,
-1, color = null,
eventType, type = eventType,
false, teacherId = -1,
-1, subjectId = -1,
-1, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -8,12 +8,14 @@ import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
import java.io.File import java.io.File
class MobidziennikWebGetAttachment(override val data: DataMobidziennik, class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
val message: Message, val owner: Any,
val attachmentId: Long, val attachmentId: Long,
val attachmentName: String, val attachmentName: String,
val onSuccess: () -> Unit val onSuccess: () -> Unit
@ -25,25 +27,40 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
init { init {
val targetFile = File(Utils.getStorageDir(), attachmentName) val targetFile = File(Utils.getStorageDir(), attachmentName)
val typeUrl = if (message.type == Message.TYPE_SENT) val typeUrl = when (owner) {
"wiadwyslana" is Message -> if (owner.type == Message.TYPE_SENT)
else "dziennik/wiadwyslana/?id="
"wiadodebrana" else
"dziennik/wiadodebrana/?id="
webGetFile(TAG, "/dziennik/$typeUrl/?id=${message.id}&zalacznik=$attachmentId", targetFile, { file -> is Event -> if (owner.date >= Date.getToday())
"dziennik/wyslijzadanie/?id_zadania="
else
"dziennik/wyslijzadanie/?id_zadania="
else -> ""
}
val ownerId = when (owner) {
is Message -> owner.id
is Event -> owner.id
else -> -1
}
webGetFile(TAG, "/$typeUrl${ownerId}&uczen=${data.studentId}&zalacznik=$attachmentId", targetFile, { file ->
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_FINISHED, AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath file.absolutePath
) )
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}") val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName) Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event) EventBus.getDefault().postSticky(event)
onSuccess() onSuccess()
@ -51,13 +68,13 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
// TODO make use of bytesTotal // TODO make use of bytesTotal
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_PROGRESS, AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written bytesWritten = written
) )
EventBus.getDefault().post(event) EventBus.getDefault().postSticky(event)
} }
} }
} }

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-31.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date
class MobidziennikWebGetHomework(override val data: DataMobidziennik,
val event: EventFull,
val onSuccess: () -> Unit
) : MobidziennikWeb(data, null) {
companion object {
private const val TAG = "MobidziennikWebHomework"
}
init {
val endpoint = if (event.date >= Date.getToday())
"zadaniadomowe"
else
"zadaniadomowearchiwalne"
webGet(TAG, "/mobile/$endpoint") { text ->
MobidziennikLuckyNumberExtractor(data, text)
Regexes.MOBIDZIENNIK_HOMEWORK_ROW.findAll(text).forEach { homeworkMatch ->
val tableRow = homeworkMatch[1].ifBlank { return@forEach }
val id = Regexes.MOBIDZIENNIK_HOMEWORK_ID.find(tableRow)?.get(1)?.toLongOrNull() ?: return@forEach
if (event.id != id)
return@forEach
event.attachmentIds = mutableListOf()
event.attachmentNames = mutableListOf()
Regexes.MOBIDZIENNIK_HOMEWORK_ATTACHMENT.findAll(tableRow).forEach {
event.attachmentIds?.add(it[2].toLongOrNull() ?: return@forEach)
event.attachmentNames?.add(it[3])
}
event.homeworkBody = ""
}
data.eventList.add(event)
data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
}
}

View File

@ -11,7 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidzienn
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
@ -61,19 +61,17 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
} }
val recipient = MessageRecipientFull( val recipient = MessageRecipientFull(
profileId, profileId = profileId,
-1, id = -1,
-1, messageId = message.id,
readDate, readDate = readDate
message.id
) )
recipient.fullName = profile?.accountName ?: profile?.studentNameLong ?: "" recipient.fullName = profile?.accountName ?: profile?.studentNameLong ?: ""
messageRecipientList.add(recipient) messageRecipientList.add(recipient)
} else { } else {
message.senderId = -1 message.senderId = null
message.senderReplyId = -1
content.select("table.spis tr:has(td)")?.forEach { recipientEl -> content.select("table.spis tr:has(td)")?.forEach { recipientEl ->
val senderEl = recipientEl.select("td:eq(0)").first() val senderEl = recipientEl.select("td:eq(0)").first()
@ -100,11 +98,10 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
} }
val recipient = MessageRecipientFull( val recipient = MessageRecipientFull(
profileId, profileId = profileId,
receiverId, id = receiverId,
-1, messageId = message.id,
readDate, readDate = readDate
message.id
) )
recipient.fullName = teacher?.fullName ?: "?" recipient.fullName = teacher?.fullName ?: "?"
@ -149,7 +146,9 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
message.recipients = messageRecipientList message.recipients = messageRecipientList
data.messageRecipientList.addAll(messageRecipientList) data.messageRecipientList.addAll(messageRecipientList)
data.messageList.add(message) data.messageList.add(message)
data.messageListReplace = true
EventBus.getDefault().postSticky(MessageGetEvent(message)) EventBus.getDefault().postSticky(MessageGetEvent(message))
onSuccess() onSuccess()

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-31.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.get
class MobidziennikWebHomework(override val data: DataMobidziennik,
override val lastSync: Long?,
val type: Int = TYPE_CURRENT,
val event: EventFull,
val onSuccess: (endpointId: Int) -> Unit
) : MobidziennikWeb(data, lastSync) {
companion object {
private const val TAG = "MobidziennikWebHomework"
const val TYPE_CURRENT = 0
const val TYPE_PAST = 1
}
init {
val endpoint = when (type) {
TYPE_PAST -> "zadaniadomowearchiwalne"
else -> "zadaniadomowe"
}
webGet(TAG, "/mobile/$endpoint") { text ->
MobidziennikLuckyNumberExtractor(data, text)
Regexes.MOBIDZIENNIK_HOMEWORK_ROW.findAll(text).forEach { homeworkMatch ->
val tableRow = homeworkMatch[1].ifBlank { return@forEach }
/*val items = Regexes.MOBIDZIENNIK_HOMEWORK_ITEM.findAll(tableRow).map { match ->
match[1] to match[2].fixWhiteSpaces()
}.toList()*/
val id = Regexes.MOBIDZIENNIK_HOMEWORK_ID.find(tableRow)?.get(1)?.toLongOrNull() ?: return@forEach
if (event.id != id)
return@forEach
//val homeworkBody = Regexes.MOBIDZIENNIK_HOMEWORK_BODY.find(tableRow)?.get(1) ?: ""
event.attachmentIds = mutableListOf()
event.attachmentNames = mutableListOf()
Regexes.MOBIDZIENNIK_HOMEWORK_ATTACHMENT.findAll(tableRow).forEach {
event.attachmentIds?.add(it[1].toLongOrNull() ?: return@forEach)
event.attachmentNames?.add(it[2])
}
event.homeworkBody = ""
}
//data.eventList.add(eventObject)
//data.metadataList.add(
// Metadata(
// profileId,
// Metadata.TYPE_EVENT,
// eventObject.id,
// profile?.empty ?: false,
// profile?.empty ?: false,
// System.currentTimeMillis() /* no addedDate here though */
// ))
// not used as an endpoint
//data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS)
data.eventList.add(event)
data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_HOMEWORK)
}
}
}

View File

@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidzienn
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.fixName import pl.szczodrzynski.edziennik.fixName
@ -54,12 +54,12 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
type = TYPE_SENT type = TYPE_SENT
val senderEl = item.select("td:eq(3) div").first() val senderEl = item.select("td:eq(3) div").first()
var senderId: Long = -1 var senderId: Long? = null
if (type == TYPE_RECEIVED) { if (type == TYPE_RECEIVED) {
// search sender teacher // search sender teacher
val senderName = senderEl.text().fixName() val senderName = senderEl.text().fixName()
senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id ?: -1 senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id
data.messageRecipientList.add(MessageRecipient(profileId, -1, id)) data.messageRecipientList.add(MessageRecipient(profileId, -1, id))
} else { } else {
// TYPE_SENT, so multiple recipients possible // TYPE_SENT, so multiple recipients possible
@ -72,16 +72,15 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
} }
val message = Message( val message = Message(
profileId, profileId = profileId,
id, id = id,
subject, type = type,
null, subject = subject,
type, body = null,
senderId, senderId = senderId
-1
) )
data.messageIgnoreList.add(message) data.messageList.add(message)
data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, addedDate)) data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, addedDate))
} }

View File

@ -52,25 +52,24 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
val senderEl = item.select("td:eq(2)").first() val senderEl = item.select("td:eq(2)").first()
val senderName = senderEl.ownText().fixName() val senderName = senderEl.ownText().fixName()
val senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id ?: -1 val senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id
data.messageRecipientIgnoreList.add(MessageRecipient(profileId, -1, id)) data.messageRecipientIgnoreList.add(MessageRecipient(profileId, -1, id))
val isRead = item.select("td:eq(3) span").first().hasClass("wiadomosc_przeczytana") val isRead = item.select("td:eq(3) span").first().hasClass("wiadomosc_przeczytana")
val message = Message( val message = Message(
profileId, profileId = profileId,
id, id = id,
subject, type = Message.TYPE_RECEIVED,
null, subject = subject,
Message.TYPE_RECEIVED, body = null,
senderId, senderId = senderId
-1
) )
if (hasAttachments) if (hasAttachments)
message.setHasAttachments() message.hasAttachments = true
data.messageIgnoreList.add(message) data.messageList.add(message)
data.setSeenMetadataList.add( data.setSeenMetadataList.add(
Metadata( Metadata(
profileId, profileId,

View File

@ -73,19 +73,18 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
val addedDate = Date.fromIsoHm(addedDateEl.text()) val addedDate = Date.fromIsoHm(addedDateEl.text())
val message = Message( val message = Message(
profileId, profileId = profileId,
id, id = id,
subject, type = Message.TYPE_SENT,
null, subject = subject,
Message.TYPE_SENT, body = null,
-1, senderId = null
-1
) )
if (hasAttachments) if (hasAttachments)
message.setHasAttachments() message.hasAttachments = true
data.messageIgnoreList.add(message) data.messageList.add(message)
data.setSeenMetadataList.add( data.setSeenMetadataList.add(
Metadata( Metadata(
profileId, profileId,

View File

@ -43,7 +43,7 @@ class MobidziennikWebSendMessage(override val data: DataMobidziennik,
// TODO create MobidziennikWebMessagesSent and replace this // TODO create MobidziennikWebMessagesSent and replace this
MobidziennikWebMessagesAll(data, null) { MobidziennikWebMessagesAll(data, null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject } val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)

View File

@ -16,10 +16,10 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.prepare import pl.szczodrzynski.edziennik.data.api.prepare
import pl.szczodrzynski.edziennik.data.api.templateLoginMethods import pl.szczodrzynski.edziennik.data.api.templateLoginMethods
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -79,7 +79,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
} }
@ -87,6 +87,10 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
} }
override fun getEvent(eventFull: EventFull) {
}
override fun firstLogin() { override fun firstLogin() {
TemplateFirstLogin(data) { TemplateFirstLogin(data) {
completed() completed()
@ -96,6 +100,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")
data.cancel() data.cancel()
callback.onCompleted()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {

View File

@ -5,26 +5,31 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan
import com.google.gson.JsonObject import com.google.gson.JsonObject
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_API import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.helper.OneDriveDownloadAttachment
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanData import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanData
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiAttachments
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiMessagesChangeStatus import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiMessagesChangeStatus
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiSendMessage import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.firstlogin.VulcanFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.firstlogin.VulcanFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLogin import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLogin
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.prepare
import pl.szczodrzynski.edziennik.data.api.prepareFor
import pl.szczodrzynski.edziennik.data.api.vulcanLoginMethods
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import java.io.File
class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
companion object { companion object {
@ -87,8 +92,29 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
override fun getMessage(message: MessageFull) { override fun getMessage(message: MessageFull) {
login(LOGIN_METHOD_VULCAN_API) { login(LOGIN_METHOD_VULCAN_API) {
VulcanApiMessagesChangeStatus(data, message) { if (message.attachmentIds != null) {
completed() VulcanApiMessagesChangeStatus(data, message) {
completed()
}
return@login
}
val list = data.app.db.messageDao().getAllNow(data.profileId)
VulcanApiAttachments(data, list, message, MessageFull::class) { _ ->
list.forEach {
if (it.attachmentIds == null)
it.attachmentIds = mutableListOf()
data.messageList.add(it)
}
data.messageListReplace = true
if (message.seen) {
EventBus.getDefault().postSticky(MessageGetEvent(message))
completed()
return@VulcanApiAttachments
}
VulcanApiMessagesChangeStatus(data, message) {
completed()
}
} }
} }
} }
@ -101,26 +127,74 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
} }
override fun markAllAnnouncementsAsRead() { override fun markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {}
override fun getRecipientList() {}
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
val fileUrl = attachmentName.substringAfter(":")
if (attachmentName == fileUrl) {
data.error(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD))
return
}
OneDriveDownloadAttachment(
app,
fileUrl,
onSuccess = { file ->
val event = AttachmentGetEvent(
data.profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${data.profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().postSticky(event)
completed()
},
onProgress = { written, total ->
val event = AttachmentGetEvent(
data.profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().postSticky(event)
},
onError = { apiError ->
data.error(apiError)
}
)
} }
override fun getAnnouncement(announcement: AnnouncementFull) { override fun getEvent(eventFull: EventFull) {
login(LOGIN_METHOD_VULCAN_API) {
} val list = data.app.db.eventDao().getAllNow(data.profileId).filter { !it.addedManually }
VulcanApiAttachments(data, list, eventFull, EventFull::class) { _ ->
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { list.forEach {
it.homeworkBody = ""
} data.eventList.add(it)
}
override fun getRecipientList() { data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(eventFull))
completed()
}
}
} }
override fun firstLogin() { VulcanFirstLogin(data) { completed() } } override fun firstLogin() { VulcanFirstLogin(data) { completed() } }
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")
data.cancel() data.cancel()
callback.onCompleted()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-6.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api
import pl.szczodrzynski.edziennik.asJsonObjectList
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_HOMEWORK_ATTACHMENTS
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_MESSAGES_ATTACHMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getLong
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.reflect.KClass
class VulcanApiAttachments(override val data: DataVulcan,
val list: List<*>,
val owner: Any?,
val ownerClass: KClass<*>,
val onSuccess: (list: List<*>) -> Unit
) : VulcanApi(data, null) {
companion object {
const val TAG = "VulcanApiAttachments"
}
init { run {
val endpoint = when (ownerClass) {
MessageFull::class -> VULCAN_API_ENDPOINT_MESSAGES_ATTACHMENTS
EventFull::class -> VULCAN_API_ENDPOINT_HOMEWORK_ATTACHMENTS
else -> null
} ?: return@run
val idName = when (ownerClass) {
MessageFull::class -> "IdWiadomosc"
EventFull::class -> "IdZadanieDomowe"
else -> null
} ?: return@run
val startDate = profile?.getSemesterStart(profile?.currentSemester ?: 1)?.inUnix ?: 0
val endDate = Date.getToday().stepForward(0, 1, 0).inUnix
apiGet(TAG, endpoint, parameters = mapOf(
"DataPoczatkowa" to startDate,
"DataKoncowa" to endDate,
"LoginId" to data.studentLoginId,
"IdUczen" to data.studentId
)) { json, _ ->
json.getJsonArray("Data")?.asJsonObjectList()?.forEach { attachment ->
val id = attachment.getLong("Id") ?: return@forEach
val itemId = attachment.getLong(idName) ?: return@forEach
val url = attachment.getString("Url") ?: return@forEach
val fileName = "${attachment.getString("NazwaPliku")}:$url"
list.forEach {
if (it is MessageFull
&& it.profileId == profileId
&& it.id == itemId
&& it.attachmentIds?.contains(id) != true) {
if (it.attachmentIds == null)
it.attachmentIds = mutableListOf()
if (it.attachmentNames == null)
it.attachmentNames = mutableListOf()
it.attachmentIds?.add(id)
it.attachmentNames?.add(fileName)
}
if (it is EventFull
&& it.profileId == profileId
&& it.id == itemId
&& it.attachmentIds?.contains(id) != true) {
if (it.attachmentIds == null)
it.attachmentIds = mutableListOf()
if (it.attachmentNames == null)
it.attachmentNames = mutableListOf()
it.attachmentIds?.add(id)
it.attachmentNames?.add(fileName)
}
if (owner is MessageFull
&& it is MessageFull
&& owner.profileId == it.profileId
&& owner.id == it.id) {
owner.attachmentIds = it.attachmentIds
owner.attachmentNames = it.attachmentNames
}
if (owner is EventFull
&& it is EventFull
&& owner.profileId == it.profileId
&& owner.id == it.id) {
owner.attachmentIds = it.attachmentIds
owner.attachmentNames = it.attachmentNames
}
}
}
/*if (owner is MessageFull) {
list.forEach {
(it as? MessageFull)?.let { message ->
data.messageList.add(message)
}
}
data.messageListReplace = true
}
if (owner is EventFull) {
list.forEach {
(it as? EventFull)?.let { it1 ->
it1.homeworkBody = ""
data.eventList.add(it1)
}
}
data.eventListReplace = true
}*/
onSuccess(list)
}
}}
}

View File

@ -57,7 +57,7 @@ class VulcanApiEvents(override val data: DataVulcan,
val eventDate = Date.fromY_m_d(event.getString("DataTekst") ?: return@forEach) val eventDate = Date.fromY_m_d(event.getString("DataTekst") ?: return@forEach)
val subjectId = event.getLong("IdPrzedmiot") ?: -1 val subjectId = event.getLong("IdPrzedmiot") ?: -1
val teacherId = event.getLong("IdPracownik") ?: -1 val teacherId = event.getLong("IdPracownik") ?: -1
val topic = event.getString("Opis") ?: "" val topic = event.getString("Opis")?.trim() ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate) val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
@ -72,17 +72,16 @@ class VulcanApiEvents(override val data: DataVulcan,
val teamId = event.getLong("IdOddzial") ?: data.teamClass?.id ?: -1 val teamId = event.getLong("IdOddzial") ?: data.teamClass?.id ?: -1
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
type, type = type,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = teamId
teamId
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)

View File

@ -9,7 +9,7 @@ import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_MESSAGES_CHANGE_S
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_MESSAGES_INBOX import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.db.entity.* import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.text.replace import kotlin.text.replace
@ -44,8 +44,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
val body = message.getString("Tresc") ?: "" val body = message.getString("Tresc") ?: ""
val senderLoginId = message.getString("NadawcaId") ?: return@forEach val senderLoginId = message.getString("NadawcaId") ?: return@forEach
val senderId = data.teacherList val senderId = data.teacherList.singleOrNull { it.loginId == senderLoginId }?.id ?: {
.singleOrNull { it.loginId == senderLoginId }?.id ?: {
val senderName = message.getString("Nadawca") ?: "" val senderName = message.getString("Nadawca") ?: ""
@ -60,7 +59,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
data.teacherList.put(teacherObject.id, teacherObject) data.teacherList.put(teacherObject.id, teacherObject)
teacherObject.id teacherObject.id
} }
}.invoke() ?: -1 }.invoke()
val sentDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 } val sentDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 }
?: -1 ?: -1
@ -68,13 +67,12 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
?: -1 ?: -1
val messageObject = Message( val messageObject = Message(
profileId, profileId = profileId,
id, id = id,
subject, type = TYPE_RECEIVED,
body.replace("\n", "<br>"), subject = subject,
TYPE_RECEIVED, body = body.replace("\n", "<br>"),
senderId, senderId = senderId
-1
) )
val messageRecipientObject = MessageRecipient( val messageRecipientObject = MessageRecipient(
@ -85,7 +83,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
id id
) )
data.messageIgnoreList.add(messageObject) data.messageList.add(messageObject)
data.messageRecipientList.add(messageRecipientObject) data.messageRecipientList.add(messageRecipientObject)
data.setSeenMetadataList.add(Metadata( data.setSeenMetadataList.add(Metadata(
profileId, profileId,

View File

@ -11,7 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_MESSAGES_SENT import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
@ -92,16 +92,15 @@ class VulcanApiMessagesSent(override val data: DataVulcan,
} }
val messageObject = Message( val messageObject = Message(
profileId, profileId = profileId,
id, id = id,
subject, type = TYPE_SENT,
body.replace("\n", "<br>"), subject = subject,
TYPE_SENT, body = body.replace("\n", "<br>"),
-1, senderId = null
-1
) )
data.messageIgnoreList.add(messageObject) data.messageList.add(messageObject)
data.setSeenMetadataList.add(Metadata( data.setSeenMetadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_MESSAGE, Metadata.TYPE_MESSAGE,

View File

@ -52,7 +52,7 @@ class VulcanApiSendMessage(override val data: DataVulcan,
} }
VulcanApiMessagesSent(data, null) { VulcanApiMessagesSent(data, null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject } val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == messageId } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == messageId }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)

View File

@ -4,11 +4,21 @@
package pl.szczodrzynski.edziennik.data.api.events package pl.szczodrzynski.edziennik.data.api.events
data class AttachmentGetEvent(val profileId: Int, val messageId: Long, val attachmentId: Long, import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Message
data class AttachmentGetEvent(val profileId: Int, val owner: Any, val attachmentId: Long,
var eventType: Int = TYPE_PROGRESS, val fileName: String? = null, var eventType: Int = TYPE_PROGRESS, val fileName: String? = null,
val bytesWritten: Long = 0) { val bytesWritten: Long = 0) {
companion object { companion object {
const val TYPE_PROGRESS = 0 const val TYPE_PROGRESS = 0
const val TYPE_FINISHED = 1 const val TYPE_FINISHED = 1
} }
val ownerId
get() = when (owner) {
is Message -> owner.id
is Event -> owner.id
else -> -1
}
} }

View File

@ -0,0 +1,9 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-31.
*/
package pl.szczodrzynski.edziennik.data.api.events
import pl.szczodrzynski.edziennik.data.db.full.EventFull
data class EventGetEvent(val event: EventFull)

View File

@ -5,9 +5,9 @@
package pl.szczodrzynski.edziennik.data.api.interfaces package pl.szczodrzynski.edziennik.data.api.interfaces
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
interface EdziennikInterface { interface EdziennikInterface {
@ -16,8 +16,9 @@ interface EdziennikInterface {
fun sendMessage(recipients: List<Teacher>, subject: String, text: String) fun sendMessage(recipients: List<Teacher>, subject: String, text: String)
fun markAllAnnouncementsAsRead() fun markAllAnnouncementsAsRead()
fun getAnnouncement(announcement: AnnouncementFull) fun getAnnouncement(announcement: AnnouncementFull)
fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String)
fun getRecipientList() fun getRecipientList()
fun getEvent(eventFull: EventFull)
fun firstLogin() fun firstLogin()
fun cancel() fun cancel()
} }

View File

@ -86,6 +86,8 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
val gradeCategories = LongSparseArray<GradeCategory>() val gradeCategories = LongSparseArray<GradeCategory>()
var teacherOnConflictStrategy = OnConflictStrategy.IGNORE var teacherOnConflictStrategy = OnConflictStrategy.IGNORE
var eventListReplace = false
var messageListReplace = false
val classrooms = LongSparseArray<Classroom>() val classrooms = LongSparseArray<Classroom>()
val attendanceTypes = LongSparseArray<AttendanceType>() val attendanceTypes = LongSparseArray<AttendanceType>()
@ -125,7 +127,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
val teacherAbsenceList = mutableListOf<TeacherAbsence>() val teacherAbsenceList = mutableListOf<TeacherAbsence>()
val messageList = mutableListOf<Message>() val messageList = mutableListOf<Message>()
val messageIgnoreList = mutableListOf<Message>()
val messageRecipientList = mutableListOf<MessageRecipient>() val messageRecipientList = mutableListOf<MessageRecipient>()
val messageRecipientIgnoreList = mutableListOf<MessageRecipient>() val messageRecipientIgnoreList = mutableListOf<MessageRecipient>()
@ -181,7 +182,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
luckyNumberList.clear() luckyNumberList.clear()
teacherAbsenceList.clear() teacherAbsenceList.clear()
messageList.clear() messageList.clear()
messageIgnoreList.clear()
messageRecipientList.clear() messageRecipientList.clear()
messageRecipientIgnoreList.clear() messageRecipientIgnoreList.clear()
metadataList.clear() metadataList.clear()
@ -197,6 +197,13 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
profile.userCode = generateUserCode() profile.userCode = generateUserCode()
// update profile subname with class name, school year and account type
profile.subname = joinNotNullStrings(
" - ",
profile.studentClassName,
"${profile.studentSchoolYearStart}/${profile.studentSchoolYearStart + 1}"
) + " " + app.getString(if (profile.isParent) R.string.login_summary_account_parent else R.string.login_summary_account_child)
db.profileDao().add(profile) db.profileDao().add(profile)
db.loginStoreDao().add(loginStore) db.loginStoreDao().add(loginStore)
@ -284,7 +291,10 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
db.gradeDao().addAll(gradeList) db.gradeDao().addAll(gradeList)
} }
if (eventList.isNotEmpty()) { if (eventList.isNotEmpty()) {
db.eventDao().addAll(eventList) if (eventListReplace)
db.eventDao().replaceAll(eventList)
else
db.eventDao().upsertAll(eventList, removeNotKept = true)
} }
if (noticeList.isNotEmpty()) { if (noticeList.isNotEmpty()) {
db.noticeDao().clear(profile.id) db.noticeDao().clear(profile.id)
@ -301,10 +311,12 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
if (teacherAbsenceList.isNotEmpty()) if (teacherAbsenceList.isNotEmpty())
db.teacherAbsenceDao().addAll(teacherAbsenceList) db.teacherAbsenceDao().addAll(teacherAbsenceList)
if (messageList.isNotEmpty()) if (messageList.isNotEmpty()) {
db.messageDao().addAll(messageList) if (messageListReplace)
if (messageIgnoreList.isNotEmpty()) db.messageDao().replaceAll(messageList)
db.messageDao().addAllIgnore(messageIgnoreList) else
db.messageDao().upsertAll(messageList, removeNotKept = false) // TODO dataRemoveModel for messages
}
if (messageRecipientList.isNotEmpty()) if (messageRecipientList.isNotEmpty())
db.messageRecipientDao().addAll(messageRecipientList) db.messageRecipientDao().addAll(messageRecipientList)
if (messageRecipientIgnoreList.isNotEmpty()) if (messageRecipientIgnoreList.isNotEmpty())

View File

@ -56,9 +56,9 @@ open class DataRemoveModel {
} }
fun commit(profileId: Int, dao: EventDao) { fun commit(profileId: Int, dao: EventDao) {
type?.let { dao.removeFutureWithType(profileId, Date.getToday(), it) } type?.let { dao.dontKeepFutureWithType(profileId, Date.getToday(), it) }
exceptType?.let { dao.removeFutureExceptType(profileId, Date.getToday(), it) } exceptType?.let { dao.dontKeepFutureExceptType(profileId, Date.getToday(), it) }
exceptTypes?.let { dao.removeFutureExceptTypes(profileId, Date.getToday(), it) } exceptTypes?.let { dao.dontKeepFutureExceptTypes(profileId, Date.getToday(), it) }
} }
} }

View File

@ -196,6 +196,11 @@ class SzkolnyApi(val app: App) : CoroutineScope {
// skip blacklisted events // skip blacklisted events
if (event.id in blacklistedIds) if (event.id in blacklistedIds)
return@forEach return@forEach
// force nullable non-negative colors
if (event.color == -1)
event.color = null
// create the event for every matching team and profile // 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

View File

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

View File

@ -12,7 +12,8 @@ data class EventShareRequest (
val action: String = "event", val action: String = "event",
val sharedByName: String, /* If null, the server shows an error */
val sharedByName: String?,
val shareTeamCode: String? = null, val shareTeamCode: String? = null,
val unshareTeamCode: String? = null, val unshareTeamCode: String? = null,
val requesterName: String? = null, val requesterName: String? = null,

View File

@ -34,7 +34,7 @@ class AppSync(val app: App, val notifications: MutableList<Notification>, val pr
if (events.isNotEmpty()) { if (events.isNotEmpty()) {
val today = Date.getToday() val today = Date.getToday()
app.db.metadataDao().addAllIgnore(events.map { event -> app.db.metadataDao().addAllIgnore(events.map { event ->
val isPast = event.eventDate < today val isPast = event.date < today
Metadata( Metadata(
event.profileId, event.profileId,
Metadata.TYPE_EVENT, Metadata.TYPE_EVENT,
@ -44,7 +44,7 @@ class AppSync(val app: App, val notifications: MutableList<Notification>, val pr
event.addedDate event.addedDate
) )
}) })
return app.db.eventDao().addAll(events).size return app.db.eventDao().upsertAll(events).size
} }
return 0; return 0;
} }

View File

@ -34,6 +34,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
announcementNotifications() announcementNotifications()
messageNotifications() messageNotifications()
luckyNumberNotifications() luckyNumberNotifications()
teacherAbsenceNotifications()
} }
private fun timetableNotifications() { private fun timetableNotifications() {
@ -58,7 +59,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
} }
private fun eventNotifications() { private fun eventNotifications() {
for (event in app.db.eventDao().notNotifiedNow.filter { it.eventDate >= today }) { for (event in app.db.eventDao().getNotNotifiedNow().filter { it.date >= today }) {
val text = if (event.type == Event.TYPE_HOMEWORK) val text = if (event.type == Event.TYPE_HOMEWORK)
app.getString( app.getString(
if (event.subjectLongName.isNullOrEmpty()) if (event.subjectLongName.isNullOrEmpty())
@ -66,7 +67,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
else else
R.string.notification_homework_format, R.string.notification_homework_format,
event.subjectLongName, event.subjectLongName,
event.eventDate.formattedString event.date.formattedString
) )
else else
app.getString( app.getString(
@ -74,8 +75,8 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
R.string.notification_event_no_subject_format R.string.notification_event_no_subject_format
else else
R.string.notification_event_format, R.string.notification_event_format,
event.typeName, event.typeName ?: "wydarzenie",
event.eventDate.formattedString, event.date.formattedString,
event.subjectLongName event.subjectLongName
) )
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
@ -88,17 +89,17 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
profileName = profiles.singleOrNull { it.id == event.profileId }?.name, profileName = profiles.singleOrNull { it.id == event.profileId }?.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 = event.addedDate addedDate = event.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong()) ).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong())
} }
} }
fun sharedEventNotifications() { fun sharedEventNotifications() {
for (event in app.db.eventDao().notNotifiedNow.filter { it.eventDate >= today && it.sharedBy != null }) { for (event in app.db.eventDao().getNotNotifiedNow().filter { it.date >= today && it.sharedBy != null }) {
val text = app.getString( val text = app.getString(
R.string.notification_shared_event_format, R.string.notification_shared_event_format,
event.sharedByName, event.sharedByName,
event.typeName ?: "wydarzenie", event.typeName ?: "wydarzenie",
event.eventDate.formattedString, event.date.formattedString,
event.topic event.topic
) )
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
@ -111,7 +112,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
profileName = profiles.singleOrNull { it.id == event.profileId }?.name, profileName = profiles.singleOrNull { it.id == event.profileId }?.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 = event.addedDate addedDate = event.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong()) ).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong())
} }
} }
@ -226,10 +227,10 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
} }
private fun messageNotifications() { private fun messageNotifications() {
for (message in app.db.messageDao().receivedNotNotifiedNow) { for (message in app.db.messageDao().getNotNotifiedNow()) {
val text = app.getString( val text = app.getString(
R.string.notification_message_format, R.string.notification_message_format,
message.senderFullName, message.senderName,
message.subject message.subject
) )
notifications += Notification( notifications += Notification(
@ -274,4 +275,23 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
) )
} }
} }
private fun teacherAbsenceNotifications() {
for (teacherAbsence in app.db.teacherAbsenceDao().getNotNotifiedNow()) {
val message = app.getString(
R.string.notification_teacher_absence_new_format,
teacherAbsence.teacherFullName
)
notifications += Notification(
id = Notification.buildId(teacherAbsence.profileId, Notification.TYPE_TEACHER_ABSENCE, teacherAbsence.id),
title = app.getNotificationTitle(Notification.TYPE_TEACHER_ABSENCE),
text = message,
type = Notification.TYPE_TEACHER_ABSENCE,
profileId = teacherAbsence.profileId,
profileName = profiles.singleOrNull { it.id == teacherAbsence.profileId }?.name,
viewId = MainActivity.DRAWER_ITEM_AGENDA,
addedDate = teacherAbsence.addedDate
).addExtra("eventDate", teacherAbsence.dateFrom.value.toLong())
}
}
} }

View File

@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
LibrusLesson::class, LibrusLesson::class,
TimetableManual::class, TimetableManual::class,
Metadata::class Metadata::class
], version = 79) ], version = 85)
@TypeConverters( @TypeConverters(
ConverterTime::class, ConverterTime::class,
ConverterDate::class, ConverterDate::class,
@ -164,7 +164,13 @@ abstract class AppDb : RoomDatabase() {
Migration76(), Migration76(),
Migration77(), Migration77(),
Migration78(), Migration78(),
Migration79() Migration79(),
Migration80(),
Migration81(),
Migration82(),
Migration83(),
Migration84(),
Migration85()
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()
} }
} }

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
*/
package pl.szczodrzynski.edziennik.data.db.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery
import pl.szczodrzynski.edziennik.data.db.entity.Keepable
@Dao
interface BaseDao<T : Keepable, F : T> {
@RawQuery
fun getRaw(query: SupportSQLiteQuery): LiveData<List<F>>
fun getRaw(query: String) = getRaw(SimpleSQLiteQuery(query))
@RawQuery
fun getRawNow(query: SupportSQLiteQuery): List<F>
fun getRawNow(query: String) = getRawNow(SimpleSQLiteQuery(query))
@RawQuery
fun getOneNow(query: SupportSQLiteQuery): F?
fun getOneNow(query: String) = getOneNow(SimpleSQLiteQuery(query))
@Query("DELETE FROM events WHERE keep = 0")
fun removeNotKept()
/**
* INSERT an [item] into the database,
* ignoring any conflicts.
*/
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun add(item: T): Long
/**
* INSERT [items] into the database,
* ignoring any conflicts.
*/
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun addAll(items: List<T>): LongArray
/**
* REPLACE an [item] in the database,
* removing any conflicting rows.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun replace(item: T)
/**
* REPLACE [items] in the database,
* removing any conflicting rows.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun replaceAll(items: List<T>)
/**
* Selective UPDATE an [item] in the database.
* Do nothing if a matching item does not exist.
*/
fun update(item: T): Long
/**
* Selective UPDATE [items] in the database.
* Do nothing for those items which do not exist.
*/
fun updateAll(items: List<T>): LongArray
/**
* Remove all items from the database,
* that match the given [profileId].
*/
fun clear(profileId: Int)
/**
* INSERT an [item] into the database,
* doing a selective [update] on conflicts.
* @return the newly inserted item's ID or -1L if the item was updated instead
*/
@Transaction
fun upsert(item: T): Long {
val id = add(item)
if (id == -1L) update(item)
return id
}
/**
* INSERT [items] into the database,
* doing a selective [update] on conflicts.
* @return a [LongArray] of IDs of newly inserted items or -1L if the item existed before
*/
@Transaction
fun upsertAll(items: List<T>, removeNotKept: Boolean = false): LongArray {
val insertResult = addAll(items)
val updateList = mutableListOf<T>()
insertResult.forEachIndexed { index, result ->
if (result == -1L) updateList.add(items[index])
}
if (updateList.isNotEmpty()) updateAll(items)
if (removeNotKept) removeNotKept()
return insertResult
}
}

View File

@ -1,186 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.dao;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.RawQuery;
import androidx.room.Transaction;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.Event;
import pl.szczodrzynski.edziennik.data.db.full.EventFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_EVENT;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_LESSON_CHANGE;
@Dao
public abstract class EventDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract long add(Event event);
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract long[] addAll(List<Event> eventList);
@Query("DELETE FROM events WHERE profileId = :profileId")
public abstract void clear(int profileId);
@Query("DELETE FROM events WHERE profileId = :profileId AND eventId = :id")
public abstract void remove(int profileId, long id);
@Query("DELETE FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND thingId = :thingId")
public abstract void removeMetadata(int profileId, int thingType, long thingId);
@Transaction
public void remove(int profileId, long type, long id) {
remove(profileId, id);
removeMetadata(profileId, type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, id);
}
@Transaction
public void remove(Event event) {
remove(event.profileId, event.type, event.id);
}
@Transaction
public void remove(int profileId, Event event) {
remove(profileId, event.type, event.id);
}
@Query("DELETE FROM events WHERE teamId = :teamId AND eventId = :id")
public abstract void removeByTeamId(long teamId, long id);
@RawQuery(observedEntities = {Event.class})
abstract LiveData<List<EventFull>> getAll(SupportSQLiteQuery query);
public LiveData<List<EventFull>> getAll(int profileId, String filter, String limit) {
String query = "SELECT \n" +
"*, \n" +
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName,\n" +
"eventTypes.eventTypeName AS typeName,\n" +
"eventTypes.eventTypeColor AS typeColor\n" +
"FROM events\n" +
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
"LEFT JOIN teams USING(profileId, teamId)\n" +
"LEFT JOIN eventTypes USING(profileId, eventType)\n" +
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = "+profileId+"\n" +
"WHERE events.profileId = "+profileId+" AND events.eventBlacklisted = 0 AND "+filter+"\n" +
"GROUP BY eventId\n" +
"ORDER BY eventDate, eventStartTime ASC "+limit;
Log.d("DB", query);
return getAll(new SimpleSQLiteQuery(query));
}
public LiveData<List<EventFull>> getAll(int profileId) {
return getAll(profileId, "1", "");
}
public List<EventFull> getAllNow(int profileId) {
return getAllNow(profileId, "1");
}
public LiveData<List<EventFull>> getAllWhere(int profileId, String filter) {
return getAll(profileId, filter, "");
}
public LiveData<List<EventFull>> getAllByType(int profileId, long type, String filter) {
return getAll(profileId, "eventType = "+type+" AND "+filter, "");
}
public LiveData<List<EventFull>> getAllByDate(int profileId, @NonNull Date date) {
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"'", "");
}
public List<EventFull> getAllByDateNow(int profileId, @NonNull Date date) {
return getAllNow(profileId, "eventDate = '"+date.getStringY_m_d()+"'");
}
public LiveData<List<EventFull>> getAllByDateTime(int profileId, @NonNull Date date, Time time) {
if (time == null)
return getAllByDate(profileId, date);
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"' AND eventStartTime = '"+time.getStringValue()+"'", "");
}
public LiveData<List<EventFull>> getAllNearest(int profileId, @NonNull Date today, int limit) {
return getAll(profileId, "eventDate >= '"+today.getStringY_m_d()+"'", "LIMIT "+limit);
}
@RawQuery
abstract List<EventFull> getAllNow(SupportSQLiteQuery query);
public List<EventFull> getAllNow(int profileId, String filter) {
return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
"*, \n" +
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName,\n" +
"eventTypes.eventTypeName AS typeName,\n" +
"eventTypes.eventTypeColor AS typeColor\n" +
"FROM events \n" +
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
"LEFT JOIN teams USING(profileId, teamId)\n" +
"LEFT JOIN eventTypes USING(profileId, eventType)\n" +
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = "+profileId+"\n" +
"WHERE events.profileId = "+profileId+" AND events.eventBlacklisted = 0 AND "+filter+"\n" +
"GROUP BY eventId\n" +
"ORDER BY eventStartTime, addedDate ASC"));
}
public List<EventFull> getNotNotifiedNow(int profileId) {
return getAllNow(profileId, "notified = 0");
}
@Query("SELECT eventId FROM events WHERE profileId = :profileId AND eventBlacklisted = 1")
public abstract List<Long> getBlacklistedIds(int profileId);
@Query("SELECT eventId FROM events WHERE eventBlacklisted = 1")
public abstract List<Long> getBlacklistedIds();
@Query("SELECT " +
"*, " +
"eventTypes.eventTypeName AS typeName, " +
"eventTypes.eventTypeColor AS typeColor " +
"FROM events " +
"LEFT JOIN subjects USING(profileId, subjectId) " +
"LEFT JOIN eventTypes USING(profileId, eventType) " +
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = events.profileId " +
"WHERE events.eventBlacklisted = 0 AND notified = 0 " +
"GROUP BY eventId " +
"ORDER BY addedDate ASC")
public abstract List<EventFull> getNotNotifiedNow();
public EventFull getByIdNow(int profileId, long eventId) {
List<EventFull> eventList = getAllNow(profileId, "eventId = "+eventId);
return eventList.size() == 0 ? null : eventList.get(0);
}
@Query("UPDATE events SET eventAddedManually = 1 WHERE profileId = :profileId AND eventDate < :date")
public abstract void convertOlderToManual(int profileId, Date date);
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0")
public abstract void removeNotManual(int profileId);
@RawQuery
abstract long removeFuture(SupportSQLiteQuery query);
@Transaction
public void removeFuture(int profileId, Date todayDate, String filter) {
removeFuture(new SimpleSQLiteQuery("DELETE FROM events WHERE profileId = " + profileId
+ " AND eventAddedManually = 0 AND eventDate >= '" + todayDate.getStringY_m_d() + "'" +
" AND " + filter));
}
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType = :type")
public abstract void removeFutureWithType(int profileId, Date todayDate, long type);
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType != :exceptType")
public abstract void removeFutureExceptType(int profileId, Date todayDate, long exceptType);
@Transaction
public void removeFutureExceptTypes(int profileId, Date todayDate, List<Long> exceptTypes) {
removeFuture(profileId, todayDate, "eventType NOT IN " + exceptTypes.toString().replace('[', '(').replace(']', ')'));
}
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = "+TYPE_EVENT+" OR thingType = "+TYPE_LESSON_CHANGE+" OR thingType = "+TYPE_HOMEWORK+") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
public abstract void setSeenByDate(int profileId, Date date, boolean seen);
@Query("UPDATE events SET eventBlacklisted = :blacklisted WHERE profileId = :profileId AND eventId = :eventId")
public abstract void setBlacklisted(int profileId, long eventId, boolean blacklisted);
}

View File

@ -0,0 +1,151 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Query
import androidx.room.RawQuery
import androidx.room.Transaction
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.annotation.SelectiveDao
import pl.szczodrzynski.edziennik.annotation.UpdateSelective
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@Dao
@SelectiveDao(db = AppDb::class)
abstract class EventDao : BaseDao<Event, EventFull> {
companion object {
private const val QUERY = """
SELECT
*,
teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName,
eventTypes.eventTypeName AS typeName,
eventTypes.eventTypeColor AS typeColor
FROM events
LEFT JOIN subjects USING(profileId, subjectId)
LEFT JOIN teachers USING(profileId, teacherId)
LEFT JOIN teams USING(profileId, teamId)
LEFT JOIN eventTypes USING(profileId, eventType)
LEFT JOIN metadata ON eventId = thingId AND (thingType = ${Metadata.TYPE_EVENT} OR thingType = ${Metadata.TYPE_HOMEWORK}) AND metadata.profileId = events.profileId
"""
private const val ORDER_BY = """GROUP BY eventId ORDER BY eventDate, eventTime, addedDate ASC"""
private const val NOT_BLACKLISTED = """events.eventBlacklisted = 0"""
private const val NOT_DONE = """events.eventIsDone = 0"""
}
private val selective by lazy { EventDaoSelective(App.db) }
@RawQuery(observedEntities = [Event::class])
abstract override fun getRaw(query: SupportSQLiteQuery): LiveData<List<EventFull>>
// SELECTIVE UPDATE
@UpdateSelective(primaryKeys = ["profileId", "eventId"], skippedColumns = ["eventIsDone", "eventBlacklisted", "homeworkBody", "attachmentIds", "attachmentNames"])
override fun update(item: Event) = selective.update(item)
override fun updateAll(items: List<Event>) = selective.updateAll(items)
// CLEAR
@Query("DELETE FROM events WHERE profileId = :profileId")
abstract override fun clear(profileId: Int)
// GET ALL - LIVE DATA
fun getAll(profileId: Int) =
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId $ORDER_BY")
fun getAllByType(profileId: Int, type: Long, filter: String = "1") =
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventType = $type AND $filter $ORDER_BY")
fun getAllByDate(profileId: Int, date: Date) =
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
fun getAllByDateTime(profileId: Int, date: Date, time: Time) =
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' AND eventTime = '${time.stringValue}' $ORDER_BY")
fun getNearestNotDone(profileId: Int, today: Date, limit: Int) =
getRaw("$QUERY WHERE $NOT_BLACKLISTED AND $NOT_DONE AND events.profileId = $profileId AND eventDate >= '${today.stringY_m_d}' $ORDER_BY LIMIT $limit")
// GET ALL - NOW
fun getAllNow(profileId: Int) =
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId $ORDER_BY")
fun getNotNotifiedNow() =
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND notified = 0 $ORDER_BY")
fun getNotNotifiedNow(profileId: Int) =
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND notified = 0 $ORDER_BY")
fun getAllByDateNow(profileId: Int, date: Date) =
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
// GET ONE - NOW
fun getByIdNow(profileId: Int, id: Long) =
getOneNow("$QUERY WHERE events.profileId = $profileId AND eventId = $id")
@Query("SELECT eventId FROM events WHERE profileId = :profileId AND eventBlacklisted = 1")
abstract fun getBlacklistedIds(profileId: Int): List<Long>
@get:Query("SELECT eventId FROM events WHERE eventBlacklisted = 1")
abstract val blacklistedIds: List<Long>
/*@Query("UPDATE events SET eventAddedManually = 1 WHERE profileId = :profileId AND eventDate < :date")
abstract fun convertOlderToManual(profileId: Int, date: Date?)
@Query("DELETE FROM events WHERE teamId = :teamId AND eventId = :id")
abstract fun removeByTeamId(teamId: Long, id: Long)
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0")
abstract fun removeNotManual(profileId: Int)*/
@RawQuery
abstract fun dontKeepFuture(query: SupportSQLiteQuery?): Long
@Transaction
open fun dontKeepFuture(profileId: Int, todayDate: Date, filter: String) {
dontKeepFuture(SimpleSQLiteQuery("UPDATE events SET keep = 0 WHERE profileId = " + profileId
+ " AND eventAddedManually = 0 AND eventDate >= '" + todayDate.stringY_m_d + "'" +
" AND " + filter))
}
@Query("UPDATE events SET keep = 0 WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType = :type")
abstract fun dontKeepFutureWithType(profileId: Int, todayDate: Date, type: Long)
@Query("UPDATE events SET keep = 0 WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType != :exceptType")
abstract fun dontKeepFutureExceptType(profileId: Int, todayDate: Date, exceptType: Long)
@Transaction
open fun dontKeepFutureExceptTypes(profileId: Int, todayDate: Date, exceptTypes: List<Long>) {
dontKeepFuture(profileId, todayDate, "eventType NOT IN " + exceptTypes.toString().replace('[', '(').replace(']', ')'))
}
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = " + Metadata.TYPE_EVENT + " OR thingType = " + Metadata.TYPE_LESSON_CHANGE + " OR thingType = " + Metadata.TYPE_HOMEWORK + ") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
abstract fun setSeenByDate(profileId: Int, date: Date, seen: Boolean)
@Query("UPDATE events SET eventBlacklisted = :blacklisted WHERE profileId = :profileId AND eventId = :eventId")
abstract fun setBlacklisted(profileId: Int, eventId: Long, blacklisted: Boolean)
@Query("DELETE FROM events WHERE profileId = :profileId AND eventId = :id")
abstract fun remove(profileId: Int, id: Long)
@Query("DELETE FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND thingId = :thingId")
abstract fun removeMetadata(profileId: Int, thingType: Int, thingId: Long)
@Transaction
open fun remove(profileId: Int, type: Long, id: Long) {
remove(profileId, id)
removeMetadata(profileId, if (type == Event.TYPE_HOMEWORK) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT, id)
}
@Transaction
open fun remove(event: Event) {
remove(event.profileId, event.type, event.id)
}
@Transaction
open fun remove(profileId: Int, event: Event) {
remove(profileId, event.type, event.id)
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.EventType;
@Dao
public interface EventTypeDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void add(EventType gradeCategory);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void addAll(List<EventType> gradeCategoryList);
@Query("DELETE FROM eventTypes WHERE profileId = :profileId")
void clear(int profileId);
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
EventType getByIdNow(int profileId, long typeId);
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
LiveData<List<EventType>> getAll(int profileId);
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
List<EventType> getAllNow(int profileId);
@Query("SELECT * FROM eventTypes")
List<EventType> getAllNow();
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.dao
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_CLASS_EVENT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_DEFAULT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ESSAY
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXAM
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXCURSION
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_INFORMATION
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PROJECT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PT_MEETING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_READING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_SHORT_QUIZ
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_CLASS_EVENT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_DEFAULT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ESSAY
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXAM
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXCURSION
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_INFORMATION
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PROJECT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PT_MEETING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_READING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_SHORT_QUIZ
import pl.szczodrzynski.edziennik.data.db.entity.EventType
@Dao
abstract class EventTypeDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun add(eventType: EventType)
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun addAll(eventTypeList: List<EventType>)
@Query("DELETE FROM eventTypes WHERE profileId = :profileId")
abstract fun clear(profileId: Int)
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
abstract fun getByIdNow(profileId: Int, typeId: Long): EventType?
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
abstract fun getAll(profileId: Int): LiveData<List<EventType>>
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
abstract fun getAllNow(profileId: Int): List<EventType>
@get:Query("SELECT * FROM eventTypes")
abstract val allNow: List<EventType>
fun addDefaultTypes(context: Context, profileId: Int): List<EventType> {
val typeList = listOf(
EventType(profileId, TYPE_HOMEWORK, context.getString(R.string.event_type_homework), COLOR_HOMEWORK),
EventType(profileId, TYPE_DEFAULT, context.getString(R.string.event_other), COLOR_DEFAULT),
EventType(profileId, TYPE_EXAM, context.getString(R.string.event_exam), COLOR_EXAM),
EventType(profileId, TYPE_SHORT_QUIZ, context.getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ),
EventType(profileId, TYPE_ESSAY, context.getString(R.string.event_essay), COLOR_ESSAY),
EventType(profileId, TYPE_PROJECT, context.getString(R.string.event_project), COLOR_PROJECT),
EventType(profileId, TYPE_PT_MEETING, context.getString(R.string.event_pt_meeting), COLOR_PT_MEETING),
EventType(profileId, TYPE_EXCURSION, context.getString(R.string.event_excursion), COLOR_EXCURSION),
EventType(profileId, TYPE_READING, context.getString(R.string.event_reading), COLOR_READING),
EventType(profileId, TYPE_CLASS_EVENT, context.getString(R.string.event_class_event), COLOR_CLASS_EVENT),
EventType(profileId, TYPE_INFORMATION, context.getString(R.string.event_information), COLOR_INFORMATION)
)
addAll(typeList)
return typeList
}
}

View File

@ -1,114 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.dao;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.RawQuery;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.Message;
import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
import pl.szczodrzynski.edziennik.data.db.full.MessageFull;
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED;
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED;
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_MESSAGE;
@Dao
public abstract class MessageDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract long add(Message message);
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void addAll(List<Message> messageList);
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void addAllIgnore(List<Message> messageList);
@Query("DELETE FROM messages WHERE profileId = :profileId")
public abstract void clear(int profileId);
@RawQuery(observedEntities = {Message.class})
abstract LiveData<List<MessageFull>> getAll(SupportSQLiteQuery query);
@RawQuery(observedEntities = {Message.class, Metadata.class})
abstract List<MessageFull> getNow(SupportSQLiteQuery query);
@RawQuery(observedEntities = {Message.class, Metadata.class})
abstract MessageFull getOneNow(SupportSQLiteQuery query);
public LiveData<List<MessageFull>> getWithMetadataAndSenderName(int profileId, int messageType, String filter) {
return getAll(new SimpleSQLiteQuery("SELECT \n" +
"*, \n" +
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
"FROM messages \n" +
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
"WHERE messages.profileId = "+profileId+" AND messageType = "+messageType+" AND "+filter+"\n" +
"ORDER BY addedDate DESC"));
}
public LiveData<List<MessageFull>> getWithMetadata(int profileId, int messageType, String filter) {
return getAll(new SimpleSQLiteQuery("SELECT \n" +
"* \n" +
"FROM messages \n" +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
"WHERE messages.profileId = "+profileId+" AND messageType = "+messageType+" AND "+filter+"\n" +
"ORDER BY addedDate DESC"));
}
@Nullable
public MessageFull getById(int profileId, long messageId) {
return getOneNow(new SimpleSQLiteQuery("SELECT \n" +
"*, \n" +
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
"FROM messages \n" +
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
"WHERE messages.profileId = "+profileId+" AND messageId = "+messageId+"\n" +
"ORDER BY addedDate DESC"));
}
public LiveData<List<MessageFull>> getReceived(int profileId) {
return getWithMetadataAndSenderName(profileId, TYPE_RECEIVED, "1");
}
public LiveData<List<MessageFull>> getDeleted(int profileId) {
return getWithMetadataAndSenderName(profileId, TYPE_DELETED, "1");
}
public LiveData<List<MessageFull>> getSent(int profileId) {
return getWithMetadata(profileId, TYPE_SENT, "1");
}
public List<MessageFull> getReceivedNow(int profileId, String filter) {
return getNow(new SimpleSQLiteQuery("SELECT \n" +
"*, \n" +
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
"FROM messages \n" +
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
"WHERE messages.profileId = "+profileId+" AND messageType = 0 AND "+filter+"\n" +
"ORDER BY addedDate DESC"));
}
public List<MessageFull> getReceivedNotNotifiedNow(int profileId) {
return getReceivedNow(profileId, "notified = 0");
}
@Query("SELECT " +
"*, " +
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName " +
"FROM messages " +
"LEFT JOIN teachers ON teachers.profileId = messages.profileId AND teacherId = senderId " +
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = messages.profileId " +
"WHERE messageType = 0 AND notified = 0 " +
"ORDER BY addedDate DESC")
public abstract List<MessageFull> getReceivedNotNotifiedNow();
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Query
import androidx.room.RawQuery
import androidx.sqlite.db.SupportSQLiteQuery
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.annotation.SelectiveDao
import pl.szczodrzynski.edziennik.annotation.UpdateSelective
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
@Dao
@SelectiveDao(db = AppDb::class)
abstract class MessageDao : BaseDao<Message, MessageFull> {
companion object {
private const val QUERY = """
SELECT
*,
teachers.teacherName ||" "|| teachers.teacherSurname AS senderName
FROM messages
LEFT JOIN teachers ON teachers.profileId = messages.profileId AND teacherId = senderId
LEFT JOIN metadata ON messageId = thingId AND thingType = ${Metadata.TYPE_MESSAGE} AND metadata.profileId = messages.profileId
"""
private const val ORDER_BY = """ORDER BY messageIsPinned, addedDate DESC"""
}
private val selective by lazy { MessageDaoSelective(App.db) }
@RawQuery(observedEntities = [Message::class])
abstract override fun getRaw(query: SupportSQLiteQuery): LiveData<List<MessageFull>>
@UpdateSelective(primaryKeys = ["profileId", "messageId"], skippedColumns = ["messageType", "messageBody", "messageIsPinned", "attachmentIds", "attachmentNames", "attachmentSizes"])
override fun update(item: Message) = selective.update(item)
override fun updateAll(items: List<Message>) = selective.updateAll(items)
// CLEAR
@Query("DELETE FROM messages WHERE profileId = :profileId")
abstract override fun clear(profileId: Int)
// GET ALL - LIVE DATA
fun getAll(profileId: Int) =
getRaw("$QUERY WHERE messages.profileId = $profileId $ORDER_BY")
fun getAllByType(profileId: Int, type: Int) =
getRaw("$QUERY WHERE messages.profileId = $profileId AND messageType = $type $ORDER_BY")
fun getReceived(profileId: Int) = getAllByType(profileId, Message.TYPE_RECEIVED)
fun getSent(profileId: Int) = getAllByType(profileId, Message.TYPE_SENT)
fun getDeleted(profileId: Int) = getAllByType(profileId, Message.TYPE_DELETED)
fun getDraft(profileId: Int) = getAllByType(profileId, Message.TYPE_DRAFT)
// GET ALL - NOW
fun getAllNow(profileId: Int) =
getRawNow("$QUERY WHERE messages.profileId = $profileId $ORDER_BY")
fun getNotNotifiedNow() =
getRawNow("$QUERY WHERE notified = 0 AND messageType = ${Message.TYPE_RECEIVED} $ORDER_BY")
// GET ONE - NOW
fun getByIdNow(profileId: Int, id: Long) =
getOneNow("$QUERY WHERE messages.profileId = $profileId AND messageId = $id")
}

View File

@ -78,8 +78,8 @@ public abstract class MetadataDao {
} }
} }
if (o instanceof Event) { if (o instanceof Event) {
if (add(new Metadata(profileId, ((Event) o).type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).id, seen, false, 0)) == -1) { if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen, false, 0)) == -1) {
updateSeen(profileId, ((Event) o).type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).id, seen); updateSeen(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen);
} }
} }
if (o instanceof LessonFull) { if (o instanceof LessonFull) {
@ -93,8 +93,8 @@ public abstract class MetadataDao {
} }
} }
if (o instanceof Message) { if (o instanceof Message) {
if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).id, seen, false, 0)) == -1) { if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_MESSAGE, ((Message) o).id, seen); updateSeen(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen);
} }
} }
} }
@ -117,8 +117,8 @@ public abstract class MetadataDao {
} }
} }
if (o instanceof Event) { if (o instanceof Event) {
if (add(new Metadata(profileId, ((Event) o).type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).id, false, notified, 0)) == -1) { if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), false, notified, 0)) == -1) {
updateNotified(profileId, ((Event) o).type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).id, notified); updateNotified(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), notified);
} }
} }
if (o instanceof LessonFull) { if (o instanceof LessonFull) {
@ -132,8 +132,8 @@ public abstract class MetadataDao {
} }
} }
if (o instanceof Message) { if (o instanceof Message) {
if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).id, false, notified, 0)) == -1) { if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_MESSAGE, ((Message) o).id, notified); updateNotified(profileId, TYPE_MESSAGE, ((Message) o).getId(), notified);
} }
} }
} }
@ -141,9 +141,9 @@ public abstract class MetadataDao {
@Transaction @Transaction
public void setBoth(int profileId, Event o, boolean seen, boolean notified, long addedDate) { public void setBoth(int profileId, Event o, boolean seen, boolean notified, long addedDate) {
if (o != null) { if (o != null) {
if (add(new Metadata(profileId, o.type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.id, seen, notified, addedDate)) == -1) { if (add(new Metadata(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen, notified, addedDate)) == -1) {
updateSeen(profileId, o.type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.id, seen); updateSeen(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen);
updateNotified(profileId, o.type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.id, notified); updateNotified(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), notified);
} }
} }
} }

View File

@ -49,6 +49,17 @@ interface TeacherAbsenceDao {
"AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo") "AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo")
fun getAllByDateNow(profileId: Int, date: Date): List<TeacherAbsenceFull> fun getAllByDateNow(profileId: Int, date: Date): List<TeacherAbsenceFull>
@Query("""
SELECT *,
teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName
FROM teacherAbsence
LEFT JOIN teachers USING (profileId, teacherId)
LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = ${Metadata.TYPE_TEACHER_ABSENCE}
AND teachers.profileId = metadata.profileId WHERE metadata.notified = 0
ORDER BY addedDate DESC
""")
fun getNotNotifiedNow(): List<TeacherAbsenceFull>
@Query("DELETE FROM teacherAbsence WHERE profileId = :profileId") @Query("DELETE FROM teacherAbsence WHERE profileId = :profileId")
fun clear(profileId: Int) fun clear(profileId: Int)
} }

View File

@ -1,10 +1,8 @@
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.SupportSQLiteQuery
import androidx.room.OnConflictStrategy
import androidx.room.Query
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
@Dao @Dao
@ -18,6 +16,9 @@ interface TeacherDao {
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
fun addAllIgnore(teacherList: List<Teacher>) fun addAllIgnore(teacherList: List<Teacher>)
@RawQuery
fun query(query: SupportSQLiteQuery): Int
@Query("DELETE FROM teachers WHERE profileId = :profileId") @Query("DELETE FROM teachers WHERE profileId = :profileId")
fun clear(profileId: Int) fun clear(profileId: Int)

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