diff --git a/app/build.gradle b/app/build.gradle
index ae884511..9bc32c1a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -145,7 +145,11 @@ dependencies {
implementation("com.github.ozodrukh:CircularReveal:2.0.1@aar") {transitive = true}
implementation "com.heinrichreimersoftware:material-intro:1.5.8" // do not update
implementation "com.jaredrummler:colorpicker:1.0.2"
- implementation "com.squareup.okhttp3:okhttp:3.12.2"
+ implementation("com.squareup.okhttp3:okhttp") {
+ version {
+ strictly "3.12.2"
+ }
+ }
implementation "com.theartofdev.edmodo:android-image-cropper:2.8.0" // do not update
implementation "com.wdullaer:materialdatetimepicker:4.1.2"
implementation "com.yuyh.json:jsonviewer:1.0.6"
@@ -201,6 +205,11 @@ dependencies {
implementation 'com.qifan.powerpermission:powerpermission:1.0.0'
implementation 'com.qifan.powerpermission:powerpermission-coroutines:1.0.0'
+
+ implementation 'com.github.kuba2k2.FSLogin:lib:master-SNAPSHOT'
+ implementation 'pl.droidsonroids:jspoon:1.3.2'
+ implementation "com.squareup.retrofit2:converter-scalars:2.8.1"
+ implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
}
repositories {
mavenCentral()
diff --git a/app/proguard/app.pro b/app/proguard/app.pro
index 711a62e4..68d1fde8 100644
--- a/app/proguard/app.pro
+++ b/app/proguard/app.pro
@@ -66,3 +66,4 @@
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; }
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }
+-keepclassmembernames class pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo.Platform { *; }
diff --git a/app/sampledata/vulcan/edu.lublin.eu.png b/app/sampledata/vulcan/edu.lublin.eu.png
new file mode 100644
index 00000000..ece8b925
Binary files /dev/null and b/app/sampledata/vulcan/edu.lublin.eu.png differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cc82fed3..6e8fc491 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -24,7 +24,6 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
- android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme.Dark"
android:usesCleartextTraffic="true"
@@ -135,9 +134,6 @@
android:configChanges="orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/AppTheme.Light" />
-
Wersja 4.0, 2020-04-19
+Wersja 4.2, 2020-05-16
- - Wysyłanie wiadomości - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏
- - Przebudowaliśmy cały moduł synchronizacji, co oznacza większą stabilność aplikacji, szybkość oraz poprawność pobieranych danych
- - Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze
- - Wyszukiwarka wiadomości, pozwalająca na łatwe znalezienie potrzebnej konwersacji.
- - Możliwość pobierania załączników do zadań domowych oraz wiadomości w każdym dzienniku.
- - Nowa Strona główna - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu
- - Nowy Plan lekcji - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie
- - Nowe Oceny - z możliwością zmiany wartości plusów oraz minusów oraz wyłączenia niektórych ocen ze średniej
- - Opcja wyłączenia wybranych powiadomień z aplikacji
- - Znaczki nieprzeczytanych informacji na obrazkach profili.
-
-
- - Udoskonalone tłumaczenie na j.angielski (dzięki @Predator)
- - Nowe okienka informacji o wydarzeniach oraz lekcjach
- - Nowe, przyjemniejsze powiadomienia
- - Dużo poprawek w widoku Wiadomości oraz Ogłoszeń
- - Częściowa Obsługa dziennika EduDziennik
- - Librus: opcja logowania w dziennikach Jednostek Samorządu Terytorialnego oraz Oświata w Radomiu
- - Librus: poprawione obliczanie frekwencji
- - Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)
- - Lepsze przekazywanie powiadomień na komputer oraz łatwiejsze parowanie
- - Łatwiejsze dodawanie własnych wydarzeń
- - Poprawiliśmy synchronizację w tle na niektórych telefonach
- - Usunąłem denerwujący brak zaznaczenia w lewym menu
- - Znaczna ilość błędów z poprzednich wersji już nie występuje
+ - Naprawiony błąd braku dostępu do Wiadomości w Librusie.
+ - Vulcan: wyświetlane dane z 1 semestru w dzienniku.
+ - Odświeżone logo aplikacji.
+ - Obsługa dziennika Podlaskiej Platformy Edukacyjnej (Prymus).
+ - Poprawione liczenie i wyświetlanie niektórych rodzajów frekwencji.
+ - Nowy ekran logowania.
diff --git a/app/src/main/cpp/szkolny-signing.cpp b/app/src/main/cpp/szkolny-signing.cpp
index 6463e8af..f90eb9ef 100644
--- a/app/src/main/cpp/szkolny-signing.cpp
+++ b/app/src/main/cpp/szkolny-signing.cpp
@@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/
static toys AES_IV[16] = {
- 0x38, 0xd4, 0x73, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ 0x3b, 0xa6, 0xd4, 0x50, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.kt b/app/src/main/java/pl/szczodrzynski/edziennik/App.kt
index 7194aa04..568538e7 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/App.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.kt
@@ -66,6 +66,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
val timetableManager by lazy { TimetableManager(this) }
val eventManager by lazy { EventManager(this) }
val permissionManager by lazy { PermissionManager(this) }
+ val attendanceManager by lazy { AttendanceManager(this) }
val db
get() = App.db
@@ -100,9 +101,9 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cookieJar(cookieJar)
- .connectTimeout(20, TimeUnit.SECONDS)
- .writeTimeout(5, TimeUnit.SECONDS)
- .readTimeout(10, TimeUnit.SECONDS)
+ .connectTimeout(15, TimeUnit.SECONDS)
+ .writeTimeout(10, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
builder.installHttpsSupport(this)
if (debugMode || BuildConfig.DEBUG) {
@@ -172,6 +173,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
}
devMode = BuildConfig.DEBUG
+ if (BuildConfig.DEBUG)
+ debugMode = true
Signing.getCert(this)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
index e95854e7..2443679b 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
@@ -1245,3 +1245,5 @@ val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener
operator fun Iterable>.get(key: K): V? {
return firstOrNull { it.first == key }?.second
}
+
+fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt
index 19b2886d..3033f0f0 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt
@@ -18,6 +18,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.ColorUtils
+import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.navigation.NavOptions
import com.danimahardhika.cafebar.CafeBar
@@ -294,6 +295,13 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
+ if (BuildConfig.VERSION_NAME.contains("nightly")) {
+ b.nightlyText.isVisible = true
+ b.nightlyText.text = "Nightly\n"+BuildConfig.VERSION_NAME.substringAfterLast(".")
+ }
+ else
+ b.nightlyText.isVisible = false
+
navLoading = true
b.navView.apply {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfig.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfig.kt
index d956c1ec..c4c65ac3 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfig.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfig.kt
@@ -30,6 +30,7 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List false }
+ LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_MAIN, VulcanLoginWebMain::class.java)
+ .withIsPossible { _, loginStore -> loginStore.hasLoginData("webHost") }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
- LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_NEW, VulcanLoginWebNew::class.java)
+ /*LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_NEW, VulcanLoginWebNew::class.java)
.withIsPossible { _, _ -> false }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_VULCAN_WEB_MAIN },
@@ -118,7 +119,7 @@ val vulcanLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_API, VulcanLoginApi::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, loginStore ->
- if (loginStore.mode == LOGIN_MODE_VULCAN_WEB) LOGIN_METHOD_VULCAN_WEB_NEW else LOGIN_METHOD_NOT_NEEDED
+ if (loginStore.mode == LOGIN_MODE_VULCAN_WEB) LOGIN_METHOD_VULCAN_WEB_MAIN else LOGIN_METHOD_NOT_NEEDED
}
)
@@ -133,6 +134,7 @@ val idziennikLoginMethods = listOf(
)
const val LOGIN_TYPE_EDUDZIENNIK = 5
+const val LOGIN_MODE_EDUDZIENNIK_WEB = 0
const val LOGIN_METHOD_EDUDZIENNIK_WEB = 100
val edudziennikLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_EDUDZIENNIK, LOGIN_METHOD_EDUDZIENNIK_WEB, EdudziennikLoginWeb::class.java)
@@ -140,6 +142,15 @@ val edudziennikLoginMethods = listOf(
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
)
+const val LOGIN_TYPE_PODLASIE = 6
+const val LOGIN_MODE_PODLASIE_API = 0
+const val LOGIN_METHOD_PODLASIE_API = 100
+val podlasieLoginMethods = listOf(
+ LoginMethod(LOGIN_TYPE_PODLASIE, LOGIN_METHOD_PODLASIE_API, PodlasieLoginApi::class.java)
+ .withIsPossible { _, _ -> true }
+ .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
+)
+
val templateLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java)
.withIsPossible { _, _ -> true }
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt
index 57dceb0e..aa3fac8d 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt
@@ -68,6 +68,9 @@ object Regexes {
}
+ val MOBIDZIENNIK_ATTENDANCE_TYPES by lazy {
+ """Legenda:.+?normal;">(.+?)""".toRegex(DOT_MATCHES_ALL)
+ }
val MOBIDZIENNIK_ATTENDANCE_TABLE by lazy {
"""""".toRegex(DOT_MATCHES_ALL)
}
@@ -81,7 +84,7 @@ object Regexes {
"""([0-9:]+) - .+? (.+?)""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_ATTENDANCE_LESSON by lazy {
- """(.+?) - (.*?).+?.+?\((.+?), .+?(.+?)\)""".toRegex(DOT_MATCHES_ALL)
+ """(.+?)\s*\s*\((.+?),\s*(.+?)\)""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_ROW by lazy {
@@ -139,12 +142,21 @@ object Regexes {
val VULCAN_SHIFT_ANNOTATION by lazy {
"""\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
}
+ val VULCAN_WEB_PERMISSIONS by lazy {
+ """permissions: '([A-z0-9/=+\-_]+?)'""".toRegex()
+ }
+ val VULCAN_WEB_SYMBOL_VALIDATE by lazy {
+ """[A-z0-9]+""".toRegex(IGNORE_CASE)
+ }
val LIBRUS_ATTACHMENT_KEY by lazy {
"""singleUseKey=([0-9A-z_]+)""".toRegex()
}
+ val LIBRUS_MESSAGE_ID by lazy {
+ """/wiadomosci/[0-9]+/[0-9]+/([0-9]+?)/""".toRegex()
+ }
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt
index 13de2f17..4d3f0aca 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt
@@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.Edudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.Idziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.Librus
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.Podlasie
import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
@@ -19,6 +20,7 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
+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.EventFull
@@ -28,6 +30,9 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
companion object {
private const val TAG = "EdziennikTask"
+ var profile: Profile? = null
+ var loginStore: LoginStore? = null
+
fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore))
fun sync() = EdziennikTask(-1, SyncRequest())
fun syncProfile(profileId: Int, viewIds: List>? = null, onlyEndpoints: List? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, onlyEndpoints, arguments))
@@ -59,6 +64,8 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
// save the profile ID and name as the current task's
taskName = app.getString(R.string.edziennik_notification_api_sync_title_format, profile.name)
}
+ EdziennikTask.profile = this.profile
+ EdziennikTask.loginStore = this.loginStore
}
private var edziennikInterface: EdziennikInterface? = null
@@ -74,6 +81,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback)
LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback)
LOGIN_TYPE_EDUDZIENNIK -> Edudziennik(app, profile, loginStore, taskCallback)
+ LOGIN_TYPE_PODLASIE -> Podlasie(app, profile, loginStore, taskCallback)
LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
else -> null
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAnnouncements.kt
index 11f048b5..32ca353c 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAnnouncements.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAnnouncements.kt
@@ -45,24 +45,25 @@ class EdudziennikWebAnnouncements(override val data: DataEdudziennik,
val addedDate = Date.fromIsoHm(dateString)
val announcementObject = Announcement(
- profileId,
- id,
- subject,
- null,
- startDate,
- null,
- teacher.id,
- longId
- )
+ profileId = profileId,
+ id = id,
+ subject = subject,
+ text = null,
+ startDate = startDate,
+ endDate = null,
+ teacherId = teacher.id,
+ addedDate = addedDate
+ ).also {
+ it.idString = longId
+ }
- data.announcementIgnoreList.add(announcementObject)
+ data.announcementList.add(announcementObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ANNOUNCEMENT,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAttendance.kt
index 92545117..7fe18171 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAttendance.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAttendance.kt
@@ -39,12 +39,12 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
val attendanceTypes = EDUDZIENNIK_ATTENDANCE_TYPES.find(text)?.get(1)?.split(',')?.map {
val type = EDUDZIENNIK_ATTENDANCE_TYPE.find(it.trim())
- val symbol = type?.get(1)?.trim()
- val name = type?.get(2)?.trim()
+ val symbol = type?.get(1)?.trim() ?: "?"
+ val name = type?.get(2)?.trim() ?: "nieznany rodzaj"
return@map Triple(
symbol,
name,
- when (name?.toLowerCase(Locale.ROOT)) {
+ when (name.toLowerCase(Locale.ROOT)) {
"obecność" -> Attendance.TYPE_PRESENT
"nieobecność" -> Attendance.TYPE_ABSENT
"spóźnienie" -> Attendance.TYPE_BELATED
@@ -52,7 +52,7 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
"dzień wolny" -> Attendance.TYPE_DAY_FREE
"brak zajęć" -> Attendance.TYPE_DAY_FREE
"oddelegowany" -> Attendance.TYPE_RELEASED
- else -> Attendance.TYPE_CUSTOM
+ else -> Attendance.TYPE_UNKNOWN
}
)
} ?: emptyList()
@@ -62,38 +62,42 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
val lessonNumber = attendanceElement[2].toInt()
val attendanceSymbol = attendanceElement[3]
- val lessons = data.app.db.timetableDao().getForDateNow(profileId, date)
+ val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val lesson = lessons.firstOrNull { it.lessonNumber == lessonNumber }
val id = "${date.stringY_m_d}:$lessonNumber:$attendanceSymbol".crc32()
- val (_, name, type) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol }
+ val (typeSymbol, typeName, baseType) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol }
?: return@forEach
val startTime = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }?.startTime
?: return@forEach
val attendanceObject = Attendance(
- profileId,
- id,
- lesson?.displayTeacherId ?: -1,
- lesson?.displaySubjectId ?: -1,
- profile.currentSemester,
- name,
- date,
- lesson?.displayStartTime ?: startTime,
- type
- )
+ profileId = profileId,
+ id = id,
+ baseType = baseType,
+ typeName = typeName,
+ typeShort = data.app.attendanceManager.getTypeShort(baseType),
+ typeSymbol = typeSymbol,
+ typeColor = null,
+ date = date,
+ startTime = lesson?.displayStartTime ?: startTime,
+ semester = profile.currentSemester,
+ teacherId = lesson?.displayTeacherId ?: -1,
+ subjectId = lesson?.displaySubjectId ?: -1
+ ).also {
+ it.lessonNumber = lessonNumber
+ }
data.attendanceList.add(attendanceObject)
- if(type != Attendance.TYPE_PRESENT) {
+ if (baseType != Attendance.TYPE_PRESENT) {
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ATTENDANCE,
id,
- profile.empty,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN,
+ profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebEvents.kt
index 52833b9a..35c68478 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebEvents.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebEvents.kt
@@ -57,8 +57,7 @@ class EdudziennikWebEvents(override val data: DataEdudziennik,
Metadata.TYPE_EVENT,
id,
profile.empty,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebExams.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebExams.kt
index 8e7836fd..f6abf637 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebExams.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebExams.kt
@@ -46,7 +46,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
if (dateString.isBlank()) return@forEach
val date = Date.fromY_m_d(dateString)
- val lessons = data.app.db.timetableDao().getForDateNow(profileId, date)
+ val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val startTime = lessons.firstOrNull { it.displaySubjectId == subject.id }?.displayStartTime
val eventTypeElement = examElement.child(3).child(0)
@@ -74,8 +74,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
Metadata.TYPE_EVENT,
id,
profile.empty,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGrades.kt
index 50557333..459f13aa 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGrades.kt
@@ -126,7 +126,8 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
comment = null,
semester = semester,
teacherId = teacher.id,
- subjectId = subject.id
+ subjectId = subject.id,
+ addedDate = addedDate
)
data.gradeList.add(gradeObject)
@@ -135,8 +136,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
Metadata.TYPE_GRADE,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
@@ -168,8 +168,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
Metadata.TYPE_GRADE,
proposedGradeObject.id,
profile.empty,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty
))
}
@@ -201,8 +200,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
Metadata.TYPE_GRADE,
finalGradeObject.id,
profile.empty,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebHomework.kt
index acabcfa6..169a5127 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebHomework.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebHomework.kt
@@ -43,7 +43,7 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
val subjectName = subjectElement.text()
val subject = data.getSubject(subjectId, subjectName)
- val lessons = data.app.db.timetableDao().getForDateNow(profileId, date)
+ val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val startTime = lessons.firstOrNull { it.subjectId == subject.id }?.displayStartTime
val teacherName = homeworkElement.child(2).text()
@@ -72,8 +72,7 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
Metadata.TYPE_HOMEWORK,
id,
profile.empty,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebLuckyNumber.kt
index 70c6849a..db8164c8 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebLuckyNumber.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebLuckyNumber.kt
@@ -24,9 +24,9 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
webGet(TAG, data.schoolEndpoint + "Lucky", xhr = true) { text ->
text.toIntOrNull()?.also { luckyNumber ->
val luckyNumberObject = LuckyNumber(
- profileId,
- Date.getToday(),
- luckyNumber
+ profileId = profileId,
+ date = Date.getToday(),
+ number = luckyNumber
)
data.luckyNumberList.add(luckyNumberObject)
@@ -35,8 +35,7 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
true,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebNotes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebNotes.kt
index a111ef79..35a78168 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebNotes.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebNotes.kt
@@ -41,12 +41,15 @@ class EdudziennikWebNotes(override val data: DataEdudziennik,
val description = noteElement.child(3).text()
val noticeObject = Notice(
- profileId,
- id,
- description,
- profile.currentSemester,
- Notice.TYPE_NEUTRAL,
- teacher.id
+ profileId = profileId,
+ id = id,
+ type = Notice.TYPE_NEUTRAL,
+ semester = profile.currentSemester,
+ text = description,
+ category = null,
+ points = null,
+ teacherId = teacher.id,
+ addedDate = addedDate
)
data.noticeList.add(noticeObject)
@@ -55,8 +58,7 @@ class EdudziennikWebNotes(override val data: DataEdudziennik,
Metadata.TYPE_NOTICE,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebTimetable.kt
index 3a31e208..94db15ef 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebTimetable.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebTimetable.kt
@@ -124,8 +124,7 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
Metadata.TYPE_LESSON_CHANGE,
lessonObject.id,
seen,
- seen,
- System.currentTimeMillis()
+ seen
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/firstlogin/EdudziennikFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/firstlogin/EdudziennikFirstLogin.kt
index e47c7842..a93632b1 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/firstlogin/EdudziennikFirstLogin.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/firstlogin/EdudziennikFirstLogin.kt
@@ -59,7 +59,7 @@ class EdudziennikFirstLogin(val data: DataEdudziennik, val onSuccess: () -> Unit
profileList.add(profile)
}
- EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess()
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/helper/DownloadAttachment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/helper/DownloadAttachment.kt
new file mode 100644
index 00000000..a968818a
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/helper/DownloadAttachment.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-14
+ */
+
+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 pl.szczodrzynski.edziennik.data.api.ERROR_FILE_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 DownloadAttachment(
+ 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 = "DownloadAttachment"
+ }
+
+ init {
+ val targetFile = Utils.getStorageDir()
+
+ val callback = object : FileCallbackHandler(targetFile) {
+ override fun onSuccess(file: File?, response: Response?) {
+ if (file == null) {
+ onError(ApiError(TAG, ERROR_FILE_DOWNLOAD)
+ .withResponse(response))
+ return
+ }
+
+ try {
+ onSuccess(file)
+ } catch (e: Exception) {
+ onError(ApiError(TAG, ERROR_FILE_DOWNLOAD)
+ .withResponse(response)
+ .withThrowable(e))
+ }
+ }
+
+ override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
+ try {
+ this@DownloadAttachment.onProgress(bytesWritten, bytesTotal)
+ } catch (e: Exception) {
+ onError(ApiError(TAG, ERROR_FILE_DOWNLOAD)
+ .withThrowable(e))
+ }
+ }
+
+ override fun onFailure(response: Response?, throwable: Throwable?) {
+ onError(ApiError(TAG, ERROR_REQUEST_FAILURE)
+ .withResponse(response)
+ .withThrowable(throwable))
+ }
+ }
+
+ Request.builder()
+ .url(fileUrl)
+ .userAgent(SYSTEM_USER_AGENT)
+ .callback(callback)
+ .build()
+ .enqueue()
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiCurrentRegister.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiCurrentRegister.kt
index 0e000fb1..d5e59596 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiCurrentRegister.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiCurrentRegister.kt
@@ -68,9 +68,9 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
val luckyNumberObject = LuckyNumber(
- data.profileId,
- luckyNumberDate,
- luckyNumber
+ profileId = data.profileId,
+ date = luckyNumberDate,
+ number = luckyNumber
)
data.luckyNumberList.add(luckyNumberObject)
@@ -80,8 +80,7 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
true,
- data.profile?.empty ?: false,
- System.currentTimeMillis()
+ data.profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesInbox.kt
index 58e7660c..622068e2 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesInbox.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesInbox.kt
@@ -69,7 +69,8 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
type = if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED,
subject = subject,
body = body,
- senderId = rTeacher.id
+ senderId = rTeacher.id,
+ addedDate = sentDate
)
val messageRecipient = MessageRecipient(
@@ -87,8 +88,7 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
Metadata.TYPE_MESSAGE,
message.id,
readDate > 0,
- readDate > 0 || profile?.empty ?: false,
- sentDate
+ readDate > 0 || profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesSent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesSent.kt
index 286c9896..87b3264d 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesSent.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesSent.kt
@@ -51,7 +51,8 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
type = TYPE_SENT,
subject = subject,
body = body,
- senderId = null
+ senderId = null,
+ addedDate = sentDate
)
for (recipientEl in jMessage.getAsJsonArray("odbiorcy")) {
@@ -76,7 +77,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
}
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))
}
data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAnnouncements.kt
index f8744efa..531cbeeb 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAnnouncements.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAnnouncements.kt
@@ -52,14 +52,14 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik,
val startDate = jAnnouncement.getString("DataWydarzenia")?.replace("[^\\d]".toRegex(), "")?.toLongOrNull()?.let { Date.fromMillis(it) }
val announcementObject = Announcement(
- profileId,
- announcementId,
- jAnnouncement.get("Temat").asString,
- jAnnouncement.get("Tresc").asString,
- startDate,
- null,
- rTeacher.id,
- null
+ profileId = profileId,
+ id = announcementId,
+ subject = jAnnouncement.get("Temat").asString,
+ text = jAnnouncement.get("Tresc").asString,
+ startDate = startDate,
+ endDate = null,
+ teacherId = rTeacher.id,
+ addedDate = addedDate
)
data.announcementList.add(announcementObject)
data.metadataList.add(Metadata(
@@ -67,8 +67,7 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik,
Metadata.TYPE_ANNOUNCEMENT,
announcementObject.id,
profile?.empty ?: false,
- profile?.empty ?: false,
- addedDate
+ profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAttendance.kt
index aa0a9998..e77c9eac 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAttendance.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAttendance.kt
@@ -12,10 +12,18 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
-import pl.szczodrzynski.edziennik.data.db.entity.Attendance.*
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT_EXCUSED
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_BELATED
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT_CUSTOM
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_UNKNOWN
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
+import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.getJsonObject
+import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@@ -51,71 +59,97 @@ class IdziennikWebAttendance(override val data: DataIdziennik,
for (jAttendanceEl in json.getAsJsonArray("Obecnosci")) {
val jAttendance = jAttendanceEl.asJsonObject
// jAttendance
- val attendanceTypeIdziennik = jAttendance.get("TypObecnosci").asInt
- if (attendanceTypeIdziennik == 5 || attendanceTypeIdziennik == 7)
- continue
- val attendanceDate = Date.fromY_m_d(jAttendance.get("Data").asString)
- val attendanceTime = Time.fromH_m(jAttendance.get("OdDoGodziny").asString)
- if (attendanceDate.combineWith(attendanceTime) > System.currentTimeMillis())
+ val type = jAttendance.get("TypObecnosci").asInt
+
+ // skip "zajęcia nie odbyły się" and "Ferie"
+ if (type == 5 || type == 7)
continue
- val attendanceId = jAttendance.get("IdLesson").asString.crc16().toLong()
+ val date = Date.fromY_m_d(jAttendance.get("Data").asString)
+ val time = Time.fromH_m(jAttendance.get("OdDoGodziny").asString)
+ if (date.combineWith(time) > System.currentTimeMillis())
+ continue
+
+ val id = jAttendance.get("IdLesson").asString.crc16().toLong()
val rSubject = data.getSubject(jAttendance.get("Przedmiot").asString, jAttendance.get("IdPrzedmiot").asLong, "")
val rTeacher = data.getTeacherByFDotSpaceLast(jAttendance.get("PrzedmiotNauczyciel").asString)
- var attendanceName = "obecność"
- var attendanceType = Attendance.TYPE_CUSTOM
+ var baseType = TYPE_UNKNOWN
+ var typeName = "nieznany rodzaj"
+ var typeSymbol: String? = null
+ var typeColor: Long? = null
- when (attendanceTypeIdziennik) {
- 1 /* nieobecność usprawiedliwiona */ -> {
- attendanceName = "nieobecność usprawiedliwiona"
- attendanceType = TYPE_ABSENT_EXCUSED
+ /* https://iuczniowie.progman.pl/idziennik/mod_panelRodzica/obecnosci/obecnosciUcznia_lmt637231494660000000.js */
+ /* https://iuczniowie.progman.pl/idziennik/mod_panelRodzica/obecnosci/obecnosci_lmt637231494660000000.css */
+ when (type) {
+ 1 -> {
+ baseType = TYPE_ABSENT_EXCUSED
+ typeName = "nieobecność usprawiedliwiona"
+ typeColor = 0xffffe099
}
- 2 /* spóźnienie */ -> {
- attendanceName = "spóźnienie"
- attendanceType = TYPE_BELATED
+ 2 -> {
+ baseType = TYPE_BELATED
+ typeName = "spóźnienie"
+ typeColor = 0xffffffaa
}
- 3 /* nieobecność nieusprawiedliwiona */ -> {
- attendanceName = "nieobecność nieusprawiedliwiona"
- attendanceType = TYPE_ABSENT
+ 3 -> {
+ baseType = TYPE_ABSENT
+ typeName = "nieobecność nieusprawiedliwiona"
+ typeColor = 0xffffad99
}
- 4 /* zwolnienie */, 9 /* zwolniony / obecny */ -> {
- attendanceType = TYPE_RELEASED
- if (attendanceTypeIdziennik == 4)
- attendanceName = "zwolnienie"
- if (attendanceTypeIdziennik == 9)
- attendanceName = "zwolnienie / obecność"
+ 4, 9 -> {
+ baseType = TYPE_RELEASED
+ if (type == 4) {
+ typeName = "zwolnienie"
+ typeColor = 0xffa8beff
+ }
+ if (type == 9) {
+ typeName = "zwolniony / obecny"
+ typeSymbol = "zb"
+ typeColor = 0xffff69b4
+ }
}
- 0 /* obecny */, 8 /* Wycieczka */ -> {
- attendanceType = TYPE_PRESENT
- if (attendanceTypeIdziennik == 8)
- attendanceName = "wycieczka"
+ 8 -> {
+ baseType = TYPE_PRESENT_CUSTOM
+ typeName = "wycieczka"
+ typeSymbol = "w"
+ typeColor = null
+ }
+ 0 -> {
+ baseType = TYPE_PRESENT
+ typeName = "obecny"
+ typeColor = 0xffccffcc
}
}
- val semester = profile?.dateToSemester(attendanceDate) ?: 1
+ val semester = profile?.dateToSemester(date) ?: 1
val attendanceObject = Attendance(
- profileId,
- attendanceId,
- rTeacher.id,
- rSubject.id,
- semester,
- attendanceName,
- attendanceDate,
- attendanceTime,
- attendanceType
- )
+ profileId = profileId,
+ id = id,
+ baseType = baseType,
+ typeName = typeName,
+ typeShort = typeSymbol ?: data.app.attendanceManager.getTypeShort(baseType),
+ typeSymbol = typeSymbol ?: data.app.attendanceManager.getTypeShort(baseType),
+ typeColor = typeColor?.toInt(),
+ date = date,
+ startTime = time,
+ semester = semester,
+ teacherId = rTeacher.id,
+ subjectId = rSubject.id
+ ).also {
+ it.lessonTopic = jAttendance.getString("PrzedmiotTemat")
+ it.lessonNumber = jAttendance.getInt("Godzina")
+ }
data.attendanceList.add(attendanceObject)
- if (attendanceObject.type != TYPE_PRESENT) {
+ if (attendanceObject.baseType != TYPE_PRESENT) {
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ATTENDANCE,
attendanceObject.id,
- profile?.empty ?: false,
- profile?.empty ?: false,
- System.currentTimeMillis()
+ profile?.empty ?: false || baseType == TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN,
+ profile?.empty ?: false || baseType == TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebExams.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebExams.kt
index 82e63e79..25c06945 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebExams.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebExams.kt
@@ -68,7 +68,7 @@ class IdziennikWebExams(override val data: DataIdziennik,
val teacherId = data.getTeacherByLastFirst(teacherName).id
val topic = exam.getString("zakres")?.trim() ?: ""
- val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate)
+ val lessonList = data.db.timetableDao().getAllForDateNow(profileId, examDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
val eventType = when (exam.getString("rodzaj")?.toLowerCase(Locale.getDefault())) {
@@ -98,8 +98,7 @@ class IdziennikWebExams(override val data: DataIdziennik,
Metadata.TYPE_EVENT,
eventObject.id,
profile?.empty ?: false,
- profile?.empty ?: false,
- System.currentTimeMillis()
+ profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetMessage.kt
index e709122d..bd1808df 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetMessage.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetMessage.kt
@@ -94,8 +94,7 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
Metadata.TYPE_MESSAGE,
message.id,
message.seen,
- message.notified,
- message.addedDate
+ message.notified
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGrades.kt
index b3237c82..90e17401 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGrades.kt
@@ -63,6 +63,8 @@ class IdziennikWebGrades(override val data: DataIdziennik,
colorInt = Color.parseColor("#$gradeColor")
}
+ val addedDate = grade.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()
+
val gradeObject = Grade(
profileId = profileId,
id = id,
@@ -76,7 +78,9 @@ class IdziennikWebGrades(override val data: DataIdziennik,
comment = null,
semester = semester,
teacherId = teacher.id,
- subjectId = subject.id)
+ subjectId = subject.id,
+ addedDate = addedDate
+ )
when (grade.getInt("Typ")) {
0 -> {
@@ -100,6 +104,8 @@ class IdziennikWebGrades(override val data: DataIdziennik,
colorInt = Color.parseColor("#$historyColor")
}
+ val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()
+
val historyObject = Grade(
profileId = profileId,
id = gradeObject.id * -1,
@@ -113,19 +119,18 @@ class IdziennikWebGrades(override val data: DataIdziennik,
comment = null,
semester = historyItem.getInt("Semestr") ?: 1,
teacherId = teacher.id,
- subjectId = subject.id)
+ subjectId = subject.id,
+ addedDate = addedDate
+ )
historyObject.parentId = gradeObject.id
- val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()
-
data.gradeList.add(historyObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
historyObject.id,
true,
- true,
- addedDate
+ true
))
}
// update the current grade's value with an average of all historical grades and itself
@@ -147,8 +152,6 @@ class IdziennikWebGrades(override val data: DataIdziennik,
}
}
- val addedDate = grade.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()
-
data.gradeList.add(gradeObject)
data.metadataList.add(
Metadata(
@@ -156,8 +159,7 @@ class IdziennikWebGrades(override val data: DataIdziennik,
Metadata.TYPE_GRADE,
id,
data.profile.empty,
- data.profile.empty,
- addedDate
+ data.profile.empty
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebHomework.kt
index 2289e13d..10b4a0c3 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebHomework.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebHomework.kt
@@ -57,7 +57,7 @@ class IdziennikWebHomework(override val data: DataIdziennik,
val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = homework.getString("usr") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id
- val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
+ val lessonList = data.db.timetableDao().getAllForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime
val topic = homework.getString("tytul")?.trim() ?: ""
@@ -77,7 +77,8 @@ class IdziennikWebHomework(override val data: DataIdziennik,
type = Event.TYPE_HOMEWORK,
teacherId = teacherId,
subjectId = subjectId,
- teamId = data.teamClass?.id ?: -1
+ teamId = data.teamClass?.id ?: -1,
+ addedDate = addedDate.inMillis
)
data.eventList.add(eventObject)
@@ -86,8 +87,7 @@ class IdziennikWebHomework(override val data: DataIdziennik,
Metadata.TYPE_HOMEWORK,
eventObject.id,
seen,
- seen,
- addedDate.inMillis
+ seen
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebNotices.kt
index dcc560d5..64108bfd 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebNotices.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebNotices.kt
@@ -13,9 +13,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notice
-import pl.szczodrzynski.edziennik.data.db.entity.Notice.*
+import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_NEGATIVE
+import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_NEUTRAL
+import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_POSITIVE
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getJsonObject
+import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebNotices(override val data: DataIdziennik,
@@ -53,20 +56,24 @@ class IdziennikWebNotices(override val data: DataIdziennik,
}
val noticeObject = Notice(
- profileId,
- noticeId,
- jNotice.get("Tresc").asString,
- jNotice.get("Semestr").asInt,
- nType,
- rTeacher.id)
+ profileId = profileId,
+ id = noticeId,
+ type = nType,
+ semester = jNotice.get("Semestr").asInt,
+ text = jNotice.getString("Tresc") ?: "",
+ category = null,
+ points = null,
+ teacherId = rTeacher.id,
+ addedDate = addedDate.inMillis
+ )
+
data.noticeList.add(noticeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_NOTICE,
noticeObject.id,
profile?.empty ?: false,
- profile?.empty ?: false,
- addedDate.inMillis
+ profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebProposedGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebProposedGrades.kt
index c2639e51..b23e9f98 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebProposedGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebProposedGrades.kt
@@ -76,6 +76,11 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
else -1
if (semester1Proposed != "") {
+ val addedDate = if (data.profile.empty)
+ data.profile.dateSemester1Start.inMillis
+ else
+ System.currentTimeMillis()
+
val gradeObject = Grade(
profileId = profileId,
id = semester1Id,
@@ -89,26 +94,26 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
comment = null,
semester = 1,
teacherId = -1,
- subjectId = subjectObject.id
+ subjectId = subjectObject.id,
+ addedDate = addedDate
)
- val addedDate = if (data.profile.empty)
- data.profile.dateSemester1Start.inMillis
- else
- System.currentTimeMillis()
-
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
gradeObject.id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
if (semester2Proposed != "") {
+ val addedDate = if (data.profile.empty)
+ data.profile.dateSemester2Start.inMillis
+ else
+ System.currentTimeMillis()
+
val gradeObject = Grade(
profileId = profileId,
id = semester2Id,
@@ -122,22 +127,17 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
comment = null,
semester = 2,
teacherId = -1,
- subjectId = subjectObject.id
+ subjectId = subjectObject.id,
+ addedDate = addedDate
)
- val addedDate = if (data.profile.empty)
- data.profile.dateSemester2Start.inMillis
- else
- System.currentTimeMillis()
-
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
gradeObject.id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebSendMessage.kt
index 973bc74f..343ebcac 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebSendMessage.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebSendMessage.kt
@@ -59,7 +59,7 @@ class IdziennikWebSendMessage(override val data: DataIdziennik,
IdziennikApiMessagesSent(data, null) {
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 event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
+ val event = MessageSentEvent(data.profileId, message, message?.addedDate)
EventBus.getDefault().postSticky(event)
onSuccess()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebTimetable.kt
index 066346cd..e7d293c4 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebTimetable.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebTimetable.kt
@@ -165,8 +165,7 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
Metadata.TYPE_LESSON_CHANGE,
lessonObject.id,
seen,
- seen,
- System.currentTimeMillis()
+ seen
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/firstlogin/IdziennikFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/firstlogin/IdziennikFirstLogin.kt
index 892c12c8..1a07c609 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/firstlogin/IdziennikFirstLogin.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/firstlogin/IdziennikFirstLogin.kt
@@ -89,7 +89,7 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
profileList.add(profile)
}
- EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess()
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/login/IdziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/login/IdziennikLoginWeb.kt
index e15f9410..2c6ad0e7 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/login/IdziennikLoginWeb.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/login/IdziennikLoginWeb.kt
@@ -74,9 +74,9 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
Regexes.IDZIENNIK_WEB_LUCKY_NUMBER.find(text)?.also {
val number = it[1].toIntOrNull() ?: return@also
val luckyNumberObject = LuckyNumber(
- data.profileId,
- Date.getToday(),
- number
+ profileId = data.profileId,
+ date = Date.getToday(),
+ number = number
)
data.luckyNumberList.add(luckyNumberObject)
@@ -86,8 +86,7 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
true,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/DataLibrus.kt
index 80a85a9f..e77dd50d 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/DataLibrus.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/DataLibrus.kt
@@ -120,7 +120,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiLogin: String? = null
var apiLogin: String?
get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin }
- set(value) { profile?.putStudentData("accountLogin", value) ?: return; mApiLogin = value }
+ set(value) { profile?.putStudentData("accountLogin", value); mApiLogin = value }
/**
* A Synergia password.
* Used: for login (API Login Method) in Synergia mode.
@@ -129,7 +129,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiPassword: String? = null
var apiPassword: String?
get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword }
- set(value) { profile?.putStudentData("accountPassword", value) ?: return; mApiPassword = value }
+ set(value) { profile?.putStudentData("accountPassword", value); mApiPassword = value }
/**
* A JST login Code.
@@ -138,8 +138,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiCode: String? = null
var apiCode: String?
get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode }
- set(value) {
- loginStore.putLoginData("accountCode", value); mApiCode = value }
+ set(value) { profile?.putStudentData("accountCode", value); mApiCode = value }
/**
* A JST login PIN.
* Used only during first login in JST mode.
@@ -147,8 +146,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiPin: String? = null
var apiPin: String?
get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin }
- set(value) {
- loginStore.putLoginData("accountPin", value); mApiPin = value }
+ set(value) { profile?.putStudentData("accountPin", value); mApiPin = value }
/**
* A Synergia API access token.
@@ -277,4 +275,10 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
var timetableNotPublic: Boolean
get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false }
set(value) { profile?.putStudentData("timetableNotPublic", value) ?: return; mTimetableNotPublic = value }
+
+ /**
+ * Set to false when Recaptcha helper doesn't provide a working token.
+ * When it's set to false uses Synergia for messages.
+ */
+ var messagesLoginSuccessful: Boolean = true
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/Librus.kt
index a79a66f8..48f308f8 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/Librus.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/Librus.kt
@@ -13,9 +13,7 @@ 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.LibrusMessagesGetRecipientList
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.*
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.interfaces.EdziennikCallback
@@ -91,9 +89,8 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
override fun getMessage(message: MessageFull) {
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
- LibrusMessagesGetMessage(data, message) {
- completed()
- }
+ if (data.messagesLoginSuccessful) LibrusMessagesGetMessage(data, message) { completed() }
+ else LibrusSynergiaGetMessage(data, message) { completed() }
}
}
@@ -124,10 +121,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
when (owner) {
is Message -> {
- login(LOGIN_METHOD_LIBRUS_MESSAGES) {
- LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) {
- completed()
- }
+ login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
+ if (data.messagesLoginSuccessful) LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) { completed() }
+ LibrusSynergiaGetAttachment(data, owner, attachmentId, attachmentName) { completed() }
}
}
is EventFull -> {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusFeatures.kt
index 98caab4e..c14a00df 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusFeatures.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusFeatures.kt
@@ -50,6 +50,8 @@ const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 1130
const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 2010
const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 2020
const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030
+const val ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_RECEIVED = 2040
+const val ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_SENT = 2050
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010
const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020
const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusRecaptchaHelper.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusRecaptchaHelper.kt
new file mode 100644
index 00000000..94ab5ed1
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusRecaptchaHelper.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-8.
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.librus
+
+import android.content.Context
+import android.webkit.WebView
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import pl.szczodrzynski.edziennik.startCoroutineTimer
+import kotlin.coroutines.CoroutineContext
+
+class LibrusRecaptchaHelper(
+ val context: Context,
+ url: String,
+ html: String,
+ val onSuccess: (url: String) -> Unit,
+ val onTimeout: () -> Unit
+) : CoroutineScope {
+ companion object {
+ private const val TAG = "LibrusRecaptchaHelper"
+ }
+
+ private val job: Job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Default
+
+ private val webView by lazy {
+ WebView(context).also {
+ it.settings.javaScriptEnabled = true
+ it.webViewClient = WebViewClient()
+ }
+ }
+
+ private var timeout: Job? = null
+
+ inner class WebViewClient : android.webkit.WebViewClient() {
+ override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
+ timeout?.cancel()
+ onSuccess(url)
+ return true
+ }
+ }
+
+ init {
+ launch(Dispatchers.Main) {
+ webView.loadDataWithBaseURL(url, html, "text/html", "UTF-8", null)
+ }
+ timeout = startCoroutineTimer(delayMillis = 10000L) {
+ onTimeout()
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusData.kt
index bf1fdab4..877d803b 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusData.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusData.kt
@@ -8,6 +8,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetList
+import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaGetMessages
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaHomework
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaInfo
import pl.szczodrzynski.edziennik.data.db.entity.Message
@@ -201,17 +202,27 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_student_info)
LibrusSynergiaInfo(data, lastSync, onSuccess)
}
+ ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_RECEIVED -> {
+ data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
+ LibrusSynergiaGetMessages(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess)
+ }
+ ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_SENT -> {
+ data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
+ LibrusSynergiaGetMessages(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess)
+ }
/**
* MESSAGES
*/
ENDPOINT_LIBRUS_MESSAGES_RECEIVED -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
- LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess)
+ if (data.messagesLoginSuccessful) LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess)
+ else LibrusSynergiaGetMessages(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess)
}
ENDPOINT_LIBRUS_MESSAGES_SENT -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
- LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess)
+ if (data.messagesLoginSuccessful) LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess)
+ else LibrusSynergiaGetMessages(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess)
}
else -> onSuccess(endpointId)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusSynergia.kt
index 96f28a65..72faf801 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusSynergia.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusSynergia.kt
@@ -91,6 +91,8 @@ open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) {
}
fun redirectUrlGet(tag: String, url: String, onSuccess: (url: String) -> Unit) {
+ d(tag, "Request: Librus/Synergia - $url")
+
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response) {
val redirectUrl = response.headers().get("Location")
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncementMarkAsRead.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncementMarkAsRead.kt
index d40c1a9a..23cfceb4 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncementMarkAsRead.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncementMarkAsRead.kt
@@ -37,8 +37,7 @@ class LibrusApiAnnouncementMarkAsRead(override val data: DataLibrus,
Metadata.TYPE_ANNOUNCEMENT,
announcement.id,
announcement.seen,
- announcement.notified,
- announcement.addedDate
+ announcement.notified
))
onSuccess()
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncements.kt
index 8256b859..d19be231 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncements.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncements.kt
@@ -38,15 +38,17 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
val read = announcement.getBoolean("WasRead") ?: false
val announcementObject = Announcement(
- profileId,
- id,
- subject,
- text,
- startDate,
- endDate,
- teacherId,
- longId
- )
+ profileId = profileId,
+ id = id,
+ subject = subject,
+ text = text,
+ startDate = startDate,
+ endDate = endDate,
+ teacherId = teacherId,
+ addedDate = addedDate
+ ).also {
+ it.idString = longId
+ }
data.announcementList.add(announcementObject)
data.setSeenMetadataList.add(Metadata(
@@ -54,8 +56,7 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
Metadata.TYPE_ANNOUNCEMENT,
id,
read,
- profile.empty || read,
- addedDate
+ profile.empty || read
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendanceTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendanceTypes.kt
index d7cbc00c..c620d13d 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendanceTypes.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendanceTypes.kt
@@ -26,25 +26,39 @@ class LibrusApiAttendanceTypes(override val data: DataLibrus,
attendanceTypes?.forEach { attendanceType ->
val id = attendanceType.getLong("Id") ?: return@forEach
- val name = attendanceType.getString("Name") ?: ""
- val color = attendanceType.getString("ColorRGB")?.let { Color.parseColor("#$it") } ?: -1
- val standardId = when (attendanceType.getBoolean("Standard") ?: false) {
- true -> id
- false -> attendanceType.getJsonObject("StandardType")?.getLong("Id") ?: id
- }
- val type = when (standardId) {
+ val typeName = attendanceType.getString("Name") ?: ""
+ val typeSymbol = attendanceType.getString("Short") ?: ""
+ val typeColor = attendanceType.getString("ColorRGB")?.let { Color.parseColor("#$it") }
+
+ val isStandard = attendanceType.getBoolean("Standard") ?: false
+ val baseType = when (attendanceType.getJsonObject("StandardType")?.getLong("Id") ?: id) {
1L -> Attendance.TYPE_ABSENT
2L -> Attendance.TYPE_BELATED
3L -> Attendance.TYPE_ABSENT_EXCUSED
4L -> Attendance.TYPE_RELEASED
- /*100*/else -> Attendance.TYPE_PRESENT
+ /*100*/else -> when (isStandard) {
+ true -> Attendance.TYPE_PRESENT
+ false -> Attendance.TYPE_PRESENT_CUSTOM
+ }
+ }
+ val typeShort = when (isStandard) {
+ true -> data.app.attendanceManager.getTypeShort(baseType)
+ false -> typeSymbol
}
- data.attendanceTypes.put(id, AttendanceType(profileId, id, name, type, color))
+ data.attendanceTypes.put(id, AttendanceType(
+ profileId,
+ id,
+ baseType,
+ typeName,
+ typeShort,
+ typeSymbol,
+ typeColor
+ ))
}
- data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 4*DAY)
+ data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 2*DAY)
onSuccess(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES)
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendances.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendances.kt
index 3727788d..7857dc2a 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendances.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendances.kt
@@ -13,7 +13,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date
-import pl.szczodrzynski.edziennik.utils.models.Time
class LibrusApiAttendances(override val data: DataLibrus,
override val lastSync: Long?,
@@ -42,9 +41,9 @@ class LibrusApiAttendances(override val data: DataLibrus,
val lessonDate = Date.fromY_m_d(attendance.getString("Date"))
val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id")
val semester = attendance.getInt("Semester") ?: return@forEach
- val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
- val typeObject = data.attendanceTypes[type] ?: null
- val topic = typeObject?.name ?: ""
+
+ val typeId = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
+ val type = data.attendanceTypes[typeId] ?: null
val startTime = data.lessonRanges.get(lessonNo)?.startTime
@@ -52,29 +51,34 @@ class LibrusApiAttendances(override val data: DataLibrus,
data.librusLessons.singleOrNull { it.lessonId == lessonId }
else null
- val attendanceObject = Attendance(
- profileId,
- id,
- teacherId ?: lesson?.teacherId ?: -1,
- lesson?.subjectId ?: -1,
- semester,
- topic,
- lessonDate,
- startTime ?: Time(0, 0, 0),
- typeObject?.type ?: Attendance.TYPE_CUSTOM
- )
-
val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach)
+ val attendanceObject = Attendance(
+ profileId = profileId,
+ id = id,
+ baseType = type?.baseType ?: Attendance.TYPE_UNKNOWN,
+ typeName = type?.typeName ?: "nieznany rodzaj",
+ typeShort = type?.typeShort ?: "?",
+ typeSymbol = type?.typeSymbol ?: "?",
+ typeColor = type?.typeColor,
+ date = lessonDate,
+ startTime = startTime,
+ semester = semester,
+ teacherId = teacherId ?: lesson?.teacherId ?: -1,
+ subjectId = lesson?.subjectId ?: -1,
+ addedDate = addedDate
+ ).also {
+ it.lessonNumber = lessonNo
+ }
+
data.attendanceList.add(attendanceObject)
- if(typeObject?.type != Attendance.TYPE_PRESENT) {
+ if(type?.baseType != Attendance.TYPE_PRESENT) {
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ATTENDANCE,
id,
- profile?.empty ?: false,
- profile?.empty ?: false,
- addedDate
+ profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN,
+ profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiBehaviourGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiBehaviourGrades.kt
index 5a374ec9..fae79a50 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiBehaviourGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiBehaviourGrades.kt
@@ -55,7 +55,8 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
comment = null,
semester = 1,
teacherId = -1,
- subjectId = 1
+ subjectId = 1,
+ addedDate = profile.getSemesterStart(1).inMillis
)
data.gradeList.add(semester1StartGradeObject)
@@ -64,8 +65,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE,
semester1StartGradeObject.id,
true,
- true,
- profile.getSemesterStart(1).inMillis
+ true
))
}
@@ -83,7 +83,8 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
comment = null,
semester = 2,
teacherId = -1,
- subjectId = 1
+ subjectId = 1,
+ addedDate = profile.getSemesterStart(2).inMillis
)
data.gradeList.add(semester2StartGradeObject)
@@ -92,8 +93,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE,
semester2StartGradeObject.id,
true,
- true,
- profile.getSemesterStart(2).inMillis
+ true
))
}
@@ -155,7 +155,8 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
comment = if (text != null) description.join(" - ") else null,
semester = semester,
teacherId = teacherId,
- subjectId = 1
+ subjectId = 1,
+ addedDate = addedDate
).apply {
valueMax = valueTo
}
@@ -166,8 +167,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiDescriptiveGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiDescriptiveGrades.kt
index 42e318e0..8cc4f1c1 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiDescriptiveGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiDescriptiveGrades.kt
@@ -65,7 +65,8 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
comment = null,
semester = semester,
teacherId = teacherId,
- subjectId = subjectId
+ subjectId = subjectId,
+ addedDate = addedDate
)
data.gradeList.add(gradeObject)
@@ -74,8 +75,7 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiEvents.kt
index 52f52844..7a76f88e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiEvents.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiEvents.kt
@@ -35,7 +35,7 @@ class LibrusApiEvents(override val data: DataLibrus,
events?.forEach { event ->
val id = event.getLong("Id") ?: return@forEach
val eventDate = Date.fromY_m_d(event.getString("Date"))
- val topic = event.getString("Content")?.trim() ?: ""
+ var topic = event.getString("Content")?.trim() ?: ""
val type = event.getJsonObject("Category")?.getLong("Id") ?: -1
val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1
val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1
@@ -46,6 +46,12 @@ class LibrusApiEvents(override val data: DataLibrus,
val startTime = lessonRange?.startTime ?: Time.fromH_m(event.getString("TimeFrom"))
val addedDate = Date.fromIso(event.getString("AddDate"))
+ event.getJsonObject("onlineLessonUrl")?.let { onlineLesson ->
+ val text = onlineLesson.getString("text")?.let { "$it - " } ?: ""
+ val url = onlineLesson.getString("url")
+ topic += "\n\n$text$url"
+ }
+
val eventObject = Event(
profileId = profileId,
id = id,
@@ -56,7 +62,8 @@ class LibrusApiEvents(override val data: DataLibrus,
type = type,
teacherId = teacherId,
subjectId = subjectId,
- teamId = teamId
+ teamId = teamId,
+ addedDate = addedDate
)
data.eventList.add(eventObject)
@@ -66,8 +73,7 @@ class LibrusApiEvents(override val data: DataLibrus,
Metadata.TYPE_EVENT,
id,
profile?.empty ?: false,
- profile?.empty ?: false,
- addedDate
+ profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiGrades.kt
index 7ff42fde..c2e2d0ce 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiGrades.kt
@@ -79,7 +79,8 @@ class LibrusApiGrades(override val data: DataLibrus,
comment = null,
semester = semester,
teacherId = teacherId,
- subjectId = subjectId
+ subjectId = subjectId,
+ addedDate = addedDate
)
grade.getJsonObject("Improvement")?.also {
@@ -98,8 +99,7 @@ class LibrusApiGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiHomework.kt
index d47fbfdf..c1183c19 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiHomework.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiHomework.kt
@@ -43,7 +43,8 @@ class LibrusApiHomework(override val data: DataLibrus,
type = -1,
teacherId = teacherId,
subjectId = -1,
- teamId = -1
+ teamId = -1,
+ addedDate = addedDate.inMillis
)
data.eventList.add(eventObject)
@@ -52,8 +53,7 @@ class LibrusApiHomework(override val data: DataLibrus,
Metadata.TYPE_HOMEWORK,
id,
profile?.empty ?: false,
- profile?.empty ?: false,
- addedDate.inMillis
+ profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiLuckyNumber.kt
index 5a52cd69..80e6e299 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiLuckyNumber.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiLuckyNumber.kt
@@ -33,9 +33,9 @@ class LibrusApiLuckyNumber(override val data: DataLibrus,
val luckyNumberDate = Date.fromY_m_d(luckyNumberEl.getString("LuckyNumberDay")) ?: Date.getToday()
val luckyNumber = luckyNumberEl.getInt("LuckyNumber") ?: -1
val luckyNumberObject = LuckyNumber(
- profileId,
- luckyNumberDate,
- luckyNumber
+ profileId = profileId,
+ date = luckyNumberDate,
+ number = luckyNumber
)
if (luckyNumberDate >= Date.getToday())
@@ -50,8 +50,7 @@ class LibrusApiLuckyNumber(override val data: DataLibrus,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
true,
- profile?.empty ?: false,
- System.currentTimeMillis()
+ profile?.empty ?: false
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiNotices.kt
index e05cbfd7..ba531afe 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiNotices.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiNotices.kt
@@ -46,12 +46,15 @@ class LibrusApiNotices(override val data: DataLibrus,
val semester = profile?.dateToSemester(addedDate) ?: 1
val noticeObject = Notice(
- profileId,
- id,
- categoryText + "\n" + text,
- semester,
- type,
- teacherId
+ profileId = profileId,
+ id = id,
+ type = type,
+ semester = semester,
+ text = text,
+ category = categoryText,
+ points = null,
+ teacherId = teacherId,
+ addedDate = addedDate.inMillis
)
data.noticeList.add(noticeObject)
@@ -61,8 +64,7 @@ class LibrusApiNotices(override val data: DataLibrus,
Metadata.TYPE_NOTICE,
id,
profile?.empty ?: false,
- profile?.empty ?: false,
- addedDate.inMillis
+ profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPointGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPointGrades.kt
index fae45318..9da2c435 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPointGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPointGrades.kt
@@ -56,7 +56,8 @@ class LibrusApiPointGrades(override val data: DataLibrus,
comment = null,
semester = semester,
teacherId = teacherId,
- subjectId = subjectId
+ subjectId = subjectId,
+ addedDate = addedDate
).apply {
valueMax = category?.valueTo ?: 0f
}
@@ -67,8 +68,7 @@ class LibrusApiPointGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPtMeetings.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPtMeetings.kt
index de696a99..6c0a9dd7 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPtMeetings.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPtMeetings.kt
@@ -58,8 +58,7 @@ class LibrusApiPtMeetings(override val data: DataLibrus,
Metadata.TYPE_EVENT,
id,
profile?.empty ?: false,
- profile?.empty ?: false,
- System.currentTimeMillis()
+ profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTeacherFreeDays.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTeacherFreeDays.kt
index 3d4db1aa..973ada46 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTeacherFreeDays.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTeacherFreeDays.kt
@@ -43,15 +43,15 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus,
val timeTo = teacherAbsence.getString("TimeTo")?.let { Time.fromH_m_s(it) }
val teacherAbsenceObject = TeacherAbsence(
- profileId,
- id,
- teacherId,
- type,
- name,
- dateFrom,
- dateTo,
- timeFrom,
- timeTo
+ profileId = profileId,
+ id = id,
+ type = type,
+ name = name,
+ dateFrom = dateFrom,
+ dateTo = dateTo,
+ timeFrom = timeFrom,
+ timeTo = timeTo,
+ teacherId = teacherId
)
data.teacherAbsenceList.add(teacherAbsenceObject)
@@ -60,8 +60,7 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus,
Metadata.TYPE_TEACHER_ABSENCE,
id,
true,
- profile?.empty ?: false,
- System.currentTimeMillis()
+ profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTextGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTextGrades.kt
index cf020425..7b7c8c6f 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTextGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTextGrades.kt
@@ -60,7 +60,8 @@ class LibrusApiTextGrades(override val data: DataLibrus,
comment = grade.getString("Phrase") /* whatever it is */,
semester = semester,
teacherId = teacherId,
- subjectId = subjectId
+ subjectId = subjectId,
+ addedDate = addedDate
)
data.gradeList.add(gradeObject)
@@ -69,8 +70,7 @@ class LibrusApiTextGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTimetables.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTimetables.kt
index 7c1eec61..9a463a6b 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTimetables.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTimetables.kt
@@ -198,8 +198,7 @@ class LibrusApiTimetables(override val data: DataLibrus,
Metadata.TYPE_LESSON_CHANGE,
lessonObject.id,
seen,
- seen,
- System.currentTimeMillis()
+ seen
))
}
data.lessonList.add(lessonObject)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetList.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetList.kt
index 35148cef..9f962bf9 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetList.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetList.kt
@@ -97,7 +97,8 @@ class LibrusMessagesGetList(override val data: DataLibrus,
type = type,
subject = subject,
body = null,
- senderId = senderId
+ senderId = senderId,
+ addedDate = sentDate
)
val messageRecipientObject = MessageRecipient(
@@ -120,8 +121,7 @@ class LibrusMessagesGetList(override val data: DataLibrus,
Metadata.TYPE_MESSAGE,
id,
notified,
- notified,
- sentDate
+ notified
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetMessage.kt
index bb4c620b..49ee0d52 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetMessage.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetMessage.kt
@@ -150,8 +150,7 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
Metadata.TYPE_MESSAGE,
messageObject.id,
true,
- true,
- messageObject.addedDate
+ true
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesSendMessage.kt
index 9364f2b8..d568135e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesSendMessage.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesSendMessage.kt
@@ -50,7 +50,7 @@ class LibrusMessagesSendMessage(override val data: DataLibrus,
LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = null) {
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 event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
+ val event = MessageSentEvent(data.profileId, message, message?.addedDate)
EventBus.getDefault().postSticky(event)
onSuccess()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetAttachment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetAttachment.kt
new file mode 100644
index 00000000..6b1f2eba
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetAttachment.kt
@@ -0,0 +1,24 @@
+package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia
+
+import pl.szczodrzynski.edziennik.data.api.LIBRUS_SYNERGIA_MESSAGES_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.entity.Message
+
+class LibrusSynergiaGetAttachment(override val data: DataLibrus,
+ val message: Message,
+ val attachmentId: Long,
+ val attachmentName: String,
+ val onSuccess: () -> Unit
+) : LibrusSynergia(data, null) {
+ companion object {
+ const val TAG = "LibrusSynergiaGetAttachment"
+ }
+
+ init {
+ redirectUrlGet(TAG, "$LIBRUS_SYNERGIA_MESSAGES_ATTACHMENT_URL/${message.id}/$attachmentId") { url ->
+ LibrusSandboxDownloadAttachment(data, url, message, attachmentId, attachmentName, onSuccess)
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessage.kt
new file mode 100644
index 00000000..4cb1e246
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessage.kt
@@ -0,0 +1,160 @@
+package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia
+
+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.MessageGetEvent
+import pl.szczodrzynski.edziennik.data.db.entity.Message
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.entity.Teacher
+import pl.szczodrzynski.edziennik.data.db.full.MessageFull
+import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
+import pl.szczodrzynski.edziennik.get
+import pl.szczodrzynski.edziennik.isNotNullNorEmpty
+import pl.szczodrzynski.edziennik.singleOrNull
+import pl.szczodrzynski.edziennik.swapFirstLastName
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class LibrusSynergiaGetMessage(override val data: DataLibrus,
+ private val messageObject: MessageFull,
+ val onSuccess: () -> Unit) : LibrusSynergia(data, null) {
+ companion object {
+ const val TAG = "LibrusSynergiaGetMessage"
+ }
+
+ init {
+ val endpoint = when (messageObject.type) {
+ Message.TYPE_SENT -> "wiadomosci/1/6/${messageObject.id}/f0"
+ else -> "wiadomosci/1/5/${messageObject.id}/f0"
+ }
+
+ data.profile?.also { profile ->
+ synergiaGet(TAG, endpoint) { text ->
+ val doc = Jsoup.parse(text)
+
+ val messageElement = doc.select(".container-message tr")[0].child(1)
+ val detailsElement = messageElement.child(1)
+ val readElement = messageElement.children().last()
+
+ val body = messageElement.select(".container-message-content").html()
+
+ messageObject.apply {
+ this.body = body
+
+ clearAttachments()
+ if (messageElement.children().size >= 5) {
+ messageElement.child(3).select("tr").forEachIndexed { i, attachment ->
+ if (i == 0) return@forEachIndexed // Skip the header
+ val filename = attachment.child(0).text().trim()
+ val attachmentId = "wiadomosci\\\\/pobierz_zalacznik\\\\/[0-9]+?\\\\/([0-9]+)\"".toRegex()
+ .find(attachment.select("img").attr("onclick"))?.get(1)
+ ?: return@forEachIndexed
+ addAttachment(attachmentId.toLong(), filename, -1)
+ }
+ }
+ }
+
+ val messageRecipientList = mutableListOf()
+
+ when (messageObject.type) {
+ Message.TYPE_RECEIVED -> {
+ val senderFullName = detailsElement.child(0).select(".left").text()
+ val senderGroupName = "\\[(.+?)]".toRegex().find(senderFullName)?.get(1)?.trim()
+
+ data.teacherList.singleOrNull { it.id == messageObject.senderId }?.apply {
+ setTeacherType(when (senderGroupName) {
+ /* https://api.librus.pl/2.0/Messages/Role */
+ "Pomoc techniczna Librus", "SuperAdministrator" -> Teacher.TYPE_SUPER_ADMIN
+ "Administrator szkoły" -> Teacher.TYPE_SCHOOL_ADMIN
+ "Dyrektor Szkoły" -> Teacher.TYPE_PRINCIPAL
+ "Nauczyciel" -> Teacher.TYPE_TEACHER
+ "Rodzic", "Opiekun" -> Teacher.TYPE_PARENT
+ "Sekretariat" -> Teacher.TYPE_SECRETARIAT
+ "Uczeń" -> Teacher.TYPE_STUDENT
+ "Pedagog/Psycholog szkolny" -> Teacher.TYPE_PEDAGOGUE
+ "Pracownik biblioteki" -> Teacher.TYPE_LIBRARIAN
+ "Inny specjalista" -> Teacher.TYPE_SPECIALIST
+ "Jednostka Nadrzędna" -> {
+ typeDescription = "Jednostka Nadrzędna"
+ Teacher.TYPE_OTHER
+ }
+ "Jednostka Samorządu Terytorialnego" -> {
+ typeDescription = "Jednostka Samorządu Terytorialnego"
+ Teacher.TYPE_OTHER
+ }
+ else -> Teacher.TYPE_OTHER
+ })
+ }
+
+ val readDateText = readElement.select(".left").text()
+ val readDate = when (readDateText.isNotNullNorEmpty()) {
+ true -> Date.fromIso(readDateText)
+ else -> 0
+ }
+
+ val messageRecipientObject = MessageRecipientFull(
+ profileId = profileId,
+ id = -1,
+ messageId = messageObject.id,
+ readDate = readDate
+ )
+
+ messageRecipientObject.fullName = profile.accountName
+ ?: profile.studentNameLong
+
+ messageRecipientList.add(messageRecipientObject)
+ }
+
+ Message.TYPE_SENT -> {
+
+ readElement.select("tr").forEachIndexed { i, receiver ->
+ if (i == 0) return@forEachIndexed // Skip the header
+
+ val receiverFullName = receiver.child(0).text()
+ val receiverName = receiverFullName.split('(')[0].swapFirstLastName()
+
+ val teacher = data.teacherList.singleOrNull { it.fullName == receiverName }
+ val receiverId = teacher?.id ?: -1
+
+ val readDate = when (val readDateText = receiver.child(1).text().trim()) {
+ "NIE" -> 0
+ else -> Date.fromIso(readDateText)
+ }
+
+ val messageRecipientObject = MessageRecipientFull(
+ profileId = profileId,
+ id = receiverId,
+ messageId = messageObject.id,
+ readDate = readDate
+ )
+
+ messageRecipientObject.fullName = receiverName
+
+ messageRecipientList.add(messageRecipientObject)
+ }
+ }
+ }
+
+ if (!messageObject.seen) {
+ data.setSeenMetadataList.add(Metadata(
+ messageObject.profileId,
+ Metadata.TYPE_MESSAGE,
+ messageObject.id,
+ true,
+ true
+ ))
+ }
+
+ messageObject.recipients = messageRecipientList
+ data.messageRecipientList.addAll(messageRecipientList)
+
+ data.messageList.add(messageObject)
+ data.messageListReplace = true
+
+ EventBus.getDefault().postSticky(MessageGetEvent(messageObject))
+ onSuccess()
+ }
+ } ?: onSuccess()
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessages.kt
new file mode 100644
index 00000000..ffc8133a
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessages.kt
@@ -0,0 +1,116 @@
+package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia
+
+import org.jsoup.Jsoup
+import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.data.api.ERROR_NOT_IMPLEMENTED
+import pl.szczodrzynski.edziennik.data.api.Regexes
+import pl.szczodrzynski.edziennik.data.api.edziennik.librus.*
+import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusSynergia
+import pl.szczodrzynski.edziennik.data.db.entity.*
+import pl.szczodrzynski.edziennik.utils.Utils
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class LibrusSynergiaGetMessages(override val data: DataLibrus,
+ override val lastSync: Long?,
+ private val type: Int = Message.TYPE_RECEIVED,
+ archived: Boolean = false,
+ val onSuccess: (Int) -> Unit) : LibrusSynergia(data, lastSync) {
+ companion object {
+ const val TAG = "LibrusSynergiaGetMessages"
+ }
+
+ init {
+ val endpoint = when (type) {
+ Message.TYPE_RECEIVED -> "wiadomosci/5"
+ Message.TYPE_SENT -> "wiadomosci/6"
+ else -> null
+ }
+ val endpointId = when (type) {
+ Message.TYPE_RECEIVED -> ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_RECEIVED
+ else -> ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_SENT
+ }
+
+ if (endpoint != null) {
+ synergiaGet(TAG, endpoint) { text ->
+ val doc = Jsoup.parse(text)
+
+ fun getRecipientId(name: String): Long = data.teacherList.singleOrNull {
+ it.fullNameLastFirst == name
+ }?.id ?: {
+ val teacherObject = Teacher(
+ profileId,
+ -1 * Utils.crc16(name.swapFirstLastName().toByteArray()).toLong(),
+ name.splitName()?.second!!,
+ name.splitName()?.first!!
+ )
+ data.teacherList.put(teacherObject.id, teacherObject)
+ teacherObject.id
+ }.invoke()
+
+ doc.select(".decorated.stretch tbody > tr").forEach { messageElement ->
+ val url = messageElement.select("a").first().attr("href")
+ val id = Regexes.LIBRUS_MESSAGE_ID.find(url)?.get(1)?.toLong() ?: return@forEach
+ val subject = messageElement.child(3).text()
+ val sentDate = Date.fromIso(messageElement.child(4).text())
+ val recipientName = messageElement.child(2).text().split('(')[0].fixName()
+ val recipientId = getRecipientId(recipientName)
+ val read = messageElement.child(2).attr("style").isNullOrBlank()
+
+ val senderId = when (type) {
+ Message.TYPE_RECEIVED -> recipientId
+ else -> null
+ }
+
+ val receiverId = when (type) {
+ Message.TYPE_RECEIVED -> -1
+ else -> recipientId
+ }
+
+ val notified = when (type) {
+ Message.TYPE_SENT -> true
+ else -> read || profile?.empty ?: false
+ }
+
+ val messageObject = Message(
+ profileId = profileId,
+ id = id,
+ type = type,
+ subject = subject,
+ body = null,
+ senderId = senderId,
+ addedDate = sentDate
+ )
+
+ val messageRecipientObject = MessageRecipient(
+ profileId,
+ receiverId,
+ -1,
+ if (read) 1 else 0,
+ id
+ )
+
+ messageObject.hasAttachments = !messageElement.child(1).select("img").isEmpty()
+
+ data.messageList.add(messageObject)
+ data.messageRecipientList.add(messageRecipientObject)
+ data.setSeenMetadataList.add(Metadata(
+ profileId,
+ Metadata.TYPE_MESSAGE,
+ id,
+ notified,
+ notified
+ ))
+ }
+
+ when (type) {
+ Message.TYPE_RECEIVED -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_RECEIVED, SYNC_ALWAYS)
+ Message.TYPE_SENT -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_SENT, DAY, MainActivity.DRAWER_ITEM_MESSAGES)
+ }
+ onSuccess(endpointId)
+ }
+ } else {
+ data.error(TAG, ERROR_NOT_IMPLEMENTED)
+ onSuccess(endpointId)
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaHomework.kt
index 48f88ef4..d33f6911 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaHomework.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaHomework.kt
@@ -58,7 +58,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
elements[9].select("input").attr("onclick")
)?.get(1)?.toLong() ?: return@forEachIndexed
- val lessons = data.db.timetableDao().getForDateNow(profileId, eventDate)
+ val lessons = data.db.timetableDao().getAllForDateNow(profileId, eventDate)
val startTime = lessons.firstOrNull { it.subjectId == subjectId }?.startTime
val seen = when (profile.empty) {
@@ -76,7 +76,8 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
type = Event.TYPE_HOMEWORK,
teacherId = teacherId,
subjectId = subjectId,
- teamId = data.teamClass?.id ?: -1
+ teamId = data.teamClass?.id ?: -1,
+ addedDate = addedDate.inMillis
)
data.eventList.add(eventObject)
@@ -85,8 +86,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus,
Metadata.TYPE_HOMEWORK,
id,
seen,
- seen,
- addedDate.inMillis
+ seen
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/firstlogin/LibrusFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/firstlogin/LibrusFirstLogin.kt
index f693c41a..68f44269 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/firstlogin/LibrusFirstLogin.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/firstlogin/LibrusFirstLogin.kt
@@ -33,7 +33,7 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
val accounts = json.getJsonArray("accounts")
if (accounts == null || accounts.size() < 1) {
- EventBus.getDefault().post(FirstLoginFinishedEvent(listOf(), data.loginStore))
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(listOf(), data.loginStore))
onSuccess()
return@portalGet
}
@@ -81,7 +81,7 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
profileList.add(profile)
}
- EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess()
}
}
@@ -116,14 +116,15 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
).apply {
studentData["isPremium"] = account?.getBoolean("IsPremium") == true || account?.getBoolean("IsPremiumDemo") == true
studentData["accountId"] = account.getInt("Id") ?: 0
- studentData["accountLogin"] = login
+ studentData["accountLogin"] = data.apiLogin ?: login
+ studentData["accountPassword"] = data.apiPassword
studentData["accountToken"] = data.apiAccessToken
studentData["accountTokenTime"] = data.apiTokenExpiryTime
studentData["accountRefreshToken"] = data.apiRefreshToken
}
profileList.add(profile)
- EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess()
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginApi.kt
index fe155abe..7151cf29 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginApi.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginApi.kt
@@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.net.HttpURLConnection.*
+@Suppress("ConvertSecondaryConstructorToPrimary")
class LibrusLoginApi {
companion object {
private const val TAG = "LoginLibrusApi"
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginMessages.kt
index ba8bcf76..9bfad99f 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginMessages.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginMessages.kt
@@ -10,6 +10,7 @@ import im.wangchao.mhttp.body.MediaTypeUtils
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
+import pl.szczodrzynski.edziennik.data.api.edziennik.librus.LibrusRecaptchaHelper
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.utils.Utils.d
@@ -35,17 +36,39 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
onSuccess()
}
+ text?.contains("grecaptcha.ready") == true -> {
+ val url = response?.request()?.url()?.toString() ?: run {
+ //data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
+ data.messagesLoginSuccessful = false
+ onSuccess()
+ return
+ }
+
+ LibrusRecaptchaHelper(data.app, url, text, onSuccess = { newUrl ->
+ loginWithSynergia(newUrl)
+ }, onTimeout = {
+ //data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_TIMEOUT, response, text)
+ data.messagesLoginSuccessful = false
+ onSuccess()
+ })
+ }
+
text?.contains("ok") == true -> {
saveSessionId(response, text)
onSuccess()
}
text?.contains("Niepoprawny login i/lub hasło.") == true -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
text?.contains("stop.png") == true -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
- text?.contains("eAccessDeny") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
+ text?.contains("eAccessDeny") == true -> {
+ // data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
+ data.messagesLoginSuccessful = false
+ onSuccess()
+ }
text?.contains("OffLine") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
text?.contains("error") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
text?.contains("eVarWhitThisNameNotExists") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
text?.contains("") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
+ else -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginPortal.kt
index 166a4ae5..5198d155 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginPortal.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginPortal.kt
@@ -146,12 +146,14 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
}
val error = if (response.code() == 200) null else
json.getJsonArray("errors")?.getString(0)
+ ?: json.getJsonObject("errors")?.entrySet()?.firstOrNull()?.value?.asString
error?.let { code ->
when {
code.contains("Sesja logowania wygasła") -> ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED
code.contains("Upewnij się, że nie") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN
// this doesn't work anyway: `errors` is an object with `g-recaptcha-response` set
code.contains("robotem") -> ERROR_CAPTCHA_LIBRUS_PORTAL
+ code.contains("Podany adres e-mail jest nieprawidłowy.") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN
else -> ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR
}.let { errorCode ->
data.error(ApiError(TAG, errorCode)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiAttendance.kt
index be68de4d..473b3691 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiAttendance.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiAttendance.kt
@@ -6,7 +6,10 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
-import pl.szczodrzynski.edziennik.data.db.entity.Attendance.*
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT_EXCUSED
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List) {
@@ -23,7 +26,7 @@ class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List)
val id = cols[0].toLong()
val lessonId = cols[1].toLong()
data.mobiLessons.singleOrNull { it.id == lessonId }?.let { lesson ->
- val type = when (cols[4]) {
+ val baseType = when (cols[4]) {
"2" -> TYPE_ABSENT
"5" -> TYPE_ABSENT_EXCUSED
"4" -> TYPE_RELEASED
@@ -31,16 +34,37 @@ class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List)
}
val semester = data.profile?.dateToSemester(lesson.date) ?: 1
+ val typeName = when (baseType) {
+ TYPE_ABSENT -> "nieobecność"
+ TYPE_ABSENT_EXCUSED -> "nieobecność usprawiedliwiona"
+ TYPE_RELEASED -> "zwolnienie"
+ TYPE_PRESENT -> "obecność"
+ else -> "nieznany rodzaj"
+ }
+ val typeSymbol = when (baseType) {
+ TYPE_ABSENT -> "|"
+ TYPE_ABSENT_EXCUSED -> "+"
+ TYPE_RELEASED -> "z"
+ TYPE_PRESENT -> "."
+ else -> "?"
+ }
+
val attendanceObject = Attendance(
- data.profileId,
- id,
- lesson.teacherId,
- lesson.subjectId,
- semester,
- lesson.topic,
- lesson.date,
- lesson.startTime,
- type)
+ profileId = data.profileId,
+ id = id,
+ baseType = baseType,
+ typeName = typeName,
+ typeShort = data.app.attendanceManager.getTypeShort(baseType),
+ typeSymbol = typeSymbol,
+ typeColor = null,
+ date = lesson.date,
+ startTime = lesson.startTime,
+ semester = semester,
+ teacherId = lesson.teacherId,
+ subjectId = lesson.subjectId
+ ).also {
+ it.lessonTopic = lesson.topic
+ }
data.attendanceList.add(attendanceObject)
data.metadataList.add(
@@ -48,9 +72,8 @@ class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List)
data.profileId,
Metadata.TYPE_ATTENDANCE,
id,
- data.profile?.empty ?: false,
- data.profile?.empty ?: false,
- System.currentTimeMillis()
+ data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN,
+ data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiEvents.kt
index 02fbb67e..7a72d3de 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiEvents.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiEvents.kt
@@ -60,7 +60,9 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List) {
type = type,
teacherId = teacherId,
subjectId = subjectId,
- teamId = teamId)
+ teamId = teamId,
+ addedDate = addedDate
+ )
data.eventList.add(eventObject)
data.metadataList.add(
@@ -69,8 +71,7 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List) {
Metadata.TYPE_EVENT,
id,
data.profile?.empty ?: false,
- data.profile?.empty ?: false,
- addedDate
+ data.profile?.empty ?: false
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiGrades.kt
index 609c895c..c3c1b449 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiGrades.kt
@@ -79,7 +79,9 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List) {
comment = null,
semester = semester,
teacherId = teacherId,
- subjectId = subjectId)
+ subjectId = subjectId,
+ addedDate = addedDate
+ )
if (data.profile?.empty == true) {
addedDate = data.profile.dateSemester1Start.inMillis
@@ -92,8 +94,7 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List) {
Metadata.TYPE_GRADE,
id,
data.profile?.empty ?: false,
- data.profile?.empty ?: false,
- addedDate
+ data.profile?.empty ?: false
))
addedDate++
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt
index 4623a26d..683a8820 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt
@@ -40,7 +40,8 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List) {
type = Event.TYPE_HOMEWORK,
teacherId = teacherId,
subjectId = subjectId,
- teamId = teamId)
+ teamId = teamId
+ )
data.eventList.add(eventObject)
data.metadataList.add(
@@ -49,8 +50,7 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List) {
Metadata.TYPE_HOMEWORK,
id,
data.profile?.empty ?: false,
- data.profile?.empty ?: false,
- System.currentTimeMillis()
+ data.profile?.empty ?: false
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiNotices.kt
index f1d9aed2..66bfe1fd 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiNotices.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiNotices.kt
@@ -33,12 +33,16 @@ class MobidziennikApiNotices(val data: DataMobidziennik, rows: List) {
val addedDate = Date.fromYmd(cols[7]).inMillis
val noticeObject = Notice(
- data.profileId,
- id,
- text,
- semester,
- type,
- teacherId)
+ profileId = data.profileId,
+ id = id,
+ type = type,
+ semester = semester,
+ text = text,
+ category = null,
+ points = null,
+ teacherId = teacherId,
+ addedDate = addedDate
+ )
data.noticeList.add(noticeObject)
data.metadataList.add(
@@ -47,8 +51,7 @@ class MobidziennikApiNotices(val data: DataMobidziennik, rows: List) {
Metadata.TYPE_NOTICE,
id,
data.profile?.empty ?: false,
- data.profile?.empty ?: false,
- addedDate
+ data.profile?.empty ?: false
))
}
}}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiTimetable.kt
index 6a7ce02d..a82ab4ef 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiTimetable.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiTimetable.kt
@@ -8,9 +8,9 @@ import android.util.SparseArray
import androidx.core.util.set
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
+import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
-import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.fixName
import pl.szczodrzynski.edziennik.keys
import pl.szczodrzynski.edziennik.singleOrNull
@@ -97,8 +97,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List) {
Metadata.TYPE_LESSON_CHANGE,
it.id,
seen,
- seen,
- System.currentTimeMillis()
+ seen
))
}
data.lessonList += it
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt
index 5e1445ec..ece23d7f 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt
@@ -17,9 +17,9 @@ class MobidziennikLuckyNumberExtractor(val data: DataMobidziennik, text: String)
val luckyNumber = it.groupValues[1].toInt()
val luckyNumberObject = LuckyNumber(
- data.profileId,
- Date.getToday(),
- luckyNumber
+ profileId = data.profileId,
+ date = Date.getToday(),
+ number = luckyNumber
)
data.luckyNumberList.add(luckyNumberObject)
@@ -29,8 +29,7 @@ class MobidziennikLuckyNumberExtractor(val data: DataMobidziennik, text: String)
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
true,
- data.profile?.empty ?: false,
- System.currentTimeMillis()
+ data.profile?.empty ?: false
))
} catch (_: Exception){}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt
index c56c56d9..36dbe1e9 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt
@@ -11,7 +11,13 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBID
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
-import pl.szczodrzynski.edziennik.data.db.entity.Attendance.*
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT_EXCUSED
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_BELATED
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT_CUSTOM
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_UNKNOWN
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.fixName
@@ -71,6 +77,18 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
val start = System.currentTimeMillis()
+ val types = Regexes.MOBIDZIENNIK_ATTENDANCE_TYPES
+ .find(text)
+ ?.get(1)
+ ?.split("
")
+ ?.map {
+ it.trimEnd(',')
+ .split(" ", limit = 2)
+ .let { it.getOrNull(0) to it.getOrNull(1) }
+ }
+ ?.toMap()
+ val typeSymbols = types?.keys?.filterNotNull() ?: listOf()
+
Regexes.MOBIDZIENNIK_ATTENDANCE_TABLE.findAll(text).forEach { tableResult ->
val table = tableResult[1]
val lessonDates = mutableListOf()
@@ -92,55 +110,90 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
return@forEach
ranges.forEach { range ->
val lessonDate = dateIterator.next()
- val entry = entriesIterator.next()
+ var entry = entriesIterator.next()
if (entry.isBlank())
return@forEach
val startTime = Time.fromH_m(range[1])
- val entryIterator = entry.iterator()
+
range[2].split(" / ").mapNotNull { Regexes.MOBIDZIENNIK_ATTENDANCE_LESSON.find(it) }.forEachIndexed { index, lesson ->
- val topic = lesson[2]
- if (topic.startsWith("Lekcja odwołana: ") || !entryIterator.hasNext())
+ val topic = lesson[1].substringAfter(" - ", missingDelimiterValue = "").takeIf { it.isNotBlank() }
+ if (topic?.startsWith("Lekcja odwołana: ") == true || entry.isEmpty())
return@forEachIndexed
- val subjectName = lesson[1]
+ val subjectName = lesson[1].substringBefore(" - ")
//val team = lesson[3]
- val teacherName = lesson[4].fixName()
+ val teacherName = lesson[3].fixName()
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == teacherName }?.id ?: -1
val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1
- val type = when (entryIterator.nextChar()) {
- '.' -> TYPE_PRESENT
- '|' -> TYPE_ABSENT
- '+' -> TYPE_ABSENT_EXCUSED
- 's' -> TYPE_BELATED
- 'z' -> TYPE_RELEASED
- else -> TYPE_PRESENT
+ var typeSymbol = ""
+ for (symbol in typeSymbols) {
+ if (entry.startsWith(symbol) && symbol.length > typeSymbol.length)
+ typeSymbol = symbol
}
+ entry = entry.removePrefix(typeSymbol)
+
+ var isCounted = true
+ val baseType = when (typeSymbol) {
+ "." -> TYPE_PRESENT
+ "|" -> TYPE_ABSENT
+ "+" -> TYPE_ABSENT_EXCUSED
+ "s" -> TYPE_BELATED
+ "z" -> TYPE_RELEASED
+ else -> {
+ isCounted = false
+ when (typeSymbol) {
+ "e" -> TYPE_PRESENT_CUSTOM
+ "en" -> TYPE_ABSENT
+ "ep" -> TYPE_PRESENT_CUSTOM
+ else -> TYPE_UNKNOWN
+ }
+ }
+ }
+ val typeName = types?.get(typeSymbol) ?: ""
+ val typeColor = when (typeSymbol) {
+ "e" -> 0xff673ab7
+ "en" -> 0xffec407a
+ "ep" -> 0xff4caf50
+ else -> null
+ }?.toInt()
+
+ val typeShort = if (isCounted)
+ data.app.attendanceManager.getTypeShort(baseType)
+ else
+ typeSymbol
+
val semester = data.profile?.dateToSemester(lessonDate) ?: 1
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson[0].hashCode() and 0xFFFF) + index
val attendanceObject = Attendance(
- data.profileId,
- id,
- teacherId,
- subjectId,
- semester,
- topic,
- lessonDate,
- startTime,
- type)
+ profileId = profileId,
+ id = id,
+ baseType = baseType,
+ typeName = typeName,
+ typeShort = typeShort,
+ typeSymbol = typeSymbol,
+ typeColor = typeColor,
+ date = lessonDate,
+ startTime = startTime,
+ semester = semester,
+ teacherId = teacherId,
+ subjectId = subjectId
+ ).also {
+ it.lessonTopic = topic
+ it.isCounted = isCounted
+ }
data.attendanceList.add(attendanceObject)
- if (type != TYPE_PRESENT) {
+ if (baseType != TYPE_PRESENT) {
data.metadataList.add(
Metadata(
data.profileId,
Metadata.TYPE_ATTENDANCE,
id,
- data.profile?.empty ?: false,
- data.profile?.empty ?: false,
- System.currentTimeMillis()
+ data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN,
+ data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebCalendar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebCalendar.kt
index c1749d76..95c1f6df 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebCalendar.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebCalendar.kt
@@ -89,8 +89,8 @@ class MobidziennikWebCalendar(override val data: DataMobidziennik,
Metadata.TYPE_EVENT,
eventObject.id,
profile?.empty ?: false,
- profile?.empty ?: false,
- System.currentTimeMillis() /* no addedDate here though */
+ profile?.empty ?: false
+ /* no addedDate here though */
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetMessage.kt
index 1c44a219..105c0943 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetMessage.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetMessage.kt
@@ -139,8 +139,7 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik,
Metadata.TYPE_MESSAGE,
message.id,
true,
- true,
- message.addedDate
+ true
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGrades.kt
index de058c91..9f5fab95 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGrades.kt
@@ -125,7 +125,8 @@ class MobidziennikWebGrades(override val data: DataMobidziennik,
comment = null,
semester = gradeSemester,
teacherId = teacherId,
- subjectId = subjectId
+ subjectId = subjectId,
+ addedDate = gradeAddedDateMillis
)
gradeObject.classAverage = gradeClassAverage
@@ -137,8 +138,7 @@ class MobidziennikWebGrades(override val data: DataMobidziennik,
Metadata.TYPE_GRADE,
gradeObject.id,
profile.empty,
- profile.empty,
- gradeAddedDateMillis
+ profile.empty
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesAll.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesAll.kt
index c0373c54..4e8f900a 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesAll.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesAll.kt
@@ -77,11 +77,12 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
type = type,
subject = subject,
body = null,
- senderId = senderId
+ senderId = senderId,
+ addedDate = addedDate
)
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))
}
// sync every 7 days as we probably don't expect more than
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt
index 385ea71e..be762228 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt
@@ -63,7 +63,8 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
type = Message.TYPE_RECEIVED,
subject = subject,
body = null,
- senderId = senderId
+ senderId = senderId,
+ addedDate = addedDate
)
if (hasAttachments)
@@ -76,8 +77,7 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
Metadata.TYPE_MESSAGE,
message.id,
isRead,
- isRead || profile?.empty ?: false,
- addedDate
+ isRead || profile?.empty ?: false
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesSent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesSent.kt
index 918816c8..543df329 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesSent.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesSent.kt
@@ -78,7 +78,8 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
type = Message.TYPE_SENT,
subject = subject,
body = null,
- senderId = null
+ senderId = null,
+ addedDate = addedDate
)
if (hasAttachments)
@@ -91,8 +92,7 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
Metadata.TYPE_MESSAGE,
message.id,
true,
- true,
- addedDate
+ true
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebSendMessage.kt
index d0cee4dc..89178b8b 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebSendMessage.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebSendMessage.kt
@@ -45,7 +45,7 @@ class MobidziennikWebSendMessage(override val data: DataMobidziennik,
MobidziennikWebMessagesAll(data, null) {
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 event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
+ val event = MessageSentEvent(data.profileId, message, message?.addedDate)
EventBus.getDefault().postSticky(event)
onSuccess()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/firstlogin/MobidziennikFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/firstlogin/MobidziennikFirstLogin.kt
index 4857bd09..11b668f9 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/firstlogin/MobidziennikFirstLogin.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/firstlogin/MobidziennikFirstLogin.kt
@@ -85,7 +85,7 @@ class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Un
profileList.add(profile)
}
- EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess()
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/DataPodlasie.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/DataPodlasie.kt
new file mode 100644
index 00000000..d117c139
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/DataPodlasie.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie
+
+import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_PODLASIE_API
+import pl.szczodrzynski.edziennik.data.api.models.Data
+import pl.szczodrzynski.edziennik.data.db.entity.*
+import kotlin.text.replace
+
+class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
+
+ fun isApiLoginValid() = apiToken.isNotNullNorEmpty()
+
+ override fun satisfyLoginMethods() {
+ loginMethods.clear()
+ if (isApiLoginValid())
+ loginMethods += LOGIN_METHOD_PODLASIE_API
+ }
+
+ override fun generateUserCode(): String = "$schoolShortName:$loginShort:${studentId?.crc32()}"
+
+ /* _
+ /\ (_)
+ / \ _ __ _
+ / /\ \ | '_ \| |
+ / ____ \| |_) | |
+ /_/ \_\ .__/|_|
+ | |
+ |*/
+ private var mApiToken: String? = null
+ var apiToken: String?
+ get() { mApiToken = mApiToken ?: loginStore.getLoginData("apiToken", null); return mApiToken }
+ set(value) { loginStore.putLoginData("apiToken", value); mApiToken = value }
+
+ private var mApiUrl: String? = null
+ var apiUrl: String?
+ get() { mApiUrl = mApiUrl ?: profile?.getStudentData("apiUrl", null); return mApiUrl }
+ set(value) { profile?.putStudentData("apiUrl", value) ?: return; mApiUrl = value }
+
+ /* ____ _ _
+ / __ \| | | |
+ | | | | |_| |__ ___ _ __
+ | | | | __| '_ \ / _ \ '__|
+ | |__| | |_| | | | __/ |
+ \____/ \__|_| |_|\___|*/
+ private var mStudentId: String? = null
+ var studentId: String?
+ get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", null); return mStudentId }
+ set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value }
+
+ private var mStudentLogin: String? = null
+ var studentLogin: String?
+ get() { mStudentLogin = mStudentLogin ?: profile?.getStudentData("studentLogin", null); return mStudentLogin }
+ set(value) { profile?.putStudentData("studentLogin", value) ?: return; mStudentLogin = value }
+
+ private var mSchoolName: String? = null
+ var schoolName: String?
+ get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName }
+ set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value }
+
+ private var mClassName: String? = null
+ var className: String?
+ get() { mClassName = mClassName ?: profile?.getStudentData("className", null); return mClassName }
+ set(value) { profile?.putStudentData("className", value) ?: return; mClassName = value }
+
+ private var mSchoolYear: String? = null
+ var schoolYear: String?
+ get() { mSchoolYear = mSchoolYear ?: profile?.getStudentData("schoolYear", null); return mSchoolYear }
+ set(value) { profile?.putStudentData("schoolYear", value) ?: return; mSchoolYear = value }
+
+ private var mCurrentSemester: Int? = null
+ var currentSemester: Int
+ get() { mCurrentSemester = mCurrentSemester ?: profile?.getStudentData("currentSemester", 0); return mCurrentSemester ?: 0 }
+ set(value) { profile?.putStudentData("currentSemester", value) ?: return; mCurrentSemester = value }
+
+ val schoolShortName: String?
+ get() = studentLogin?.split('@')?.get(1)?.replace(".podlaskie.pl", "")
+
+ val loginShort: String?
+ get() = studentLogin?.split('@')?.get(0)
+
+ fun getSubject(name: String): Subject {
+ val id = name.crc32()
+ return subjectList.singleOrNull { it.id == id } ?: run {
+ val subject = Subject(profileId, id, name, name)
+ subjectList.put(id, subject)
+ subject
+ }
+ }
+
+ fun getTeacher(firstName: String, lastName: String): Teacher {
+ val name = "$firstName $lastName".fixName()
+ return teacherList.singleOrNull { it.fullName == name } ?: run {
+ val id = name.crc32()
+ val teacher = Teacher(profileId, id, firstName, lastName)
+ teacherList.put(id, teacher)
+ teacher
+ }
+ }
+
+ fun getTeam(name: String? = null): Team {
+ if (name == "cała klasa" || name == null) return teamClass ?: run {
+ val id = className!!.crc32()
+ val teamCode = "$schoolShortName:$className"
+ val team = Team(profileId, id, className, Team.TYPE_CLASS, teamCode, -1)
+ teamList.put(id, team)
+ return team
+ } else {
+ val id = name.crc32()
+ val teamCode = "$schoolShortName:$name"
+ val team = Team(profileId, id, name, Team.TYPE_VIRTUAL, teamCode, -1)
+ teamList.put(id, team)
+ return team
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/Podlasie.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/Podlasie.kt
new file mode 100644
index 00000000..30da6e02
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/Podlasie.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie
+
+import com.google.gson.JsonObject
+import org.greenrobot.eventbus.EventBus
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.data.api.edziennik.helper.DownloadAttachment
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.PodlasieData
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.firstlogin.PodlasieFirstLogin
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login.PodlasieLogin
+import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
+import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
+import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
+import pl.szczodrzynski.edziennik.data.api.models.ApiError
+import pl.szczodrzynski.edziennik.data.api.podlasieLoginMethods
+import pl.szczodrzynski.edziennik.data.api.prepare
+import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
+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.EventFull
+import pl.szczodrzynski.edziennik.data.db.full.MessageFull
+import pl.szczodrzynski.edziennik.utils.Utils
+import java.io.File
+
+class Podlasie(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
+ companion object {
+ const val TAG = "Podlasie"
+ }
+
+ val internalErrorList = mutableListOf()
+ val data: DataPodlasie
+
+ init {
+ data = DataPodlasie(app, profile, loginStore).apply {
+ callback = wrapCallback(this@Podlasie.callback)
+ satisfyLoginMethods()
+ }
+ }
+
+ private fun completed() {
+ data.saveData()
+ callback.onCompleted()
+ }
+
+ /* _______ _ _ _ _ _
+ |__ __| | /\ | | (_) | | |
+ | | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___
+ | | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \
+ | | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | |
+ |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
+ __/ |
+ |__*/
+ override fun sync(featureIds: List, viewId: Int?, onlyEndpoints: List?, arguments: JsonObject?) {
+ data.arguments = arguments
+ data.prepare(podlasieLoginMethods, PodlasieFeatures, featureIds, viewId, onlyEndpoints)
+ Utils.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
+ Utils.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
+ PodlasieLogin(data) {
+ PodlasieData(data) {
+ completed()
+ }
+ }
+ }
+
+ override fun getMessage(message: MessageFull) {
+
+ }
+
+ override fun sendMessage(recipients: List, subject: String, text: String) {
+
+ }
+
+ override fun markAllAnnouncementsAsRead() {
+
+ }
+
+ override fun getAnnouncement(announcement: AnnouncementFull) {
+
+ }
+
+ override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
+ val fileUrl = attachmentName.substringAfter(":")
+ DownloadAttachment(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, _ ->
+ val event = AttachmentGetEvent(
+ data.profileId,
+ owner,
+ attachmentId,
+ AttachmentGetEvent.TYPE_PROGRESS,
+ bytesWritten = written
+ )
+
+ EventBus.getDefault().postSticky(event)
+ },
+ onError = { apiError ->
+ data.error(apiError)
+ })
+ }
+
+ override fun getRecipientList() {
+
+ }
+
+ override fun getEvent(eventFull: EventFull) {
+
+ }
+
+ override fun firstLogin() {
+ PodlasieFirstLogin(data) {
+ completed()
+ }
+ }
+
+ override fun cancel() {
+ Utils.d(TAG, "Cancelled")
+ data.cancel()
+ callback.onCompleted()
+ }
+
+ private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
+ return object : EdziennikCallback {
+ override fun onCompleted() {
+ callback.onCompleted()
+ }
+
+ override fun onProgress(step: Float) {
+ callback.onProgress(step)
+ }
+
+ override fun onStartProgress(stringRes: Int) {
+ callback.onStartProgress(stringRes)
+ }
+
+ override fun onError(apiError: ApiError) {
+ // TODO Error handling
+ when (apiError.errorCode) {
+ in internalErrorList -> {
+ // finish immediately if the same error occurs twice during the same sync
+ callback.onError(apiError)
+ }
+ else -> callback.onError(apiError)
+ }
+ }
+
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/PodlasieFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/PodlasieFeatures.kt
new file mode 100644
index 00000000..82c6f659
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/PodlasieFeatures.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie
+
+import pl.szczodrzynski.edziennik.data.api.FEATURE_ALWAYS_NEEDED
+import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_PODLASIE_API
+import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_PODLASIE
+import pl.szczodrzynski.edziennik.data.api.models.Feature
+
+const val ENDPOINT_PODLASIE_API_MAIN = 1001
+
+val PodlasieFeatures = listOf(
+ Feature(LOGIN_TYPE_PODLASIE, FEATURE_ALWAYS_NEEDED, listOf(
+ ENDPOINT_PODLASIE_API_MAIN to LOGIN_METHOD_PODLASIE_API
+ ), listOf(LOGIN_METHOD_PODLASIE_API))
+)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/PodlasieApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/PodlasieApi.kt
new file mode 100644
index 00000000..af5ff9d7
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/PodlasieApi.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data
+
+import com.google.gson.JsonObject
+import im.wangchao.mhttp.Request
+import im.wangchao.mhttp.RequestParams
+import im.wangchao.mhttp.Response
+import im.wangchao.mhttp.callback.JsonCallbackHandler
+import pl.szczodrzynski.edziennik.data.api.*
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.models.ApiError
+import pl.szczodrzynski.edziennik.getInt
+import pl.szczodrzynski.edziennik.getJsonObject
+import pl.szczodrzynski.edziennik.toHexString
+import pl.szczodrzynski.edziennik.utils.Utils
+import java.security.MessageDigest
+import java.text.SimpleDateFormat
+import java.util.*
+
+open class PodlasieApi(open val data: DataPodlasie, open val lastSync: Long?) {
+ companion object {
+ const val TAG = "PodlasieApi"
+ }
+
+ val profileId
+ get() = data.profile?.id ?: -1
+
+ val profile
+ get() = data.profile
+
+ fun apiGet(tag: String, endpoint: String, onSuccess: (json: JsonObject) -> Unit) {
+ val url = PODLASIE_API_URL + endpoint
+
+ Utils.d(tag, "Request: Podlasie/Api - $url")
+
+ if (data.apiToken == null) {
+ data.error(tag, ERROR_PODLASIE_API_NO_TOKEN)
+ return
+ }
+
+ val callback = object : JsonCallbackHandler() {
+ override fun onSuccess(json: JsonObject?, response: Response?) {
+ if (json == null || response == null) {
+ data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
+ .withResponse(response))
+ return
+ }
+
+ val error = json.getJsonObject("system_message")?.getInt("code")
+
+ error?.let { code ->
+ when (code) {
+ 0 -> ERROR_PODLASIE_API_DATA_MISSING
+ 4 -> ERROR_LOGIN_PODLASIE_API_DEVICE_LIMIT
+ 5 -> ERROR_LOGIN_PODLASIE_API_INVALID_TOKEN
+ 200 -> null // Not an error
+ else -> ERROR_PODLASIE_API_OTHER
+ }?.let { errorCode ->
+ data.error(ApiError(tag, errorCode)
+ .withApiResponse(json)
+ .withResponse(response))
+ return@onSuccess
+ }
+ }
+
+ try {
+ onSuccess(json)
+ } catch (e: Exception) {
+ data.error(ApiError(tag, EXCEPTION_PODLASIE_API_REQUEST)
+ .withResponse(response)
+ .withThrowable(e)
+ .withApiResponse(json))
+ }
+ }
+
+ override fun onFailure(response: Response?, throwable: Throwable?) {
+ data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
+ .withResponse(response)
+ .withThrowable(throwable))
+ }
+ }
+
+ Request.builder()
+ .url(url)
+ .userAgent(SYSTEM_USER_AGENT)
+ .requestParams(RequestParams(mapOf(
+ "token" to data.apiToken,
+ "securityToken" to getSecurityToken(),
+ "mobileId" to data.app.deviceId,
+ "ver" to PODLASIE_API_VERSION
+ )))
+ .callback(callback)
+ .build()
+ .enqueue()
+ }
+
+ private fun getSecurityToken(): String {
+ val format = SimpleDateFormat("yyyy-MM-dd HH", Locale.ENGLISH)
+ .also { it.timeZone = TimeZone.getTimeZone("Europe/Warsaw") }.format(System.currentTimeMillis())
+ val instance = MessageDigest.getInstance("SHA-256")
+ val digest = instance.digest("-EYlwYu8u16miVd8tT?oO7cvoUVQrQN0vr!$format".toByteArray()).toHexString()
+ val digest2 = instance.digest(data.apiToken!!.toByteArray()).toHexString()
+ return instance.digest("$digest$digest2".toByteArray()).toHexString()
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/PodlasieData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/PodlasieData.kt
new file mode 100644
index 00000000..ce1197b8
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/PodlasieData.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data
+
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.ENDPOINT_PODLASIE_API_MAIN
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api.PodlasieApiMain
+import pl.szczodrzynski.edziennik.utils.Utils
+
+class PodlasieData(val data: DataPodlasie, val onSuccess: () -> Unit) {
+ companion object {
+ const val TAG = "PodlasieData"
+ }
+
+ init {
+ nextEndpoint(onSuccess)
+ }
+
+ private fun nextEndpoint(onSuccess: () -> Unit) {
+ if (data.targetEndpointIds.isEmpty()) {
+ onSuccess()
+ return
+ }
+ if (data.cancelled) {
+ onSuccess()
+ return
+ }
+ val id = data.targetEndpointIds.firstKey()
+ val lastSync = data.targetEndpointIds.remove(id)
+ useEndpoint(id, lastSync) {
+ data.progress(data.progressStep)
+ nextEndpoint(onSuccess)
+ }
+ }
+
+ private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
+ Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
+ when (endpointId) {
+ ENDPOINT_PODLASIE_API_MAIN -> {
+ data.startProgress(R.string.edziennik_progress_endpoint_data)
+ PodlasieApiMain(data, lastSync, onSuccess)
+ }
+ else -> onSuccess(endpointId)
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiEvents.kt
new file mode 100644
index 00000000..8a169ee7
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiEvents.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-13
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api
+
+import com.google.gson.JsonObject
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
+import pl.szczodrzynski.edziennik.data.db.entity.Event
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.getLong
+import pl.szczodrzynski.edziennik.getString
+import pl.szczodrzynski.edziennik.utils.models.Date
+import pl.szczodrzynski.edziennik.utils.models.Time
+import java.util.*
+
+class PodlasieApiEvents(val data: DataPodlasie, val rows: List) {
+ init {
+ rows.forEach { event ->
+ val id = event.getLong("ExternalId") ?: return@forEach
+ val date = event.getString("DateFrom")?.let { Date.fromY_m_d(it) } ?: return@forEach
+ val time = event.getString("DateFrom")?.let { Time.fromY_m_d_H_m_s(it) }
+ ?: return@forEach
+
+ val name = event.getString("Name")?.replace(""", "\"") ?: ""
+ val description = event.getString("Description")?.replace(""", "\"") ?: ""
+
+ val type = when (event.getString("Category")?.toLowerCase(Locale.getDefault())) {
+ "klasówka" -> Event.TYPE_EXAM
+ "praca domowa" -> Event.TYPE_HOMEWORK
+ "wycieczka" -> Event.TYPE_EXCURSION
+ else -> Event.TYPE_DEFAULT
+ }
+
+ val teacherFirstName = event.getString("PersonEnteringDataFirstName") ?: return@forEach
+ val teacherLastName = event.getString("PersonEnteringDataLastName") ?: return@forEach
+ val teacher = data.getTeacher(teacherFirstName, teacherLastName)
+
+ val lessonList = data.db.timetableDao().getAllForDateNow(data.profileId, date)
+ val lesson = lessonList.firstOrNull { it.startTime == time }
+
+ val addedDate = event.getString("CreateDate")?.let { Date.fromIso(it) }
+ ?: System.currentTimeMillis()
+
+ val eventObject = Event(
+ profileId = data.profileId,
+ id = id,
+ date = date,
+ time = time,
+ topic = name,
+ color = null,
+ type = type,
+ teacherId = teacher.id,
+ subjectId = lesson?.subjectId ?: -1,
+ teamId = data.teamClass?.id ?: -1,
+ addedDate = addedDate
+ ).apply {
+ homeworkBody = description
+ }
+
+ data.eventList.add(eventObject)
+ data.metadataList.add(
+ Metadata(
+ data.profileId,
+ if (type == Event.TYPE_HOMEWORK) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT,
+ id,
+ data.profile?.empty ?: false,
+ data.profile?.empty ?: false
+ ))
+ }
+
+ data.toRemove.add(DataRemoveModel.Events.future())
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiFinalGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiFinalGrades.kt
new file mode 100644
index 00000000..7c4575aa
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiFinalGrades.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-13
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api
+
+import com.google.gson.JsonObject
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
+import pl.szczodrzynski.edziennik.data.db.entity.Grade
+import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
+import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
+import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
+import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
+import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
+import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.getLong
+import pl.szczodrzynski.edziennik.getString
+
+class PodlasieApiFinalGrades(val data: DataPodlasie, val rows: List) {
+ init { data.profile?.also { profile ->
+ rows.forEach { grade ->
+ val id = grade.getLong("ExternalId") ?: return@forEach
+ val mark = grade.getString("Mark") ?: return@forEach
+ val proposedMark = grade.getString("ProposedMark") ?: "0"
+ val name = data.app.gradesManager.getGradeNumberName(mark)
+ val value = data.app.gradesManager.getGradeValue(name)
+ val semester = grade.getString("TermShortcut")?.length ?: return@forEach
+
+ val typeName = grade.getString("Type") ?: return@forEach
+ val type = when (typeName) {
+ "S" -> if (semester == 1) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER2_FINAL
+ "Y", "R" -> TYPE_YEAR_FINAL
+ else -> return@forEach
+ }
+
+ val subjectName = grade.getString("SchoolSubject") ?: return@forEach
+ val subject = data.getSubject(subjectName)
+
+ val addedDate = if (profile.empty) profile.getSemesterStart(semester).inMillis
+ else System.currentTimeMillis()
+
+ val gradeObject = Grade(
+ profileId = data.profileId,
+ id = id,
+ name = name,
+ type = type,
+ value = value,
+ weight = 0f,
+ color = -1,
+ category = null,
+ description = null,
+ comment = null,
+ semester = semester,
+ teacherId = -1,
+ subjectId = subject.id,
+ addedDate = addedDate
+ )
+
+ data.gradeList.add(gradeObject)
+ data.metadataList.add(
+ Metadata(
+ data.profileId,
+ Metadata.TYPE_GRADE,
+ id,
+ profile.empty,
+ profile.empty
+ ))
+
+ if (proposedMark != "0") {
+ val proposedName = data.app.gradesManager.getGradeNumberName(proposedMark)
+ val proposedValue = data.app.gradesManager.getGradeValue(proposedName)
+
+ val proposedType = when (typeName) {
+ "S" -> if (semester == 1) TYPE_SEMESTER1_PROPOSED else TYPE_SEMESTER2_PROPOSED
+ "Y", "R" -> TYPE_YEAR_PROPOSED
+ else -> return@forEach
+ }
+
+ val proposedGradeObject = Grade(
+ profileId = data.profileId,
+ id = id * (-1),
+ name = proposedName,
+ type = proposedType,
+ value = proposedValue,
+ weight = 0f,
+ color = -1,
+ category = null,
+ description = null,
+ comment = null,
+ semester = semester,
+ teacherId = -1,
+ subjectId = subject.id,
+ addedDate = addedDate
+ )
+
+ data.gradeList.add(proposedGradeObject)
+ data.metadataList.add(
+ Metadata(
+ data.profileId,
+ Metadata.TYPE_GRADE,
+ proposedGradeObject.id,
+ profile.empty,
+ profile.empty
+ ))
+ }
+ }
+
+ data.toRemove.addAll(listOf(
+ TYPE_SEMESTER1_FINAL,
+ TYPE_SEMESTER1_PROPOSED,
+ TYPE_SEMESTER2_FINAL,
+ TYPE_SEMESTER2_PROPOSED,
+ TYPE_YEAR_FINAL,
+ TYPE_YEAR_PROPOSED
+ ).map {
+ DataRemoveModel.Grades.allWithType(it)
+ })
+ }}
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiGrades.kt
new file mode 100644
index 00000000..d5696567
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiGrades.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-13
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api
+
+import android.graphics.Color
+import com.google.gson.JsonObject
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
+import pl.szczodrzynski.edziennik.data.db.entity.Grade
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.getFloat
+import pl.szczodrzynski.edziennik.getInt
+import pl.szczodrzynski.edziennik.getLong
+import pl.szczodrzynski.edziennik.getString
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class PodlasieApiGrades(val data: DataPodlasie, val rows: List) {
+ init {
+ rows.forEach { grade ->
+ val id = grade.getLong("ExternalId") ?: return@forEach
+ val name = grade.getString("Mark") ?: return@forEach
+ val value = data.app.gradesManager.getGradeValue(name)
+ val weight = grade.getFloat("Weight") ?: 0f
+ val includeToAverage = grade.getInt("IncludeToAverage") != 0
+ val color = grade.getString("Color")?.let { Color.parseColor(it) } ?: -1
+ val category = grade.getString("Category") ?: ""
+ val comment = grade.getString("Comment") ?: ""
+ val semester = grade.getString("TermShortcut")?.length ?: data.currentSemester
+
+ val teacherFirstName = grade.getString("TeacherFirstName") ?: return@forEach
+ val teacherLastName = grade.getString("TeacherLastName") ?: return@forEach
+ val teacher = data.getTeacher(teacherFirstName, teacherLastName)
+
+ val subjectName = grade.getString("SchoolSubject") ?: return@forEach
+ val subject = data.getSubject(subjectName)
+
+ val addedDate = grade.getString("ReceivedDate")?.let { Date.fromY_m_d(it).inMillis }
+ ?: System.currentTimeMillis()
+
+ val gradeObject = Grade(
+ profileId = data.profileId,
+ id = id,
+ name = name,
+ type = Grade.TYPE_NORMAL,
+ value = value,
+ weight = if (includeToAverage) weight else 0f,
+ color = color,
+ category = category,
+ description = null,
+ comment = comment,
+ semester = semester,
+ teacherId = teacher.id,
+ subjectId = subject.id,
+ addedDate = addedDate
+ )
+
+ data.gradeList.add(gradeObject)
+ data.metadataList.add(
+ Metadata(
+ data.profileId,
+ Metadata.TYPE_GRADE,
+ id,
+ data.profile?.empty ?: false,
+ data.profile?.empty ?: false
+ ))
+ }
+
+ data.toRemove.add(DataRemoveModel.Grades.allWithType(Grade.TYPE_NORMAL))
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiHomework.kt
new file mode 100644
index 00000000..6ab53328
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiHomework.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-14
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api
+
+import com.google.gson.JsonObject
+import pl.szczodrzynski.edziennik.crc32
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
+import pl.szczodrzynski.edziennik.data.db.entity.Event
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.get
+import pl.szczodrzynski.edziennik.getString
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class PodlasieApiHomework(val data: DataPodlasie, val rows: List) {
+ init {
+ rows.reversed().forEach { homework ->
+ val id = homework.getString("ExternalId")?.crc32() ?: return@forEach
+ val topic = homework.getString("Title")?.replace(""", "\"") ?: ""
+ val description = homework.getString("Message")?.replace(""", "\"") ?: ""
+ val date = Date.getToday()
+ val addedDate = System.currentTimeMillis()
+
+ val eventObject = Event(
+ profileId = data.profileId,
+ id = id,
+ date = date,
+ time = null,
+ topic = topic,
+ color = null,
+ type = Event.TYPE_HOMEWORK,
+ teacherId = -1,
+ subjectId = -1,
+ teamId = data.teamClass?.id ?: -1,
+ addedDate = addedDate
+ ).apply {
+ homeworkBody = description
+ }
+
+ eventObject.attachmentIds = mutableListOf()
+ eventObject.attachmentNames = mutableListOf()
+ homework.getString("Attachments")?.split(',')?.onEach { url ->
+ val filename = "&filename=(.*?)&".toRegex().find(url)?.get(1) ?: return@onEach
+ val ext = "&ext=(.*?)&".toRegex().find(url)?.get(1) ?: return@onEach
+ eventObject.attachmentIds?.add(url.crc32())
+ eventObject.attachmentNames?.add("$filename.$ext:$url")
+ }
+
+ data.eventList.add(eventObject)
+ data.metadataList.add(
+ Metadata(
+ data.profileId,
+ Metadata.TYPE_HOMEWORK,
+ id,
+ data.profile?.empty ?: false,
+ data.profile?.empty ?: false
+ ))
+ }
+
+ data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiLuckyNumber.kt
new file mode 100644
index 00000000..725ec727
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiLuckyNumber.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-13
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api
+
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class PodlasieApiLuckyNumber(val data: DataPodlasie, val luckyNumber: Int) {
+ init {
+ val luckyNumberObject = LuckyNumber(
+ profileId = data.profileId,
+ date = Date.getToday(),
+ number = luckyNumber
+ )
+
+ data.luckyNumberList.add(luckyNumberObject)
+ data.metadataList.add(
+ Metadata(
+ data.profileId,
+ Metadata.TYPE_LUCKY_NUMBER,
+ luckyNumberObject.date.value.toLong(),
+ true,
+ data.profile?.empty ?: false
+ ))
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiMain.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiMain.kt
new file mode 100644
index 00000000..6fe77b5c
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiMain.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api
+
+import pl.szczodrzynski.edziennik.asJsonObjectList
+import pl.szczodrzynski.edziennik.data.api.PODLASIE_API_USER_ENDPOINT
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.ENDPOINT_PODLASIE_API_MAIN
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.PodlasieApi
+import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
+import pl.szczodrzynski.edziennik.getInt
+import pl.szczodrzynski.edziennik.getJsonArray
+
+class PodlasieApiMain(override val data: DataPodlasie,
+ override val lastSync: Long?,
+ val onSuccess: (endpointId: Int) -> Unit) : PodlasieApi(data, lastSync) {
+ companion object {
+ const val TAG = "PodlasieApiTimetable"
+ }
+
+ init {
+ apiGet(TAG, PODLASIE_API_USER_ENDPOINT) { json ->
+ data.getTeam() // Save the class team when it doesn't exist.
+
+ json.getInt("LuckyNumber")?.let { PodlasieApiLuckyNumber(data, it) }
+ json.getJsonArray("Teacher")?.asJsonObjectList()?.let { PodlasieApiTeachers(data, it) }
+ json.getJsonArray("Timetable")?.asJsonObjectList()?.let { PodlasieApiTimetable(data, it) }
+ json.getJsonArray("Marks")?.asJsonObjectList()?.let { PodlasieApiGrades(data, it) }
+ json.getJsonArray("MarkFinal")?.asJsonObjectList()?.let { PodlasieApiFinalGrades(data, it) }
+ json.getJsonArray("News")?.asJsonObjectList()?.let { PodlasieApiEvents(data, it) }
+ json.getJsonArray("Tasks")?.asJsonObjectList()?.let { PodlasieApiHomework(data, it) }
+
+ data.setSyncNext(ENDPOINT_PODLASIE_API_MAIN, SYNC_ALWAYS)
+ onSuccess(ENDPOINT_PODLASIE_API_MAIN)
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiTeachers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiTeachers.kt
new file mode 100644
index 00000000..ca1fa7b4
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiTeachers.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-13
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api
+
+import com.google.gson.JsonObject
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.db.entity.Teacher
+import pl.szczodrzynski.edziennik.getInt
+import pl.szczodrzynski.edziennik.getLong
+import pl.szczodrzynski.edziennik.getString
+
+class PodlasieApiTeachers(val data: DataPodlasie, val rows: List) {
+ init {
+ rows.forEach { teacher ->
+ val id = teacher.getLong("ExternalId") ?: return@forEach
+ val firstName = teacher.getString("FirstName") ?: return@forEach
+ val lastName = teacher.getString("LastName") ?: return@forEach
+ val isEducator = teacher.getInt("Educator") == 1
+
+ val teacherObject = Teacher(
+ profileId = data.profileId,
+ id = id,
+ name = firstName,
+ surname = lastName,
+ loginId = null
+ )
+
+ data.teacherList.put(id, teacherObject)
+
+ val teamClass = data.teamClass
+ if (isEducator && teamClass != null) {
+ data.teamList.put(teamClass.id, teamClass.apply {
+ teacherId = id
+ })
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiTimetable.kt
new file mode 100644
index 00000000..a9a12d5d
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/data/api/PodlasieApiTimetable.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.api
+
+import com.google.gson.JsonObject
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
+import pl.szczodrzynski.edziennik.data.db.entity.Lesson
+import pl.szczodrzynski.edziennik.getInt
+import pl.szczodrzynski.edziennik.getString
+import pl.szczodrzynski.edziennik.utils.models.Date
+import pl.szczodrzynski.edziennik.utils.models.Time
+import pl.szczodrzynski.edziennik.utils.models.Week
+
+class PodlasieApiTimetable(val data: DataPodlasie, rows: List) {
+ init {
+ val currentWeekStart = Week.getWeekStart()
+
+ if (Date.getToday().weekDay > 4) {
+ currentWeekStart.stepForward(0, 0, 7)
+ }
+
+ val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
+
+ val weekStart = Date.fromY_m_d(getDate)
+ val weekEnd = weekStart.clone().stepForward(0, 0, 6)
+
+ val days = mutableListOf()
+ var startDate: Date? = null
+ var endDate: Date? = null
+
+ rows.forEach { lesson ->
+ val date = lesson.getString("Date")?.let { Date.fromY_m_d(it) } ?: return@forEach
+
+ if ((date > weekEnd || date < weekStart) && data.profile?.empty != true) return@forEach
+ if (startDate == null) startDate = date.clone()
+ endDate = date.clone()
+ if (date.value !in days) days += date.value
+
+ val lessonNumber = lesson.getInt("LessonNumber") ?: return@forEach
+ val startTime = lesson.getString("TimeFrom")?.let { Time.fromH_m_s(it) }
+ ?: return@forEach
+ val endTime = lesson.getString("TimeTo")?.let { Time.fromH_m_s(it) } ?: return@forEach
+ val subject = lesson.getString("SchoolSubject")?.let { data.getSubject(it) }
+ ?: return@forEach
+
+ val teacherFirstName = lesson.getString("TeacherFirstName") ?: return@forEach
+ val teacherLastName = lesson.getString("TeacherLastName") ?: return@forEach
+ val teacher = data.getTeacher(teacherFirstName, teacherLastName)
+
+ val team = lesson.getString("Group")?.let { data.getTeam(it) } ?: return@forEach
+ val classroom = lesson.getString("Room")
+
+ Lesson(data.profileId, -1).also {
+ it.type = Lesson.TYPE_NORMAL
+ it.date = date
+ it.lessonNumber = lessonNumber
+ it.startTime = startTime
+ it.endTime = endTime
+ it.subjectId = subject.id
+ it.teacherId = teacher.id
+ it.teamId = team.id
+ it.classroom = classroom
+
+ it.id = it.buildId()
+ data.lessonList += it
+ }
+ }
+
+ if (startDate != null && endDate != null) {
+ if (weekEnd > endDate!!) endDate = weekEnd
+
+ while (startDate!! <= endDate!!) {
+ if (startDate!!.value !in days) {
+ val lessonDate = startDate!!.clone()
+ data.lessonList += Lesson(data.profileId, lessonDate.value.toLong()).apply {
+ type = Lesson.TYPE_NO_LESSONS
+ date = lessonDate
+ }
+ }
+ startDate!!.stepForward(0, 0, 1)
+ }
+ }
+
+ data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/firstlogin/PodlasieFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/firstlogin/PodlasieFirstLogin.kt
new file mode 100644
index 00000000..9b92d541
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/firstlogin/PodlasieFirstLogin.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.firstlogin
+
+import org.greenrobot.eventbus.EventBus
+import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_PODLASIE
+import pl.szczodrzynski.edziennik.data.api.PODLASIE_API_USER_ENDPOINT
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.PodlasieApi
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login.PodlasieLoginApi
+import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
+import pl.szczodrzynski.edziennik.data.db.entity.Profile
+
+class PodlasieFirstLogin(val data: DataPodlasie, val onSuccess: () -> Unit) {
+ companion object {
+ const val TAG = "PodlasieFirstLogin"
+ }
+
+ private val api = PodlasieApi(data, null)
+
+ init {
+ val loginStoreId = data.loginStore.id
+ val loginStoreType = LOGIN_TYPE_PODLASIE
+
+ PodlasieLoginApi(data) {
+ api.apiGet(TAG, PODLASIE_API_USER_ENDPOINT) { json ->
+ val uuid = json.getString("Uuid")
+ val login = json.getString("Login")
+ val firstName = json.getString("FirstName")
+ val lastName = json.getString("LastName")
+ val studentNameLong = "$firstName $lastName".fixName()
+ val studentNameShort = studentNameLong.getShortName()
+ val schoolName = json.getString("SchoolName")
+ val className = json.getString("SchoolClass")
+ val schoolYear = json.getString("ActualSchoolYear")?.replace(' ', '/')
+ val semester = json.getString("ActualTermShortcut")?.length
+ val apiUrl = json.getString("URL")
+
+ val profile = Profile(
+ loginStoreId,
+ loginStoreId,
+ loginStoreType,
+ studentNameLong,
+ login,
+ studentNameLong,
+ studentNameShort,
+ null
+ ).apply {
+ studentData["studentId"] = uuid
+ studentData["studentLogin"] = login
+ studentData["schoolName"] = schoolName
+ studentData["className"] = className
+ studentData["schoolYear"] = schoolYear
+ studentData["currentSemester"] = semester ?: 1
+ studentData["apiUrl"] = apiUrl
+
+ schoolYear?.split('/')?.get(0)?.toInt()?.let {
+ studentSchoolYearStart = it
+ }
+ studentClassName = className
+ }
+
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(listOf(profile), data.loginStore))
+ onSuccess()
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/login/PodlasieLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/login/PodlasieLogin.kt
new file mode 100644
index 00000000..406bb997
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/login/PodlasieLogin.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login
+
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_PODLASIE_API
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.utils.Utils
+
+class PodlasieLogin(val data: DataPodlasie, val onSuccess: () -> Unit) {
+ companion object {
+ const val TAG = "PodlasieLogin"
+ }
+
+ private var cancelled = false
+
+ init {
+ nextLoginMethod(onSuccess)
+ }
+
+ private fun nextLoginMethod(onSuccess: () -> Unit) {
+ if (data.targetLoginMethodIds.isEmpty()) {
+ onSuccess()
+ return
+ }
+ if (cancelled) {
+ onSuccess()
+ return
+ }
+ useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId ->
+ data.progress(data.progressStep)
+ if (usedMethodId != -1)
+ data.loginMethods.add(usedMethodId)
+ nextLoginMethod(onSuccess)
+ }
+ }
+
+ private fun useLoginMethod(loginMethodId: Int, onSuccess: (usedMethodId: Int) -> Unit) {
+ // this should never be true
+ if (data.loginMethods.contains(loginMethodId)) {
+ onSuccess(-1)
+ return
+ }
+ Utils.d(TAG, "Using login method $loginMethodId")
+ when (loginMethodId) {
+ LOGIN_METHOD_PODLASIE_API -> {
+ data.startProgress(R.string.edziennik_progress_login_podlasie_api)
+ PodlasieLoginApi(data) { onSuccess(loginMethodId) }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/login/PodlasieLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/login/PodlasieLoginApi.kt
new file mode 100644
index 00000000..6eb70d06
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/login/PodlasieLoginApi.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Kacper Ziubryniewicz 2020-5-12
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login
+
+import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_DATA_MISSING
+import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie
+import pl.szczodrzynski.edziennik.data.api.models.ApiError
+
+class PodlasieLoginApi(val data: DataPodlasie, val onSuccess: () -> Unit) {
+ companion object {
+ const val TAG = "PodlasieLoginApi"
+ }
+
+ init { run {
+ if (data.isApiLoginValid()) {
+ onSuccess()
+ } else {
+ data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
+ }
+ }}
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt
index d326ebd4..4b397f5d 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt
@@ -17,9 +17,14 @@ import pl.szczodrzynski.edziennik.values
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
+ fun isWebMainLoginValid() = webExpiryTime-30 > currentTimeUnix()
+ && webAuthCookie.isNotNullNorEmpty()
+ && webHost.isNotNullNorEmpty()
+ && webType.isNotNullNorEmpty()
+ && symbol.isNotNullNorEmpty()
fun isApiLoginValid() = currentSemesterEndDate-30 > currentTimeUnix()
- && apiCertificateKey.isNotNullNorEmpty()
- && apiCertificatePrivate.isNotNullNorEmpty()
+ && apiFingerprint[symbol].isNotNullNorEmpty()
+ && apiPrivateKey[symbol].isNotNullNorEmpty()
&& symbol.isNotNullNorEmpty()
override fun satisfyLoginMethods() {
@@ -40,7 +45,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
id,
name,
Team.TYPE_CLASS,
- "$schoolName:$name",
+ "$schoolCode:$name",
-1
)
teamList.put(id, teamObject)
@@ -48,7 +53,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
}
}
- override fun generateUserCode() = "$schoolName:$studentId"
+ override fun generateUserCode() = "$schoolCode:$studentId"
/**
* A UONET+ client symbol.
@@ -59,8 +64,8 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
*/
private var mSymbol: String? = null
var symbol: String?
- get() { mSymbol = mSymbol ?: loginStore.getLoginData("deviceSymbol", null); return mSymbol }
- set(value) { loginStore.putLoginData("deviceSymbol", value); mSymbol = value }
+ get() { mSymbol = mSymbol ?: profile?.getStudentData("symbol", null); return mSymbol }
+ set(value) { profile?.putStudentData("symbol", value); mSymbol = value }
/**
* Group symbol/number of the student's school.
@@ -75,16 +80,26 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
set(value) { profile?.putStudentData("schoolSymbol", value) ?: return; mSchoolSymbol = value }
/**
- * A school ID consisting of the [symbol] and [schoolSymbol].
+ * Short name of the school, used in some places.
+ *
+ * ListaUczniow/JednostkaSprawozdawczaSkrot, e.g. "SP Wilkow"
+ */
+ private var mSchoolShort: String? = null
+ var schoolShort: String?
+ get() { mSchoolShort = mSchoolShort ?: profile?.getStudentData("schoolShort", null); return mSchoolShort }
+ set(value) { profile?.putStudentData("schoolShort", value) ?: return; mSchoolShort = value }
+
+ /**
+ * A school code consisting of the [symbol] and [schoolSymbol].
*
* [symbol]_[schoolSymbol]
*
* e.g. "poznan_000088"
*/
- private var mSchoolName: String? = null
- var schoolName: String?
- get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName }
- set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value }
+ private var mSchoolCode: String? = null
+ var schoolCode: String?
+ get() { mSchoolCode = mSchoolCode ?: profile?.getStudentData("schoolName", null); return mSchoolCode }
+ set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolCode = value }
/**
* ID of the student.
@@ -124,6 +139,15 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
get() { mStudentSemesterId = mStudentSemesterId ?: profile?.getStudentData("studentSemesterId", 0); return mStudentSemesterId ?: 0 }
set(value) { profile?.putStudentData("studentSemesterId", value) ?: return; mStudentSemesterId = value }
+ private var mSemester1Id: Int? = null
+ var semester1Id: Int
+ get() { mSemester1Id = mSemester1Id ?: profile?.getStudentData("semester1Id", 0); return mSemester1Id ?: 0 }
+ set(value) { profile?.putStudentData("semester1Id", value) ?: return; mSemester1Id = value }
+ private var mSemester2Id: Int? = null
+ var semester2Id: Int
+ get() { mSemester2Id = mSemester2Id ?: profile?.getStudentData("semester2Id", 0); return mSemester2Id ?: 0 }
+ set(value) { profile?.putStudentData("semester2Id", value) ?: return; mSemester2Id = value }
+
/**
* ListaUczniow/OkresNumer, e.g. 1 or 2
*/
@@ -154,45 +178,34 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
* After first login only 3 first characters are stored here.
* This is later used to determine the API URL address.
*/
- private var mApiToken: String? = null
- var apiToken: String?
- get() { mApiToken = mApiToken ?: loginStore.getLoginData("deviceToken", null); return mApiToken }
- set(value) { loginStore.putLoginData("deviceToken", value); mApiToken = value }
+ private var mApiToken: Map? = null
+ var apiToken: Map = mapOf()
+ get() { mApiToken = mApiToken ?: loginStore.getLoginData("apiToken", null)?.let { app.gson.fromJson(it, field.toMutableMap()::class.java) }; return mApiToken ?: mapOf() }
+ set(value) { loginStore.putLoginData("apiToken", app.gson.toJson(value)); mApiToken = value }
/**
* A mobile API registration PIN.
*
* After first login, this is removed and/or set to null.
*/
- private var mApiPin: String? = null
- var apiPin: String?
- get() { mApiPin = mApiPin ?: loginStore.getLoginData("devicePin", null); return mApiPin }
- set(value) { loginStore.putLoginData("devicePin", value); mApiPin = value }
+ private var mApiPin: Map? = null
+ var apiPin: Map = mapOf()
+ get() { mApiPin = mApiPin ?: loginStore.getLoginData("apiPin", null)?.let { app.gson.fromJson(it, field.toMutableMap()::class.java) }; return mApiPin ?: mapOf() }
+ set(value) { loginStore.putLoginData("apiPin", app.gson.toJson(value)); mApiPin = value }
- private var mApiCertificateKey: String? = null
- var apiCertificateKey: String?
- get() { mApiCertificateKey = mApiCertificateKey ?: loginStore.getLoginData("certificateKey", null); return mApiCertificateKey }
- set(value) { loginStore.putLoginData("certificateKey", value); mApiCertificateKey = value }
+ private var mApiFingerprint: Map? = null
+ var apiFingerprint: Map = mapOf()
+ get() { mApiFingerprint = mApiFingerprint ?: loginStore.getLoginData("apiFingerprint", null)?.let { app.gson.fromJson(it, field.toMutableMap()::class.java) }; return mApiFingerprint ?: mapOf() }
+ set(value) { loginStore.putLoginData("apiFingerprint", app.gson.toJson(value)); mApiFingerprint = value }
- /**
- * This is not meant for normal usage.
- *
- * It provides a backward compatibility (<4.0) in order
- * to migrate and use private keys instead of PFX.
- */
- private var mApiCertificatePfx: String? = null
- var apiCertificatePfx: String?
- get() { mApiCertificatePfx = mApiCertificatePfx ?: loginStore.getLoginData("certificatePfx", null); return mApiCertificatePfx }
- set(value) { loginStore.putLoginData("certificatePfx", value); mApiCertificatePfx = value }
-
- private var mApiCertificatePrivate: String? = null
- var apiCertificatePrivate: String?
- get() { mApiCertificatePrivate = mApiCertificatePrivate ?: loginStore.getLoginData("certificatePrivate", null); return mApiCertificatePrivate }
- set(value) { loginStore.putLoginData("certificatePrivate", value); mApiCertificatePrivate = value }
+ private var mApiPrivateKey: Map? = null
+ var apiPrivateKey: Map = mapOf()
+ get() { mApiPrivateKey = mApiPrivateKey ?: loginStore.getLoginData("apiPrivateKey", null)?.let { app.gson.fromJson(it, field.toMutableMap()::class.java) }; return mApiPrivateKey ?: mapOf() }
+ set(value) { loginStore.putLoginData("apiPrivateKey", app.gson.toJson(value)); mApiPrivateKey = value }
val apiUrl: String?
get() {
- val url = when (apiToken?.substring(0, 3)) {
+ val url = when (apiToken[symbol]?.substring(0, 3)) {
"3S1" -> "https://lekcjaplus.vulcan.net.pl"
"TA1" -> "https://uonetplus-komunikacja.umt.tarnow.pl"
"OP1" -> "https://uonetplus-komunikacja.eszkola.opolskie.pl"
@@ -217,4 +230,95 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
get() {
return "$apiUrl$schoolSymbol/"
}
+
+ /* __ __ _ ______ _____ _ _
+ \ \ / / | | | ____/ ____| | | (_)
+ \ \ /\ / /__| |__ | |__ | (___ | | ___ __ _ _ _ __
+ \ \/ \/ / _ \ '_ \ | __| \___ \ | | / _ \ / _` | | '_ \
+ \ /\ / __/ |_) | | | ____) | | |___| (_) | (_| | | | | |
+ \/ \/ \___|_.__/ |_| |_____/ |______\___/ \__, |_|_| |_|
+ __/ |
+ |__*/
+ /**
+ * Federation Services login type.
+ * This might be one of: cufs, adfs, adfslight.
+ */
+ var webType: String?
+ get() { mWebType = mWebType ?: loginStore.getLoginData("webType", null); return mWebType }
+ set(value) { loginStore.putLoginData("webType", value); mWebType = value }
+ private var mWebType: String? = null
+
+ /**
+ * Web server providing the federation services login.
+ * If this is present, WEB_MAIN login is considered as available.
+ */
+ var webHost: String?
+ get() { mWebHost = mWebHost ?: loginStore.getLoginData("webHost", null); return mWebHost }
+ set(value) { loginStore.putLoginData("webHost", value); mWebHost = value }
+ private var mWebHost: String? = null
+
+ /**
+ * An ID used in ADFS & ADFSLight login types.
+ */
+ var webAdfsId: String?
+ get() { mWebAdfsId = mWebAdfsId ?: loginStore.getLoginData("webAdfsId", null); return mWebAdfsId }
+ set(value) { loginStore.putLoginData("webAdfsId", value); mWebAdfsId = value }
+ private var mWebAdfsId: String? = null
+
+ /**
+ * A domain override for ADFS Light.
+ */
+ var webAdfsDomain: String?
+ get() { mWebAdfsDomain = mWebAdfsDomain ?: loginStore.getLoginData("webAdfsDomain", null); return mWebAdfsDomain }
+ set(value) { loginStore.putLoginData("webAdfsDomain", value); mWebAdfsDomain = value }
+ private var mWebAdfsDomain: String? = null
+
+ var webIsHttpCufs: Boolean
+ get() { mWebIsHttpCufs = mWebIsHttpCufs ?: loginStore.getLoginData("webIsHttpCufs", false); return mWebIsHttpCufs ?: false }
+ set(value) { loginStore.putLoginData("webIsHttpCufs", value); mWebIsHttpCufs = value }
+ private var mWebIsHttpCufs: Boolean? = null
+
+ var webIsScopedAdfs: Boolean
+ get() { mWebIsScopedAdfs = mWebIsScopedAdfs ?: loginStore.getLoginData("webIsScopedAdfs", false); return mWebIsScopedAdfs ?: false }
+ set(value) { loginStore.putLoginData("webIsScopedAdfs", value); mWebIsScopedAdfs = value }
+ private var mWebIsScopedAdfs: Boolean? = null
+
+ var webEmail: String?
+ get() { mWebEmail = mWebEmail ?: loginStore.getLoginData("webEmail", null); return mWebEmail }
+ set(value) { loginStore.putLoginData("webEmail", value); mWebEmail = value }
+ private var mWebEmail: String? = null
+ var webUsername: String?
+ get() { mWebUsername = mWebUsername ?: loginStore.getLoginData("webUsername", null); return mWebUsername }
+ set(value) { loginStore.putLoginData("webUsername", value); mWebUsername = value }
+ private var mWebUsername: String? = null
+ var webPassword: String?
+ get() { mWebPassword = mWebPassword ?: loginStore.getLoginData("webPassword", null); return mWebPassword }
+ set(value) { loginStore.putLoginData("webPassword", value); mWebPassword = value }
+ private var mWebPassword: String? = null
+
+ /**
+ * Expiry time of a certificate POSTed to a LoginEndpoint of the specific symbol.
+ * If the time passes, the certificate needs to be POSTed again (if valid)
+ * or re-generated.
+ */
+ var webExpiryTime: Long
+ get() { mWebExpiryTime = mWebExpiryTime ?: profile?.getStudentData("webExpiryTime", 0L); return mWebExpiryTime ?: 0L }
+ set(value) { profile?.putStudentData("webExpiryTime", value); mWebExpiryTime = value }
+ private var mWebExpiryTime: Long? = null
+
+ /**
+ * EfebSsoAuthCookie retrieved after posting a certificate
+ */
+ var webAuthCookie: String?
+ get() { mWebAuthCookie = mWebAuthCookie ?: profile?.getStudentData("webAuthCookie", null); return mWebAuthCookie }
+ set(value) { profile?.putStudentData("webAuthCookie", value); mWebAuthCookie = value }
+ private var mWebAuthCookie: String? = null
+
+ /**
+ * Permissions needed to get JSONs from home page
+ */
+ var webPermissions: String?
+ get() { mWebPermissions = mWebPermissions ?: profile?.getStudentData("webPermissions", null); return mWebPermissions }
+ set(value) { profile?.putStudentData("webPermissions", value); mWebPermissions = value }
+ private var mWebPermissions: String? = null
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/Vulcan.kt
index 89b60543..e17d059f 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/Vulcan.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/Vulcan.kt
@@ -157,7 +157,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
completed()
},
- onProgress = { written, total ->
+ onProgress = { written, _ ->
val event = AttachmentGetEvent(
data.profileId,
owner,
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/VulcanFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/VulcanFeatures.kt
index d1f11822..ab032e0c 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/VulcanFeatures.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/VulcanFeatures.kt
@@ -19,6 +19,7 @@ const val ENDPOINT_VULCAN_API_NOTICES = 1070
const val ENDPOINT_VULCAN_API_ATTENDANCE = 1080
const val ENDPOINT_VULCAN_API_MESSAGES_INBOX = 1090
const val ENDPOINT_VULCAN_API_MESSAGES_SENT = 1100
+const val ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS = 2010
val VulcanFeatures = listOf(
// timetable
@@ -61,6 +62,13 @@ val VulcanFeatures = listOf(
!data.app.config.sync.tokenVulcanList.contains(data.profileId)
},
+ /**
+ * Lucky number - using WEB Main.
+ */
+ Feature(LOGIN_TYPE_VULCAN, FEATURE_LUCKY_NUMBER, listOf(
+ ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS to LOGIN_METHOD_VULCAN_WEB_MAIN
+ ), listOf(LOGIN_METHOD_VULCAN_WEB_MAIN)).withShouldSync { data -> data.shouldSyncLuckyNumber() },
+
Feature(LOGIN_TYPE_VULCAN, FEATURE_ALWAYS_NEEDED, listOf(
ENDPOINT_VULCAN_API_UPDATE_SEMESTER to LOGIN_METHOD_VULCAN_API,
ENDPOINT_VULCAN_API_DICTIONARIES to LOGIN_METHOD_VULCAN_API
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanApi.kt
index 172be3c0..c500e3e7 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanApi.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanApi.kt
@@ -106,11 +106,11 @@ open class VulcanApi(open val data: DataVulcan, open val lastSync: Long?) {
Request.builder()
.url(url)
.userAgent(VULCAN_API_USER_AGENT)
- .addHeader("RequestCertificateKey", data.apiCertificateKey)
+ .addHeader("RequestCertificateKey", data.apiFingerprint[data.symbol])
.addHeader("RequestSignatureValue",
try {
signContent(
- data.apiCertificatePrivate ?: "",
+ data.apiPrivateKey[data.symbol] ?: "",
finalPayload.toString()
)
} catch (e: Exception) {e.printStackTrace();""})
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanData.kt
index 9ab94c30..491e8ea6 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanData.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanData.kt
@@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.*
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.*
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.web.VulcanWebLuckyNumber
import pl.szczodrzynski.edziennik.utils.Utils
class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) {
@@ -86,6 +87,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
VulcanApiMessagesSent(data, lastSync, onSuccess)
}
+ ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS -> {
+ data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
+ VulcanWebLuckyNumber(data, lastSync, onSuccess)
+ }
else -> onSuccess(endpointId)
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanWebMain.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanWebMain.kt
new file mode 100644
index 00000000..39433657
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanWebMain.kt
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-17.
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data
+
+import com.google.gson.JsonArray
+import com.google.gson.JsonObject
+import com.google.gson.JsonParser
+import im.wangchao.mhttp.Request
+import im.wangchao.mhttp.Response
+import im.wangchao.mhttp.callback.TextCallbackHandler
+import pl.droidsonroids.jspoon.Jspoon
+import pl.szczodrzynski.edziennik.data.api.*
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.CufsCertificate
+import pl.szczodrzynski.edziennik.data.api.models.ApiError
+import pl.szczodrzynski.edziennik.get
+import pl.szczodrzynski.edziennik.isNotNullNorBlank
+import pl.szczodrzynski.edziennik.utils.Utils
+import pl.szczodrzynski.edziennik.utils.models.Date
+import java.io.File
+import java.net.HttpURLConnection
+
+open class VulcanWebMain(open val data: DataVulcan, open val lastSync: Long?) {
+ companion object {
+ const val TAG = "VulcanWebMain"
+ const val WEB_MAIN = 0
+ const val WEB_OLD = 1
+ const val WEB_NEW = 2
+ const val WEB_MESSAGES = 3
+ const val STATE_SUCCESS = 0
+ const val STATE_NO_REGISTER = 1
+ const val STATE_LOGGED_OUT = 2
+ }
+
+ val profileId
+ get() = data.profile?.id ?: -1
+
+ val profile
+ get() = data.profile
+
+ private val certificateAdapter by lazy {
+ Jspoon.create().adapter(CufsCertificate::class.java)
+ }
+
+ fun saveCertificate(xml: String) {
+ val file = File(data.app.filesDir, "cert_"+(data.webUsername ?: data.webEmail)+".xml")
+ file.writeText(xml)
+ }
+
+ fun readCertificate(): String? {
+ val file = File(data.app.filesDir, "cert_"+(data.webUsername ?: data.webEmail)+".xml")
+ if (file.canRead())
+ return file.readText()
+ return null
+ }
+
+ fun parseCertificate(xml: String): CufsCertificate {
+ val xmlParsed = xml
+ .replace("<[a-z]+?:".toRegex(), "<")
+ .replace("[a-z]+?:".toRegex(), "")
+ .replace("\\sxmlns.*?=\".+?\"".toRegex(), "")
+
+ return certificateAdapter.fromHtml(xmlParsed).also {
+ it.xml = xml
+ }
+ }
+
+ fun postCertificate(certificate: CufsCertificate, symbol: String, onResult: (symbol: String, state: Int) -> Unit): Boolean {
+ // check if the certificate is valid
+ if (Date.fromIso(certificate.expiryDate) < System.currentTimeMillis())
+ return false
+
+ val callback = object : TextCallbackHandler() {
+ override fun onSuccess(text: String?, response: Response?) {
+ if (response?.headers()?.get("Location")?.contains("LoginEndpoint.aspx") == true
+ || response?.headers()?.get("Location")?.contains("?logout=true") == true) {
+ onResult(symbol, STATE_LOGGED_OUT)
+ return
+ }
+ if (text?.contains("LoginEndpoint.aspx?logout=true") == true) {
+ onResult(symbol, STATE_NO_REGISTER)
+ return
+ }
+ if (!validateCallback(text, response, jsonResponse = false)) {
+ return
+ }
+ data.webExpiryTime = Date.fromIso(certificate.expiryDate) / 1000L
+ onResult(symbol, STATE_SUCCESS)
+ }
+
+ override fun onFailure(response: Response?, throwable: Throwable?) {
+ data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
+ .withResponse(response)
+ .withThrowable(throwable))
+ }
+ }
+
+ Request.builder()
+ .url("https://uonetplus.${data.webHost}/$symbol/LoginEndpoint.aspx")
+ .withClient(data.app.httpLazy)
+ .userAgent(SYSTEM_USER_AGENT)
+ .post()
+ .addParameter("wa", "wsignin1.0")
+ .addParameter("wctx", certificate.targetUrl)
+ .addParameter("wresult", certificate.xml)
+ .allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
+ .allowErrorCode(HttpURLConnection.HTTP_FORBIDDEN)
+ .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
+ .allowErrorCode(HttpURLConnection.HTTP_UNAVAILABLE)
+ .allowErrorCode(429)
+ .callback(callback)
+ .build()
+ .enqueue()
+
+ return true
+ }
+
+ fun getStartPage(symbol: String = data.symbol ?: "default", postErrors: Boolean = true, onSuccess: (html: String, schoolSymbols: List) -> Unit) {
+ val callback = object : TextCallbackHandler() {
+ override fun onSuccess(text: String?, response: Response?) {
+ if (!validateCallback(text, response, jsonResponse = false) || text == null) {
+ return
+ }
+
+ if (postErrors) {
+ when {
+ text.contains("status absolwenta") -> ERROR_VULCAN_WEB_GRADUATE_ACCOUNT
+ else -> null
+ }?.let { errorCode ->
+ data.error(ApiError(TAG, errorCode)
+ .withResponse(response)
+ .withApiResponse(text))
+ return
+ }
+ }
+
+ data.webPermissions = Regexes.VULCAN_WEB_PERMISSIONS.find(text)?.let { it[1] }
+
+ val schoolSymbols = mutableListOf()
+ val clientUrl = "://uonetplus-uczen.${data.webHost}/$symbol/"
+ var clientIndex = text.indexOf(clientUrl)
+ var count = 0
+ while (clientIndex != -1 && count < 100) {
+ val startIndex = clientIndex + clientUrl.length
+ val endIndex = text.indexOf('/', startIndex = startIndex)
+ val schoolSymbol = text.substring(startIndex, endIndex)
+ schoolSymbols += schoolSymbol
+ clientIndex = text.indexOf(clientUrl, startIndex = endIndex)
+ count++
+ }
+ schoolSymbols.removeAll {
+ it.toLowerCase() == "default"
+ || !it.matches(Regexes.VULCAN_WEB_SYMBOL_VALIDATE)
+ }
+
+ if (postErrors && schoolSymbols.isEmpty()) {
+ data.error(ApiError(TAG, ERROR_VULCAN_WEB_NO_SCHOOLS)
+ .withResponse(response)
+ .withApiResponse(text))
+ return
+ }
+
+ onSuccess(text, schoolSymbols)
+ }
+
+ override fun onFailure(response: Response?, throwable: Throwable?) {
+ data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
+ .withResponse(response)
+ .withThrowable(throwable))
+ }
+ }
+
+ Request.builder()
+ .url("https://uonetplus.${data.webHost}/$symbol/Start.mvc/Index")
+ .userAgent(SYSTEM_USER_AGENT)
+ .get()
+ .allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
+ .allowErrorCode(HttpURLConnection.HTTP_FORBIDDEN)
+ .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
+ .allowErrorCode(HttpURLConnection.HTTP_UNAVAILABLE)
+ .allowErrorCode(429)
+ .callback(callback)
+ .build()
+ .enqueue()
+ }
+
+ private fun validateCallback(text: String?, response: Response?, jsonResponse: Boolean = true): Boolean {
+ if (text == null) {
+ data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
+ .withResponse(response))
+ return false
+ }
+
+ if (response?.code() !in 200..302 || (jsonResponse && !text.startsWith("{"))) {
+ when {
+ text.contains("The custom error module") -> ERROR_VULCAN_WEB_429
+ else -> ERROR_VULCAN_WEB_OTHER
+ }.let { errorCode ->
+ data.error(ApiError(TAG, errorCode)
+ .withApiResponse(text)
+ .withResponse(response))
+ return false
+ }
+ }
+
+ val cookies = data.app.cookieJar.getAll(data.webHost ?: "vulcan.net.pl")
+ val authCookie = cookies["EfebSsoAuthCookie"]
+ if ((authCookie == null || authCookie == "null") && data.webAuthCookie != null) {
+ data.app.cookieJar.set(data.webHost ?: "vulcan.net.pl", "EfebSsoAuthCookie", data.webAuthCookie)
+ }
+ else if (authCookie.isNotNullNorBlank() && authCookie != "null" && authCookie != data.webAuthCookie) {
+ data.webAuthCookie = authCookie
+ }
+ return true
+ }
+
+ fun webGetJson(
+ tag: String,
+ webType: Int,
+ endpoint: String,
+ method: Int = POST,
+ parameters: Map = emptyMap(),
+ onSuccess: (json: JsonObject, response: Response?) -> Unit
+ ) {
+ val url = "https://" + when (webType) {
+ WEB_MAIN -> "uonetplus"
+ WEB_OLD -> "uonetplus-opiekun"
+ WEB_NEW -> "uonetplus-uczen"
+ WEB_MESSAGES -> "uonetplus-uzytkownik"
+ else -> "uonetplus"
+ } + ".${data.webHost}/${data.symbol}/$endpoint"
+
+ Utils.d(tag, "Request: Vulcan/WebMain - $url")
+
+ val payload = JsonObject()
+ parameters.map { (name, value) ->
+ when (value) {
+ is JsonObject -> payload.add(name, value)
+ is JsonArray -> payload.add(name, value)
+ is String -> payload.addProperty(name, value)
+ is Int -> payload.addProperty(name, value)
+ is Long -> payload.addProperty(name, value)
+ is Float -> payload.addProperty(name, value)
+ is Char -> payload.addProperty(name, value)
+ is Boolean -> payload.addProperty(name, value)
+ }
+ }
+
+ val callback = object : TextCallbackHandler() {
+ override fun onSuccess(text: String?, response: Response?) {
+ if (!validateCallback(text, response))
+ return
+
+ try {
+ val json = JsonParser().parse(text).asJsonObject
+ onSuccess(json, response)
+ } catch (e: Exception) {
+ data.error(ApiError(tag, EXCEPTION_VULCAN_WEB_REQUEST)
+ .withResponse(response)
+ .withThrowable(e)
+ .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(SYSTEM_USER_AGENT)
+ .apply {
+ when (method) {
+ GET -> get()
+ POST -> post()
+ }
+ }
+ .setJsonBody(payload)
+ .allowErrorCode(429)
+ .callback(callback)
+ .build()
+ .enqueue()
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiAttendance.kt
index 2ec240a4..2814a1cf 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiAttendance.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiAttendance.kt
@@ -7,8 +7,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_ATTENDANCE
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
-import pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_PRESENT
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date
@@ -25,57 +26,85 @@ class VulcanApiAttendance(override val data: DataVulcan,
data.db.attendanceTypeDao().getAllNow(profileId).toSparseArray(data.attendanceTypes) { it.id }
}
- val startDate: String = profile.getSemesterStart(profile.currentSemester).stringY_m_d
- val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d
+ val semesterId = data.studentSemesterId
+ val semesterNumber = data.studentSemesterNumber
+ if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) {
+ getAttendance(profile, semesterId - 1, semesterNumber - 1) {
+ getAttendance(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+ }
+ else {
+ getAttendance(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+
+ } ?: onSuccess(ENDPOINT_VULCAN_API_ATTENDANCE) }
+
+ private fun finish() {
+ data.setSyncNext(ENDPOINT_VULCAN_API_ATTENDANCE, SYNC_ALWAYS)
+ onSuccess(ENDPOINT_VULCAN_API_ATTENDANCE)
+ }
+
+ private fun getAttendance(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) {
+ val startDate = profile.getSemesterStart(semesterNumber).stringY_m_d
+ val endDate = profile.getSemesterEnd(semesterNumber).stringY_m_d
apiGet(TAG, VULCAN_API_ENDPOINT_ATTENDANCE, parameters = mapOf(
"DataPoczatkowa" to startDate,
"DataKoncowa" to endDate,
"IdOddzial" to data.studentClassId,
"IdUczen" to data.studentId,
- "IdOkresKlasyfikacyjny" to data.studentSemesterId
+ "IdOkresKlasyfikacyjny" to semesterId
)) { json, _ ->
json.getJsonObject("Data")?.getJsonArray("Frekwencje")?.forEach { attendanceEl ->
val attendance = attendanceEl.asJsonObject
- val attendanceCategory = data.attendanceTypes.get(attendance.getLong("IdKategoria") ?: return@forEach)
+ val type = data.attendanceTypes.get(attendance.getLong("IdKategoria") ?: return@forEach)
?: return@forEach
- val type = attendanceCategory.type
-
val id = (attendance.getInt("Dzien") ?: 0) + (attendance.getInt("Numer") ?: 0)
val lessonDateMillis = Date.fromY_m_d(attendance.getString("DzienTekst")).inMillis
val lessonDate = Date.fromMillis(lessonDateMillis)
+ val startTime = data.lessonRanges.get(attendance.getInt("Numer") ?: 0)?.startTime
- val lessonSemester = profile.dateToSemester(lessonDate)
+ val lessonSemester = semesterNumber
val attendanceObject = Attendance(
- profileId,
- id.toLong(),
- -1,
- attendance.getLong("IdPrzedmiot") ?: -1,
- lessonSemester,
- attendance.getString("PrzedmiotNazwa") + attendanceCategory.name.let { " - $it" },
- lessonDate,
- data.lessonRanges.get(attendance.getInt("Numer") ?: 0)?.startTime,
- type)
+ profileId = profileId,
+ id = id.toLong(),
+ baseType = type.baseType,
+ typeName = type.typeName,
+ typeShort = type.typeShort,
+ typeSymbol = type.typeSymbol,
+ typeColor = type.typeColor,
+ date = lessonDate,
+ startTime = startTime,
+ semester = lessonSemester,
+ teacherId = -1,
+ subjectId = attendance.getLong("IdPrzedmiot") ?: -1,
+ addedDate = lessonDate.combineWith(startTime)
+ ).also {
+ it.lessonNumber = attendance.getInt("Numer")
+ it.isCounted = it.baseType != Attendance.TYPE_RELEASED
+ }
data.attendanceList.add(attendanceObject)
- if (attendanceObject.type != TYPE_PRESENT) {
+ if (type.baseType != TYPE_PRESENT) {
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ATTENDANCE,
attendanceObject.id,
- profile.empty,
- profile.empty,
- attendanceObject.lessonDate.combineWith(attendanceObject.startTime)
+ profile.empty || type.baseType == Attendance.TYPE_PRESENT_CUSTOM || type.baseType == Attendance.TYPE_UNKNOWN,
+ profile.empty || type.baseType == Attendance.TYPE_PRESENT_CUSTOM || type.baseType == Attendance.TYPE_UNKNOWN
))
}
}
- data.setSyncNext(ENDPOINT_VULCAN_API_ATTENDANCE, SYNC_ALWAYS)
- onSuccess(ENDPOINT_VULCAN_API_ATTENDANCE)
+ onSuccess()
}
- } ?: onSuccess(ENDPOINT_VULCAN_API_ATTENDANCE) }
+ }
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiDictionaries.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiDictionaries.kt
index 76f53cf5..2dc9f6bc 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiDictionaries.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiDictionaries.kt
@@ -114,11 +114,11 @@ class VulcanApiDictionaries(override val data: DataVulcan,
private fun saveAttendanceType(attendanceType: JsonObject) {
val id = attendanceType.getLong("Id") ?: return
- val name = attendanceType.getString("Nazwa") ?: ""
+ val typeName = attendanceType.getString("Nazwa") ?: ""
val absent = attendanceType.getBoolean("Nieobecnosc") ?: false
val excused = attendanceType.getBoolean("Usprawiedliwione") ?: false
- val type = if (absent) {
+ val baseType = if (absent) {
if (excused)
Attendance.TYPE_ABSENT_EXCUSED
else
@@ -137,15 +137,35 @@ class VulcanApiDictionaries(override val data: DataVulcan,
else if (present)
Attendance.TYPE_PRESENT
else
- Attendance.TYPE_CUSTOM
+ Attendance.TYPE_UNKNOWN
+ }
+
+ val (typeColor, typeSymbol) = when (id.toInt()) {
+ 1 -> 0xffffffff to "●" // obecność
+ 2 -> 0xffffa687 to "—" // nieobecność
+ 3 -> 0xfffcc150 to "u" // nieobecność usprawiedliwiona
+ 4 -> 0xffede049 to "s" // spóźnienie
+ 5 -> 0xffbbdd5f to "su" // spóźnienie usprawiedliwione
+ 6 -> 0xffa9c9fd to "ns" // nieobecny z przyczyn szkolnych
+ 7 -> 0xffddbbe5 to "z" // zwolniony
+ 8 -> 0xffffffff to "" // usunięty wpis
+ else -> null to "?"
+ }
+
+ val typeShort = when (id.toInt()) {
+ 6 -> "ns" // nieobecny z przyczyn szkolnych
+ 8 -> "" // usunięty wpis
+ else -> data.app.attendanceManager.getTypeShort(baseType)
}
val attendanceTypeObject = AttendanceType(
profileId,
id,
- name,
- type,
- -1
+ baseType,
+ typeName,
+ typeShort,
+ typeSymbol,
+ typeColor?.toInt()
)
data.attendanceTypes.put(id, attendanceTypeObject)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiEvents.kt
index 4ba7c460..6f045828 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiEvents.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiEvents.kt
@@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getBoolean
import pl.szczodrzynski.edziennik.getJsonArray
@@ -31,11 +32,43 @@ class VulcanApiEvents(override val data: DataVulcan,
init { data.profile?.also { profile ->
- val startDate: String = when (profile.empty) {
- true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d
+ val semesterId = data.studentSemesterId
+ val semesterNumber = data.studentSemesterNumber
+ if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) {
+ getEvents(profile, semesterId - 1, semesterNumber - 1) {
+ getEvents(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+ }
+ else {
+ getEvents(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+
+ } ?: onSuccess(if (isHomework) ENDPOINT_VULCAN_API_HOMEWORK else ENDPOINT_VULCAN_API_EVENTS) }
+
+ private fun finish() {
+ when (isHomework) {
+ true -> {
+ data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
+ data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS)
+ }
+ false -> {
+ data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
+ data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS)
+ }
+ }
+ onSuccess(if (isHomework) ENDPOINT_VULCAN_API_HOMEWORK else ENDPOINT_VULCAN_API_EVENTS)
+ }
+
+ private fun getEvents(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) {
+ val startDate = when (profile.empty) {
+ true -> profile.getSemesterStart(semesterNumber).stringY_m_d
else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d
}
- val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d
+ val endDate = profile.getSemesterEnd(semesterNumber).stringY_m_d
val endpoint = when (isHomework) {
true -> VULCAN_API_ENDPOINT_HOMEWORK
@@ -46,7 +79,7 @@ class VulcanApiEvents(override val data: DataVulcan,
"DataKoncowa" to endDate,
"IdOddzial" to data.studentClassId,
"IdUczen" to data.studentId,
- "IdOkresKlasyfikacyjny" to data.studentSemesterId
+ "IdOkresKlasyfikacyjny" to semesterId
)) { json, _ ->
val events = json.getJsonArray("Data")
@@ -59,7 +92,7 @@ class VulcanApiEvents(override val data: DataVulcan,
val teacherId = event.getLong("IdPracownik") ?: -1
val topic = event.getString("Opis")?.trim() ?: ""
- val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
+ val lessonList = data.db.timetableDao().getAllForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
val type = when (isHomework) {
@@ -90,22 +123,11 @@ class VulcanApiEvents(override val data: DataVulcan,
if (isHomework) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT,
id,
profile.empty,
- profile.empty,
- System.currentTimeMillis()
+ profile.empty
))
}
- when (isHomework) {
- true -> {
- data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
- data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS)
- }
- false -> {
- data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
- data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS)
- }
- }
- onSuccess(if (isHomework) ENDPOINT_VULCAN_API_HOMEWORK else ENDPOINT_VULCAN_API_EVENTS)
+ onSuccess()
}
- } ?: onSuccess(if (isHomework) ENDPOINT_VULCAN_API_HOMEWORK else ENDPOINT_VULCAN_API_EVENTS) }
+ }
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiGrades.kt
index f62778fc..9a354ea5 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiGrades.kt
@@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import java.text.DecimalFormat
import kotlin.math.roundToInt
@@ -27,9 +28,33 @@ class VulcanApiGrades(override val data: DataVulcan,
init { data.profile?.also { profile ->
+ val semesterId = data.studentSemesterId
+ val semesterNumber = data.studentSemesterNumber
+ if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) {
+ getGrades(profile, semesterId - 1, semesterNumber - 1) {
+ getGrades(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+ }
+ else {
+ getGrades(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+
+ } ?: onSuccess(ENDPOINT_VULCAN_API_GRADES) }
+
+ private fun finish() {
+ data.toRemove.add(DataRemoveModel.Grades.semesterWithType(data.studentSemesterNumber, TYPE_NORMAL))
+ data.setSyncNext(ENDPOINT_VULCAN_API_GRADES, SYNC_ALWAYS)
+ onSuccess(ENDPOINT_VULCAN_API_GRADES)
+ }
+
+ private fun getGrades(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) {
apiGet(TAG, VULCAN_API_ENDPOINT_GRADES, parameters = mapOf(
"IdUczen" to data.studentId,
- "IdOkresKlasyfikacyjny" to data.studentSemesterId
+ "IdOkresKlasyfikacyjny" to semesterId
)) { json, _ ->
val grades = json.getJsonArray("Data")
@@ -99,9 +124,10 @@ class VulcanApiGrades(override val data: DataVulcan,
category = category,
description = finalDescription,
comment = null,
- semester = data.studentSemesterNumber,
+ semester = semesterNumber,
teacherId = teacherId,
- subjectId = subjectId
+ subjectId = subjectId,
+ addedDate = addedDate
)
data.gradeList.add(gradeObject)
@@ -110,15 +136,11 @@ class VulcanApiGrades(override val data: DataVulcan,
Metadata.TYPE_GRADE,
id,
profile.empty,
- profile.empty,
- addedDate
-
+ profile.empty
))
}
- data.toRemove.add(DataRemoveModel.Grades.semesterWithType(data.studentSemesterNumber, Grade.TYPE_NORMAL))
- data.setSyncNext(ENDPOINT_VULCAN_API_GRADES, SYNC_ALWAYS)
- onSuccess(ENDPOINT_VULCAN_API_GRADES)
+ onSuccess()
}
- } ?: onSuccess(ENDPOINT_VULCAN_API_GRADES) }
+ }
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesChangeStatus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesChangeStatus.kt
index 5e226849..b0b7795d 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesChangeStatus.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesChangeStatus.kt
@@ -37,8 +37,7 @@ class VulcanApiMessagesChangeStatus(override val data: DataVulcan,
Metadata.TYPE_MESSAGE,
messageObject.id,
true,
- true,
- messageObject.addedDate
+ true
))
messageObject.seen = true
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesInbox.kt
index 388a97ad..dea913c5 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesInbox.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesInbox.kt
@@ -72,7 +72,8 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
type = TYPE_RECEIVED,
subject = subject,
body = body.replace("\n", "
"),
- senderId = senderId
+ senderId = senderId,
+ addedDate = sentDate
)
val messageRecipientObject = MessageRecipient(
@@ -90,8 +91,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan,
Metadata.TYPE_MESSAGE,
id,
readDate > 0,
- readDate > 0,
- sentDate
+ readDate > 0
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesSent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesSent.kt
index d190c00f..94cfdcba 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesSent.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesSent.kt
@@ -97,7 +97,8 @@ class VulcanApiMessagesSent(override val data: DataVulcan,
type = TYPE_SENT,
subject = subject,
body = body.replace("\n", "
"),
- senderId = null
+ senderId = null,
+ addedDate = sentDate
)
data.messageList.add(messageObject)
@@ -106,8 +107,7 @@ class VulcanApiMessagesSent(override val data: DataVulcan,
Metadata.TYPE_MESSAGE,
id,
true,
- true,
- sentDate
+ true
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiNotices.kt
index b2202981..45155a5b 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiNotices.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiNotices.kt
@@ -11,12 +11,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notice
+import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getLong
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.toSparseArray
-import pl.szczodrzynski.edziennik.utils.models.Date
class VulcanApiNotices(override val data: DataVulcan,
override val lastSync: Long?,
@@ -31,6 +31,29 @@ class VulcanApiNotices(override val data: DataVulcan,
data.db.noticeTypeDao().getAllNow(profileId).toSparseArray(data.noticeTypes) { it.id }
}
+ val semesterId = data.studentSemesterId
+ val semesterNumber = data.studentSemesterNumber
+ if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) {
+ getNotices(profile, semesterId - 1, semesterNumber - 1) {
+ getNotices(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+ }
+ else {
+ getNotices(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+
+ } ?: onSuccess(ENDPOINT_VULCAN_API_NOTICES) }
+
+ private fun finish() {
+ data.setSyncNext(ENDPOINT_VULCAN_API_NOTICES, SYNC_ALWAYS)
+ onSuccess(ENDPOINT_VULCAN_API_NOTICES)
+ }
+
+ private fun getNotices(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) {
apiGet(TAG, VULCAN_API_ENDPOINT_NOTICES, parameters = mapOf(
"IdUczen" to data.studentId,
"IdOkresKlasyfikacyjny" to data.studentSemesterId
@@ -41,15 +64,21 @@ class VulcanApiNotices(override val data: DataVulcan,
val id = notice.getLong("Id") ?: return@forEach
val text = notice.getString("TrescUwagi") ?: return@forEach
val teacherId = notice.getLong("IdPracownik") ?: -1
- val addedDate = Date.fromY_m_d(notice.getString("DataWpisuTekst")).inMillis
+ val addedDate = notice.getLong("DataModyfikacji")?.times(1000) ?: System.currentTimeMillis()
+
+ val categoryId = notice.getLong("IdKategoriaUwag") ?: -1
+ val categoryText = data.noticeTypes[categoryId]?.name ?: ""
val noticeObject = Notice(
- profileId,
- id,
- text,
- profile.currentSemester,
- Notice.TYPE_NEUTRAL,
- teacherId
+ profileId = profileId,
+ id = id,
+ type = Notice.TYPE_NEUTRAL,
+ semester = profile.currentSemester,
+ text = text,
+ category = categoryText,
+ points = null,
+ teacherId = teacherId,
+ addedDate = addedDate
)
data.noticeList.add(noticeObject)
@@ -58,13 +87,11 @@ class VulcanApiNotices(override val data: DataVulcan,
Metadata.TYPE_NOTICE,
id,
profile.empty,
- profile.empty,
- addedDate
+ profile.empty
))
}
- data.setSyncNext(ENDPOINT_VULCAN_API_NOTICES, SYNC_ALWAYS)
- onSuccess(ENDPOINT_VULCAN_API_NOTICES)
+ onSuccess()
}
- } ?: onSuccess(ENDPOINT_VULCAN_API_NOTICES) }
+ }
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiProposedGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiProposedGrades.kt
index ee1e250f..e897df92 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiProposedGrades.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiProposedGrades.kt
@@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.utils.Utils
@@ -27,32 +28,54 @@ class VulcanApiProposedGrades(override val data: DataVulcan,
init { data.profile?.also { profile ->
+ val semesterId = data.studentSemesterId
+ val semesterNumber = data.studentSemesterNumber
+ if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) {
+ getProposedGrades(profile, semesterId - 1, semesterNumber - 1) {
+ getProposedGrades(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+ }
+ else {
+ getProposedGrades(profile, semesterId, semesterNumber) {
+ finish()
+ }
+ }
+
+ } ?: onSuccess(ENDPOINT_VULCAN_API_GRADES_SUMMARY) }
+
+ private fun finish() {
+ data.setSyncNext(ENDPOINT_VULCAN_API_GRADES_SUMMARY, 6*HOUR)
+ onSuccess(ENDPOINT_VULCAN_API_GRADES_SUMMARY)
+ }
+
+ private fun getProposedGrades(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) {
apiGet(TAG, VULCAN_API_ENDPOINT_GRADES_PROPOSITIONS, parameters = mapOf(
"IdUczen" to data.studentId,
- "IdOkresKlasyfikacyjny" to data.studentSemesterId
+ "IdOkresKlasyfikacyjny" to semesterId
)) { json, _ ->
val grades = json.getJsonObject("Data")
grades.getJsonArray("OcenyPrzewidywane")?.let {
- processGradeList(it, isFinal = false)
+ processGradeList(it, semesterNumber, isFinal = false)
}
grades.getJsonArray("OcenyKlasyfikacyjne")?.let {
- processGradeList(it, isFinal = true)
+ processGradeList(it, semesterNumber, isFinal = true)
}
- data.setSyncNext(ENDPOINT_VULCAN_API_GRADES_SUMMARY, 6*HOUR)
- onSuccess(ENDPOINT_VULCAN_API_GRADES_SUMMARY)
+ onSuccess()
}
- } ?: onSuccess(ENDPOINT_VULCAN_API_GRADES_SUMMARY) }
+ }
- private fun processGradeList(grades: JsonArray, isFinal: Boolean) {
- grades.asJsonObjectList()?.forEach { grade ->
+ private fun processGradeList(grades: JsonArray, semesterNumber: Int, isFinal: Boolean) {
+ grades.asJsonObjectList().forEach { grade ->
val name = grade.get("Wpis").asString
val value = Utils.getGradeValue(name)
val subjectId = grade.get("IdPrzedmiot").asLong
- val id = subjectId * -100 - data.studentSemesterNumber
+ val id = subjectId * -100 - semesterNumber
val color = Utils.getVulcanGradeColor(name)
@@ -60,7 +83,7 @@ class VulcanApiProposedGrades(override val data: DataVulcan,
profileId = profileId,
id = id,
name = name,
- type = if (data.studentSemesterNumber == 1) {
+ type = if (semesterNumber == 1) {
if (isFinal) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER1_PROPOSED
} else {
if (isFinal) TYPE_SEMESTER2_FINAL else TYPE_SEMESTER2_PROPOSED
@@ -71,7 +94,7 @@ class VulcanApiProposedGrades(override val data: DataVulcan,
category = "",
description = null,
comment = null,
- semester = data.studentSemesterNumber,
+ semester = semesterNumber,
teacherId = -1,
subjectId = subjectId
)
@@ -82,8 +105,7 @@ class VulcanApiProposedGrades(override val data: DataVulcan,
Metadata.TYPE_GRADE,
gradeObject.id,
data.profile?.empty ?: false,
- data.profile?.empty ?: false,
- System.currentTimeMillis()
+ data.profile?.empty ?: false
))
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiSendMessage.kt
index b88ccdb6..8c89b620 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiSendMessage.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiSendMessage.kt
@@ -54,7 +54,7 @@ class VulcanApiSendMessage(override val data: DataVulcan,
VulcanApiMessagesSent(data, null) {
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 event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
+ val event = MessageSentEvent(data.profileId, message, message?.addedDate)
EventBus.getDefault().postSticky(event)
onSuccess()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiTimetable.kt
index d8899dbf..46b52de5 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiTimetable.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiTimetable.kt
@@ -80,7 +80,7 @@ class VulcanApiTimetable(override val data: DataVulcan,
id,
name,
Team.TYPE_VIRTUAL,
- "${data.schoolName}:$name",
+ "${data.schoolCode}:$name",
teacherId ?: oldTeacherId ?: -1
)
data.teamList[id] = team
@@ -184,8 +184,7 @@ class VulcanApiTimetable(override val data: DataVulcan,
Metadata.TYPE_LESSON_CHANGE,
lessonObject.id,
seen,
- seen,
- System.currentTimeMillis()
+ seen
))
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiUpdateSemester.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiUpdateSemester.kt
index 1f03484c..50fb178a 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiUpdateSemester.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiUpdateSemester.kt
@@ -60,6 +60,7 @@ class VulcanApiUpdateSemester(override val data: DataVulcan,
data.studentClassId = studentClassId
data.studentSemesterId = studentSemesterId
data.studentSemesterNumber = studentSemesterNumber
+ data.profile.studentData["semester${studentSemesterNumber}Id"] = studentSemesterId
data.currentSemesterEndDate = currentSemesterEndDate
profile.studentClassName = studentClassName
dateSemester1Start?.let {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/web/HomepageTile.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/web/HomepageTile.kt
new file mode 100644
index 00000000..4d657ac7
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/web/HomepageTile.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-20.
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.web
+
+import com.google.gson.annotations.SerializedName
+
+data class HomepageTile(
+ @SerializedName("Nazwa")
+ val name: String?,
+ @SerializedName("Url")
+ val url: String?,
+ @SerializedName("Zawartosc")
+ val children: List
+)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/web/VulcanWebLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/web/VulcanWebLuckyNumber.kt
new file mode 100644
index 00000000..cbf24c2a
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/web/VulcanWebLuckyNumber.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-20.
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.web
+
+import pl.szczodrzynski.edziennik.DAY
+import pl.szczodrzynski.edziennik.data.api.VULCAN_WEB_ENDPOINT_LUCKY_NUMBER
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanWebMain
+import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
+import pl.szczodrzynski.edziennik.getJsonArray
+import pl.szczodrzynski.edziennik.utils.models.Date
+import pl.szczodrzynski.edziennik.utils.models.Time
+import pl.szczodrzynski.edziennik.utils.models.Week
+
+class VulcanWebLuckyNumber(override val data: DataVulcan,
+ override val lastSync: Long?,
+ val onSuccess: (endpointId: Int) -> Unit
+) : VulcanWebMain(data, lastSync) {
+ companion object {
+ const val TAG = "VulcanWebLuckyNumber"
+ }
+
+ init {
+ webGetJson(TAG, WEB_MAIN, VULCAN_WEB_ENDPOINT_LUCKY_NUMBER, parameters = mapOf(
+ "permissions" to data.webPermissions
+ )) { json, _ ->
+ val tiles = json
+ .getJsonArray("data")
+ ?.mapNotNull { data.app.gson.fromJson(it.toString(), HomepageTile::class.java) }
+ ?.flatMap { it.children }
+
+ if (tiles == null) {
+ data.setSyncNext(ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS, SYNC_ALWAYS)
+ onSuccess(ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS)
+ return@webGetJson
+ }
+
+ var nextSync = System.currentTimeMillis() + 1* DAY *1000
+
+ tiles.firstOrNull { it.name == data.schoolShort }?.children?.firstOrNull()?.let { tile ->
+ // "Szczęśliwy numer w dzienniku: 16"
+ return@let tile.name?.substringAfterLast(' ')?.toIntOrNull()?.let { number ->
+ // lucky number present
+ val luckyNumberObject = LuckyNumber(
+ profileId,
+ Date.getToday(),
+ number
+ )
+
+ data.luckyNumberList.add(luckyNumberObject)
+ data.metadataList.add(
+ Metadata(
+ profileId,
+ Metadata.TYPE_LUCKY_NUMBER,
+ luckyNumberObject.date.value.toLong(),
+ true,
+ profile?.empty ?: false
+ ))
+ }
+ } ?: {
+ // no lucky number
+ if (Date.getToday().weekDay <= Week.FRIDAY && Time.getNow().hour >= 22) {
+ // working days, after 10PM
+ // consider the lucky number is disabled; sync in 4 days
+ nextSync = System.currentTimeMillis() + 4*DAY*1000
+ }
+ else if (Date.getToday().weekDay <= Week.FRIDAY && Time.getNow().hour < 22) {
+ // working days, before 10PM
+
+ }
+ else {
+ // weekends
+ nextSync = Week.getNearestWeekDayDate(Week.MONDAY).combineWith(Time(5, 0, 0))
+ }
+ }()
+
+ data.setSyncNext(ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS, SYNC_ALWAYS)
+ onSuccess(ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS)
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/firstlogin/VulcanFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/firstlogin/VulcanFirstLogin.kt
index 291cb37b..ed541c69 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/firstlogin/VulcanFirstLogin.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/firstlogin/VulcanFirstLogin.kt
@@ -6,12 +6,15 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.firstlogin
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.*
-import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
-import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_STUDENT_LIST
+import pl.szczodrzynski.edziennik.data.api.*
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.VulcanWebMain
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.CufsCertificate
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginApi
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginWebMain
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
+import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.utils.models.Date
@@ -21,19 +24,97 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
}
private val api = VulcanApi(data, null)
+ private val web = VulcanWebMain(data, null)
private val profileList = mutableListOf()
+ private val loginStoreId = data.loginStore.id
+ private var firstProfileId = loginStoreId
+ private val tryingSymbols = mutableListOf()
init {
- val loginStoreId = data.loginStore.id
- val loginStoreType = LOGIN_TYPE_VULCAN
- var firstProfileId = loginStoreId
+ if (data.loginStore.mode == LOGIN_MODE_VULCAN_WEB) {
+ VulcanLoginWebMain(data) {
+ val xml = web.readCertificate() ?: run {
+ data.error(ApiError(TAG, ERROR_VULCAN_WEB_NO_CERTIFICATE))
+ return@VulcanLoginWebMain
+ }
+ val certificate = web.parseCertificate(xml)
+ if (data.symbol != null && data.symbol != "default") {
+ tryingSymbols += data.symbol ?: "default"
+ }
+ else {
+
+ tryingSymbols += certificate.userInstances
+ }
+
+ checkSymbol(certificate)
+ }
+ }
+ else {
+ registerDevice {
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
+ onSuccess()
+ }
+ }
+ }
+
+ private fun checkSymbol(certificate: CufsCertificate) {
+ if (tryingSymbols.isEmpty()) {
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
+ onSuccess()
+ return
+ }
+
+ val result = web.postCertificate(certificate, tryingSymbols.removeAt(0)) { symbol, state ->
+ when (state) {
+ VulcanWebMain.STATE_NO_REGISTER -> {
+ checkSymbol(certificate)
+ }
+ VulcanWebMain.STATE_LOGGED_OUT -> data.error(ApiError(TAG, ERROR_VULCAN_WEB_LOGGED_OUT))
+ VulcanWebMain.STATE_SUCCESS -> {
+ webRegisterDevice(symbol) {
+ checkSymbol(certificate)
+ }
+ }
+ }
+ }
+
+ // postCertificate returns false if the cert is not valid anymore
+ if (!result) {
+ data.error(ApiError(TAG, ERROR_VULCAN_WEB_CERTIFICATE_EXPIRED)
+ .withApiResponse(certificate.xml))
+ }
+ }
+
+ private fun webRegisterDevice(symbol: String, onSuccess: () -> Unit) {
+ web.getStartPage(symbol, postErrors = false) { _, schoolSymbols ->
+ if (schoolSymbols.isEmpty()) {
+ onSuccess()
+ return@getStartPage
+ }
+ data.symbol = symbol
+ val schoolSymbol = data.schoolSymbol ?: schoolSymbols.firstOrNull()
+ web.webGetJson(TAG, VulcanWebMain.WEB_NEW, "$schoolSymbol/$VULCAN_WEB_ENDPOINT_REGISTER_DEVICE") { result, _ ->
+ val json = result.getJsonObject("data")
+ data.symbol = symbol
+ data.apiToken = data.apiToken.toMutableMap().also {
+ it[symbol] = json.getString("TokenKey")
+ }
+ data.apiPin = data.apiPin.toMutableMap().also {
+ it[symbol] = json.getString("PIN")
+ }
+ registerDevice(onSuccess)
+ }
+ }
+ }
+
+ private fun registerDevice(onSuccess: () -> Unit) {
VulcanLoginApi(data) {
- api.apiGet(TAG, VULCAN_API_ENDPOINT_STUDENT_LIST, baseUrl = true) { json, response ->
+ api.apiGet(TAG, VULCAN_API_ENDPOINT_STUDENT_LIST, baseUrl = true) { json, _ ->
val students = json.getJsonArray("Data")
if (students == null || students.isEmpty()) {
- EventBus.getDefault().post(FirstLoginFinishedEvent(listOf(), data.loginStore))
+ EventBus.getDefault().postSticky(FirstLoginFinishedEvent(listOf(), data.loginStore))
onSuccess()
return@apiGet
}
@@ -42,7 +123,8 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
val student = studentEl.asJsonObject
val schoolSymbol = student.getString("JednostkaSprawozdawczaSymbol") ?: return@forEach
- val schoolName = "${data.symbol}_$schoolSymbol"
+ val schoolShort = student.getString("JednostkaSprawozdawczaSkrot") ?: return@forEach
+ val schoolCode = "${data.symbol}_$schoolSymbol"
val studentId = student.getInt("Id") ?: return@forEach
val studentLoginId = student.getInt("UzytkownikLoginId") ?: return@forEach
val studentClassId = student.getInt("IdOddzial") ?: return@forEach
@@ -80,7 +162,7 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
val profile = Profile(
firstProfileId++,
loginStoreId,
- loginStoreType,
+ LOGIN_TYPE_VULCAN,
studentNameLong,
userLogin,
studentNameLong,
@@ -88,13 +170,17 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
accountName
).apply {
this.studentClassName = studentClassName
+ studentData["symbol"] = data.symbol
+
studentData["studentId"] = studentId
studentData["studentLoginId"] = studentLoginId
studentData["studentClassId"] = studentClassId
studentData["studentSemesterId"] = studentSemesterId
studentData["studentSemesterNumber"] = studentSemesterNumber
+ studentData["semester${studentSemesterNumber}Id"] = studentSemesterId
studentData["schoolSymbol"] = schoolSymbol
- studentData["schoolName"] = schoolName
+ studentData["schoolShort"] = schoolShort
+ studentData["schoolName"] = schoolCode
studentData["currentSemesterEndDate"] = currentSemesterEndDate
}
dateSemester1Start?.let {
@@ -107,7 +193,6 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
profileList.add(profile)
}
- EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess()
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/CufsCertificate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/CufsCertificate.kt
new file mode 100644
index 00000000..45fc96d2
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/CufsCertificate.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-17.
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login
+
+import pl.droidsonroids.jspoon.annotation.Selector
+
+class CufsCertificate {
+ @Selector(value = "EndpointReference Address")
+ var targetUrl: String = ""
+
+ @Selector(value = "Lifetime Created")
+ var createdDate: String = ""
+
+ @Selector(value = "Lifetime Expires")
+ var expiryDate: String = ""
+
+ @Selector(value = "Attribute[AttributeName=UserInstance] AttributeValue")
+ var userInstances: List = listOf()
+
+ var xml = ""
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLogin.kt
index 4c11caf3..45c0153d 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLogin.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLogin.kt
@@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_API
+import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_WEB_MAIN
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.utils.Utils
@@ -45,6 +46,10 @@ class VulcanLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
}
Utils.d(TAG, "Using login method $loginMethodId")
when (loginMethodId) {
+ LOGIN_METHOD_VULCAN_WEB_MAIN -> {
+ data.startProgress(R.string.edziennik_progress_login_vulcan_web_main)
+ VulcanLoginWebMain(data) { onSuccess(loginMethodId) }
+ }
LOGIN_METHOD_VULCAN_API -> {
data.startProgress(R.string.edziennik_progress_login_vulcan_api)
VulcanLoginApi(data) { onSuccess(loginMethodId) }
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt
index 4385b18a..0cf1c895 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt
@@ -10,14 +10,11 @@ import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.JsonCallbackHandler
import io.github.wulkanowy.signer.android.getPrivateKeyFromCert
-import pl.szczodrzynski.edziennik.currentTimeUnix
+import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiUpdateSemester
import pl.szczodrzynski.edziennik.data.api.models.ApiError
-import pl.szczodrzynski.edziennik.getJsonObject
-import pl.szczodrzynski.edziennik.getString
-import pl.szczodrzynski.edziennik.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.net.HttpURLConnection.HTTP_BAD_REQUEST
import java.util.*
@@ -29,28 +26,19 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
}
init { run {
+ if (data.studentSemesterNumber == 1 && data.semester1Id == 0)
+ data.semester1Id = data.studentSemesterNumber
+ if (data.studentSemesterNumber == 2 && data.semester2Id == 0)
+ data.semester2Id = data.studentSemesterNumber
+
+ copyFromLoginStore()
+
if (data.profile != null && data.isApiLoginValid()) {
onSuccess()
}
else {
- // < v4.0 - PFX to Private Key migration
- if (data.apiCertificatePfx.isNotNullNorEmpty()) {
- try {
- data.apiCertificatePrivate = getPrivateKeyFromCert(
- if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
- data.apiCertificatePfx ?: ""
- )
- data.loginStore.removeLoginData("certificatePfx")
- } catch (e: Throwable) {
- e.printStackTrace()
- } finally {
- onSuccess()
- return@run
- }
- }
-
- if (data.apiCertificateKey.isNotNullNorEmpty()
- && data.apiCertificatePrivate.isNotNullNorEmpty()
+ if (data.apiFingerprint[data.symbol].isNotNullNorEmpty()
+ && data.apiPrivateKey[data.symbol].isNotNullNorEmpty()
&& data.symbol.isNotNullNorEmpty()) {
// (see data.isApiLoginValid())
// the semester end date is over
@@ -58,7 +46,7 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
return@run
}
- if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) {
+ if (data.symbol.isNotNullNorEmpty() && data.apiToken[data.symbol].isNotNullNorEmpty() && data.apiPin[data.symbol].isNotNullNorEmpty()) {
loginWithToken()
}
else {
@@ -67,6 +55,64 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
}
}}
+ private fun copyFromLoginStore() {
+ data.loginStore.data.apply {
+ // < v4.0 - PFX to Private Key migration
+ if (has("certificatePfx")) {
+ try {
+ val privateKey = getPrivateKeyFromCert(
+ if (data.apiToken[data.symbol]?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
+ getString("certificatePfx") ?: ""
+ )
+ data.apiPrivateKey = mapOf(
+ data.symbol to privateKey
+ )
+ remove("certificatePfx")
+ } catch (e: Throwable) {
+ e.printStackTrace()
+ }
+ }
+
+ // 4.0 - new login form - copy user input to profile
+ if (has("symbol")) {
+ data.symbol = getString("symbol")
+ remove("symbol")
+ }
+
+ // 4.0 - before Vulcan Web impl - migrate from strings to Map of Symbol to String
+ if (has("deviceSymbol")) {
+ data.symbol = getString("deviceSymbol")
+ remove("deviceSymbol")
+ }
+ if (has("certificateKey")) {
+ data.apiFingerprint = data.apiFingerprint.toMutableMap().also {
+ it[data.symbol] = getString("certificateKey")
+ }
+ remove("certificateKey")
+ }
+ if (has("certificatePrivate")) {
+ data.apiPrivateKey = data.apiPrivateKey.toMutableMap().also {
+ it[data.symbol] = getString("certificatePrivate")
+ }
+ remove("certificatePrivate")
+ }
+
+ // map form inputs to the symbol
+ if (has("deviceToken")) {
+ data.apiToken = data.apiToken.toMutableMap().also {
+ it[data.symbol] = getString("deviceToken")
+ }
+ remove("deviceToken")
+ }
+ if (has("devicePin")) {
+ data.apiPin = data.apiPin.toMutableMap().also {
+ it[data.symbol] = getString("devicePin")
+ }
+ remove("devicePin")
+ }
+ }
+ }
+
private fun loginWithToken() {
d(TAG, "Request: Vulcan/Login/Api - ${data.apiUrl}/$VULCAN_API_ENDPOINT_CERTIFICATE")
@@ -118,14 +164,22 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
return
}
- data.apiCertificateKey = cert.getString("CertyfikatKlucz")
- data.apiToken = data.apiToken?.substring(0, 3)
- data.apiCertificatePrivate = getPrivateKeyFromCert(
- if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
+ val privateKey = getPrivateKeyFromCert(
+ if (data.apiToken[data.symbol]?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
cert.getString("CertyfikatPfx") ?: ""
)
+
+ data.apiFingerprint = data.apiFingerprint.toMutableMap().also {
+ it[data.symbol] = cert.getString("CertyfikatKlucz")
+ }
+ data.apiToken = data.apiToken.toMutableMap().also {
+ it[data.symbol] = it[data.symbol]?.substring(0, 3)
+ }
+ data.apiPrivateKey = data.apiPrivateKey.toMutableMap().also {
+ it[data.symbol] = privateKey
+ }
data.loginStore.removeLoginData("certificatePfx")
- data.loginStore.removeLoginData("devicePin")
+ data.loginStore.removeLoginData("apiPin")
onSuccess()
}
@@ -136,14 +190,26 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
}
}
+ val deviceId = data.app.deviceId.padStart(16, '0')
+ val loginStoreId = data.loginStore.id.toString(16).padStart(4, '0')
+ val symbol = data.symbol?.crc16()?.toString(16)?.take(2) ?: "00"
+ val uuid =
+ deviceId.substring(0..7) +
+ "-" + deviceId.substring(8..11) +
+ "-" + deviceId.substring(12..15) +
+ "-" + loginStoreId +
+ "-" + symbol + "6f72616e7a"
+
+ val deviceNameSuffix = " - nie usuwać"
+
Request.builder()
.url("${data.apiUrl}$VULCAN_API_ENDPOINT_CERTIFICATE")
.userAgent(VULCAN_API_USER_AGENT)
.addHeader("RequestMobileType", "RegisterDevice")
- .addParameter("PIN", data.apiPin)
- .addParameter("TokenKey", data.apiToken)
- .addParameter("DeviceId", UUID.randomUUID().toString())
- .addParameter("DeviceName", VULCAN_API_DEVICE_NAME)
+ .addParameter("PIN", data.apiPin[data.symbol])
+ .addParameter("TokenKey", data.apiToken[data.symbol])
+ .addParameter("DeviceId", uuid)
+ .addParameter("DeviceName", VULCAN_API_DEVICE_NAME.take(50 - deviceNameSuffix.length) + deviceNameSuffix)
.addParameter("DeviceNameUser", "")
.addParameter("DeviceDescription", "")
.addParameter("DeviceSystemType", "Android")
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginWebMain.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginWebMain.kt
new file mode 100644
index 00000000..467beea3
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginWebMain.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-16.
+ */
+
+package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login
+
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.data.api.*
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
+import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanWebMain
+import pl.szczodrzynski.edziennik.data.api.models.ApiError
+import pl.szczodrzynski.edziennik.getString
+import pl.szczodrzynski.edziennik.isNotNullNorEmpty
+import pl.szczodrzynski.edziennik.utils.models.Date
+import pl.szczodrzynski.fslogin.FSLogin
+import pl.szczodrzynski.fslogin.realm.CufsRealm
+
+class VulcanLoginWebMain(val data: DataVulcan, val onSuccess: () -> Unit) {
+ companion object {
+ private const val TAG = "VulcanLoginWebMain"
+ }
+
+ private val web by lazy { VulcanWebMain(data, null) }
+
+ init { run {
+ copyFromLoginStore()
+
+ if (data.profile != null && data.isWebMainLoginValid()) {
+ onSuccess()
+ }
+ else {
+ if (data.symbol.isNotNullNorEmpty()
+ && data.webType.isNotNullNorEmpty()
+ && data.webHost.isNotNullNorEmpty()
+ && (data.webEmail.isNotNullNorEmpty() || data.webUsername.isNotNullNorEmpty())
+ && data.webPassword.isNotNullNorEmpty()) {
+ try {
+ val success = loginWithCredentials()
+ if (!success)
+ data.error(ApiError(TAG, ERROR_VULCAN_WEB_DATA_MISSING))
+ } catch (e: Exception) {
+ data.error(ApiError(TAG, EXCEPTION_VULCAN_WEB_LOGIN)
+ .withThrowable(e))
+ }
+ }
+ else {
+ data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
+ }
+ }
+ }}
+
+ private fun copyFromLoginStore() {
+ data.loginStore.data.apply {
+ // 4.0 - new login form - copy user input to profile
+ if (has("symbol")) {
+ data.symbol = getString("symbol")
+ remove("symbol")
+ }
+ }
+ }
+
+ private fun loginWithCredentials(): Boolean {
+ val realm = when (data.webType) {
+ "cufs" -> CufsRealm(
+ host = data.webHost ?: return false,
+ symbol = data.symbol ?: "default",
+ httpCufs = data.webIsHttpCufs
+ )
+ "adfs" -> CufsRealm(
+ host = data.webHost ?: return false,
+ symbol = data.symbol ?: "default",
+ httpCufs = data.webIsHttpCufs
+ ).toAdfsRealm(id = data.webAdfsId ?: return false)
+ "adfslight" -> CufsRealm(
+ host = data.webHost ?: return false,
+ symbol = data.symbol ?: "default",
+ httpCufs = data.webIsHttpCufs
+ ).toAdfsLightRealm(
+ id = data.webAdfsId ?: return false,
+ domain = data.webAdfsDomain ?: "adfslight",
+ isScoped = data.webIsScopedAdfs
+ )
+ else -> return false
+ }
+
+ val certificate = web.readCertificate()?.let { web.parseCertificate(it) }
+ if (certificate != null && Date.fromIso(certificate.expiryDate) > System.currentTimeMillis()) {
+ useCertificate(certificate)
+ return true
+ }
+
+ val fsLogin = FSLogin(data.app.http, debug = App.debugMode)
+ fsLogin.performLogin(
+ realm = realm,
+ username = data.webUsername ?: data.webEmail ?: return false,
+ password = data.webPassword ?: return false,
+ onSuccess = { fsCertificate ->
+ web.saveCertificate(fsCertificate.wresult)
+ useCertificate(web.parseCertificate(fsCertificate.wresult))
+ },
+ onFailure = { errorText ->
+ // TODO
+ data.error(ApiError(TAG, 0).withThrowable(RuntimeException(errorText)))
+ }
+ )
+
+ return true
+ }
+
+ private fun useCertificate(certificate: CufsCertificate) {
+ // auto-post certificate when not first login
+ if (data.profile != null && data.symbol != null && data.symbol != "default") {
+ val result = web.postCertificate(certificate, data.symbol ?: "default") { _, state ->
+ when (state) {
+ VulcanWebMain.STATE_SUCCESS -> {
+ web.getStartPage { _, _ -> onSuccess() }
+ }
+ VulcanWebMain.STATE_NO_REGISTER -> data.error(ApiError(TAG, ERROR_VULCAN_WEB_NO_REGISTER))
+ VulcanWebMain.STATE_LOGGED_OUT -> data.error(ApiError(TAG, ERROR_VULCAN_WEB_LOGGED_OUT))
+ }
+ }
+ // postCertificate returns false if the cert is not valid anymore
+ if (!result) {
+ data.error(ApiError(TAG, ERROR_VULCAN_WEB_CERTIFICATE_EXPIRED)
+ .withApiResponse(certificate.xml))
+ }
+ }
+ else {
+ // first login - succeed immediately
+ onSuccess()
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt
index 2f0c88af..6bd9aa83 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt
@@ -88,6 +88,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
var teacherOnConflictStrategy = OnConflictStrategy.IGNORE
var eventListReplace = false
var messageListReplace = false
+ var announcementListReplace = false
val classrooms = LongSparseArray()
val attendanceTypes = LongSparseArray()
@@ -120,7 +121,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
val attendanceList = mutableListOf()
val announcementList = mutableListOf()
- val announcementIgnoreList = mutableListOf()
val luckyNumberList = mutableListOf()
@@ -178,7 +178,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
noticeList.clear()
attendanceList.clear()
announcementList.clear()
- announcementIgnoreList.clear()
luckyNumberList.clear()
teacherAbsenceList.clear()
messageList.clear()
@@ -202,7 +201,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
" - ",
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)
+ ) + " " + app.getString(if (profile.isParent) R.string.account_type_parent else R.string.account_type_child)
db.profileDao().add(profile)
db.loginStoreDao().add(loginStore)
@@ -284,39 +283,19 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
d("Metadata saved in ${System.currentTimeMillis()-startTime} ms")
startTime = System.currentTimeMillis()
- if (lessonList.isNotEmpty()) {
- db.timetableDao() += lessonList
- }
- if (gradeList.isNotEmpty()) {
- db.gradeDao().addAll(gradeList)
- }
- if (eventList.isNotEmpty()) {
- if (eventListReplace)
- db.eventDao().replaceAll(eventList)
- else
- db.eventDao().upsertAll(eventList, removeNotKept = true)
- }
+ db.timetableDao().putAll(lessonList, removeNotKept = true)
+ db.gradeDao().putAll(gradeList, removeNotKept = true)
+ db.eventDao().putAll(eventList, forceReplace = eventListReplace, removeNotKept = true)
if (noticeList.isNotEmpty()) {
db.noticeDao().clear(profile.id)
- db.noticeDao().addAll(noticeList)
+ db.noticeDao().putAll(noticeList)
}
- if (attendanceList.isNotEmpty())
- db.attendanceDao().addAll(attendanceList)
- if (announcementList.isNotEmpty())
- db.announcementDao().addAll(announcementList)
- if (announcementIgnoreList.isNotEmpty())
- db.announcementDao().addAllIgnore(announcementIgnoreList)
- if (luckyNumberList.isNotEmpty())
- db.luckyNumberDao().addAll(luckyNumberList)
- if (teacherAbsenceList.isNotEmpty())
- db.teacherAbsenceDao().addAll(teacherAbsenceList)
+ db.attendanceDao().putAll(attendanceList, removeNotKept = true)
+ db.announcementDao().putAll(announcementList, forceReplace = announcementListReplace, removeNotKept = false)
+ db.luckyNumberDao().putAll(luckyNumberList)
+ db.teacherAbsenceDao().putAll(teacherAbsenceList)
- if (messageList.isNotEmpty()) {
- if (messageListReplace)
- db.messageDao().replaceAll(messageList)
- else
- db.messageDao().upsertAll(messageList, removeNotKept = false) // TODO dataRemoveModel for messages
- }
+ db.messageDao().putAll(messageList, forceReplace = messageListReplace, removeNotKept = false)
if (messageRecipientList.isNotEmpty())
db.messageRecipientDao().addAll(messageRecipientList)
if (messageRecipientIgnoreList.isNotEmpty())
@@ -357,7 +336,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
}
fun shouldSyncLuckyNumber(): Boolean {
- return (db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday().value) ?: -1) == -1
+ return db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday()) == null
}
/*fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/DataRemoveModel.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/DataRemoveModel.kt
index f247ee19..49b57999 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/DataRemoveModel.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/DataRemoveModel.kt
@@ -20,10 +20,10 @@ open class DataRemoveModel {
fun commit(profileId: Int, dao: TimetableDao) {
if (dateFrom != null && dateTo != null) {
- dao.clearBetweenDates(profileId, dateFrom, dateTo)
+ dao.dontKeepBetweenDates(profileId, dateFrom, dateTo)
} else {
- dateFrom?.let { dateFrom -> dao.clearFromDate(profileId, dateFrom) }
- dateTo?.let { dateTo -> dao.clearToDate(profileId, dateTo) }
+ dateFrom?.let { dateFrom -> dao.dontKeepFromDate(profileId, dateFrom) }
+ dateTo?.let { dateTo -> dao.dontKeepToDate(profileId, dateTo) }
}
}
}
@@ -38,12 +38,12 @@ open class DataRemoveModel {
fun commit(profileId: Int, dao: GradeDao) {
if (all) {
- if (type != null) dao.clearWithType(profileId, type)
+ if (type != null) dao.dontKeepWithType(profileId, type)
else dao.clear(profileId)
}
semester?.let {
- if (type != null) dao.clearForSemesterWithType(profileId, it, type)
- else dao.clearForSemester(profileId, it)
+ if (type != null) dao.dontKeepForSemesterWithType(profileId, it, type)
+ else dao.dontKeepForSemester(profileId, it)
}
}
}
@@ -53,12 +53,15 @@ open class DataRemoveModel {
fun futureExceptType(exceptType: Long) = Events(null, exceptType, null)
fun futureExceptTypes(exceptTypes: List) = Events(null, null, exceptTypes)
fun futureWithType(type: Long) = Events(type, null, null)
+ fun future() = Events(null, null, null)
}
fun commit(profileId: Int, dao: EventDao) {
type?.let { dao.dontKeepFutureWithType(profileId, Date.getToday(), it) }
exceptType?.let { dao.dontKeepFutureExceptType(profileId, Date.getToday(), it) }
exceptTypes?.let { dao.dontKeepFutureExceptTypes(profileId, Date.getToday(), it) }
+ if (type == null && exceptType == null && exceptTypes == null)
+ dao.dontKeepFuture(profileId, Date.getToday())
}
}
@@ -69,7 +72,7 @@ open class DataRemoveModel {
fun commit(profileId: Int, dao: AttendanceDao) {
if (dateFrom != null) {
- dao.clearAfterDate(profileId, dateFrom)
+ dao.dontKeepAfterDate(profileId, dateFrom)
}
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt
index bdd14f33..ae82ff8f 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt
@@ -27,6 +27,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDetailsDialog
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
+import pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import retrofit2.Response
@@ -73,7 +74,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
suspend inline fun runCatching(errorSnackbar: ErrorSnackbar, crossinline block: SzkolnyApi.() -> T?): T? {
return try {
- withContext(Dispatchers.Default) { block() }
+ withContext(Dispatchers.Default) { block.invoke(this@SzkolnyApi) }
}
catch (e: Exception) {
errorSnackbar.addError(e.toApiError(TAG)).show()
@@ -82,7 +83,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
}
suspend inline fun runCatching(activity: AppCompatActivity, crossinline block: SzkolnyApi.() -> T?): T? {
return try {
- withContext(Dispatchers.Default) { block() }
+ withContext(Dispatchers.Default) { block.invoke(this@SzkolnyApi) }
}
catch (e: Exception) {
ErrorDetailsDialog(
@@ -95,7 +96,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
}
inline fun runCatching(block: SzkolnyApi.() -> T, onError: (e: Throwable) -> Unit): T? {
return try {
- block()
+ block.invoke(this@SzkolnyApi)
}
catch (e: Exception) {
onError(e)
@@ -327,4 +328,11 @@ class SzkolnyApi(val app: App) : CoroutineScope {
return parseResponse(response).message
}
+
+ @Throws(Exception::class)
+ fun getPlatforms(registerName: String): List {
+ val response = api.appLoginPlatforms(registerName).execute()
+
+ return parseResponse(response)
+ }
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt
index 0e3dd574..bce3aef0 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt
@@ -6,11 +6,9 @@ package pl.szczodrzynski.edziennik.data.api.szkolny
import pl.szczodrzynski.edziennik.data.api.szkolny.request.*
import pl.szczodrzynski.edziennik.data.api.szkolny.response.*
+import pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo
import retrofit2.Call
-import retrofit2.http.Body
-import retrofit2.http.GET
-import retrofit2.http.POST
-import retrofit2.http.Query
+import retrofit2.http.*
interface SzkolnyService {
@@ -34,4 +32,7 @@ interface SzkolnyService {
@POST("feedbackMessage")
fun feedbackMessage(@Body request: FeedbackMessageRequest): Call>
+
+ @GET("appLogin/platforms/{registerName}")
+ fun appLoginPlatforms(@Path("registerName") registerName: String): Call>>
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt
index 65a3be61..6741a01a 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt
@@ -39,13 +39,13 @@ object Signing {
val appPassword by lazy {
iLoveApple(
"ThisIsOurHardWorkPleaseDoNotCopyOrSteal(c)2019.KubaSz".sha256(),
- BuildConfig.VERSION_NAME,
+ BuildConfig.VERSION_NAME.substringBeforeLast('+'),
BuildConfig.VERSION_CODE.toLong()
)
}
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
- return "$param1.MTIzNDU2Nzg5MDP/4SAI6B===.$param2".sha256()
+ return "$param1.MTIzNDU2Nzg5MDCEfzNqNH===.$param2".sha256()
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/AppSync.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/AppSync.kt
index 63d4ec88..c10bab3e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/AppSync.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/AppSync.kt
@@ -40,8 +40,7 @@ class AppSync(val app: App, val notifications: MutableList, val pr
Metadata.TYPE_EVENT,
event.id,
isPast || markAsSeen || event.seen,
- isPast || markAsSeen || event.notified,
- event.addedDate
+ isPast || markAsSeen || event.notified
)
})
return app.db.eventDao().upsertAll(events).size
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt
index 39b68499..21a5c112 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt
@@ -53,7 +53,7 @@ class Notifications(val app: App, val notifications: MutableList,
profileId = lesson.profileId,
profileName = profiles.singleOrNull { it.id == lesson.profileId }?.name,
viewId = MainActivity.DRAWER_ITEM_TIMETABLE,
- addedDate = lesson.addedDate
+ addedDate = System.currentTimeMillis()
).addExtra("timetableDate", lesson.displayDate?.stringY_m_d ?: "")
}
}
@@ -117,7 +117,7 @@ class Notifications(val app: App, val notifications: MutableList,
}
private fun gradeNotifications() {
- for (grade in app.db.gradeDao().notNotifiedNow) {
+ for (grade in app.db.gradeDao().getNotNotifiedNow()) {
val gradeName = when (grade.type) {
Grade.TYPE_SEMESTER1_PROPOSED, Grade.TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format_2, grade.name)
Grade.TYPE_SEMESTER1_FINAL, Grade.TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format_2, grade.name)
@@ -144,7 +144,7 @@ class Notifications(val app: App, val notifications: MutableList,
}
private fun behaviourNotifications() {
- for (notice in app.db.noticeDao().notNotifiedNow) {
+ for (notice in app.db.noticeDao().getNotNotifiedNow()) {
val noticeTypeStr = when (notice.type) {
Notice.TYPE_POSITIVE -> app.getString(R.string.notification_notice_praise)
@@ -155,7 +155,7 @@ class Notifications(val app: App, val notifications: MutableList,
val text = app.getString(
R.string.notification_notice_format,
noticeTypeStr,
- notice.teacherFullName,
+ notice.teacherName,
Date.fromMillis(notice.addedDate).formattedString
)
notifications += Notification(
@@ -172,9 +172,9 @@ class Notifications(val app: App, val notifications: MutableList,
}
private fun attendanceNotifications() {
- for (attendance in app.db.attendanceDao().notNotifiedNow) {
+ for (attendance in app.db.attendanceDao().getNotNotifiedNow()) {
- val attendanceTypeStr = when (attendance.type) {
+ val attendanceTypeStr = when (attendance.baseType) {
Attendance.TYPE_ABSENT -> app.getString(R.string.notification_absence)
Attendance.TYPE_ABSENT_EXCUSED -> app.getString(R.string.notification_absence_excused)
Attendance.TYPE_BELATED -> app.getString(R.string.notification_belated)
@@ -191,7 +191,7 @@ class Notifications(val app: App, val notifications: MutableList,
R.string.notification_attendance_format,
attendanceTypeStr,
attendance.subjectLongName,
- attendance.lessonDate.formattedString
+ attendance.date.formattedString
)
notifications += Notification(
id = Notification.buildId(attendance.profileId, Notification.TYPE_NEW_ATTENDANCE, attendance.id),
@@ -207,10 +207,10 @@ class Notifications(val app: App, val notifications: MutableList,
}
private fun announcementNotifications() {
- for (announcement in app.db.announcementDao().notNotifiedNow) {
+ for (announcement in app.db.announcementDao().getNotNotifiedNow()) {
val text = app.getString(
R.string.notification_announcement_format,
- announcement.teacherFullName,
+ announcement.teacherName,
announcement.subject
)
notifications += Notification(
@@ -247,9 +247,9 @@ class Notifications(val app: App, val notifications: MutableList,
}
private fun luckyNumberNotifications() {
- val luckyNumbers = app.db.luckyNumberDao().notNotifiedNow
- luckyNumbers?.removeAll { it.date < today }
- luckyNumbers?.forEach { luckyNumber ->
+ val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow().toMutableList()
+ luckyNumbers.removeAll { it.date < today }
+ luckyNumbers.forEach { luckyNumber ->
val profile = profiles.singleOrNull { it.id == luckyNumber.profileId } ?: return@forEach
val text = when (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) {
true -> when (luckyNumber.date.value) {
@@ -271,7 +271,7 @@ class Notifications(val app: App, val notifications: MutableList,
profileId = luckyNumber.profileId,
profileName = profile.name,
viewId = MainActivity.DRAWER_ITEM_HOME,
- addedDate = luckyNumber.addedDate
+ addedDate = System.currentTimeMillis()
)
}
}
@@ -280,7 +280,7 @@ class Notifications(val app: App, val notifications: MutableList,
for (teacherAbsence in app.db.teacherAbsenceDao().getNotNotifiedNow()) {
val message = app.getString(
R.string.notification_teacher_absence_new_format,
- teacherAbsence.teacherFullName
+ teacherAbsence.teacherName
)
notifications += Notification(
id = Notification.buildId(teacherAbsence.profileId, Notification.TYPE_TEACHER_ABSENCE, teacherAbsence.id),
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt
index b57cc4d4..333967b0 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt
@@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
LibrusLesson::class,
TimetableManual::class,
Metadata::class
-], version = 85)
+], version = 88)
@TypeConverters(
ConverterTime::class,
ConverterDate::class,
@@ -170,7 +170,10 @@ abstract class AppDb : RoomDatabase() {
Migration82(),
Migration83(),
Migration84(),
- Migration85()
+ Migration85(),
+ Migration86(),
+ Migration87(),
+ Migration88()
).allowMainThreadQueries().build()
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.java
deleted file mode 100644
index ab32fb7c..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.java
+++ /dev/null
@@ -1,82 +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 androidx.room.RawQuery;
-import androidx.sqlite.db.SimpleSQLiteQuery;
-import androidx.sqlite.db.SupportSQLiteQuery;
-
-import java.util.List;
-
-import pl.szczodrzynski.edziennik.data.db.entity.Announcement;
-import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
-import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull;
-
-import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ANNOUNCEMENT;
-
-@Dao
-public abstract class AnnouncementDao {
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract long add(Announcement announcement);
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract void addAll(List announcementList);
-
- @Insert(onConflict = OnConflictStrategy.IGNORE)
- public abstract void addAllIgnore(List announcementList);
-
- @Query("DELETE FROM announcements WHERE profileId = :profileId")
- public abstract void clear(int profileId);
-
- @RawQuery(observedEntities = {Announcement.class, Metadata.class})
- abstract LiveData> getAll(SupportSQLiteQuery query);
- public LiveData> getAll(int profileId, String filter) {
- return getAll(new SimpleSQLiteQuery("SELECT \n" +
- "*, \n" +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
- "FROM announcements \n" +
- "LEFT JOIN teachers USING(profileId, teacherId)\n" +
- "LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = "+profileId+"\n" +
- "WHERE announcements.profileId = "+profileId+" AND "+filter+"\n" +
- "ORDER BY addedDate DESC"));
- }
- public LiveData> getAll(int profileId) {
- return getAll(profileId, "1");
- }
- public LiveData> getAllWhere(int profileId, String filter) {
- return getAll(profileId, filter);
- }
-
- @RawQuery(observedEntities = {Announcement.class, Metadata.class})
- abstract List getAllNow(SupportSQLiteQuery query);
- public List getAllNow(int profileId, String filter) {
- return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
- "*, \n" +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
- "FROM announcements \n" +
- "LEFT JOIN teachers USING(profileId, teacherId)\n" +
- "LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = "+profileId+"\n" +
- "WHERE announcements.profileId = "+profileId+" AND "+filter+"\n" +
- "ORDER BY addedDate DESC"));
- }
- public List getNotNotifiedNow(int profileId) {
- return getAllNow(profileId, "notified = 0");
- }
-
- @Query("SELECT " +
- "*, " +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName " +
- "FROM announcements " +
- "LEFT JOIN teachers USING(profileId, teacherId) " +
- "LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = announcements.profileId " +
- "WHERE notified = 0 " +
- "ORDER BY addedDate DESC")
- public abstract List getNotNotifiedNow();
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.kt
new file mode 100644
index 00000000..67e034c5
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
+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.Announcement
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
+
+@Dao
+@SelectiveDao(db = AppDb::class)
+abstract class AnnouncementDao : BaseDao {
+ companion object {
+ private const val QUERY = """
+ SELECT
+ *,
+ teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName
+ FROM announcements
+ LEFT JOIN teachers USING(profileId, teacherId)
+ LEFT JOIN metadata ON announcementId = thingId AND thingType = ${Metadata.TYPE_ANNOUNCEMENT} AND metadata.profileId = announcements.profileId
+ """
+
+ private const val ORDER_BY = """ORDER BY addedDate DESC"""
+ }
+
+ private val selective by lazy { AnnouncementDaoSelective(App.db) }
+
+ @RawQuery(observedEntities = [Announcement::class])
+ abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [Announcement::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
+
+ // SELECTIVE UPDATE
+ @UpdateSelective(primaryKeys = ["profileId", "announcementId"], skippedColumns = ["addedDate", "announcementText"])
+ override fun update(item: Announcement) = selective.update(item)
+ override fun updateAll(items: List) = selective.updateAll(items)
+
+ // CLEAR
+ @Query("DELETE FROM announcements WHERE profileId = :profileId")
+ abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM announcements WHERE keep = 0")
+ abstract override fun removeNotKept()
+
+ // GET ALL - LIVE DATA
+ fun getAll(profileId: Int) =
+ getRaw("$QUERY WHERE announcements.profileId = $profileId $ORDER_BY")
+
+ // GET ALL - NOW
+ fun getAllNow(profileId: Int) =
+ getRawNow("$QUERY WHERE announcements.profileId = $profileId $ORDER_BY")
+ fun getNotNotifiedNow() =
+ getRawNow("$QUERY WHERE notified = 0 $ORDER_BY")
+ fun getNotNotifiedNow(profileId: Int) =
+ getRawNow("$QUERY WHERE announcements.profileId = $profileId AND notified = 0 $ORDER_BY")
+
+ // GET ONE - NOW
+ fun getByIdNow(profileId: Int, id: Long) =
+ getOneNow("$QUERY WHERE announcements.profileId = $profileId AND announcementId = $id")
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.java
deleted file mode 100644
index 95c3147f..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.java
+++ /dev/null
@@ -1,108 +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 androidx.room.RawQuery;
-import androidx.sqlite.db.SimpleSQLiteQuery;
-import androidx.sqlite.db.SupportSQLiteQuery;
-
-import java.util.List;
-
-import pl.szczodrzynski.edziennik.data.db.entity.Attendance;
-import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull;
-import pl.szczodrzynski.edziennik.utils.models.Date;
-
-import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ATTENDANCE;
-
-@Dao
-public abstract class AttendanceDao {
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract long add(Attendance attendance);
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract void addAll(List attendanceList);
-
- @Query("DELETE FROM attendances WHERE profileId = :profileId")
- public abstract void clear(int profileId);
-
- @Query("DELETE FROM attendances WHERE profileId = :profileId AND attendanceLessonDate > :date")
- public abstract void clearAfterDate(int profileId, Date date);
-
- @RawQuery(observedEntities = {Attendance.class})
- abstract LiveData> getAll(SupportSQLiteQuery query);
- public LiveData> getAll(int profileId, String filter) {
- return getAll(new SimpleSQLiteQuery("SELECT \n" +
- "*, \n" +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
- "FROM attendances \n" +
- "LEFT JOIN teachers USING(profileId, teacherId)\n" +
- "LEFT JOIN subjects USING(profileId, subjectId)\n" +
- "LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = "+profileId+"\n" +
- "WHERE attendances.profileId = "+profileId+" AND "+filter+"\n" +
- "ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC"));
- }
- public LiveData> getAll(int profileId) {
- return getAll(profileId, "1");
- }
- public LiveData> getAllWhere(int profileId, String filter) {
- return getAll(profileId, filter);
- }
-
- @RawQuery
- abstract List getAllNow(SupportSQLiteQuery query);
- public List getAllNow(int profileId, String filter) {
- return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
- "*, \n" +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
- "FROM attendances \n" +
- "LEFT JOIN teachers USING(profileId, teacherId)\n" +
- "LEFT JOIN subjects USING(profileId, subjectId)\n" +
- "LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = "+profileId+"\n" +
- "WHERE attendances.profileId = "+profileId+" AND "+filter+"\n" +
- "ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC"));
- }
- public List getNotNotifiedNow(int profileId) {
- return getAllNow(profileId, "notified = 0");
- }
-
- @Query("SELECT * FROM attendances " +
- "LEFT JOIN subjects USING(profileId, subjectId) " +
- "LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = attendances.profileId " +
- "WHERE notified = 0 " +
- "ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC")
- public abstract List getNotNotifiedNow();
-
- // only absent and absent_excused count as absences
- // all the other types are counted as being present
- @Query("SELECT \n" +
- "CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+ Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" +
- " / \n" +
- "CAST(count() AS float)*100 \n" +
- "FROM attendances \n" +
- "WHERE profileId = :profileId")
- public abstract LiveData getAttendancePercentage(int profileId);
-
- @Query("SELECT \n" +
- "CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" +
- " / \n" +
- "CAST(count() AS float)*100 \n" +
- "FROM attendances \n" +
- "WHERE profileId = :profileId AND attendanceSemester = :semester")
- public abstract float getAttendancePercentageNow(int profileId, int semester);
-
- @Query("SELECT \n" +
- "CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" +
- " / \n" +
- "CAST(count() AS float)*100 \n" +
- "FROM attendances \n" +
- "WHERE profileId = :profileId")
- public abstract float getAttendancePercentageNow(int profileId);
-}
-
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.kt
new file mode 100644
index 00000000..97513689
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-24.
+ */
+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.Attendance
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+@Dao
+@SelectiveDao(db = AppDb::class)
+abstract class AttendanceDao : BaseDao {
+ companion object {
+ private const val QUERY = """
+ SELECT
+ *,
+ teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName
+ FROM attendances
+ LEFT JOIN teachers USING(profileId, teacherId)
+ LEFT JOIN subjects USING(profileId, subjectId)
+ LEFT JOIN metadata ON attendanceId = thingId AND thingType = ${Metadata.TYPE_ATTENDANCE} AND metadata.profileId = attendances.profileId
+ """
+
+ private const val ORDER_BY = """ORDER BY attendanceDate DESC, attendanceTime DESC"""
+ }
+
+ private val selective by lazy { AttendanceDaoSelective(App.db) }
+
+ @RawQuery(observedEntities = [Attendance::class])
+ abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [Attendance::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
+
+ // SELECTIVE UPDATE
+ @UpdateSelective(primaryKeys = ["profileId", "attendanceId"], skippedColumns = ["addedDate", "announcementText"])
+ override fun update(item: Attendance) = selective.update(item)
+ override fun updateAll(items: List) = selective.updateAll(items)
+
+ // CLEAR
+ @Query("DELETE FROM attendances WHERE profileId = :profileId")
+ abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM attendances WHERE keep = 0")
+ abstract override fun removeNotKept()
+
+ // GET ALL - LIVE DATA
+ fun getAll(profileId: Int) =
+ getRaw("$QUERY WHERE attendances.profileId = $profileId $ORDER_BY")
+
+ // GET ALL - NOW
+ fun getAllNow(profileId: Int) =
+ getRawNow("$QUERY WHERE attendances.profileId = $profileId $ORDER_BY")
+ fun getNotNotifiedNow() =
+ getRawNow("$QUERY WHERE notified = 0 $ORDER_BY")
+ fun getNotNotifiedNow(profileId: Int) =
+ getRawNow("$QUERY WHERE attendances.profileId = $profileId AND notified = 0 $ORDER_BY")
+
+ // GET ONE - NOW
+ fun getByIdNow(profileId: Int, id: Long) =
+ getOneNow("$QUERY WHERE attendances.profileId = $profileId AND attendanceId = $id")
+
+ @Query("UPDATE attendances SET keep = 0 WHERE profileId = :profileId AND attendanceDate >= :date")
+ abstract fun dontKeepAfterDate(profileId: Int, date: Date?)
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/BaseDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/BaseDao.kt
index 8c13dca9..0ae961db 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/BaseDao.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/BaseDao.kt
@@ -16,13 +16,15 @@ interface BaseDao {
fun getRaw(query: SupportSQLiteQuery): LiveData>
fun getRaw(query: String) = getRaw(SimpleSQLiteQuery(query))
@RawQuery
+ fun getOne(query: SupportSQLiteQuery): LiveData
+ fun getOne(query: String) = getOne(SimpleSQLiteQuery(query))
+ @RawQuery
fun getRawNow(query: SupportSQLiteQuery): List
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()
/**
@@ -41,12 +43,14 @@ interface BaseDao {
/**
* REPLACE an [item] in the database,
* removing any conflicting rows.
+ * Creates the item if it does not exist yet.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun replace(item: T)
/**
* REPLACE [items] in the database,
* removing any conflicting rows.
+ * Creates items if it does not exist yet.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun replaceAll(items: List)
@@ -97,4 +101,23 @@ interface BaseDao {
if (removeNotKept) removeNotKept()
return insertResult
}
+
+ /**
+ * Make sure that [items] are in the database.
+ * When [forceReplace] == false, do a selective update (UPSERT).
+ * When [forceReplace] == true, add all items replacing any conflicting ones (REPLACE).
+ *
+ * @param forceReplace whether to replace all items instead of selectively updating
+ * @param removeNotKept whether to remove all items whose [keep] parameter is false
+ */
+ fun putAll(items: List, forceReplace: Boolean = false, removeNotKept: Boolean = false) {
+ if (items.isEmpty())
+ return
+ if (forceReplace)
+ replaceAll(items)
+ else
+ upsertAll(items, removeNotKept = false)
+
+ if (removeNotKept) removeNotKept()
+ }
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt
index b2017cc5..a532602b 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt
@@ -31,8 +31,8 @@ abstract class EventDao : BaseDao {
eventTypes.eventTypeName AS typeName,
eventTypes.eventTypeColor AS typeColor
FROM events
- LEFT JOIN subjects USING(profileId, subjectId)
LEFT JOIN teachers USING(profileId, teacherId)
+ LEFT JOIN subjects USING(profileId, subjectId)
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
@@ -47,6 +47,8 @@ abstract class EventDao : BaseDao {
@RawQuery(observedEntities = [Event::class])
abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [Event::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
// SELECTIVE UPDATE
@UpdateSelective(primaryKeys = ["profileId", "eventId"], skippedColumns = ["eventIsDone", "eventBlacklisted", "homeworkBody", "attachmentIds", "attachmentNames"])
@@ -56,6 +58,9 @@ abstract class EventDao : BaseDao {
// CLEAR
@Query("DELETE FROM events WHERE profileId = :profileId")
abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM events WHERE keep = 0")
+ abstract override fun removeNotKept()
// GET ALL - LIVE DATA
fun getAll(profileId: Int) =
@@ -109,6 +114,9 @@ abstract class EventDao : BaseDao {
" AND " + filter))
}
+ @Query("UPDATE events SET keep = 0 WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate")
+ abstract fun dontKeepFuture(profileId: Int, todayDate: Date)
+
@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)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/GradeDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/GradeDao.kt
index 9c3e93c5..4fcfc721 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/GradeDao.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/GradeDao.kt
@@ -1,102 +1,103 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-24.
*/
package pl.szczodrzynski.edziennik.data.db.dao
import androidx.lifecycle.LiveData
-import androidx.room.*
-import androidx.sqlite.db.SimpleSQLiteQuery
+import androidx.room.Dao
+import androidx.room.Query
+import androidx.room.RawQuery
+import androidx.room.Transaction
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.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
+import pl.szczodrzynski.edziennik.utils.models.Date
import java.util.*
-import kotlin.collections.List
import kotlin.collections.component1
import kotlin.collections.component2
-import kotlin.collections.iterator
import kotlin.collections.set
@Dao
-abstract class GradeDao {
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- abstract fun add(grade: Grade): Long
+@SelectiveDao(db = AppDb::class)
+abstract class GradeDao : BaseDao {
+ companion object {
+ private const val QUERY = """
+ SELECT
+ *,
+ teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName
+ FROM grades
+ LEFT JOIN teachers USING(profileId, teacherId)
+ LEFT JOIN subjects USING(profileId, subjectId)
+ LEFT JOIN metadata ON gradeId = thingId AND thingType = ${Metadata.TYPE_GRADE} AND metadata.profileId = grades.profileId
+ """
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- abstract fun addAll(gradeList: List)
+ private const val ORDER_BY = """ORDER BY addedDate DESC"""
+ }
- @Query("DELETE FROM grades WHERE profileId = :profileId")
- abstract fun clear(profileId: Int)
-
- @Query("DELETE FROM grades WHERE profileId = :profileId AND gradeType = :type")
- abstract fun clearWithType(profileId: Int, type: Int)
-
- @Query("DELETE FROM grades WHERE profileId = :profileId AND gradeSemester = :semester")
- abstract fun clearForSemester(profileId: Int, semester: Int)
-
- @Query("DELETE FROM grades WHERE profileId = :profileId AND gradeSemester = :semester AND gradeType = :type")
- abstract fun clearForSemesterWithType(profileId: Int, semester: Int, type: Int)
+ private val selective by lazy { GradeDaoSelective(App.db) }
@RawQuery(observedEntities = [Grade::class])
- abstract fun getAll(query: SupportSQLiteQuery?): LiveData>
+ abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [Grade::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
- fun getAll(profileId: Int, filter: String, orderBy: String): LiveData> {
- return getAll(SimpleSQLiteQuery("SELECT \n" +
- "*, \n" +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
- "FROM grades \n" +
- "LEFT JOIN subjects USING(profileId, subjectId)\n" +
- "LEFT JOIN teachers USING(profileId, teacherId)\n" +
- "LEFT JOIN metadata ON gradeId = thingId AND thingType = " + Metadata.TYPE_GRADE + " AND metadata.profileId = " + profileId + "\n" +
- "WHERE grades.profileId = " + profileId + " AND " + filter + "\n" +
- "ORDER BY " + orderBy)) // TODO: 2019-04-30 why did I add sorting by gradeType???
- }
+ // SELECTIVE UPDATE
+ @UpdateSelective(primaryKeys = ["profileId", "gradeId"], skippedColumns = ["addedDate", "gradeClassAverage"])
+ override fun update(item: Grade) = selective.update(item)
+ override fun updateAll(items: List) = selective.updateAll(items)
- fun getAllOrderBy(profileId: Int, orderBy: String): LiveData> {
- return getAll(profileId, "1", orderBy)
- }
+ // CLEAR
+ @Query("DELETE FROM grades WHERE profileId = :profileId")
+ abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM grades WHERE keep = 0")
+ abstract override fun removeNotKept()
- fun getAllWhere(profileId: Int, filter: String): LiveData> {
- return getAll(profileId, filter, "addedDate DESC")
- }
+ // GET ALL - LIVE DATA
+ fun getAll(profileId: Int) =
+ getRaw("$QUERY WHERE grades.profileId = $profileId $ORDER_BY")
+ fun getAllFromDate(profileId: Int, date: Date) =
+ getRaw("$QUERY WHERE grades.profileId = $profileId AND addedDate > ${date.inMillis} $ORDER_BY")
+ fun getAllBySubject(profileId: Int, subjectId: Long) =
+ getRaw("$QUERY WHERE grades.profileId = $profileId AND subjectId = $subjectId $ORDER_BY")
+ fun getAllOrderBy(profileId: Int, orderBy: String) =
+ getRaw("$QUERY WHERE grades.profileId = $profileId ORDER BY $orderBy")
- @RawQuery
- abstract fun getAllNow(query: SupportSQLiteQuery?): List
+ // GET ALL - NOW
+ fun getAllNow(profileId: Int) =
+ getRawNow("$QUERY WHERE grades.profileId = $profileId $ORDER_BY")
+ fun getNotNotifiedNow() =
+ getRawNow("$QUERY WHERE notified = 0 $ORDER_BY")
+ fun getNotNotifiedNow(profileId: Int) =
+ getRawNow("$QUERY WHERE grades.profileId = $profileId AND notified = 0 $ORDER_BY")
+ fun getByParentIdNow(profileId: Int, parentId: Long) =
+ getRawNow("$QUERY WHERE grades.profileId = $profileId AND gradeParentId = $parentId $ORDER_BY")
- fun getAllNow(profileId: Int, filter: String): List {
- return getAllNow(SimpleSQLiteQuery("SELECT \n" +
- "*, \n" +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
- "FROM grades \n" +
- "LEFT JOIN subjects USING(profileId, subjectId)\n" +
- "LEFT JOIN teachers USING(profileId, teacherId)\n" +
- "LEFT JOIN metadata ON gradeId = thingId AND thingType = " + Metadata.TYPE_GRADE + " AND metadata.profileId = " + profileId + "\n" +
- "WHERE grades.profileId = " + profileId + " AND " + filter + "\n" +
- "ORDER BY addedDate DESC"))
- }
+ // GET ONE - NOW
+ fun getByIdNow(profileId: Int, id: Long) =
+ getOneNow("$QUERY WHERE grades.profileId = $profileId AND gradeId = $id")
- fun getNotNotifiedNow(profileId: Int): List {
- return getAllNow(profileId, "notified = 0")
- }
+ @Query("UPDATE grades SET keep = 0 WHERE profileId = :profileId AND gradeType = :type")
+ abstract fun dontKeepWithType(profileId: Int, type: Int)
- fun getAllWithParentIdNow(profileId: Int, parentId: Long): List {
- return getAllNow(profileId, "gradeParentId = $parentId")
- }
+ @Query("UPDATE grades SET keep = 0 WHERE profileId = :profileId AND gradeSemester = :semester")
+ abstract fun dontKeepForSemester(profileId: Int, semester: Int)
- @get:Query("SELECT * FROM grades " +
- "LEFT JOIN subjects USING(profileId, subjectId) " +
- "LEFT JOIN metadata ON gradeId = thingId AND thingType = " + Metadata.TYPE_GRADE + " AND metadata.profileId = grades.profileId " +
- "WHERE notified = 0 " +
- "ORDER BY addedDate DESC")
- abstract val notNotifiedNow: List
+ @Query("UPDATE grades SET keep = 0 WHERE profileId = :profileId AND gradeSemester = :semester AND gradeType = :type")
+ abstract fun dontKeepForSemesterWithType(profileId: Int, semester: Int, type: Int)
- @RawQuery
- abstract fun getNow(query: SupportSQLiteQuery): GradeFull?
+
+ // GRADE DETAILS - MOBIDZIENNIK
@Query("UPDATE grades SET gradeClassAverage = :classAverage, gradeColor = :color WHERE profileId = :profileId AND gradeId = :gradeId")
abstract fun updateDetailsById(profileId: Int, gradeId: Long, classAverage: Float, color: Int)
- @Query("UPDATE metadata SET addedDate = :addedDate WHERE profileId = :profileId AND thingType = " + Metadata.TYPE_GRADE + " AND thingId = :gradeId")
+ @Query("UPDATE grades SET addedDate = :addedDate WHERE profileId = :profileId AND gradeId = :gradeId")
abstract fun updateAddedDateById(profileId: Int, gradeId: Long, addedDate: Long)
@Transaction
@@ -118,7 +119,7 @@ abstract class GradeDao {
@Query("SELECT gradeColor FROM grades WHERE profileId = :profileId ORDER BY gradeId")
abstract fun getColors(profileId: Int): List
- @Query("SELECT addedDate FROM metadata WHERE profileId = :profileId AND thingType = " + Metadata.TYPE_GRADE + " ORDER BY thingId")
+ @Query("SELECT addedDate FROM grades WHERE profileId = :profileId ORDER BY gradeId")
abstract fun getAddedDates(profileId: Int): List
@Transaction
@@ -134,8 +135,4 @@ abstract class GradeDao {
gradeAddedDates[gradeId] = addedDates.next()
}
}
-
- fun getAllFromDate(profileId: Int, date: Long): LiveData> {
- return getAllWhere(profileId, "addedDate > $date")
- }
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.java
deleted file mode 100644
index c1c33c23..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.java
+++ /dev/null
@@ -1,87 +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.LuckyNumber;
-import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
-import pl.szczodrzynski.edziennik.data.db.entity.Notice;
-import pl.szczodrzynski.edziennik.data.db.full.LuckyNumberFull;
-import pl.szczodrzynski.edziennik.utils.models.Date;
-
-import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_LUCKY_NUMBER;
-
-@Dao
-public abstract class LuckyNumberDao {
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract void add(LuckyNumber luckyNumber);
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract void addAll(List luckyNumberList);
-
- @Query("DELETE FROM luckyNumbers WHERE profileId = :profileId")
- public abstract void clear(int profileId);
-
- @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate = :date")
- public abstract LiveData getByDate(int profileId, Date date);
-
- @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate = :date")
- public abstract LuckyNumber getByDateNow(int profileId, Date date);
-
- @Nullable
- @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1")
- public abstract LuckyNumber getNearestFutureNow(int profileId, int date);
-
- @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1")
- public abstract LiveData getNearestFuture(int profileId, int date);
-
- @RawQuery(observedEntities = {LuckyNumber.class})
- abstract LiveData> getAll(SupportSQLiteQuery query);
- public LiveData> getAll(int profileId, String filter) {
- return getAll(new SimpleSQLiteQuery("SELECT\n" +
- "*\n" +
- "FROM luckyNumbers\n" +
- "LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = "+profileId+"\n" +
- "WHERE luckyNumbers.profileId = "+profileId+" AND "+filter+"\n" +
- "ORDER BY addedDate DESC"));
- }
- public LiveData> getAll(int profileId) {
- return getAll(profileId, "1");
- }
- public LiveData> getAllWhere(int profileId, String filter) {
- return getAll(profileId, filter);
- }
-
- @RawQuery(observedEntities = {Notice.class, Metadata.class})
- abstract List getAllNow(SupportSQLiteQuery query);
- public List getAllNow(int profileId, String filter) {
- return getAllNow(new SimpleSQLiteQuery("SELECT\n" +
- "*\n" +
- "FROM luckyNumbers\n" +
- "LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = "+profileId+"\n" +
- "WHERE luckyNumbers.profileId = "+profileId+" AND "+filter+"\n" +
- "ORDER BY addedDate DESC"));
- }
- public List getNotNotifiedNow(int profileId) {
- return getAllNow(profileId, "notified = 0");
- }
-
- @Query("SELECT * FROM luckyNumbers\n" +
- "LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = luckyNumbers.profileId " +
- "WHERE notified = 0 " +
- "ORDER BY addedDate DESC")
- public abstract List getNotNotifiedNow();
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.kt
new file mode 100644
index 00000000..ad44c0d3
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
+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.LuckyNumber
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.data.db.full.LuckyNumberFull
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+@Dao
+@SelectiveDao(db = AppDb::class)
+abstract class LuckyNumberDao : BaseDao {
+ companion object {
+ private const val QUERY = """
+ SELECT
+ *
+ FROM luckyNumbers
+ LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = ${Metadata.TYPE_LUCKY_NUMBER} AND metadata.profileId = luckyNumbers.profileId
+ """
+
+ private const val ORDER_BY = """ORDER BY luckyNumberDate DESC"""
+ }
+
+ private val selective by lazy { LuckyNumberDaoSelective(App.db) }
+
+ @RawQuery(observedEntities = [LuckyNumber::class])
+ abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [LuckyNumber::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
+
+ // SELECTIVE UPDATE
+ @UpdateSelective(primaryKeys = ["profileId", "luckyNumberDate"], skippedColumns = ["addedDate"])
+ override fun update(item: LuckyNumber) = selective.update(item)
+ override fun updateAll(items: List) = selective.updateAll(items)
+
+ // CLEAR
+ @Query("DELETE FROM luckyNumbers WHERE profileId = :profileId")
+ abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM luckyNumbers WHERE keep = 0")
+ abstract override fun removeNotKept()
+
+ // GET ALL - LIVE DATA
+ fun getAll(profileId: Int) =
+ getRaw("$QUERY WHERE luckyNumbers.profileId = $profileId $ORDER_BY")
+
+ // GET ALL - NOW
+ fun getAllNow(profileId: Int) =
+ getRawNow("$QUERY WHERE luckyNumbers.profileId = $profileId $ORDER_BY")
+ fun getNotNotifiedNow() =
+ getRawNow("$QUERY WHERE notified = 0 $ORDER_BY")
+ fun getNotNotifiedNow(profileId: Int) =
+ getRawNow("$QUERY WHERE luckyNumbers.profileId = $profileId AND notified = 0 $ORDER_BY")
+
+ // GET ONE - LIVE DATA
+ fun getNearestFuture(profileId: Int, today: Date) =
+ getOne("$QUERY WHERE luckyNumbers.profileId = $profileId AND luckyNumberDate >= ${today.value} $ORDER_BY LIMIT 1")
+
+ // GET ONE - NOW
+ fun getByIdNow(profileId: Int, id: Long) =
+ getOneNow("$QUERY WHERE attendances.profileId = $profileId AND attendanceId = $id")
+ fun getNearestFutureNow(profileId: Int, today: Date) =
+ getOneNow("$QUERY WHERE luckyNumbers.profileId = $profileId AND luckyNumberDate >= ${today.value} $ORDER_BY LIMIT 1")
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MessageDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MessageDao.kt
index 606f29a7..a0be36ab 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MessageDao.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MessageDao.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
*/
package pl.szczodrzynski.edziennik.data.db.dao
@@ -36,7 +36,10 @@ abstract class MessageDao : BaseDao {
@RawQuery(observedEntities = [Message::class])
abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [Message::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
+ // SELECTIVE UPDATE
@UpdateSelective(primaryKeys = ["profileId", "messageId"], skippedColumns = ["messageType", "messageBody", "messageIsPinned", "attachmentIds", "attachmentNames", "attachmentSizes"])
override fun update(item: Message) = selective.update(item)
override fun updateAll(items: List) = selective.updateAll(items)
@@ -44,6 +47,9 @@ abstract class MessageDao : BaseDao {
// CLEAR
@Query("DELETE FROM messages WHERE profileId = :profileId")
abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM messages WHERE keep = 0")
+ abstract override fun removeNotKept()
// GET ALL - LIVE DATA
fun getAll(profileId: Int) =
@@ -64,4 +70,6 @@ abstract class MessageDao : BaseDao {
// GET ONE - NOW
fun getByIdNow(profileId: Int, id: Long) =
getOneNow("$QUERY WHERE messages.profileId = $profileId AND messageId = $id")
+
+
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MetadataDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MetadataDao.java
index 7a074bca..eac92f21 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MetadataDao.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MetadataDao.java
@@ -63,37 +63,37 @@ public abstract class MetadataDao {
@Transaction
public void setSeen(int profileId, Object o, boolean seen) {
if (o instanceof Grade) {
- if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), seen, false, 0)) == -1) {
+ if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), seen, false)) == -1) {
updateSeen(profileId, TYPE_GRADE, ((Grade) o).getId(), seen);
}
}
if (o instanceof Attendance) {
- if (add(new Metadata(profileId, TYPE_ATTENDANCE, ((Attendance) o).id, seen, false, 0)) == -1) {
- updateSeen(profileId, TYPE_ATTENDANCE, ((Attendance) o).id, seen);
+ if (add(new Metadata(profileId, TYPE_ATTENDANCE, ((Attendance) o).getId(), seen, false)) == -1) {
+ updateSeen(profileId, TYPE_ATTENDANCE, ((Attendance) o).getId(), seen);
}
}
if (o instanceof Notice) {
- if (add(new Metadata(profileId, TYPE_NOTICE, ((Notice) o).id, seen, false, 0)) == -1) {
- updateSeen(profileId, TYPE_NOTICE, ((Notice) o).id, seen);
+ if (add(new Metadata(profileId, TYPE_NOTICE, ((Notice) o).getId(), seen, false)) == -1) {
+ updateSeen(profileId, TYPE_NOTICE, ((Notice) o).getId(), seen);
}
}
if (o instanceof Event) {
- if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen, false, 0)) == -1) {
+ if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen, false)) == -1) {
updateSeen(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen);
}
}
if (o instanceof LessonFull) {
- if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), seen, false, 0)) == -1) {
+ if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), seen, false)) == -1) {
updateSeen(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), seen);
}
}
if (o instanceof Announcement) {
- if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen, false, 0)) == -1) {
- updateSeen(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen);
+ if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).getId(), seen, false)) == -1) {
+ updateSeen(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).getId(), seen);
}
}
if (o instanceof Message) {
- if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen, false, 0)) == -1) {
+ if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen, false)) == -1) {
updateSeen(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen);
}
}
@@ -102,37 +102,37 @@ public abstract class MetadataDao {
@Transaction
public void setNotified(int profileId, Object o, boolean notified) {
if (o instanceof Grade) {
- if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), false, notified, 0)) == -1) {
+ if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), false, notified)) == -1) {
updateNotified(profileId, TYPE_GRADE, ((Grade) o).getId(), notified);
}
}
if (o instanceof Attendance) {
- if (add(new Metadata(profileId, TYPE_ATTENDANCE, ((Attendance) o).id, false, notified, 0)) == -1) {
- updateNotified(profileId, TYPE_ATTENDANCE, ((Attendance) o).id, notified);
+ if (add(new Metadata(profileId, TYPE_ATTENDANCE, ((Attendance) o).getId(), false, notified)) == -1) {
+ updateNotified(profileId, TYPE_ATTENDANCE, ((Attendance) o).getId(), notified);
}
}
if (o instanceof Notice) {
- if (add(new Metadata(profileId, TYPE_NOTICE, ((Notice) o).id, false, notified, 0)) == -1) {
- updateNotified(profileId, TYPE_NOTICE, ((Notice) o).id, notified);
+ if (add(new Metadata(profileId, TYPE_NOTICE, ((Notice) o).getId(), false, notified)) == -1) {
+ updateNotified(profileId, TYPE_NOTICE, ((Notice) o).getId(), notified);
}
}
if (o instanceof Event) {
- if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), false, notified, 0)) == -1) {
+ if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), false, notified)) == -1) {
updateNotified(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), notified);
}
}
if (o instanceof LessonFull) {
- if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), false, notified, 0)) == -1) {
+ if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), false, notified)) == -1) {
updateNotified(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), notified);
}
}
if (o instanceof Announcement) {
- if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, false, notified, 0)) == -1) {
- updateNotified(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, notified);
+ if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).getId(), false, notified)) == -1) {
+ updateNotified(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).getId(), notified);
}
}
if (o instanceof Message) {
- if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), false, notified, 0)) == -1) {
+ if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), false, notified)) == -1) {
updateNotified(profileId, TYPE_MESSAGE, ((Message) o).getId(), notified);
}
}
@@ -141,7 +141,7 @@ public abstract class MetadataDao {
@Transaction
public void setBoth(int profileId, Event o, boolean seen, boolean notified, long addedDate) {
if (o != null) {
- if (add(new Metadata(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen, notified, addedDate)) == -1) {
+ if (add(new Metadata(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen, notified)) == -1) {
updateSeen(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen);
updateNotified(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), notified);
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.java
deleted file mode 100644
index 110544e4..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.java
+++ /dev/null
@@ -1,82 +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 androidx.room.RawQuery;
-import androidx.sqlite.db.SimpleSQLiteQuery;
-import androidx.sqlite.db.SupportSQLiteQuery;
-
-import java.util.List;
-
-import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
-import pl.szczodrzynski.edziennik.data.db.entity.Notice;
-import pl.szczodrzynski.edziennik.data.db.full.NoticeFull;
-
-import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_NOTICE;
-
-@Dao
-public abstract class NoticeDao {
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract long add(Notice notice);
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- public abstract void addAll(List noticeList);
-
- @Query("DELETE FROM notices WHERE profileId = :profileId")
- public abstract void clear(int profileId);
-
- @Query("DELETE FROM notices WHERE profileId = :profileId AND noticeSemester = :semester")
- public abstract void clearForSemester(int profileId, int semester);
-
- @RawQuery(observedEntities = {Notice.class})
- abstract LiveData> getAll(SupportSQLiteQuery query);
- public LiveData> getAll(int profileId, String filter) {
- return getAll(new SimpleSQLiteQuery("SELECT \n" +
- "*, \n" +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
- "FROM notices \n" +
- "LEFT JOIN teachers USING(profileId, teacherId)\n" +
- "LEFT JOIN metadata ON noticeId = thingId AND thingType = "+TYPE_NOTICE+" AND metadata.profileId = "+profileId+"\n" +
- "WHERE notices.profileId = "+profileId+" AND "+filter+"\n" +
- "ORDER BY addedDate DESC"));
- }
- public LiveData> getAll(int profileId) {
- return getAll(profileId, "1");
- }
- public LiveData> getAllWhere(int profileId, String filter) {
- return getAll(profileId, filter);
- }
-
- @RawQuery(observedEntities = {Notice.class, Metadata.class})
- abstract List getAllNow(SupportSQLiteQuery query);
- public List getAllNow(int profileId, String filter) {
- return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
- "*, \n" +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
- "FROM notices \n" +
- "LEFT JOIN teachers USING(profileId, teacherId)\n" +
- "LEFT JOIN metadata ON noticeId = thingId AND thingType = "+TYPE_NOTICE+" AND metadata.profileId = "+profileId+"\n" +
- "WHERE notices.profileId = "+profileId+" AND "+filter+"\n" +
- "ORDER BY addedDate DESC"));
- }
- public List getNotNotifiedNow(int profileId) {
- return getAllNow(profileId, "notified = 0");
- }
-
- @Query("SELECT " +
- "*, " +
- "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName " +
- "FROM notices " +
- "LEFT JOIN teachers USING(profileId, teacherId) " +
- "LEFT JOIN metadata ON noticeId = thingId AND thingType = "+TYPE_NOTICE+" AND metadata.profileId = notices.profileId " +
- "WHERE notified = 0 " +
- "ORDER BY addedDate DESC")
- public abstract List getNotNotifiedNow();
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.kt
new file mode 100644
index 00000000..79bae175
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
+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.Metadata
+import pl.szczodrzynski.edziennik.data.db.entity.Notice
+import pl.szczodrzynski.edziennik.data.db.full.NoticeFull
+
+@Dao
+@SelectiveDao(db = AppDb::class)
+abstract class NoticeDao : BaseDao {
+ companion object {
+ private const val QUERY = """
+ SELECT
+ *,
+ teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName
+ FROM notices
+ LEFT JOIN teachers USING(profileId, teacherId)
+ LEFT JOIN metadata ON noticeId = thingId AND thingType = ${Metadata.TYPE_NOTICE} AND metadata.profileId = notices.profileId
+ """
+
+ private const val ORDER_BY = """ORDER BY addedDate DESC"""
+ }
+
+ private val selective by lazy { NoticeDaoSelective(App.db) }
+
+ @RawQuery(observedEntities = [Notice::class])
+ abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [Notice::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
+
+ // SELECTIVE UPDATE
+ @UpdateSelective(primaryKeys = ["profileId", "noticeId"], skippedColumns = ["addedDate"])
+ override fun update(item: Notice) = selective.update(item)
+ override fun updateAll(items: List) = selective.updateAll(items)
+
+ // CLEAR
+ @Query("DELETE FROM notices WHERE profileId = :profileId")
+ abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM notices WHERE keep = 0")
+ abstract override fun removeNotKept()
+
+ // GET ALL - LIVE DATA
+ fun getAll(profileId: Int) =
+ getRaw("$QUERY WHERE notices.profileId = $profileId $ORDER_BY")
+
+ // GET ALL - NOW
+ fun getAllNow(profileId: Int) =
+ getRawNow("$QUERY WHERE notices.profileId = $profileId $ORDER_BY")
+ fun getNotNotifiedNow() =
+ getRawNow("$QUERY WHERE notified = 0 $ORDER_BY")
+ fun getNotNotifiedNow(profileId: Int) =
+ getRawNow("$QUERY WHERE notices.profileId = $profileId AND notified = 0 $ORDER_BY")
+
+ // GET ONE - NOW
+ fun getByIdNow(profileId: Int, id: Long) =
+ getOneNow("$QUERY WHERE notices.profileId = $profileId AND noticeId = $id")
+
+ @Query("UPDATE notices SET keep = 0 WHERE profileId = :profileId AND noticeSemester = :semester")
+ abstract fun dontKeepSemester(profileId: Int, semester: Int)
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TeacherAbsenceDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TeacherAbsenceDao.kt
index 1d4881bb..1528adf1 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TeacherAbsenceDao.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TeacherAbsenceDao.kt
@@ -1,65 +1,74 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
*/
-
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 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.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.TeacherAbsence
import pl.szczodrzynski.edziennik.data.db.full.TeacherAbsenceFull
import pl.szczodrzynski.edziennik.utils.models.Date
@Dao
-interface TeacherAbsenceDao {
+@SelectiveDao(db = AppDb::class)
+abstract class TeacherAbsenceDao : BaseDao {
+ companion object {
+ private const val QUERY = """
+ SELECT
+ *,
+ teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName
+ FROM teacherAbsence
+ LEFT JOIN teachers USING(profileId, teacherId)
+ LEFT JOIN metadata ON teacherAbsenceId = thingId AND thingType = ${Metadata.TYPE_TEACHER_ABSENCE} AND metadata.profileId = teacherAbsence.profileId
+ """
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- fun add(teacherAbsence: TeacherAbsence)
+ private const val ORDER_BY = """ORDER BY teacherAbsenceDateFrom ASC"""
+ }
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- fun addAll(teacherAbsenceList: List)
+ private val selective by lazy { TeacherAbsenceDaoSelective(App.db) }
- @Query("SELECT * FROM teacherAbsence WHERE profileId = :profileId")
- fun getAll(profileId: Int): List
+ @RawQuery(observedEntities = [TeacherAbsence::class])
+ abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [TeacherAbsence::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
- @Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " +
- "metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " +
- "LEFT JOIN teachers USING (profileId, teacherId) " +
- "LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE +
- " AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId")
- fun getAllFullNow(profileId: Int): List
-
- @Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " +
- "metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " +
- "LEFT JOIN teachers USING (profileId, teacherId) " +
- "LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE +
- " AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId " +
- "AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo")
- fun getAllByDateFull(profileId: Int, date: Date): LiveData>
-
- @Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " +
- "metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " +
- "LEFT JOIN teachers USING (profileId, teacherId) " +
- "LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE +
- " AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId " +
- "AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo")
- fun getAllByDateNow(profileId: Int, date: Date): List
-
- @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
+ // SELECTIVE UPDATE
+ @UpdateSelective(primaryKeys = ["profileId", "teacherAbsenceId"], skippedColumns = ["addedDate"])
+ override fun update(item: TeacherAbsence) = selective.update(item)
+ override fun updateAll(items: List) = selective.updateAll(items)
+ // CLEAR
@Query("DELETE FROM teacherAbsence WHERE profileId = :profileId")
- fun clear(profileId: Int)
+ abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM teacherAbsence WHERE keep = 0")
+ abstract override fun removeNotKept()
+
+ // GET ALL - LIVE DATA
+ fun getAll(profileId: Int) =
+ getRaw("$QUERY WHERE teacherAbsence.profileId = $profileId $ORDER_BY")
+ fun getAllByDate(profileId: Int, date: Date) =
+ getRaw("$QUERY WHERE teacherAbsence.profileId = $profileId AND '${date.stringY_m_d}' BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo $ORDER_BY")
+
+ // GET ALL - NOW
+ fun getAllNow(profileId: Int) =
+ getRawNow("$QUERY WHERE teacherAbsence.profileId = $profileId $ORDER_BY")
+ fun getNotNotifiedNow() =
+ getRawNow("$QUERY WHERE notified = 0 $ORDER_BY")
+ fun getNotNotifiedNow(profileId: Int) =
+ getRawNow("$QUERY WHERE teacherAbsence.profileId = $profileId AND notified = 0 $ORDER_BY")
+ fun getAllByDateNow(profileId: Int, date: Date) =
+ getRawNow("$QUERY WHERE teacherAbsence.profileId = $profileId AND '${date.stringY_m_d}' BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo $ORDER_BY")
+
+ // GET ONE - NOW
+ fun getByIdNow(profileId: Int, id: Long) =
+ getOneNow("$QUERY WHERE teacherAbsence.profileId = $profileId AND teacherAbsenceId = $id")
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TimetableDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TimetableDao.kt
index 9ac9709b..cc236dc6 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TimetableDao.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TimetableDao.kt
@@ -1,20 +1,25 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
*/
-
package pl.szczodrzynski.edziennik.data.db.dao
import androidx.lifecycle.LiveData
-import androidx.room.*
-import androidx.sqlite.db.SimpleSQLiteQuery
+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.Lesson
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.utils.models.Date
@Dao
-interface TimetableDao {
+@SelectiveDao(db = AppDb::class)
+abstract class TimetableDao : BaseDao {
companion object {
private const val QUERY = """
SELECT
@@ -25,7 +30,7 @@ interface TimetableDao {
oldS.subjectLongName AS oldSubjectName,
oldT.teacherName ||" "|| oldT.teacherSurname AS oldTeacherName,
oldG.teamName AS oldTeamName,
- metadata.seen, metadata.notified, metadata.addedDate
+ metadata.seen, metadata.notified
FROM timetable
LEFT JOIN subjects USING(profileId, subjectId)
LEFT JOIN teachers USING(profileId, teacherId)
@@ -35,111 +40,77 @@ interface TimetableDao {
LEFT JOIN teams AS oldG ON timetable.profileId = oldG.profileId AND timetable.oldTeamId = oldG.teamId
LEFT JOIN metadata ON id = thingId AND thingType = ${Metadata.TYPE_LESSON_CHANGE} AND metadata.profileId = timetable.profileId
"""
+
+ private const val ORDER_BY = """ORDER BY profileId, id, type"""
+ private const val IS_CHANGED = """type != -1 AND type != 0"""
}
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- operator fun plusAssign(lessonList: List)
-
- @Query("DELETE FROM timetable WHERE profileId = :profileId")
- fun clear(profileId: Int)
-
- @Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom))")
- fun clearFromDate(profileId: Int, dateFrom: Date)
-
- @Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate <= :dateTo))")
- fun clearToDate(profileId: Int, dateTo: Date)
-
- @Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo))")
- fun clearBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date)
+ private val selective by lazy { TimetableDaoSelective(App.db) }
@RawQuery(observedEntities = [Lesson::class])
- fun getRaw(query: SupportSQLiteQuery): LiveData>
+ abstract override fun getRaw(query: SupportSQLiteQuery): LiveData>
+ @RawQuery(observedEntities = [Lesson::class])
+ abstract override fun getOne(query: SupportSQLiteQuery): LiveData
- @Query("""
- $QUERY
- WHERE timetable.profileId = :profileId AND type != -1 AND type != 0
- ORDER BY id, type
- """)
- fun getAllChangesNow(profileId: Int): List
+ // SELECTIVE UPDATE
+ @UpdateSelective(primaryKeys = ["profileId", "id"], skippedColumns = ["addedDate"])
+ override fun update(item: Lesson) = selective.update(item)
+ override fun updateAll(items: List) = selective.updateAll(items)
- @Query("""
- $QUERY
- WHERE timetable.profileId = :profileId AND type != -1 AND type != 0 AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
- ORDER BY id, type
- """)
- fun getChangesForDateNow(profileId: Int, date: Date): List
+ // CLEAR
+ @Query("DELETE FROM timetable WHERE profileId = :profileId")
+ abstract override fun clear(profileId: Int)
+ // REMOVE NOT KEPT
+ @Query("DELETE FROM timetable WHERE keep = 0")
+ abstract override fun removeNotKept()
- fun getForDate(profileId: Int, date: Date) = getRaw(SimpleSQLiteQuery("""
- $QUERY
- WHERE timetable.profileId = $profileId AND ((type != 3 AND date = "${date.stringY_m_d}") OR ((type = 3 OR type = 1) AND oldDate = "${date.stringY_m_d}"))
- ORDER BY id, type
- """))
+ // GET ALL - LIVE DATA
+ fun getAll(profileId: Int) =
+ getRaw("$QUERY WHERE timetable.profileId = $profileId $ORDER_BY")
+ fun getAllForDate(profileId: Int, date: Date) =
+ getRaw("$QUERY WHERE timetable.profileId = $profileId AND ((type != 3 AND date = '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate = '${date.stringY_m_d}')) $ORDER_BY")
+ fun getNextWithSubject(profileId: Int, date: Date, subjectId: Long) =
+ getOne("$QUERY " +
+ "WHERE timetable.profileId = $profileId " +
+ "AND ((type != 3 AND date > '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate > '${date.stringY_m_d}')) " +
+ "AND timetable.subjectId = $subjectId " +
+ "LIMIT 1")
+ fun getNextWithSubjectAndTeam(profileId: Int, date: Date, subjectId: Long, teamId: Long) =
+ getOne("$QUERY " +
+ "WHERE timetable.profileId = $profileId " +
+ "AND ((type != 3 AND date > '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate > '${date.stringY_m_d}')) " +
+ "AND timetable.subjectId = $subjectId " +
+ "AND timetable.teamId = $teamId " +
+ "LIMIT 1")
+ fun getBetweenDates(dateFrom: Date, dateTo: Date) =
+ getRaw("$QUERY WHERE (type != 3 AND date >= '${dateFrom.stringY_m_d}' AND date <= '${dateTo.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate >= '${dateFrom.stringY_m_d}' AND oldDate <= '${dateTo.stringY_m_d}') $ORDER_BY")
- @Query("""
- $QUERY
- WHERE timetable.profileId = :profileId AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
- ORDER BY id, type
- """)
- fun getForDateNow(profileId: Int, date: Date): List
+ // GET ALL - NOW
+ fun getAllNow(profileId: Int) =
+ getRawNow("$QUERY WHERE timetable.profileId = $profileId $ORDER_BY")
+ fun getNotNotifiedNow() =
+ getRawNow("$QUERY WHERE notified = 0 AND timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) $ORDER_BY")
+ fun getNotNotifiedNow(profileId: Int) =
+ getRawNow("$QUERY WHERE timetable.profileId = $profileId AND notified = 0 AND timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) $ORDER_BY")
+ fun getAllForDateNow(profileId: Int, date: Date) =
+ getRawNow("$QUERY WHERE timetable.profileId = $profileId AND ((type != 3 AND date = '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate = '${date.stringY_m_d}')) $ORDER_BY")
+ fun getChangesNow(profileId: Int) =
+ getRawNow("$QUERY WHERE timetable.profileId = $profileId AND $IS_CHANGED $ORDER_BY")
+ fun getChangesForDateNow(profileId: Int, date: Date) =
+ getRawNow("$QUERY WHERE timetable.profileId = $profileId AND $IS_CHANGED AND ((type != 3 AND date = '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate = '${date.stringY_m_d}')) $ORDER_BY")
+ fun getBetweenDatesNow(dateFrom: Date, dateTo: Date) =
+ getRawNow("$QUERY WHERE (type != 3 AND date >= '${dateFrom.stringY_m_d}' AND date <= '${dateTo.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate >= '${dateFrom.stringY_m_d}' AND oldDate <= '${dateTo.stringY_m_d}') $ORDER_BY")
- @Query("""
- $QUERY
- WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId
- ORDER BY id, type
- LIMIT 1
- """)
- fun getNextWithSubject(profileId: Int, today: Date, subjectId: Long): LiveData
+ // GET ONE - NOW
+ fun getByIdNow(profileId: Int, id: Long) =
+ getOneNow("$QUERY WHERE timetable.profileId = $profileId AND timetable.id = $id")
- @Query("""
- $QUERY
- WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId AND timetable.teamId = :teamId
- ORDER BY id, type
- LIMIT 1
- """)
- fun getNextWithSubjectAndTeam(profileId: Int, today: Date, subjectId: Long, teamId: Long): LiveData
+ @Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom))")
+ abstract fun dontKeepFromDate(profileId: Int, dateFrom: Date)
- @Query("""
- $QUERY
- WHERE (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo)
- ORDER BY profileId, id, type
- """)
- fun getBetweenDatesNow(dateFrom: Date, dateTo: Date): List
+ @Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate <= :dateTo))")
+ abstract fun dontKeepToDate(profileId: Int, dateTo: Date)
- @Query("""
- $QUERY
- WHERE (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo)
- ORDER BY profileId, id, type
- """)
- fun getBetweenDates(dateFrom: Date, dateTo: Date): LiveData>
-
- @Query("""
- $QUERY
- WHERE timetable.profileId = :profileId AND timetable.id = :lessonId
- ORDER BY id, type
- """)
- fun getByIdNow(profileId: Int, lessonId: Long): LessonFull?
-
- @Query("""
- $QUERY
- WHERE timetable.profileId = :profileId AND timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) AND metadata.notified = 0
- """)
- fun getNotNotifiedNow(profileId: Int): List
-
- @Query("""
- SELECT
- timetable.*,
- subjects.subjectLongName AS subjectName,
- teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName,
- oldS.subjectLongName AS oldSubjectName,
- oldT.teacherName ||" "|| oldT.teacherSurname AS oldTeacherName,
- metadata.seen, metadata.notified, metadata.addedDate
- FROM timetable
- LEFT JOIN subjects USING(profileId, subjectId)
- LEFT JOIN teachers USING(profileId, teacherId)
- LEFT JOIN subjects AS oldS ON timetable.profileId = oldS.profileId AND timetable.oldSubjectId = oldS.subjectId
- LEFT JOIN teachers AS oldT ON timetable.profileId = oldT.profileId AND timetable.oldTeacherId = oldT.teacherId
- LEFT JOIN metadata ON id = thingId AND thingType = ${Metadata.TYPE_LESSON_CHANGE} AND metadata.profileId = timetable.profileId
- WHERE timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) AND metadata.notified = 0
- """)
- fun getNotNotifiedNow(): List
+ @Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo))")
+ abstract fun dontKeepBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date)
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.java
deleted file mode 100644
index af17e0c3..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
- */
-
-package pl.szczodrzynski.edziennik.data.db.entity;
-
-import androidx.annotation.Nullable;
-import androidx.room.ColumnInfo;
-import androidx.room.Entity;
-import androidx.room.Ignore;
-import androidx.room.Index;
-
-import pl.szczodrzynski.edziennik.utils.models.Date;
-
-@Entity(tableName = "announcements",
- primaryKeys = {"profileId", "announcementId"},
- indices = {@Index(value = {"profileId"})})
-public class Announcement {
- public int profileId;
-
- @ColumnInfo(name = "announcementId")
- public long id;
-
- @ColumnInfo(name = "announcementSubject")
- public String subject;
- @Nullable
- @ColumnInfo(name = "announcementText")
- public String text;
- @Nullable
- @ColumnInfo(name = "announcementStartDate")
- public Date startDate;
- @Nullable
- @ColumnInfo(name = "announcementEndDate")
- public Date endDate;
-
- public long teacherId;
-
- @Nullable
- @ColumnInfo(name = "announcementIdString")
- public String idString;
-
- @Ignore
- public Announcement() {}
-
- public Announcement(int profileId, long id, String subject, String text, @Nullable Date startDate, @Nullable Date endDate, long teacherId, @Nullable String idString) {
- this.profileId = profileId;
- this.id = id;
- this.subject = subject;
- this.text = text;
- this.startDate = startDate;
- this.endDate = endDate;
- this.teacherId = teacherId;
- this.idString = idString;
- }
-}
-
-
-
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.kt
new file mode 100644
index 00000000..82f7bb78
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-24.
+ */
+package pl.szczodrzynski.edziennik.data.db.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.Index
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+@Entity(tableName = "announcements",
+ primaryKeys = ["profileId", "announcementId"],
+ indices = [
+ Index(value = ["profileId"])
+ ])
+open class Announcement(
+ val profileId: Int,
+ @ColumnInfo(name = "announcementId")
+ var id: Long,
+ @ColumnInfo(name = "announcementSubject")
+ var subject: String,
+ @ColumnInfo(name = "announcementText")
+ var text: String?,
+
+ @ColumnInfo(name = "announcementStartDate")
+ var startDate: Date?,
+ @ColumnInfo(name = "announcementEndDate")
+ var endDate: Date?,
+
+ var teacherId: Long,
+ var addedDate: Long = System.currentTimeMillis()
+) : Keepable() {
+
+ @ColumnInfo(name = "announcementIdString")
+ var idString: String? = null
+
+ @Ignore
+ var showAsUnseen: Boolean? = null
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.java
deleted file mode 100644
index 039dcb0c..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
- */
-
-package pl.szczodrzynski.edziennik.data.db.entity;
-
-import androidx.annotation.NonNull;
-import androidx.room.ColumnInfo;
-import androidx.room.Entity;
-import androidx.room.Ignore;
-import androidx.room.Index;
-
-import pl.szczodrzynski.edziennik.utils.models.Date;
-import pl.szczodrzynski.edziennik.utils.models.Time;
-
-@Entity(tableName = "attendances",
- primaryKeys = {"profileId", "attendanceId", "attendanceLessonDate", "attendanceStartTime"},
- indices = {@Index(value = {"profileId"})})
-public class Attendance {
- public int profileId;
-
- @ColumnInfo(name = "attendanceId")
- public long id;
-
- @NonNull
- @ColumnInfo(name = "attendanceLessonDate")
- public Date lessonDate;
- @NonNull
- @ColumnInfo(name = "attendanceStartTime")
- public Time startTime;
- @ColumnInfo(name = "attendanceLessonTopic")
- public String lessonTopic;
- @ColumnInfo(name = "attendanceSemester")
- public int semester;
- public static final int TYPE_PRESENT = 0;
- public static final int TYPE_ABSENT = 1;
- public static final int TYPE_ABSENT_EXCUSED = 2;
- public static final int TYPE_RELEASED = 3;
- public static final int TYPE_BELATED = 4;
- public static final int TYPE_BELATED_EXCUSED = 5;
- public static final int TYPE_CUSTOM = 6;
- public static final int TYPE_DAY_FREE = 7;
- @ColumnInfo(name = "attendanceType")
- public int type = TYPE_PRESENT;
-
- public long teacherId;
- public long subjectId;
-
- @Ignore
- public Attendance() {
- this(-1, -1, -1, -1, 0, "", Date.getToday(), Time.getNow(), TYPE_PRESENT);
- }
-
- public Attendance(int profileId, long id, long teacherId, long subjectId, int semester, String lessonTopic, Date lessonDate, Time startTime, int type) {
- this.profileId = profileId;
- this.id = id;
- this.teacherId = teacherId;
- this.subjectId = subjectId;
- this.semester = semester;
- this.lessonTopic = lessonTopic;
- this.lessonDate = lessonDate;
- this.startTime = startTime;
- this.type = type;
- }
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt
new file mode 100644
index 00000000..b2ebb5a0
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-24.
+ */
+package pl.szczodrzynski.edziennik.data.db.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.Index
+import pl.szczodrzynski.edziennik.utils.models.Date
+import pl.szczodrzynski.edziennik.utils.models.Time
+
+@Entity(tableName = "attendances",
+ primaryKeys = ["profileId", "attendanceId"],
+ indices = [
+ Index(value = ["profileId"])
+ ])
+open class Attendance(
+ val profileId: Int,
+ @ColumnInfo(name = "attendanceId")
+ var id: Long,
+ /** Base type ID used to count attendance stats */
+ @ColumnInfo(name = "attendanceBaseType")
+ var baseType: Int,
+ /** A full type name coming from the e-register */
+ @ColumnInfo(name = "attendanceTypeName")
+ var typeName: String,
+ /** A short name to display by default, might be different for non-standard types */
+ @ColumnInfo(name = "attendanceTypeShort")
+ val typeShort: String,
+ /** A short name that the e-register would display */
+ @ColumnInfo(name = "attendanceTypeSymbol")
+ var typeSymbol: String,
+ /** A color that the e-register would display, null falls back to app's default */
+ @ColumnInfo(name = "attendanceTypeColor")
+ var typeColor: Int?,
+
+ @ColumnInfo(name = "attendanceDate")
+ var date: Date,
+ @ColumnInfo(name = "attendanceTime")
+ var startTime: Time?,
+ @ColumnInfo(name = "attendanceSemester")
+ var semester: Int,
+
+ var teacherId: Long,
+ var subjectId: Long,
+ var addedDate: Long = System.currentTimeMillis()
+) : Keepable() {
+ companion object {
+ const val TYPE_UNKNOWN = -1 // #3f51b5
+ const val TYPE_PRESENT = 0 // #009688
+ const val TYPE_PRESENT_CUSTOM = 10 // count as presence AND show in the list + custom color, fallback: #3f51b5
+ const val TYPE_ABSENT = 1 // #ff3d00
+ const val TYPE_ABSENT_EXCUSED = 2 // #76ff03
+ const val TYPE_RELEASED = 3 // #9e9e9e
+ const val TYPE_BELATED = 4 // #ffc107
+ const val TYPE_BELATED_EXCUSED = 5 // #ffc107
+ const val TYPE_DAY_FREE = 6 // #43a047
+
+ // attendance bar order:
+ // day_free, present, present_custom, unknown, belated_excused, belated, released, absent_excused, absent,
+ }
+
+ @ColumnInfo(name = "attendanceLessonTopic")
+ var lessonTopic: String? = null
+ @ColumnInfo(name = "attendanceLessonNumber")
+ var lessonNumber: Int? = null
+ @ColumnInfo(name = "attendanceIsCounted")
+ var isCounted: Boolean = true
+
+ @Ignore
+ var showAsUnseen: Boolean? = null
+
+ @delegate:Ignore
+ val typeObject by lazy {
+ AttendanceType(profileId, baseType.toLong(), baseType, typeName, typeShort, typeSymbol, typeColor).also { it.isCounted = isCounted }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt
index 0e3e86f9..c5fccc4b 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt
@@ -5,19 +5,55 @@
package pl.szczodrzynski.edziennik.data.db.entity
import androidx.room.Entity
+import androidx.room.Ignore
@Entity(tableName = "attendanceTypes",
primaryKeys = ["profileId", "id"])
data class AttendanceType (
-
val profileId: Int,
-
val id: Long,
+ /** Base type ID used to count attendance stats */
+ val baseType: Int,
+ /** A full type name coming from the e-register */
+ val typeName: String,
+ /** A short name to display by default, might be different for non-standard types */
+ val typeShort: String,
+ /** A short name that the e-register would display */
+ val typeSymbol: String,
+ /** A color that the e-register would display, null falls back to app's default */
+ val typeColor: Int?
+) : Comparable {
- val name: String,
+ @Ignore
+ var isCounted: Boolean = true
- val type: Int,
-
- val color: Int
-
-)
+ // attendance bar order:
+ // day_free, present, present_custom, unknown, belated_excused, belated, released, absent_excused, absent,
+ override fun compareTo(other: AttendanceType): Int {
+ val type1 = when (baseType) {
+ Attendance.TYPE_DAY_FREE -> 0
+ Attendance.TYPE_PRESENT -> 1
+ Attendance.TYPE_PRESENT_CUSTOM -> 2
+ Attendance.TYPE_UNKNOWN -> 3
+ Attendance.TYPE_BELATED_EXCUSED -> 4
+ Attendance.TYPE_BELATED -> 5
+ Attendance.TYPE_RELEASED -> 6
+ Attendance.TYPE_ABSENT_EXCUSED -> 7
+ Attendance.TYPE_ABSENT -> 8
+ else -> 9
+ }
+ val type2 = when (other.baseType) {
+ Attendance.TYPE_DAY_FREE -> 0
+ Attendance.TYPE_PRESENT -> 1
+ Attendance.TYPE_PRESENT_CUSTOM -> 2
+ Attendance.TYPE_UNKNOWN -> 3
+ Attendance.TYPE_BELATED_EXCUSED -> 4
+ Attendance.TYPE_BELATED -> 5
+ Attendance.TYPE_RELEASED -> 6
+ Attendance.TYPE_ABSENT_EXCUSED -> 7
+ Attendance.TYPE_ABSENT -> 8
+ else -> 9
+ }
+ return type1 - type2
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt
index b811ff61..9d13e883 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt
@@ -41,7 +41,8 @@ open class Event(
var teacherId: Long,
var subjectId: Long,
- var teamId: Long
+ var teamId: Long,
+ var addedDate: Long = System.currentTimeMillis()
) : Keepable() {
companion object {
const val TYPE_UNDEFINED = -2L
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt
index 393ab332..5bf3bd86 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-24.
*/
package pl.szczodrzynski.edziennik.data.db.entity
@@ -8,23 +8,11 @@ import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.Index
-/*public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) {
- this.profileId = profileId;
- this.id = id;
- this.category = category;
- this.color = color;
- this.description = description;
- this.name = name;
- this.value = value;
- this.weight = weight;
- this.semester = semester;
- this.teacherId = teacherId;
- this.subjectId = subjectId;
- }*/
-
@Entity(tableName = "grades",
primaryKeys = ["profileId", "gradeId"],
- indices = [Index(value = ["profileId"])])
+ indices = [
+ Index(value = ["profileId"])
+ ])
open class Grade(
val profileId: Int,
@ColumnInfo(name = "gradeId")
@@ -40,6 +28,7 @@ open class Grade(
var weight: Float,
@ColumnInfo(name = "gradeColor")
var color: Int,
+
@ColumnInfo(name = "gradeCategory")
var category: String?,
@ColumnInfo(name = "gradeDescription")
@@ -50,8 +39,9 @@ open class Grade(
@ColumnInfo(name = "gradeSemester")
val semester: Int,
val teacherId: Long,
- val subjectId: Long
-) {
+ val subjectId: Long,
+ var addedDate: Long = System.currentTimeMillis()
+) : Keepable() {
companion object {
const val TYPE_NORMAL = 0
const val TYPE_SEMESTER1_PROPOSED = 1
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Lesson.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Lesson.kt
index ceb6cf9c..0c97ba24 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Lesson.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Lesson.kt
@@ -1,7 +1,6 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
*/
-
package pl.szczodrzynski.edziennik.data.db.entity
import androidx.room.Entity
@@ -11,12 +10,15 @@ import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@Entity(tableName = "timetable",
+ primaryKeys = ["profileId", "id"],
indices = [
Index(value = ["profileId", "type", "date"]),
Index(value = ["profileId", "type", "oldDate"])
- ],
- primaryKeys = ["profileId", "id"])
-open class Lesson(val profileId: Int, var id: Long) {
+ ])
+open class Lesson(
+ val profileId: Int,
+ var id: Long
+) : Keepable() {
companion object {
const val TYPE_NO_LESSONS = -1
const val TYPE_NORMAL = 0
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.java
deleted file mode 100644
index bca49ec5..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
- */
-
-package pl.szczodrzynski.edziennik.data.db.entity;
-
-import androidx.annotation.NonNull;
-import androidx.room.ColumnInfo;
-import androidx.room.Entity;
-import androidx.room.Ignore;
-
-import pl.szczodrzynski.edziennik.utils.models.Date;
-
-@Entity(tableName = "luckyNumbers",
- primaryKeys = {"profileId", "luckyNumberDate"})
-public class LuckyNumber {
- public int profileId;
-
- @NonNull
- @ColumnInfo(name = "luckyNumberDate", typeAffinity = 3)
- public Date date;
- @ColumnInfo(name = "luckyNumber")
- public int number;
-
- public LuckyNumber(int profileId, @NonNull Date date, int number) {
- this.profileId = profileId;
- this.date = date;
- this.number = number;
- }
-
- @Ignore
- public LuckyNumber() {
- this.date = Date.getToday();
- }
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.kt
new file mode 100644
index 00000000..6fb943c9
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-24.
+ */
+package pl.szczodrzynski.edziennik.data.db.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Ignore
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+@Entity(tableName = "luckyNumbers",
+ primaryKeys = ["profileId", "luckyNumberDate"])
+open class LuckyNumber(
+ val profileId: Int,
+ @ColumnInfo(name = "luckyNumberDate", typeAffinity = ColumnInfo.INTEGER)
+ var date: Date,
+ @ColumnInfo(name = "luckyNumber")
+ var number: Int
+) : Keepable() {
+ @Ignore
+ var showAsUnseen = false
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Message.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Message.kt
index 1cf9b3ba..98105057 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Message.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Message.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
*/
package pl.szczodrzynski.edziennik.data.db.entity
@@ -30,7 +30,8 @@ open class Message(
* Keep in mind that this being null does NOT
* necessarily mean the message is sent.
*/
- var senderId: Long?
+ var senderId: Long?,
+ var addedDate: Long = System.currentTimeMillis()
) : Keepable() {
companion object {
const val TYPE_RECEIVED = 0
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Metadata.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Metadata.java
index 6ea94f4e..7374d915 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Metadata.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Metadata.java
@@ -36,7 +36,6 @@ public class Metadata {
public boolean seen;
public boolean notified;
- public long addedDate;
@Ignore
public Metadata() {
@@ -45,13 +44,12 @@ public class Metadata {
this.notified = false;
}
- public Metadata(int profileId, int thingType, long thingId, boolean seen, boolean notified, long addedDate) {
+ public Metadata(int profileId, int thingType, long thingId, boolean seen, boolean notified) {
this.profileId = profileId;
this.thingType = thingType;
this.thingId = thingId;
this.seen = seen;
this.notified = notified;
- this.addedDate = addedDate;
}
public String thingType() {
@@ -86,7 +84,6 @@ public class Metadata {
", thingId=" + thingId +
", seen=" + seen +
", notified=" + notified +
- ", addedDate=" + addedDate +
'}';
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.java
deleted file mode 100644
index 306e6f92..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
- */
-
-package pl.szczodrzynski.edziennik.data.db.entity;
-
-import androidx.room.ColumnInfo;
-import androidx.room.Entity;
-import androidx.room.Ignore;
-import androidx.room.Index;
-
-@Entity(tableName = "notices",
- primaryKeys = {"profileId", "noticeId"},
- indices = {@Index(value = {"profileId"})})
-public class Notice {
- public int profileId;
-
- @ColumnInfo(name = "noticeId")
- public long id;
-
- @ColumnInfo(name = "noticeText")
- public String text;
- @ColumnInfo(name = "noticeSemester")
- public int semester;
- public static final int TYPE_NEUTRAL = 0;
- public static final int TYPE_POSITIVE = 1;
- public static final int TYPE_NEGATIVE = 2;
- @ColumnInfo(name = "noticeType")
- public int type = TYPE_NEUTRAL;
-
- public float points = 0;
- public String category = null;
-
- public long teacherId;
-
- @Ignore
- public Notice() {}
-
- public Notice(int profileId, long id, String text, int semester, int type, long teacherId) {
- this.profileId = profileId;
- this.id = id;
- this.text = text;
- this.semester = semester;
- this.type = type;
- this.teacherId = teacherId;
- }
-}
-
-
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.kt
new file mode 100644
index 00000000..0e1f21fe
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
+package pl.szczodrzynski.edziennik.data.db.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.Index
+
+@Entity(tableName = "notices",
+ primaryKeys = ["profileId", "noticeId"],
+ indices = [
+ Index(value = ["profileId"])
+ ])
+open class Notice(
+ val profileId: Int,
+ @ColumnInfo(name = "noticeId")
+ var id: Long,
+ @ColumnInfo(name = "noticeType")
+ var type: Int,
+ @ColumnInfo(name = "noticeSemester")
+ var semester: Int,
+
+ @ColumnInfo(name = "noticeText")
+ var text: String,
+ @ColumnInfo(name = "noticeCategory")
+ var category: String?,
+ @ColumnInfo(name = "noticePoints")
+ var points: Float?,
+
+ var teacherId: Long,
+ var addedDate: Long = System.currentTimeMillis()
+) : Keepable() {
+ companion object {
+ const val TYPE_NEUTRAL = 0
+ const val TYPE_POSITIVE = 1
+ const val TYPE_NEGATIVE = 2
+ }
+
+ @Ignore
+ var showAsUnseen = false
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Profile.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Profile.kt
index 1442f40c..50ad82a7 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Profile.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Profile.kt
@@ -17,6 +17,7 @@ import com.google.gson.JsonObject
import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
+import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_PODLASIE
import pl.szczodrzynski.edziennik.utils.ProfileImageHolder
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.navlib.ImageHolder
@@ -80,7 +81,7 @@ open class Profile(
var dateYearEnd = Date(studentSchoolYearStart + 1, 6, 30)
fun getSemesterStart(semester: Int) = if (semester == 1) dateSemester1Start else dateSemester2Start
fun getSemesterEnd(semester: Int) = if (semester == 1) dateSemester2Start.clone().stepForward(0, 0, -1) else dateYearEnd
- fun dateToSemester(date: Date) = if (date.value >= getSemesterStart(2).value) 2 else 1
+ fun dateToSemester(date: Date) = if (date >= dateSemester2Start) 2 else 1
@delegate:Ignore
val currentSemester by lazy { dateToSemester(Date.getToday()) }
@@ -175,6 +176,12 @@ open class Profile(
MainActivity.DRAWER_ITEM_ATTENDANCE,
MainActivity.DRAWER_ITEM_ANNOUNCEMENTS
)
+ LOGIN_TYPE_PODLASIE -> listOf(
+ MainActivity.DRAWER_ITEM_TIMETABLE,
+ MainActivity.DRAWER_ITEM_AGENDA,
+ MainActivity.DRAWER_ITEM_GRADES,
+ MainActivity.DRAWER_ITEM_HOMEWORK
+ )
else -> listOf(
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/TeacherAbsence.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/TeacherAbsence.kt
index af037851..9ec5f828 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/TeacherAbsence.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/TeacherAbsence.kt
@@ -1,37 +1,41 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
package pl.szczodrzynski.edziennik.data.db.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.Index
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@Entity(tableName = "teacherAbsence",
- primaryKeys = ["profileId", "teacherAbsenceId"])
-open class TeacherAbsence (
+ primaryKeys = ["profileId", "teacherAbsenceId"],
+ indices = [
+ Index(value = ["profileId"])
+ ])
+open class TeacherAbsence(
+ val profileId: Int,
+ @ColumnInfo(name = "teacherAbsenceId")
+ val id: Long,
+ @ColumnInfo(name = "teacherAbsenceType")
+ val type: Long,
+ @ColumnInfo(name = "teacherAbsenceName")
+ val name: String?,
- val profileId: Int,
+ @ColumnInfo(name = "teacherAbsenceDateFrom")
+ val dateFrom: Date,
+ @ColumnInfo(name = "teacherAbsenceDateTo")
+ val dateTo: Date,
+ @ColumnInfo(name = "teacherAbsenceTimeFrom")
+ val timeFrom: Time?,
+ @ColumnInfo(name = "teacherAbsenceTimeTo")
+ val timeTo: Time?,
- @ColumnInfo(name = "teacherAbsenceId")
- val id: Long,
-
- val teacherId: Long,
-
- @ColumnInfo(name = "teacherAbsenceType")
- val type: Long,
-
- @ColumnInfo(name = "teacherAbsenceName")
- val name: String?,
-
- @ColumnInfo(name = "teacherAbsenceDateFrom")
- val dateFrom: Date,
-
- @ColumnInfo(name = "teacherAbsenceDateTo")
- val dateTo: Date,
-
- @ColumnInfo(name = "teacherAbsenceTimeFrom")
- val timeFrom: Time?,
-
- @ColumnInfo(name = "teacherAbsenceTimeTo")
- val timeTo: Time?
-
-)
+ val teacherId: Long,
+ var addedDate: Long = System.currentTimeMillis()
+) : Keepable() {
+ @Ignore
+ var showAsUnseen = false
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.java
deleted file mode 100644
index 2811a0c7..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
- */
-
-package pl.szczodrzynski.edziennik.data.db.full;
-
-import pl.szczodrzynski.edziennik.data.db.entity.Announcement;
-
-public class AnnouncementFull extends Announcement {
- public String teacherFullName = "";
-
- // metadata
- public boolean seen;
- public boolean notified;
- public long addedDate;
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.kt
new file mode 100644
index 00000000..5b4cb711
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
+package pl.szczodrzynski.edziennik.data.db.full
+
+import pl.szczodrzynski.edziennik.data.db.entity.Announcement
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class AnnouncementFull(
+ profileId: Int, id: Long,
+ subject: String, text: String?,
+ startDate: Date?, endDate: Date?,
+ teacherId: Long, addedDate: Long = System.currentTimeMillis()
+) : Announcement(
+ profileId, id,
+ subject, text,
+ startDate, endDate,
+ teacherId, addedDate
+) {
+ var teacherName: String? = null
+
+ // metadata
+ var seen = false
+ var notified = false
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.java
deleted file mode 100644
index 605d06b2..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
- */
-
-package pl.szczodrzynski.edziennik.data.db.full;
-
-import pl.szczodrzynski.edziennik.data.db.entity.Attendance;
-
-public class AttendanceFull extends Attendance {
- public String teacherFullName = "";
-
- public String subjectLongName = "";
- public String subjectShortName = "";
-
- // metadata
- public boolean seen;
- public boolean notified;
- public long addedDate;
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.kt
new file mode 100644
index 00000000..f235fafd
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-24.
+ */
+package pl.szczodrzynski.edziennik.data.db.full
+
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.utils.models.Date
+import pl.szczodrzynski.edziennik.utils.models.Time
+
+class AttendanceFull(
+ profileId: Int, id: Long,
+ baseType: Int, typeName: String, typeShort: String, typeSymbol: String, typeColor: Int?,
+ date: Date, startTime: Time, semester: Int,
+ teacherId: Long, subjectId: Long, addedDate: Long = System.currentTimeMillis()
+) : Attendance(
+ profileId, id,
+ baseType, typeName, typeShort, typeSymbol, typeColor,
+ date, startTime, semester,
+ teacherId, subjectId, addedDate
+) {
+ var teacherName: String? = null
+ var subjectLongName: String? = null
+ var subjectShortName: String? = null
+
+ // metadata
+ var seen = false
+ get() = field || baseType == TYPE_PRESENT
+ var notified = false
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt
index a3dcc2cb..01e85395 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
*/
package pl.szczodrzynski.edziennik.data.db.full
@@ -11,16 +11,16 @@ import pl.szczodrzynski.edziennik.utils.models.Time
class EventFull(
profileId: Int, id: Long, date: Date, time: Time?,
topic: String, color: Int?, type: Long,
- teacherId: Long, subjectId: Long, teamId: Long
+ teacherId: Long, subjectId: Long, teamId: Long, addedDate: Long = System.currentTimeMillis()
) : Event(
profileId, id, date, time,
topic, color, type,
- teacherId, subjectId, teamId
+ teacherId, subjectId, teamId, addedDate
) {
constructor(event: Event, metadata: Metadata? = null) : this(
event.profileId, event.id, event.date, event.time,
event.topic, event.color, event.type,
- event.teacherId, event.subjectId, event.teamId) {
+ event.teacherId, event.subjectId, event.teamId, event.addedDate) {
event.let {
addedManually = it.addedManually
sharedBy = it.sharedBy
@@ -33,7 +33,6 @@ class EventFull(
metadata?.let {
seen = it.seen
notified = it.notified
- addedDate = it.addedDate
}
}
@@ -45,10 +44,10 @@ class EventFull(
var subjectShortName: String? = null
var teamName: String? = null
var teamCode: String? = null
+
// metadata
var seen = false
var notified = false
- var addedDate: Long = 0
val eventColor
get() = color ?: typeColor ?: 0xff2196f3.toInt()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/GradeFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/GradeFull.kt
index 8cc80974..f8d90c05 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/GradeFull.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/GradeFull.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-24.
*/
package pl.szczodrzynski.edziennik.data.db.full
@@ -9,18 +9,18 @@ class GradeFull(
profileId: Int, id: Long, name: String, type: Int,
value: Float, weight: Float, color: Int,
category: String?, description: String?, comment: String?,
- semester: Int, teacherId: Long, subjectId: Long
+ semester: Int, teacherId: Long, subjectId: Long, addedDate: Long = System.currentTimeMillis()
) : Grade(
profileId, id, name, type,
value, weight, color,
category, description, comment,
- semester, teacherId, subjectId
+ semester, teacherId, subjectId, addedDate
) {
+ var teacherName: String? = null
var subjectLongName: String? = null
var subjectShortName: String? = null
- var teacherFullName: String? = null
+
// metadata
var seen = false
var notified = false
- var addedDate: Long = 0
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LessonFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LessonFull.kt
index 5661a614..0e9542b6 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LessonFull.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LessonFull.kt
@@ -1,3 +1,6 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
package pl.szczodrzynski.edziennik.data.db.full
import android.content.Context
@@ -5,7 +8,11 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.utils.models.Time
-class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
+class LessonFull(
+ profileId: Int, id: Long
+) : Lesson(
+ profileId, id
+) {
var subjectName: String? = null
var teacherName: String? = null
var teamName: String? = null
@@ -126,5 +133,4 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
// metadata
var seen: Boolean = false
var notified: Boolean = false
- var addedDate: Long = 0
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.java
deleted file mode 100644
index d73cb95f..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
- */
-
-package pl.szczodrzynski.edziennik.data.db.full;
-
-import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber;
-
-public class LuckyNumberFull extends LuckyNumber {
- // metadata
- public boolean seen;
- public boolean notified;
- public long addedDate;
-}
-
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.kt
new file mode 100644
index 00000000..bb24724a
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
+package pl.szczodrzynski.edziennik.data.db.full
+
+import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class LuckyNumberFull(
+ profileId: Int, date: Date,
+ number: Int
+) : LuckyNumber(
+ profileId, date,
+ number
+) {
+ // metadata
+ var seen = false
+ var notified = false
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt
index 5b873b7a..3fdcdd0d 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
*/
package pl.szczodrzynski.edziennik.data.db.full
@@ -10,10 +10,12 @@ import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
class MessageFull(
profileId: Int, id: Long, type: Int,
- subject: String, body: String?, senderId: Long?
+ subject: String, body: String?, senderId: Long?,
+ addedDate: Long = System.currentTimeMillis()
) : Message(
profileId, id, type,
- subject, body, senderId
+ subject, body, senderId,
+ addedDate
) {
var senderName: String? = null
@Relation(parentColumn = "messageId", entityColumn = "messageId", entity = MessageRecipient::class)
@@ -33,5 +35,4 @@ class MessageFull(
// metadata
var seen = false
var notified = false
- var addedDate: Long = 0
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.java
deleted file mode 100644
index 372650fd..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) Kacper Ziubryniewicz 2020-1-6
- */
-
-package pl.szczodrzynski.edziennik.data.db.full;
-
-import pl.szczodrzynski.edziennik.data.db.entity.Notice;
-
-public class NoticeFull extends Notice {
- public String teacherFullName = "";
-
- // metadata
- public boolean seen;
- public boolean notified;
- public long addedDate;
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.kt
new file mode 100644
index 00000000..25aee14d
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
+package pl.szczodrzynski.edziennik.data.db.full
+
+import pl.szczodrzynski.edziennik.data.db.entity.Notice
+
+class NoticeFull(
+ profileId: Int, id: Long, type: Int, semester: Int,
+ text: String, category: String?, points: Float?,
+ teacherId: Long, addedDate: Long = System.currentTimeMillis()
+) : Notice(
+ profileId, id, type, semester,
+ text, category, points,
+ teacherId, addedDate
+) {
+ var teacherName: String? = null
+
+ // metadata
+ var seen = false
+ var notified = false
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/TeacherAbsenceFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/TeacherAbsenceFull.kt
index e9d53a59..39a88e0f 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/TeacherAbsenceFull.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/TeacherAbsenceFull.kt
@@ -1,17 +1,24 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-25.
+ */
package pl.szczodrzynski.edziennik.data.db.full
import pl.szczodrzynski.edziennik.data.db.entity.TeacherAbsence
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
-class TeacherAbsenceFull(profileId: Int, id: Long, teacherId: Long, type: Long, name: String?,
- dateFrom: Date, dateTo: Date, timeFrom: Time?, timeTo: Time?)
- : TeacherAbsence(profileId, id, teacherId, type, name, dateFrom, dateTo, timeFrom, timeTo) {
-
- var teacherFullName = ""
+class TeacherAbsenceFull(
+ profileId: Int, id: Long, type: Long, name: String?,
+ dateFrom: Date, dateTo: Date, timeFrom: Time?, timeTo: Time?,
+ teacherId: Long, addedDate: Long = System.currentTimeMillis()
+) : TeacherAbsence(
+ profileId, id, type, name,
+ dateFrom, dateTo, timeFrom, timeTo,
+ teacherId, addedDate
+) {
+ var teacherName: String? = null
// metadata
var seen: Boolean = false
var notified: Boolean = false
- var addedDate: Long = 0
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration86.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration86.kt
new file mode 100644
index 00000000..add43d71
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration86.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-29.
+ */
+
+package pl.szczodrzynski.edziennik.data.db.migration
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration86 : Migration(85, 86) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ // Migrating some models, moving addedDate from metadata to entities
+
+ // Announcements
+ database.execSQL("""ALTER TABLE announcements RENAME TO _announcements""")
+ database.execSQL("""CREATE TABLE `announcements` (
+ `announcementIdString` TEXT,
+ `profileId` INTEGER NOT NULL,
+ `announcementId` INTEGER NOT NULL,
+ `announcementSubject` TEXT NOT NULL,
+ `announcementText` TEXT,
+ `announcementStartDate` TEXT,
+ `announcementEndDate` TEXT,
+ `teacherId` INTEGER NOT NULL,
+ `addedDate` INTEGER NOT NULL,
+ `keep` INTEGER NOT NULL,
+ PRIMARY KEY(`profileId`, `announcementId`)
+ )""")
+ database.execSQL("""DROP INDEX IF EXISTS index_announcements_profileId""")
+ database.execSQL("""CREATE INDEX `index_announcements_profileId` ON `announcements` (`profileId`)""")
+ database.execSQL("""REPLACE INTO announcements (
+ announcementIdString, profileId, announcementId, announcementSubject, announcementText, announcementStartDate, announcementEndDate, teacherId, addedDate, keep
+ ) SELECT
+ announcementIdString, profileId, announcementId, IFNULL(announcementSubject, ""), announcementText, announcementStartDate, announcementEndDate, teacherId, 0, 1
+ FROM _announcements""")
+ database.execSQL("""DROP TABLE _announcements""")
+
+ // Attendance Types
+ database.execSQL("""ALTER TABLE attendanceTypes RENAME TO _attendanceTypes""")
+ database.execSQL("""CREATE TABLE `attendanceTypes` (
+ `profileId` INTEGER NOT NULL,
+ `id` INTEGER NOT NULL,
+ `baseType` INTEGER NOT NULL,
+ `typeName` TEXT NOT NULL,
+ `typeShort` TEXT NOT NULL,
+ `typeSymbol` TEXT NOT NULL,
+ `typeColor` INTEGER,
+ PRIMARY KEY(`profileId`, `id`)
+ )""")
+ database.execSQL("""REPLACE INTO attendanceTypes (
+ profileId, id,
+ baseType,
+ typeName,
+ typeShort,
+ typeSymbol,
+ typeColor
+ ) SELECT
+ profileId, id,
+ CASE WHEN id > 100 AND type = 0 THEN 10 ELSE type END,
+ name,
+ CASE type WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END,
+ CASE type WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END,
+ CASE color WHEN -1 THEN NULL ELSE color END
+ FROM _attendanceTypes""")
+ database.execSQL("""DROP TABLE _attendanceTypes""")
+
+ // Attendance
+ database.execSQL("""ALTER TABLE attendances RENAME TO _attendances""")
+ database.execSQL("""CREATE TABLE `attendances` (
+ `attendanceLessonTopic` TEXT,
+ `attendanceLessonNumber` INTEGER,
+ `profileId` INTEGER NOT NULL,
+ `attendanceId` INTEGER NOT NULL,
+ `attendanceBaseType` INTEGER NOT NULL,
+ `attendanceTypeName` TEXT NOT NULL,
+ `attendanceTypeShort` TEXT NOT NULL,
+ `attendanceTypeSymbol` TEXT NOT NULL,
+ `attendanceTypeColor` INTEGER,
+ `attendanceDate` TEXT NOT NULL,
+ `attendanceTime` TEXT,
+ `attendanceSemester` INTEGER NOT NULL,
+ `teacherId` INTEGER NOT NULL,
+ `subjectId` INTEGER NOT NULL,
+ `addedDate` INTEGER NOT NULL,
+ `keep` INTEGER NOT NULL,
+ PRIMARY KEY(`profileId`, `attendanceId`)
+ )""")
+ database.execSQL("""DROP INDEX IF EXISTS index_attendances_profileId""")
+ database.execSQL("""CREATE INDEX `index_attendances_profileId` ON `attendances` (`profileId`)""")
+ database.execSQL("""REPLACE INTO attendances (
+ attendanceLessonTopic, attendanceLessonNumber, profileId, attendanceId,
+ attendanceBaseType,
+ attendanceTypeName,
+ attendanceTypeShort,
+ attendanceTypeSymbol,
+ attendanceTypeColor, attendanceDate, attendanceTime, attendanceSemester, teacherId, subjectId, addedDate, keep
+ ) SELECT
+ attendanceLessonTopic, NULL, profileId, attendanceId,
+ attendanceType,
+ CASE attendanceType WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END,
+ CASE attendanceType WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END,
+ CASE attendanceType WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END,
+ NULL, attendanceLessonDate, attendanceStartTime, attendanceSemester, teacherId, subjectId, 0, 1
+ FROM _attendances""")
+ database.execSQL("""DROP TABLE _attendances""")
+
+ // Events
+ database.execSQL("""ALTER TABLE events ADD COLUMN addedDate INTEGER NOT NULL DEFAULT 0""")
+
+ // Grades
+ database.execSQL("""ALTER TABLE grades ADD COLUMN addedDate INTEGER NOT NULL DEFAULT 0""")
+ database.execSQL("""ALTER TABLE grades ADD COLUMN keep INTEGER NOT NULL DEFAULT 1""")
+
+ // Lucky Numbers
+ database.execSQL("""ALTER TABLE luckyNumbers ADD COLUMN keep INTEGER NOT NULL DEFAULT 1""")
+
+ // Messages
+ database.execSQL("""ALTER TABLE messages ADD COLUMN addedDate INTEGER NOT NULL DEFAULT 0""")
+
+ // Notices
+ database.execSQL("""ALTER TABLE notices RENAME TO _notices""")
+ database.execSQL("""CREATE TABLE `notices` (
+ `profileId` INTEGER NOT NULL,
+ `noticeId` INTEGER NOT NULL,
+ `noticeType` INTEGER NOT NULL,
+ `noticeSemester` INTEGER NOT NULL,
+ `noticeText` TEXT NOT NULL,
+ `noticeCategory` TEXT,
+ `noticePoints` REAL,
+ `teacherId` INTEGER NOT NULL,
+ `addedDate` INTEGER NOT NULL,
+ `keep` INTEGER NOT NULL,
+ PRIMARY KEY(`profileId`, `noticeId`)
+ )""")
+ database.execSQL("""DROP INDEX IF EXISTS index_notices_profileId""")
+ database.execSQL("""CREATE INDEX `index_notices_profileId` ON `notices` (`profileId`)""")
+ database.execSQL("""REPLACE INTO notices (
+ profileId, noticeId, noticeType, noticeSemester,
+ noticeText,
+ noticeCategory,
+ noticePoints,
+ teacherId, addedDate, keep
+ ) SELECT
+ profileId, noticeId, noticeType, noticeSemester,
+ CASE noticeText WHEN NULL THEN "" ELSE noticeText END,
+ category,
+ CASE points WHEN 0 THEN NULL ELSE points END,
+ teacherId, 0, 1
+ FROM _notices""")
+ database.execSQL("""DROP TABLE _notices""")
+
+ // Teacher Absence
+ database.execSQL("""ALTER TABLE teacherAbsence ADD COLUMN addedDate INTEGER NOT NULL DEFAULT 0""")
+ database.execSQL("""ALTER TABLE teacherAbsence ADD COLUMN keep INTEGER NOT NULL DEFAULT 1""")
+ database.execSQL("""CREATE INDEX IF NOT EXISTS `index_teacherAbsence_profileId` ON `teacherAbsence` (`profileId`)""")
+
+ // Timetable
+ database.execSQL("""ALTER TABLE timetable ADD COLUMN keep INTEGER NOT NULL DEFAULT 1""")
+
+ // Metadata - copy AddedDate to entities
+ database.execSQL("""UPDATE grades SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = grades.profileId AND metadata.thingId = grades.gradeId AND metadata.thingType = 1), 0)""")
+ database.execSQL("""UPDATE notices SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = notices.profileId AND metadata.thingId = notices.noticeId AND metadata.thingType = 2), 0)""")
+ database.execSQL("""UPDATE attendances SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = attendances.profileId AND metadata.thingId = attendances.attendanceId AND metadata.thingType = 3), 0)""")
+ database.execSQL("""UPDATE events SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = events.profileId AND metadata.thingId = events.eventId AND (metadata.thingType = 4 OR metadata.thingType = 5)), 0)""")
+ database.execSQL("""UPDATE announcements SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = announcements.profileId AND metadata.thingId = announcements.announcementId AND metadata.thingType = 7), 0)""")
+ database.execSQL("""UPDATE messages SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = messages.profileId AND metadata.thingId = messages.messageId AND metadata.thingType = 8), 0)""")
+
+ // Metadata - drop AddedDate column
+ database.execSQL("""ALTER TABLE metadata RENAME TO _metadata""")
+ database.execSQL("""CREATE TABLE metadata (profileId INTEGER NOT NULL, metadataId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, thingType INTEGER NOT NULL, thingId INTEGER NOT NULL, seen INTEGER NOT NULL, notified INTEGER NOT NULL)""")
+ database.execSQL("""DROP INDEX IF EXISTS index_metadata_profileId_thingType_thingId""")
+ database.execSQL("""CREATE UNIQUE INDEX index_metadata_profileId_thingType_thingId ON "metadata" (profileId, thingType, thingId)""")
+ database.execSQL("""INSERT INTO metadata (profileId, metadataId, thingType, thingId, seen, notified) SELECT profileId, metadataId, thingType, thingId, seen, notified FROM _metadata""")
+ database.execSQL("""DROP TABLE _metadata""")
+
+ database.execSQL("""DELETE FROM endpointTimers WHERE endpointId IN (1080, 1081, 2050, 1090, 1010, 1014);""")
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration87.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration87.kt
new file mode 100644
index 00000000..524cb559
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration87.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-8.
+ */
+
+package pl.szczodrzynski.edziennik.data.db.migration
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration87 : Migration(86, 87) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE attendances ADD COLUMN attendanceIsCounted INTEGER NOT NULL DEFAULT 1")
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration88.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration88.kt
new file mode 100644
index 00000000..0dcd2f8c
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration88.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-9.
+ */
+
+package pl.szczodrzynski.edziennik.data.db.migration
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration88 : Migration(87, 88) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("UPDATE endpointTimers SET endpointLastSync = 0 WHERE endpointId IN (1030, 1040, 1050, 1060, 1070, 1080);")
+ database.execSQL("UPDATE profiles SET empty = 1 WHERE loginStoreType = 4")
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt
index c394c7cc..2ebb8c66 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt
@@ -114,7 +114,8 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message:
type = json.getLong("type") ?: 0,
teacherId = json.getLong("teacherId") ?: -1,
subjectId = json.getLong("subjectId") ?: -1,
- teamId = team.id
+ teamId = team.id,
+ addedDate = json.getLong("addedDate") ?: System.currentTimeMillis()
)
if (event.color == -1)
event.color = null
@@ -128,8 +129,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message:
if (event.type == Event.TYPE_HOMEWORK) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT,
event.id,
false,
- true,
- json.getLong("addedDate") ?: System.currentTimeMillis()
+ true
)
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
@@ -144,7 +144,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message:
profileId = profile.id,
profileName = profile.name,
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
- addedDate = metadata.addedDate
+ addedDate = event.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong())
notificationList += notification
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/network/cookie/DumbCookieJar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/network/cookie/DumbCookieJar.kt
index ca11996f..39cec6eb 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/network/cookie/DumbCookieJar.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/network/cookie/DumbCookieJar.kt
@@ -26,7 +26,7 @@ class DumbCookieJar(
) : CookieJar {
private val prefs = context.getSharedPreferences("cookies", Context.MODE_PRIVATE)
- private val sessionCookies = mutableSetOf()
+ val sessionCookies = mutableSetOf()
private val savedCookies = mutableSetOf()
private fun save(dc: DumbCookie) {
sessionCookies.remove(dc)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncTimeChooseDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncTimeChooseDialog.kt
index 63601c76..4116ab83 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncTimeChooseDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncTimeChooseDialog.kt
@@ -100,7 +100,7 @@ class BellSyncTimeChooseDialog(
private fun loadTimeList() { launch {
val timeItems = withContext(Dispatchers.Default) {
- val lessons = app.db.timetableDao().getForDateNow(App.profileId, today)
+ val lessons = app.db.timetableDao().getAllForDateNow(App.profileId, today)
val items = mutableListOf()
lessons.forEach {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt
index 03cc4879..c6a7b2f9 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt
@@ -86,7 +86,7 @@ class DayDialog(
)
val lessons = withContext(Dispatchers.Default) {
- app.db.timetableDao().getForDateNow(profileId, date)
+ app.db.timetableDao().getAllForDateNow(profileId, date)
}.filter { it.type != Lesson.TYPE_NO_LESSONS }
if (lessons.isNotEmpty()) { run {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt
index d6d175d9..06b5f1a6 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt
@@ -484,7 +484,8 @@ class EventManualDialog(
type = type ?: Event.TYPE_DEFAULT,
teacherId = teacherId ?: -1,
subjectId = subjectId ?: -1,
- teamId = teamId ?: -1
+ teamId = teamId ?: -1,
+ addedDate = editingEvent?.addedDate ?: System.currentTimeMillis()
).also {
it.addedManually = true
}
@@ -497,8 +498,7 @@ class EventManualDialog(
},
eventObject.id,
true,
- true,
- editingEvent?.addedDate ?: System.currentTimeMillis()
+ true
)
launch {
@@ -536,10 +536,9 @@ class EventManualDialog(
eventObject.apply {
sharedBy = profile?.userCode
sharedByName = profile?.studentNameLong
+ addedDate = System.currentTimeMillis()
}
- metadataObject.addedDate = System.currentTimeMillis()
-
api.runCatching(activity) {
shareEvent(eventObject.withMetadata(metadataObject))
} ?: run {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt
index 0e063758..20f3ad21 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt
@@ -71,7 +71,7 @@ class GradeDetailsDialog(
launch {
val historyList = withContext(Dispatchers.Default) {
- app.db.gradeDao().getAllWithParentIdNow(App.profileId, grade.id)
+ app.db.gradeDao().getByParentIdNow(App.profileId, grade.id)
}
if (historyList.isEmpty()) {
b.historyVisible = false
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AttendanceConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AttendanceConfigDialog.kt
new file mode 100644
index 00000000..e7d66ed5
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AttendanceConfigDialog.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-4.
+ */
+
+package pl.szczodrzynski.edziennik.ui.dialogs.settings
+
+import android.annotation.SuppressLint
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.MainActivity
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.databinding.AttendanceConfigDialogBinding
+import pl.szczodrzynski.edziennik.onChange
+
+class AttendanceConfigDialog(
+ val activity: AppCompatActivity,
+ private val reloadOnDismiss: Boolean = true,
+ val onShowListener: ((tag: String) -> Unit)? = null,
+ val onDismissListener: ((tag: String) -> Unit)? = null
+) {
+ companion object {
+ const val TAG = "GradesConfigDialog"
+ }
+
+ private val app by lazy { activity.application as App }
+ private val profileConfig by lazy { app.config.getFor(app.profileId).attendance }
+
+ private lateinit var b: AttendanceConfigDialogBinding
+ private lateinit var dialog: AlertDialog
+
+ init { run {
+ if (activity.isFinishing)
+ return@run
+ b = AttendanceConfigDialogBinding.inflate(activity.layoutInflater)
+ onShowListener?.invoke(TAG)
+ dialog = MaterialAlertDialogBuilder(activity)
+ .setTitle(R.string.menu_attendance_config)
+ .setView(b.root)
+ .setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
+ .setOnDismissListener {
+ saveConfig()
+ onDismissListener?.invoke(TAG)
+ if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget()
+ }
+ .create()
+ initView()
+ loadConfig()
+ dialog.show()
+ }}
+
+ @SuppressLint("SetTextI18n")
+ private fun loadConfig() {
+ b.useSymbols.isChecked = profileConfig.useSymbols
+ b.groupConsecutiveDays.isChecked = profileConfig.groupConsecutiveDays
+ b.showPresenceInMonth.isChecked = profileConfig.showPresenceInMonth
+ }
+
+ private fun saveConfig() {
+ // nothing to do here, yet
+ }
+
+ private fun initView() {
+ b.useSymbols.onChange { _, isChecked ->
+ profileConfig.useSymbols = isChecked
+ }
+ b.groupConsecutiveDays.onChange { _, isChecked ->
+ profileConfig.groupConsecutiveDays = isChecked
+ }
+ b.showPresenceInMonth.onChange { _, isChecked ->
+ profileConfig.showPresenceInMonth = isChecked
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt
index ad29e8f7..e74f3577 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt
@@ -4,6 +4,7 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings
+import android.annotation.SuppressLint
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
@@ -59,6 +60,7 @@ class GradesConfigDialog(
dialog.show()
}}
+ @SuppressLint("SetTextI18n")
private fun loadConfig() {
b.customPlusCheckBox.isChecked = profileConfig.plusValue != null
b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt
index d8c11307..443d4734 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt
@@ -29,7 +29,7 @@ class TeacherAbsenceAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val teacherAbsence: TeacherAbsenceFull = teacherAbsenceList[position]
- holder.teacherAbsenceTeacher.text = teacherAbsence.teacherFullName
+ holder.teacherAbsenceTeacher.text = teacherAbsence.teacherName
val time = when (teacherAbsence.timeFrom != null && teacherAbsence.timeTo != null) {
true -> when (teacherAbsence.dateFrom.compareTo(teacherAbsence.dateTo)) {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceDialog.kt
index 836197f0..7ffab515 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceDialog.kt
@@ -46,7 +46,7 @@ class TeacherAbsenceDialog(
b.teacherAbsenceView.setHasFixedSize(true)
b.teacherAbsenceView.layoutManager = LinearLayoutManager(activity)
- app.db.teacherAbsenceDao().getAllByDateFull(profileId, date).observe(activity as LifecycleOwner, Observer { absenceList ->
+ app.db.teacherAbsenceDao().getAllByDate(profileId, date).observe(activity as LifecycleOwner, Observer { absenceList ->
val adapter = TeacherAbsenceAdapter(activity, date, absenceList)
b.teacherAbsenceView.adapter = adapter
b.teacherAbsenceView.visibility = View.VISIBLE
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/agenda/AgendaFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/agenda/AgendaFragment.kt
index 9ee93be9..ec212cea 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/agenda/AgendaFragment.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/agenda/AgendaFragment.kt
@@ -144,7 +144,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
if (!isAdded)
return@launch
- val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getAllChangesNow(app.profileId) }
+ val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getChangesNow(app.profileId) }
val lessonChangeCounters = mutableListOf()
lessons.forEach { lesson ->
@@ -180,7 +180,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
val showTeacherAbsences = app.profile.getStudentData("showTeacherAbsences", true)
if (showTeacherAbsences) {
- val teacherAbsenceList = withContext(Dispatchers.Default) { app.db.teacherAbsenceDao().getAllFullNow(app.profileId) }
+ val teacherAbsenceList = withContext(Dispatchers.Default) { app.db.teacherAbsenceDao().getAllNow(app.profileId) }
val teacherAbsenceCounters = mutableListOf()
teacherAbsenceList.forEach { absence ->
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsAdapter.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsAdapter.java
index 7f9b600d..ee88da07 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsAdapter.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsAdapter.java
@@ -59,17 +59,20 @@ public class AnnouncementsAdapter extends RecyclerView.Adapter {
- if (announcement.text == null || (app.getProfile().getLoginStoreType() == LOGIN_TYPE_LIBRUS && !announcement.seen && app.getNetworkUtils().isOnline())) {
+ if (announcement.getText() == null || (app.getProfile().getLoginStoreType() == LOGIN_TYPE_LIBRUS && !announcement.getSeen() && app.getNetworkUtils().isOnline())) {
EdziennikTask.Companion.announcementGet(App.Companion.getProfileId(), announcement).enqueue(requireContext());
} else {
showAnnouncementDetailsDialog(announcement);
@@ -162,14 +162,14 @@ public class AnnouncementsFragment extends Fragment {
private void showAnnouncementDetailsDialog(AnnouncementFull announcement) {
MaterialDialog dialog = new MaterialDialog.Builder(activity)
- .title(announcement.subject)
+ .title(announcement.getSubject())
.customView(R.layout.dialog_announcement, true)
.positiveText(R.string.ok)
.show();
DialogAnnouncementBinding b = DialogAnnouncementBinding.bind(dialog.getCustomView());
- b.text.setText(announcement.teacherFullName+"\n\n"+ (announcement.startDate != null ? announcement.startDate.getFormattedString() : "-") + (announcement.endDate != null ? " do " + announcement.endDate.getFormattedString() : "")+"\n\n" +announcement.text);
- if (!announcement.seen && app.getProfile().getLoginStoreType() != LOGIN_TYPE_LIBRUS) {
- announcement.seen = true;
+ b.text.setText(announcement.getTeacherName() +"\n\n"+ (announcement.getStartDate() != null ? announcement.getStartDate().getFormattedString() : "-") + (announcement.getEndDate() != null ? " do " + announcement.getEndDate().getFormattedString() : "")+"\n\n" +announcement.getText());
+ if (!announcement.getSeen() && app.getProfile().getLoginStoreType() != LOGIN_TYPE_LIBRUS) {
+ announcement.setSeen(true);
AsyncTask.execute(() -> App.db.metadataDao().setSeen(App.Companion.getProfileId(), announcement, true));
if (recyclerView.getAdapter() != null)
recyclerView.getAdapter().notifyDataSetChanged();
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.java
deleted file mode 100644
index 7b057395..00000000
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package pl.szczodrzynski.edziennik.ui.modules.attendance;
-
-import android.content.Context;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.os.AsyncTask;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import java.util.List;
-
-import pl.szczodrzynski.edziennik.App;
-import pl.szczodrzynski.edziennik.R;
-import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull;
-
-import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT;
-import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT_EXCUSED;
-import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_BELATED;
-import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_BELATED_EXCUSED;
-import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_DAY_FREE;
-import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_PRESENT;
-import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_RELEASED;
-
-public class AttendanceAdapter extends RecyclerView.Adapter {
- private Context context;
- public List attendanceList;
-
- //getting the context and product list with constructor
- public AttendanceAdapter(Context mCtx, List noticeList) {
- this.context = mCtx;
- this.attendanceList = noticeList;
- }
-
- @NonNull
- @Override
- public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- //inflating and returning our view holder
- LayoutInflater inflater = LayoutInflater.from(context);
- View view = inflater.inflate(R.layout.row_attendance_item, parent, false);
- return new ViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
- App app = (App) context.getApplicationContext();
-
- AttendanceFull attendance = attendanceList.get(position);
-
- holder.attendanceLessonTopic.setText(attendance.lessonTopic);
- holder.attendanceTeacher.setText(attendance.teacherFullName);
- holder.attendanceSubject.setText(attendance.subjectLongName);
- holder.attendanceDate.setText(attendance.lessonDate.getStringDmy());
- holder.attendanceTime.setText(attendance.startTime.getStringHM());
-
- switch (attendance.type) {
- case TYPE_DAY_FREE:
- holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff166ee0, PorterDuff.Mode.MULTIPLY));
- holder.attendanceType.setText(R.string.attendance_free_day);
- break;
- case TYPE_ABSENT:
- holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xfff44336, PorterDuff.Mode.MULTIPLY));
- holder.attendanceType.setText(R.string.attendance_absent);
- break;
- case TYPE_ABSENT_EXCUSED:
- holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffaeea00, PorterDuff.Mode.MULTIPLY));
- holder.attendanceType.setText(R.string.attendance_absent_excused);
- break;
- case TYPE_BELATED:
- holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffca28, PorterDuff.Mode.MULTIPLY));
- holder.attendanceType.setText(R.string.attendance_belated);
- break;
- case TYPE_BELATED_EXCUSED:
- holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff4bb733, PorterDuff.Mode.MULTIPLY));
- holder.attendanceType.setText(R.string.attendance_belated_excused);
- break;
- case TYPE_RELEASED:
- holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff9e9e9e, PorterDuff.Mode.MULTIPLY));
- holder.attendanceType.setText(R.string.attendance_released);
- break;
- case TYPE_PRESENT:
- holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffae00, PorterDuff.Mode.MULTIPLY));
- holder.attendanceType.setText(R.string.attendance_present);
- break;
- default:
- holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff03a9f4, PorterDuff.Mode.MULTIPLY));
- holder.attendanceType.setText("?");
- break;
- }
-
- if (!attendance.seen) {
- holder.attendanceLessonTopic.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
- holder.attendanceLessonTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
- attendance.seen = true;
- AsyncTask.execute(() -> {
- App.db.metadataDao().setSeen(App.Companion.getProfileId(), attendance, true);
- //Intent i = new Intent("android.intent.action.MAIN").putExtra(MainActivity.ACTION_UPDATE_BADGES, "yes, sure");
- //context.sendBroadcast(i);
- });
- }
- else {
- holder.attendanceLessonTopic.setBackground(null);
- }
- }
-
- @Override
- public int getItemCount() {
- return attendanceList.size();
- }
-
- class ViewHolder extends RecyclerView.ViewHolder {
-
- TextView attendanceType;
- TextView attendanceLessonTopic;
- TextView attendanceSubject;
- TextView attendanceTeacher;
- TextView attendanceDate;
- TextView attendanceTime;
-
- ViewHolder(View itemView) {
- super(itemView);
- attendanceType = itemView.findViewById(R.id.attendanceType);
- attendanceLessonTopic = itemView.findViewById(R.id.attendanceLessonTopic);
- attendanceSubject = itemView.findViewById(R.id.attendanceSubject);
- attendanceTeacher = itemView.findViewById(R.id.attendanceTeacher);
- attendanceDate = itemView.findViewById(R.id.attendanceDate);
- attendanceTime = itemView.findViewById(R.id.attendanceTime);
- }
- }
-}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.kt
new file mode 100644
index 00000000..0dcae97e
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-29.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance
+
+import android.animation.ObjectAnimator
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.isInvisible
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.startCoroutineTimer
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.*
+import pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder.*
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import kotlin.coroutines.CoroutineContext
+
+class AttendanceAdapter(
+ val activity: AppCompatActivity,
+ val type: Int,
+ var onAttendanceClick: ((item: AttendanceFull) -> Unit)? = null
+) : RecyclerView.Adapter(), CoroutineScope {
+ companion object {
+ private const val TAG = "AttendanceAdapter"
+ private const val ITEM_TYPE_ATTENDANCE = 0
+ private const val ITEM_TYPE_DAY_RANGE = 1
+ private const val ITEM_TYPE_MONTH = 2
+ private const val ITEM_TYPE_SUBJECT = 3
+ private const val ITEM_TYPE_TYPE = 4
+ private const val ITEM_TYPE_EMPTY = 5
+ const val STATE_CLOSED = 0
+ const val STATE_OPENED = 1
+ }
+
+ private val app = activity.applicationContext as App
+ // optional: place the manager here
+
+ private val job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Main
+
+ var items = mutableListOf()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ return when (viewType) {
+ ITEM_TYPE_ATTENDANCE -> AttendanceViewHolder(inflater, parent)
+ ITEM_TYPE_DAY_RANGE -> DayRangeViewHolder(inflater, parent)
+ ITEM_TYPE_MONTH -> MonthViewHolder(inflater, parent)
+ ITEM_TYPE_SUBJECT -> SubjectViewHolder(inflater, parent)
+ ITEM_TYPE_TYPE -> TypeViewHolder(inflater, parent)
+ ITEM_TYPE_EMPTY -> EmptyViewHolder(inflater, parent)
+ else -> throw IllegalArgumentException("Incorrect viewType")
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return when (items[position]) {
+ is AttendanceFull -> ITEM_TYPE_ATTENDANCE
+ is AttendanceDayRange -> ITEM_TYPE_DAY_RANGE
+ is AttendanceMonth -> ITEM_TYPE_MONTH
+ is AttendanceSubject -> ITEM_TYPE_SUBJECT
+ is AttendanceTypeGroup -> ITEM_TYPE_TYPE
+ is AttendanceEmpty -> ITEM_TYPE_EMPTY
+ else -> throw IllegalArgumentException("Incorrect viewType")
+ }
+ }
+
+ private val onClickListener = View.OnClickListener { view ->
+ val model = view.getTag(R.string.tag_key_model)
+ if (model is AttendanceFull) {
+ onAttendanceClick?.invoke(model)
+ return@OnClickListener
+ }
+ if (model !is ExpandableItemModel<*>)
+ return@OnClickListener
+ expandModel(model, view)
+ }
+
+ fun expandModel(model: ExpandableItemModel<*>?, view: View?, notifyAdapter: Boolean = true) {
+ model ?: return
+ val position = items.indexOf(model)
+ if (position == -1)
+ return
+
+ view?.findViewById(R.id.dropdownIcon)?.let { dropdownIcon ->
+ ObjectAnimator.ofFloat(
+ dropdownIcon,
+ View.ROTATION,
+ if (model.state == STATE_CLOSED) 0f else 180f,
+ if (model.state == STATE_CLOSED) 180f else 0f
+ ).setDuration(200).start();
+ }
+
+ if (model is AttendanceDayRange || model is AttendanceMonth || model is AttendanceTypeGroup) {
+ // hide the preview, show summary
+ val preview = view?.findViewById(R.id.previewContainer)
+ val summary = view?.findViewById(R.id.summaryContainer)
+ val percentage = view?.findViewById(R.id.percentage)
+ preview?.isInvisible = model.state == STATE_CLOSED
+ summary?.isInvisible = model.state != STATE_CLOSED
+ percentage?.isVisible = model.state != STATE_CLOSED
+ }
+
+ if (model.state == STATE_CLOSED) {
+
+ val subItems = when {
+ model.items.isEmpty() -> listOf(AttendanceEmpty())
+ else -> model.items
+ }
+
+ model.state = STATE_OPENED
+ items.addAll(position + 1, subItems.filterNotNull())
+ if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size)
+ }
+ else {
+ val start = position + 1
+ var end: Int = items.size
+ for (i in start until items.size) {
+ val model1 = items[i]
+ val level = (model1 as? ExpandableItemModel<*>)?.level ?: 3
+ if (level <= model.level) {
+ end = i
+ break
+ } else {
+ if (model1 is ExpandableItemModel<*> && model1.state == STATE_OPENED) {
+ model1.state = STATE_CLOSED
+ }
+ }
+ }
+
+ if (end != -1) {
+ items.subList(start, end).clear()
+ if (notifyAdapter) notifyItemRangeRemoved(start, end - start)
+ }
+
+ model.state = STATE_CLOSED
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ val item = items[position]
+ if (holder !is BindableViewHolder<*, *>)
+ return
+
+ val viewType = when (holder) {
+ is AttendanceViewHolder -> ITEM_TYPE_ATTENDANCE
+ is DayRangeViewHolder -> ITEM_TYPE_DAY_RANGE
+ is MonthViewHolder -> ITEM_TYPE_MONTH
+ is SubjectViewHolder -> ITEM_TYPE_SUBJECT
+ is TypeViewHolder -> ITEM_TYPE_TYPE
+ is EmptyViewHolder -> ITEM_TYPE_EMPTY
+ else -> throw IllegalArgumentException("Incorrect viewType")
+ }
+ holder.itemView.setTag(R.string.tag_key_view_type, viewType)
+ holder.itemView.setTag(R.string.tag_key_position, position)
+ holder.itemView.setTag(R.string.tag_key_model, item)
+
+ when {
+ holder is AttendanceViewHolder && item is AttendanceFull -> holder.onBind(activity, app, item, position, this)
+ holder is DayRangeViewHolder && item is AttendanceDayRange -> holder.onBind(activity, app, item, position, this)
+ holder is MonthViewHolder && item is AttendanceMonth -> holder.onBind(activity, app, item, position, this)
+ holder is SubjectViewHolder && item is AttendanceSubject -> holder.onBind(activity, app, item, position, this)
+ holder is TypeViewHolder && item is AttendanceTypeGroup -> holder.onBind(activity, app, item, position, this)
+ holder is EmptyViewHolder && item is AttendanceEmpty -> holder.onBind(activity, app, item, position, this)
+ }
+
+ holder.itemView.setOnClickListener(onClickListener)
+ }
+
+ fun notifyItemChanged(model: Any) {
+ startCoroutineTimer(1000L, 0L) {
+ val index = items.indexOf(model)
+ if (index != -1)
+ notifyItemChanged(index)
+ }
+ }
+
+ override fun getItemCount() = items.size
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt
new file mode 100644
index 00000000..70be9256
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-1.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.*
+import android.text.TextPaint
+import android.util.AttributeSet
+import android.view.View
+import pl.szczodrzynski.edziennik.dp
+import pl.szczodrzynski.edziennik.utils.Colors
+import kotlin.math.roundToInt
+
+/* https://github.com/JakubekWeg/Mobishit/blob/master/app/src/main/java/jakubweg/mobishit/view/AttendanceBarView.kt */
+class AttendanceBar : View {
+
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
+
+ constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
+
+ private var attendancesList = listOf()
+ private val mainPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ private val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).also {
+ it.textAlign = Paint.Align.CENTER
+ }
+ private var mPath = Path()
+ private var mCornerRadius: Float = 0.toFloat()
+
+ init {
+ mCornerRadius = 4.dp.toFloat()
+
+ if (isInEditMode)
+ setAttendanceData(listOf(
+ 0xff43a047.toInt() to 23,
+ 0xff009688.toInt() to 187,
+ 0xff3f51b5.toInt() to 46,
+ 0xff3f51b5.toInt() to 5,
+ 0xffffc107.toInt() to 5,
+ 0xff9e9e9e.toInt() to 26,
+ 0xff76ff03.toInt() to 34,
+ 0xffff3d00.toInt() to 8
+ ))
+ }
+
+ // color, count
+ private class AttendanceItem(var color: Int, var count: Int)
+
+ fun setAttendanceData(list: List>) {
+ attendancesList = list.map { AttendanceItem(it.first, it.second) }
+ setWillNotDraw(false)
+ invalidate()
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ val r = RectF(0f, 0f, w.toFloat(), h.toFloat())
+ mPath = Path().apply {
+ addRoundRect(r, mCornerRadius, mCornerRadius, Path.Direction.CW)
+ close()
+ }
+ }
+
+ @SuppressLint("DrawAllocation", "CanvasSize")
+ override fun onDraw(canvas: Canvas?) {
+ canvas ?: return
+
+ val sum = attendancesList.sumBy { it.count }
+ if (sum == 0) {
+ return
+ }
+
+ canvas.clipPath(mPath)
+
+ val top = paddingTop.toFloat()
+ val bottom = (height - paddingBottom).toFloat()
+ var left = paddingLeft.toFloat()
+ val unitWidth = (width - paddingRight - paddingLeft).toFloat() / sum.toFloat()
+
+ textPaint.color = Color.BLACK
+ textPaint.textSize = 14.dp.toFloat()
+
+ for (e in attendancesList) {
+ if (e.count == 0)
+ continue
+
+ val width = unitWidth * e.count
+ mainPaint.color = e.color
+ canvas.drawRect(left, top, left + width, bottom, mainPaint)
+
+ val percentage = (100f * e.count / sum).roundToInt().toString() + "%"
+ val textBounds = Rect()
+ textPaint.getTextBounds(percentage, 0, percentage.length, textBounds)
+ if (width > textBounds.width() + 8.dp && height > textBounds.height() + 2.dp) {
+ textPaint.color = Colors.legibleTextColor(e.color)
+ canvas.drawText(percentage, left + width / 2, bottom - height / 2 + textBounds.height()/2, textPaint)
+ }
+
+ left += width
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt
new file mode 100644
index 00000000..ce49efbd
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-9.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance
+
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.graphics.ColorUtils
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
+import pl.szczodrzynski.edziennik.setTintColor
+import kotlin.coroutines.CoroutineContext
+
+class AttendanceDetailsDialog(
+ val activity: AppCompatActivity,
+ val attendance: AttendanceFull,
+ val onShowListener: ((tag: String) -> Unit)? = null,
+ val onDismissListener: ((tag: String) -> Unit)? = null
+) : CoroutineScope {
+ companion object {
+ private const val TAG = "AttendanceDetailsDialog"
+ }
+
+ private lateinit var app: App
+ private lateinit var b: AttendanceDetailsDialogBinding
+ private lateinit var dialog: AlertDialog
+
+ private val job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Main
+
+ // local variables go here
+
+ init { run {
+ if (activity.isFinishing)
+ return@run
+ onShowListener?.invoke(TAG)
+ app = activity.applicationContext as App
+ b = AttendanceDetailsDialogBinding.inflate(activity.layoutInflater)
+ dialog = MaterialAlertDialogBuilder(activity)
+ .setView(b.root)
+ .setPositiveButton(R.string.close, null)
+ .setOnDismissListener {
+ onDismissListener?.invoke(TAG)
+ }
+ .show()
+ val manager = app.attendanceManager
+
+ val attendanceColor = manager.getAttendanceColor(attendance)
+ b.attendance = attendance
+ b.devMode = App.debugMode
+ b.attendanceName.setTextColor(if (ColorUtils.calculateLuminance(attendanceColor) > 0.3) 0xaa000000.toInt() else 0xccffffff.toInt())
+ b.attendanceName.background.setTintColor(attendanceColor)
+
+ b.attendanceIsCounted.setText(if (attendance.isCounted) R.string.yes else R.string.no)
+ }}
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.kt
new file mode 100644
index 00000000..0af177f9
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-30.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance
+
+import android.os.AsyncTask
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.databinding.AttendanceFragmentBinding
+import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog
+import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
+import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
+import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
+import kotlin.coroutines.CoroutineContext
+
+class AttendanceFragment : Fragment(), CoroutineScope {
+ companion object {
+ private const val TAG = "AttendanceFragment"
+ const val VIEW_SUMMARY = 0
+ const val VIEW_DAYS = 1
+ const val VIEW_MONTHS = 2
+ const val VIEW_TYPES = 3
+ const val VIEW_LIST = 4
+ var pageSelection = 1
+ }
+
+ private lateinit var app: App
+ private lateinit var activity: MainActivity
+ private lateinit var b: AttendanceFragmentBinding
+
+ private val job: Job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Main
+
+ // local/private variables go here
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ activity = (getActivity() as MainActivity?) ?: return null
+ context ?: return null
+ app = activity.application as App
+ b = AttendanceFragmentBinding.inflate(inflater)
+ b.refreshLayout.setParent(activity.swipeRefreshLayout)
+ return b.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ if (!isAdded) return
+
+ activity.bottomSheet.prependItems(
+ BottomSheetPrimaryItem(true)
+ .withTitle(R.string.menu_attendance_config)
+ .withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
+ .withOnClickListener(View.OnClickListener {
+ activity.bottomSheet.close()
+ AttendanceConfigDialog(activity, true, null, null)
+ }),
+ BottomSheetSeparatorItem(true),
+ BottomSheetPrimaryItem(true)
+ .withTitle(R.string.menu_mark_as_read)
+ .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
+ .withOnClickListener(View.OnClickListener {
+ activity.bottomSheet.close()
+ AsyncTask.execute { App.db.metadataDao().setAllSeen(App.profileId, Metadata.TYPE_ATTENDANCE, true) }
+ Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
+ })
+ )
+ activity.gainAttention()
+
+ if (pageSelection == 1)
+ pageSelection = app.config.forProfile().attendance.attendancePageSelection
+
+ val pagerAdapter = FragmentLazyPagerAdapter(
+ fragmentManager ?: return,
+ b.refreshLayout,
+ listOf(
+ AttendanceSummaryFragment() to getString(R.string.attendance_tab_summary),
+
+ AttendanceListFragment().apply {
+ arguments = Bundle("viewType" to VIEW_DAYS)
+ } to getString(R.string.attendance_tab_days),
+
+ AttendanceListFragment().apply {
+ arguments = Bundle("viewType" to VIEW_MONTHS)
+ } to getString(R.string.attendance_tab_months),
+
+ AttendanceListFragment().apply {
+ arguments = Bundle("viewType" to VIEW_TYPES)
+ } to getString(R.string.attendance_tab_types),
+
+ AttendanceListFragment().apply {
+ arguments = Bundle("viewType" to VIEW_LIST)
+ } to getString(R.string.attendance_tab_list)
+ )
+ )
+ b.viewPager.apply {
+ offscreenPageLimit = 1
+ adapter = pagerAdapter
+ currentItem = pageSelection
+ addOnPageSelectedListener {
+ pageSelection = it
+ app.config.forProfile().attendance.attendancePageSelection = it
+ }
+ b.tabLayout.setupWithViewPager(this)
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java
similarity index 93%
rename from app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.java
rename to app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java
index 2b5317a6..c46b3e09 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java
@@ -51,7 +51,7 @@ import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_RELEASED
import static pl.szczodrzynski.edziennik.data.db.entity.LoginStore.LOGIN_TYPE_VULCAN;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ATTENDANCE;
-public class AttendanceFragment extends Fragment {
+public class AttendanceFragment_ extends Fragment {
private App app = null;
private MainActivity activity = null;
@@ -217,21 +217,21 @@ public class AttendanceFragment extends Fragment {
subjectTotalCount = new LongSparseArray<>();
subjectAbsentCount = new LongSparseArray<>();
for (AttendanceFull attendance: attendanceList) {
- if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_VULCAN && attendance.type == TYPE_RELEASED)
+ if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_VULCAN && attendance.getBaseType() == TYPE_RELEASED)
continue;
- int[] subjectTotal = subjectTotalCount.get(attendance.subjectId, new int[3]);
- int[] subjectAbsent = subjectAbsentCount.get(attendance.subjectId, new int[3]);
+ int[] subjectTotal = subjectTotalCount.get(attendance.getSubjectId(), new int[3]);
+ int[] subjectAbsent = subjectAbsentCount.get(attendance.getSubjectId(), new int[3]);
subjectTotal[0]++;
- subjectTotal[attendance.semester]++;
+ subjectTotal[attendance.getSemester()]++;
- if (attendance.type == TYPE_ABSENT || attendance.type == TYPE_ABSENT_EXCUSED) {
+ if (attendance.getBaseType() == TYPE_ABSENT || attendance.getBaseType() == TYPE_ABSENT_EXCUSED) {
subjectAbsent[0]++;
- subjectAbsent[attendance.semester]++;
+ subjectAbsent[attendance.getSemester()]++;
}
- subjectTotalCount.put(attendance.subjectId, subjectTotal);
- subjectAbsentCount.put(attendance.subjectId, subjectAbsent);
+ subjectTotalCount.put(attendance.getSubjectId(), subjectTotal);
+ subjectAbsentCount.put(attendance.getSubjectId(), subjectAbsent);
}
}
@@ -247,13 +247,13 @@ public class AttendanceFragment extends Fragment {
List filteredList = new ArrayList<>();
for (AttendanceFull attendance: attendanceList) {
- if (displayMode != MODE_YEAR && attendance.semester != displayMode)
+ if (displayMode != MODE_YEAR && attendance.getSemester() != displayMode)
continue;
- if (subjectIdFilter != -1 && attendance.subjectId != subjectIdFilter)
+ if (subjectIdFilter != -1 && attendance.getSubjectId() != subjectIdFilter)
continue;
- if (attendance.type != TYPE_PRESENT)
+ if (attendance.getBaseType() != TYPE_PRESENT)
filteredList.add(attendance);
- switch (attendance.type) {
+ switch (attendance.getBaseType()) {
case TYPE_PRESENT:
presentCount++;
break;
@@ -278,11 +278,12 @@ public class AttendanceFragment extends Fragment {
b.attendanceView.setVisibility(View.VISIBLE);
b.attendanceNoData.setVisibility(View.GONE);
if ((adapter = (AttendanceAdapter) b.attendanceView.getAdapter()) != null) {
- adapter.attendanceList = filteredList;
+ //adapter.setItems(filteredList);
adapter.notifyDataSetChanged();
}
else {
- adapter = new AttendanceAdapter(getContext(), filteredList);
+ //adapter = new AttendanceAdapter(activity, true, null);
+ //adapter.setItems(filteredList);
b.attendanceView.setAdapter(adapter);
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt
new file mode 100644
index 00000000..8e68cdbb
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-30.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
+import kotlinx.coroutines.*
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.MainActivity
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.databinding.AttendanceListFragmentBinding
+import pl.szczodrzynski.edziennik.isNotNullNorEmpty
+import pl.szczodrzynski.edziennik.startCoroutineTimer
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceDayRange
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceMonth
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceTypeGroup
+import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
+import pl.szczodrzynski.edziennik.utils.models.Date
+import kotlin.coroutines.CoroutineContext
+
+class AttendanceListFragment : LazyFragment(), CoroutineScope {
+ companion object {
+ private const val TAG = "AttendanceListFragment"
+ }
+
+ private lateinit var app: App
+ private lateinit var activity: MainActivity
+ private lateinit var b: AttendanceListFragmentBinding
+
+ private val job: Job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Main
+
+ // local/private variables go here
+ private val manager by lazy { app.attendanceManager }
+ private var viewType = AttendanceFragment.VIEW_DAYS
+ private var expandSubjectId = 0L
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ activity = (getActivity() as MainActivity?) ?: return null
+ context ?: return null
+ app = activity.application as App
+ b = AttendanceListFragmentBinding.inflate(inflater)
+ return b.root
+ }
+
+ override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
+ if (!isAdded) return@startCoroutineTimer
+
+ viewType = arguments?.getInt("viewType") ?: AttendanceFragment.VIEW_DAYS
+ expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
+
+ val adapter = AttendanceAdapter(activity, viewType)
+ var firstRun = true
+
+ app.db.attendanceDao().getAll(App.profileId).observe(this@AttendanceListFragment, Observer { items -> this@AttendanceListFragment.launch {
+ if (!isAdded) return@launch
+
+ // load & configure the adapter
+ adapter.items = withContext(Dispatchers.Default) { processAttendance(items) }
+ if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) {
+ b.list.adapter = adapter
+ b.list.apply {
+ setHasFixedSize(true)
+ layoutManager = LinearLayoutManager(context)
+ addOnScrollListener(onScrollListener)
+ }
+ }
+ adapter.notifyDataSetChanged()
+ setSwipeToRefresh(adapter.items.isNullOrEmpty())
+
+ if (firstRun) {
+ expandSubject(adapter)
+ firstRun = false
+ }
+
+ // show/hide relevant views
+ b.progressBar.isVisible = false
+ if (adapter.items.isNullOrEmpty()) {
+ b.list.isVisible = false
+ b.noData.isVisible = true
+ } else {
+ b.list.isVisible = true
+ b.noData.isVisible = false
+ }
+ }})
+
+ adapter.onAttendanceClick = {
+ AttendanceDetailsDialog(activity, it)
+ }
+ }; return true}
+
+ private fun expandSubject(adapter: AttendanceAdapter) {
+ var expandSubjectModel: GradesSubject? = null
+ if (expandSubjectId != 0L) {
+ expandSubjectModel = adapter.items.firstOrNull { it is GradesSubject && it.subjectId == expandSubjectId } as? GradesSubject
+ adapter.expandModel(
+ model = expandSubjectModel,
+ view = null,
+ notifyAdapter = false
+ )
+ }
+
+ startCoroutineTimer(500L) {
+ if (expandSubjectModel != null) {
+ b.list.smoothScrollToPosition(
+ adapter.items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0)
+ )
+ }
+ }
+ }
+
+ @Suppress("SuspendFunctionOnCoroutineScope")
+ private fun processAttendance(attendance: List): MutableList {
+ if (attendance.isEmpty())
+ return mutableListOf()
+
+ val groupConsecutiveDays = app.config.forProfile().attendance.groupConsecutiveDays
+ val showPresenceInMonth = app.config.forProfile().attendance.showPresenceInMonth
+
+ if (viewType == AttendanceFragment.VIEW_DAYS) {
+ val items = attendance
+ .filter { it.baseType != Attendance.TYPE_PRESENT }
+ .groupBy { it.date }
+ .map { AttendanceDayRange(
+ rangeStart = it.key,
+ rangeEnd = null,
+ items = it.value.toMutableList()
+ ) }
+ .toMutableList()
+
+ if (groupConsecutiveDays) {
+ items.sortByDescending { it.rangeStart }
+ val iterator = items.listIterator()
+
+ if (!iterator.hasNext())
+ return items.toMutableList()
+ var element = iterator.next()
+ while (iterator.hasNext()) {
+ var nextElement = iterator.next()
+ while (Date.diffDays(element.rangeStart, nextElement.rangeStart) <= 1 && iterator.hasNext()) {
+ if (element.rangeEnd == null)
+ element.rangeEnd = element.rangeStart
+
+ element.items.addAll(nextElement.items)
+ element.rangeStart = nextElement.rangeStart
+ iterator.remove()
+ nextElement = iterator.next()
+ }
+ element = nextElement
+ }
+ }
+
+ return items.toMutableList()
+ }
+ else if (viewType == AttendanceFragment.VIEW_MONTHS) {
+ val items = attendance
+ .groupBy { it.date.year to it.date.month }
+ .map { AttendanceMonth(
+ year = it.key.first,
+ month = it.key.second,
+ items = it.value.toMutableList()
+ ) }
+
+ items.forEach { month ->
+ month.typeCountMap = month.items
+ .groupBy { it.typeObject }
+ .map { it.key to it.value.size }
+ .sortedBy { it.first }
+ .toMap()
+
+ val totalCount = month.typeCountMap.entries.sumBy {
+ if (!it.key.isCounted || it.key.baseType == Attendance.TYPE_UNKNOWN)
+ 0
+ else it.value
+ }
+ val presenceCount = month.typeCountMap.entries.sumBy {
+ when (it.key.baseType) {
+ Attendance.TYPE_PRESENT,
+ Attendance.TYPE_PRESENT_CUSTOM,
+ Attendance.TYPE_BELATED,
+ Attendance.TYPE_BELATED_EXCUSED,
+ Attendance.TYPE_RELEASED -> if (it.key.isCounted) it.value else 0
+ else -> 0
+ }
+ }
+
+ month.percentage = if (totalCount == 0)
+ 0f
+ else
+ presenceCount.toFloat() / totalCount.toFloat() * 100f
+
+ if (!showPresenceInMonth)
+ month.items.removeAll { it.baseType == Attendance.TYPE_PRESENT }
+ }
+
+ return items.toMutableList()
+ }
+ else if (viewType == AttendanceFragment.VIEW_TYPES) {
+ val items = attendance
+ .groupBy { it.typeObject }
+ .map { AttendanceTypeGroup(
+ type = it.key,
+ items = it.value.toMutableList()
+ ) }
+ .sortedBy { it.items.size }
+
+ items.forEach { type ->
+ type.percentage = if (attendance.isEmpty())
+ 0f
+ else
+ type.items.size.toFloat() / attendance.size.toFloat() * 100f
+
+ type.semesterCount = type.items.count { it.semester == app.profile.currentSemester }
+ }
+
+ return items.toMutableList()
+ }
+ return attendance.filter { it.baseType != Attendance.TYPE_PRESENT }.toMutableList()
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt
new file mode 100644
index 00000000..5f61020b
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-4.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance
+
+import android.graphics.Color
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.AccelerateDecelerateInterpolator
+import android.view.animation.Animation
+import android.view.animation.Transformation
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.core.graphics.ColorUtils
+import androidx.core.view.isInvisible
+import androidx.core.view.isVisible
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
+import kotlinx.coroutines.*
+import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.databinding.AttendanceSummaryFragmentBinding
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment.Companion.VIEW_SUMMARY
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceSubject
+import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
+import pl.szczodrzynski.edziennik.utils.models.Date
+import java.text.DecimalFormat
+import kotlin.coroutines.CoroutineContext
+
+class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
+ companion object {
+ private const val TAG = "AttendanceSummaryFragment"
+ private var periodSelection = 0
+ }
+
+ private lateinit var app: App
+ private lateinit var activity: MainActivity
+ private lateinit var b: AttendanceSummaryFragmentBinding
+
+ private val job: Job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Main
+
+ // local/private variables go here
+ private val manager by lazy { app.attendanceManager }
+ private var expandSubjectId = 0L
+ private var attendance = listOf()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ activity = (getActivity() as MainActivity?) ?: return null
+ context ?: return null
+ app = activity.application as App
+ b = AttendanceSummaryFragmentBinding.inflate(inflater)
+ return b.root
+ }
+
+ override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
+ if (!isAdded) return@startCoroutineTimer
+
+ expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L
+
+ val adapter = AttendanceAdapter(activity, VIEW_SUMMARY)
+ var firstRun = true
+
+ app.db.attendanceDao().getAll(App.profileId).observe(this@AttendanceSummaryFragment, Observer { items -> this@AttendanceSummaryFragment.launch {
+ if (!isAdded) return@launch
+
+ // load & configure the adapter
+ attendance = items
+ adapter.items = withContext(Dispatchers.Default) { processAttendance() }
+ if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) {
+ b.list.adapter = adapter
+ b.list.apply {
+ setHasFixedSize(false)
+ layoutManager = LinearLayoutManager(context)
+ isNestedScrollingEnabled = false
+ }
+ }
+ adapter.notifyDataSetChanged()
+ setSwipeToRefresh(adapter.items.isNullOrEmpty())
+
+ if (firstRun) {
+ expandSubject(adapter)
+ firstRun = false
+ }
+
+ // show/hide relevant views
+ b.progressBar.isVisible = false
+ if (adapter.items.isNullOrEmpty()) {
+ b.statsLayout.isVisible = false
+ b.list.isVisible = false
+ b.noData.isVisible = true
+ } else {
+ b.statsLayout.isVisible = true
+ b.list.isVisible = true
+ b.noData.isVisible = false
+ }
+ }})
+
+ adapter.onAttendanceClick = {
+ AttendanceDetailsDialog(activity, it)
+ }
+
+ b.toggleGroup.check(when (periodSelection) {
+ 0 -> R.id.allYear
+ 1 -> R.id.semester1
+ 2 -> R.id.semester2
+ else -> R.id.allYear
+ })
+ b.toggleGroup.addOnButtonCheckedListener { _, checkedId, isChecked ->
+ if (!isChecked)
+ return@addOnButtonCheckedListener
+ periodSelection = when (checkedId) {
+ R.id.allYear -> 0
+ R.id.semester1 -> 1
+ R.id.semester2 -> 2
+ else -> 0
+ }
+ this@AttendanceSummaryFragment.launch {
+ adapter.items = withContext(Dispatchers.Default) { processAttendance() }
+ if (adapter.items.isNullOrEmpty()) {
+ b.statsLayout.isVisible = false
+ b.list.isVisible = false
+ b.noData.isVisible = true
+ } else {
+ b.statsLayout.isVisible = true
+ b.list.isVisible = true
+ b.noData.isVisible = false
+ }
+ adapter.notifyDataSetChanged()
+ }
+ }
+ }; return true}
+
+ private fun expandSubject(adapter: AttendanceAdapter) {
+ var expandSubjectModel: GradesSubject? = null
+ if (expandSubjectId != 0L) {
+ expandSubjectModel = adapter.items.firstOrNull { it is GradesSubject && it.subjectId == expandSubjectId } as? GradesSubject
+ adapter.expandModel(
+ model = expandSubjectModel,
+ view = null,
+ notifyAdapter = false
+ )
+ }
+
+ startCoroutineTimer(500L) {
+ if (expandSubjectModel != null) {
+ b.list.smoothScrollToPosition(
+ adapter.items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0)
+ )
+ }
+ }
+ }
+
+ @Suppress("SuspendFunctionOnCoroutineScope")
+ private fun processAttendance(): MutableList {
+ val attendance = when (periodSelection) {
+ 0 -> attendance
+ 1 -> attendance.filter { it.semester == 1 }
+ 2 -> attendance.filter { it.semester == 2 }
+ else -> attendance
+ }
+
+ if (attendance.isEmpty())
+ return mutableListOf()
+
+ val items = attendance
+ .groupBy { it.subjectId }
+ .map { AttendanceSubject(
+ subjectId = it.key,
+ subjectName = it.value.firstOrNull()?.subjectLongName ?: "",
+ items = it.value.toMutableList()
+ ) }
+ .sortedBy { it.subjectName.toLowerCase() }
+
+ var totalCountSum = 0
+ var presenceCountSum = 0
+
+ items.forEach { subject ->
+ subject.typeCountMap = subject.items
+ .groupBy { it.typeObject }
+ .map { it.key to it.value.size }
+ .sortedBy { it.first }
+ .toMap()
+
+ val totalCount = subject.typeCountMap.entries.sumBy {
+ if (!it.key.isCounted || it.key.baseType == Attendance.TYPE_UNKNOWN)
+ 0
+ else it.value
+ }
+ val presenceCount = subject.typeCountMap.entries.sumBy {
+ when (it.key.baseType) {
+ Attendance.TYPE_PRESENT,
+ Attendance.TYPE_PRESENT_CUSTOM,
+ Attendance.TYPE_BELATED,
+ Attendance.TYPE_BELATED_EXCUSED,
+ Attendance.TYPE_RELEASED -> if (it.key.isCounted) it.value else 0
+ else -> 0
+ }
+ }
+ totalCountSum += totalCount
+ presenceCountSum += presenceCount
+
+ subject.percentage = if (totalCount == 0)
+ 0f
+ else
+ presenceCount.toFloat() / totalCount.toFloat() * 100f
+
+ if (!false /* showPresenceInSubject */)
+ subject.items.removeAll { it.baseType == Attendance.TYPE_PRESENT }
+ }
+
+ val typeCountMap = attendance
+ .groupBy { it.typeObject }
+ .map { it.key to it.value.size }
+ .sortedBy { it.first }
+ .toMap()
+
+ val percentage = if (totalCountSum == 0)
+ 0f
+ else
+ presenceCountSum.toFloat() / totalCountSum.toFloat() * 100f
+
+ launch {
+ b.attendanceBar.setAttendanceData(typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
+ b.attendanceBar.isInvisible = typeCountMap.isEmpty()
+
+ b.previewContainer.removeAllViews()
+ //val sum = typeCountMap.entries.sumBy { it.value }.toFloat()
+ typeCountMap.forEach { (type, count) ->
+ val layout = LinearLayout(activity)
+ val attendanceObject = Attendance(
+ profileId = 0,
+ id = 0,
+ baseType = type.baseType,
+ typeName = "",
+ typeShort = type.typeShort,
+ typeSymbol = type.typeSymbol,
+ typeColor = type.typeColor,
+ date = Date(0, 0, 0),
+ startTime = null,
+ semester = 0,
+ teacherId = 0,
+ subjectId = 0,
+ addedDate = 0
+ )
+ layout.addView(AttendanceView(activity, attendanceObject, manager))
+ layout.addView(TextView(activity).also {
+ //it.setText(R.string.attendance_percentage_format, count/sum*100f)
+ it.text = count.toString()
+ it.setPadding(0, 0, 5.dp, 0)
+ })
+ layout.setPadding(0, 8.dp, 0, 8.dp)
+ b.previewContainer.addView(layout)
+ }
+
+ if (percentage == 0f) {
+ b.percentage.isInvisible = true
+ b.percentageCircle.isInvisible = true
+ }
+ else {
+ b.percentage.isVisible = true
+ b.percentageCircle.isVisible = true
+ b.percentage.setText(R.string.attendance_period_summary_format, percentage)
+
+ val df = DecimalFormat("0.##")
+ b.percentageCircle.setProgressTextAdapter { value ->
+ df.format(value) + "%"
+ }
+ b.percentageCircle.maxProgress = 100.0
+ animatePercentageIndicator(percentage.toDouble())
+ }
+ }
+
+ return items.toMutableList()
+ }
+
+ private fun animatePercentageIndicator(targetProgress: Double) {
+ val startingProgress = b.percentageCircle.progress
+ val progressChange = targetProgress - startingProgress
+
+ val a: Animation = object : Animation() {
+ override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
+ val progress = startingProgress + (progressChange * interpolatedTime)
+ //if (interpolatedTime == 1f)
+ // progress = startingProgress + progressChange
+
+ val color = ColorUtils.blendARGB(Color.RED, Color.GREEN, progress.toFloat() / 100.0f)
+ b.percentageCircle.progressColor = color
+ b.percentageCircle.setProgress(progress, 100.0)
+ }
+
+ override fun willChangeBounds(): Boolean {
+ return false
+ }
+ }
+ a.duration = 1300
+ a.interpolator = AccelerateDecelerateInterpolator()
+ b.percentageCircle.startAnimation(a)
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceView.kt
new file mode 100644
index 00000000..dfe20eda
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceView.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-29.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.text.TextUtils
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import androidx.appcompat.widget.AppCompatTextView
+import androidx.core.graphics.ColorUtils
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.dp
+import pl.szczodrzynski.edziennik.setTintColor
+import pl.szczodrzynski.edziennik.utils.managers.AttendanceManager
+
+class AttendanceView : AppCompatTextView {
+
+ @JvmOverloads
+ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr)
+
+ constructor(context: Context, attendance: Attendance, manager: AttendanceManager) : this(context, null) {
+ setAttendance(attendance, manager, false)
+ }
+
+ @SuppressLint("RestrictedApi")
+ fun setAttendance(attendance: Attendance?, manager: AttendanceManager, bigView: Boolean = false) {
+ if (attendance == null) {
+ visibility = View.GONE
+ return
+ }
+ visibility = View.VISIBLE
+
+ val attendanceName = if (manager.useSymbols)
+ attendance.typeSymbol
+ else
+ attendance.typeShort
+
+ val attendanceColor = manager.getAttendanceColor(attendance)
+
+ text = when {
+ attendanceName.isBlank() -> " "
+ else -> attendanceName
+ }
+
+ setTextColor(if (ColorUtils.calculateLuminance(attendanceColor) > 0.3)
+ 0xaa000000.toInt()
+ else
+ 0xccffffff.toInt())
+
+ setBackgroundResource(if (bigView) R.drawable.bg_rounded_8dp else R.drawable.bg_rounded_4dp)
+ background.setTintColor(attendanceColor)
+ gravity = Gravity.CENTER
+
+ if (bigView) {
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 22f)
+ setAutoSizeTextTypeUniformWithConfiguration(
+ 14,
+ 32,
+ 1,
+ TypedValue.COMPLEX_UNIT_SP
+ )
+ setPadding(2.dp, 2.dp, 2.dp, 2.dp)
+ }
+ else {
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
+ setPadding(5.dp, 0, 5.dp, 0)
+ layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
+ setMargins(0, 0, 5.dp, 0)
+ }
+ maxLines = 1
+ ellipsize = TextUtils.TruncateAt.END
+ measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+ }
+ }
+}
+
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceCount.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceCount.kt
new file mode 100644
index 00000000..c2595885
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceCount.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-30.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.models
+
+class AttendanceCount {
+ var normalSum = 0f
+ var normalCount = 0
+ var normalWeightedSum = 0f
+ var normalWeightedCount = 0f
+
+ var pointSum = 0f
+
+ var pointAvgSum = 0f
+ var pointAvgMax = 0f
+
+ var normalAvg: Float? = null
+ var pointAvgPercent: Float? = null
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceDayRange.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceDayRange.kt
new file mode 100644
index 00000000..e993d785
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceDayRange.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-30.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.models
+
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+data class AttendanceDayRange(
+ var rangeStart: Date,
+ var rangeEnd: Date?,
+ override val items: MutableList = mutableListOf()
+) : ExpandableItemModel(items) {
+ override var level = 1
+
+ var lastAddedDate = 0L
+
+ var hasUnseen: Boolean = false
+ get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceEmpty.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceEmpty.kt
new file mode 100644
index 00000000..46877f63
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceEmpty.kt
@@ -0,0 +1,7 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-4.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.models
+
+class AttendanceEmpty
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceMonth.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceMonth.kt
new file mode 100644
index 00000000..c6d14914
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceMonth.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-30.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.models
+
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+
+data class AttendanceMonth(
+ val year: Int,
+ val month: Int,
+ override val items: MutableList = mutableListOf()
+) : ExpandableItemModel(items) {
+ override var level = 1
+
+ var lastAddedDate = 0L
+
+ var hasUnseen: Boolean = false
+ get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen }
+
+ var typeCountMap: Map = mapOf()
+ var percentage: Float = 0f
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceSubject.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceSubject.kt
new file mode 100644
index 00000000..b56cd62a
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceSubject.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-4.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.models
+
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+
+data class AttendanceSubject(
+ val subjectId: Long,
+ val subjectName: String,
+ override val items: MutableList = mutableListOf()
+) : ExpandableItemModel(items) {
+ override var level = 1
+
+ var lastAddedDate = 0L
+
+ var hasUnseen: Boolean = false
+ get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen }
+
+ var typeCountMap: Map = mapOf()
+ var percentage: Float = 0f
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceTypeGroup.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceTypeGroup.kt
new file mode 100644
index 00000000..d6b431ec
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceTypeGroup.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-8.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.models
+
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+
+data class AttendanceTypeGroup(
+ val type: AttendanceType,
+ override val items: MutableList = mutableListOf()
+) : ExpandableItemModel(items) {
+ override var level = 1
+
+ var lastAddedDate = 0L
+
+ var hasUnseen: Boolean = false
+ get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen }
+
+ var percentage: Float = 0f
+ var semesterCount: Int = 0
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/AttendanceViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/AttendanceViewHolder.kt
new file mode 100644
index 00000000..3e7a1d32
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/AttendanceViewHolder.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-30.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.concat
+import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
+import pl.szczodrzynski.edziennik.databinding.AttendanceItemAttendanceBinding
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceDayRange
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceMonth
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import pl.szczodrzynski.edziennik.utils.Themes
+import pl.szczodrzynski.edziennik.utils.models.Week
+
+class AttendanceViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: AttendanceItemAttendanceBinding = AttendanceItemAttendanceBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "AttendanceViewHolder"
+ }
+
+ override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceFull, position: Int, adapter: AttendanceAdapter) {
+ val manager = app.attendanceManager
+ val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
+
+ val bullet = " • "
+
+ b.attendanceView.setAttendance(item, manager, bigView = true)
+
+ b.type.text = item.typeName
+ b.subjectName.text = item.subjectLongName ?: item.lessonTopic
+ b.dateTime.text = listOf(
+ Week.getFullDayName(item.date.weekDay),
+ item.date.formattedStringShort,
+ item.startTime?.stringHM,
+ item.lessonNumber?.let { app.getString(R.string.attendance_lesson_number_format, it) }
+ ).concat(bullet)
+
+ if (item.showAsUnseen == null)
+ item.showAsUnseen = !item.seen
+
+ b.unread.isVisible = item.showAsUnseen == true
+ if (!item.seen) {
+ manager.markAsSeen(item)
+
+ val container = adapter.items.firstOrNull {
+ it is ExpandableItemModel<*> && it.items.contains(item)
+ } as? ExpandableItemModel<*> ?: return
+
+ var hasUnseen = true
+ if (container is AttendanceDayRange) {
+ hasUnseen = container.items.any { !it.seen }
+ container.hasUnseen = hasUnseen
+ }
+ if (container is AttendanceMonth) {
+ hasUnseen = container.items.any { !it.seen }
+ container.hasUnseen = hasUnseen
+ }
+
+ // check if the unseen status has changed
+ if (!hasUnseen) {
+ adapter.notifyItemChanged(container)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/DayRangeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/DayRangeViewHolder.kt
new file mode 100644
index 00000000..b42d5e16
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/DayRangeViewHolder.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-30.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.concat
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.databinding.AttendanceItemDayRangeBinding
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter.Companion.STATE_CLOSED
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceView
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceDayRange
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import pl.szczodrzynski.edziennik.utils.Themes
+
+class DayRangeViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: AttendanceItemDayRangeBinding = AttendanceItemDayRangeBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "DayRangeViewHolder"
+ }
+
+ override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceDayRange, position: Int, adapter: AttendanceAdapter) {
+ val manager = app.attendanceManager
+ val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
+
+ b.title.text = listOf(
+ item.rangeStart.formattedString,
+ item.rangeEnd?.formattedString
+ ).concat(" - ")
+
+ b.dropdownIcon.rotation = when (item.state) {
+ STATE_CLOSED -> 0f
+ else -> 180f
+ }
+
+ b.unread.isVisible = item.hasUnseen
+
+ b.previewContainer.visibility = if (item.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE
+ b.summaryContainer.visibility = if (item.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE
+
+ b.previewContainer.removeAllViews()
+
+ for (attendance in item.items) {
+ if (attendance.baseType == Attendance.TYPE_PRESENT_CUSTOM || attendance.baseType == Attendance.TYPE_UNKNOWN)
+ continue
+ b.previewContainer.addView(AttendanceView(
+ contextWrapper,
+ attendance,
+ manager
+ ))
+ }
+ if (item.items.isEmpty() || item.items.none { it.baseType != Attendance.TYPE_PRESENT_CUSTOM && it.baseType != Attendance.TYPE_UNKNOWN }) {
+ b.previewContainer.addView(TextView(contextWrapper).also {
+ it.setText(R.string.attendance_empty_text)
+ })
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/EmptyViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/EmptyViewHolder.kt
new file mode 100644
index 00000000..37fe635c
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/EmptyViewHolder.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-4.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.RecyclerView
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.databinding.AttendanceItemEmptyBinding
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceEmpty
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+
+class EmptyViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: AttendanceItemEmptyBinding = AttendanceItemEmptyBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "EmptyViewHolder"
+ }
+
+ override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceEmpty, position: Int, adapter: AttendanceAdapter) {
+
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt
new file mode 100644
index 00000000..71c139ac
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-30.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.core.view.isInvisible
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.databinding.AttendanceItemMonthBinding
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter.Companion.STATE_CLOSED
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceView
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceMonth
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import pl.szczodrzynski.edziennik.utils.Themes
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class MonthViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: AttendanceItemMonthBinding = AttendanceItemMonthBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "MonthViewHolder"
+ }
+
+ override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceMonth, position: Int, adapter: AttendanceAdapter) {
+ val manager = app.attendanceManager
+ val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
+
+ b.title.text = listOf(
+ app.resources.getStringArray(R.array.material_calendar_months_array).getOrNull(item.month - 1)?.fixName(),
+ item.year.toString()
+ ).concat(" ")
+
+ b.dropdownIcon.rotation = when (item.state) {
+ STATE_CLOSED -> 0f
+ else -> 180f
+ }
+
+ b.unread.isVisible = item.hasUnseen
+
+ b.attendanceBar.setAttendanceData(item.typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
+
+ b.previewContainer.isInvisible = item.state != STATE_CLOSED
+ b.summaryContainer.isInvisible = item.state == STATE_CLOSED
+ b.percentage.isVisible = item.state == STATE_CLOSED
+
+ b.previewContainer.removeAllViews()
+
+ val sum = item.typeCountMap.entries.sumBy { it.value }.toFloat()
+ item.typeCountMap.forEach { (type, count) ->
+ val layout = LinearLayout(contextWrapper)
+ val attendance = Attendance(
+ profileId = 0,
+ id = 0,
+ baseType = type.baseType,
+ typeName = "",
+ typeShort = type.typeShort,
+ typeSymbol = type.typeSymbol,
+ typeColor = type.typeColor,
+ date = Date(0, 0, 0),
+ startTime = null,
+ semester = 0,
+ teacherId = 0,
+ subjectId = 0,
+ addedDate = 0
+ )
+ layout.addView(AttendanceView(contextWrapper, attendance, manager))
+ layout.addView(TextView(contextWrapper).also {
+ //it.setText(R.string.attendance_percentage_format, count/sum*100f)
+ it.text = count.toString()
+ it.setPadding(0, 0, 5.dp, 0)
+ })
+ layout.setPadding(0, 8.dp, 0, 0)
+ b.previewContainer.addView(layout)
+ }
+
+ if (item.percentage == 0f) {
+ b.percentage.isVisible = false
+ b.percentage.text = null
+ b.summaryContainer.isVisible = false
+ b.summaryContainer.text = null
+ }
+ else {
+ b.percentage.setText(R.string.attendance_percentage_format, item.percentage)
+ b.summaryContainer.setText(R.string.attendance_period_summary_format, item.percentage)
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt
new file mode 100644
index 00000000..e439111d
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-4.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.databinding.AttendanceItemSubjectBinding
+import pl.szczodrzynski.edziennik.setText
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter.Companion.STATE_CLOSED
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceSubject
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import pl.szczodrzynski.edziennik.utils.Themes
+
+class SubjectViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: AttendanceItemSubjectBinding = AttendanceItemSubjectBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "SubjectViewHolder"
+ }
+
+ override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceSubject, position: Int, adapter: AttendanceAdapter) {
+ val manager = app.attendanceManager
+ val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
+
+ b.title.text = item.subjectName
+
+ b.dropdownIcon.rotation = when (item.state) {
+ STATE_CLOSED -> 0f
+ else -> 180f
+ }
+
+ b.unread.isVisible = item.hasUnseen
+
+ b.attendanceBar.setAttendanceData(item.typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
+
+ b.percentage.isVisible = true
+
+ if (item.percentage == 0f) {
+ b.percentage.isVisible = false
+ b.percentage.text = null
+ }
+ else {
+ b.percentage.setText(R.string.attendance_percentage_format, item.percentage)
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt
new file mode 100644
index 00000000..a2f8b689
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-8.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.concat
+import pl.szczodrzynski.edziennik.data.db.entity.Attendance
+import pl.szczodrzynski.edziennik.databinding.AttendanceItemTypeBinding
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
+import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceTypeGroup
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import pl.szczodrzynski.edziennik.utils.Themes
+import pl.szczodrzynski.edziennik.utils.models.Date
+
+class TypeViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: AttendanceItemTypeBinding = AttendanceItemTypeBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "TypeViewHolder"
+ }
+
+ override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceTypeGroup, position: Int, adapter: AttendanceAdapter) {
+ val manager = app.attendanceManager
+ val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
+
+ val type = item.type
+ b.title.text = type.typeName
+
+ b.dropdownIcon.rotation = when (item.state) {
+ AttendanceAdapter.STATE_CLOSED -> 0f
+ else -> 180f
+ }
+
+ b.unread.isVisible = item.hasUnseen
+
+ b.details.text = listOf(
+ app.getString(R.string.attendance_percentage_format, item.percentage),
+ app.getString(R.string.attendance_type_yearly_format, item.items.size),
+ app.getString(R.string.attendance_type_semester_format, item.semesterCount)
+ ).concat(" • ")
+
+ b.type.setAttendance(Attendance(
+ profileId = 0,
+ id = 0,
+ baseType = type.baseType,
+ typeName = "",
+ typeShort = type.typeShort,
+ typeSymbol = type.typeSymbol,
+ typeColor = type.typeColor,
+ date = Date(0, 0, 0),
+ startTime = null,
+ semester = 0,
+ teacherId = 0,
+ subjectId = 0,
+ addedDate = 0
+ ), manager, bigView = false)
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/BehaviourFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/BehaviourFragment.java
index 4ec8b4c6..d0b46f4a 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/BehaviourFragment.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/BehaviourFragment.java
@@ -134,10 +134,10 @@ public class BehaviourFragment extends Fragment {
List filteredList = new ArrayList<>();
for (NoticeFull notice: noticeList) {
- if (displayMode != MODE_YEAR && notice.semester != displayMode)
+ if (displayMode != MODE_YEAR && notice.getSemester() != displayMode)
continue;
filteredList.add(notice);
- switch (notice.type) {
+ switch (notice.getType()) {
case Notice.TYPE_POSITIVE:
praisesCount++;
break;
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt
index b1b85780..f495d61e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt
@@ -41,10 +41,10 @@ class NoticesAdapter//getting the context and product list with constructor
if (app.profile.loginStoreType == LOGIN_TYPE_MOBIDZIENNIK && false) {
holder.noticesItemReason.text = bs(null, notice.category, "\n") + notice.text
- holder.noticesItemTeacherName.text = app.getString(R.string.notices_points_format, notice.teacherFullName, if (notice.points > 0) "+" + notice.points else notice.points)
+ holder.noticesItemTeacherName.text = app.getString(R.string.notices_points_format, notice.teacherName, if (notice.points ?: 0f > 0) "+" + notice.points else notice.points)
} else {
holder.noticesItemReason.text = notice.text
- holder.noticesItemTeacherName.text = notice.teacherFullName
+ holder.noticesItemTeacherName.text = notice.teacherName
}
holder.noticesItemAddedDate.text = Date.fromMillis(notice.addedDate).formattedString
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabFragment.kt
index 45472cf9..60c30cf9 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabFragment.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabFragment.kt
@@ -9,26 +9,25 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
-import androidx.sqlite.db.SimpleSQLiteQuery
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
-import pl.szczodrzynski.edziennik.data.db.entity.Event
-import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding
-import pl.szczodrzynski.edziennik.onClick
+import pl.szczodrzynski.edziennik.addOnPageSelectedListener
+import pl.szczodrzynski.edziennik.databinding.TemplateFragmentBinding
+import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter
import kotlin.coroutines.CoroutineContext
class LabFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "LabFragment"
+ var pageSelection = 0
}
private lateinit var app: App
private lateinit var activity: MainActivity
- private lateinit var b: LabFragmentBinding
+ private lateinit var b: TemplateFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
@@ -40,29 +39,30 @@ class LabFragment : Fragment(), CoroutineScope {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
- b = LabFragmentBinding.inflate(inflater)
+ b = TemplateFragmentBinding.inflate(inflater)
+ b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return
- b.last10unseen.onClick {
- launch(Dispatchers.Default) {
- val events = app.db.eventDao().getAllNow(App.profileId)
- val ids = events.sortedBy { it.date }.filter { it.type == Event.TYPE_HOMEWORK }.takeLast(10)
- ids.forEach {
- app.db.metadataDao().setSeen(App.profileId, it, false)
- }
+ val pagerAdapter = FragmentLazyPagerAdapter(
+ fragmentManager ?: return,
+ b.refreshLayout,
+ listOf(
+ LabPageFragment() to "click me",
+ LabProfileFragment() to "JSON"
+ )
+ )
+ b.viewPager.apply {
+ offscreenPageLimit = 1
+ adapter = pagerAdapter
+ currentItem = pageSelection
+ addOnPageSelectedListener {
+ pageSelection = it
}
- }
-
- b.rodo.onClick {
- app.db.teacherDao().query(SimpleSQLiteQuery("UPDATE teachers SET teacherSurname = \"\" WHERE profileId = ${App.profileId}"))
- }
-
- b.removeHomework.onClick {
- app.db.eventDao().getRawNow("UPDATE events SET homeworkBody = NULL WHERE profileId = ${App.profileId}")
+ b.tabLayout.setupWithViewPager(this)
}
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabJsonAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabJsonAdapter.kt
new file mode 100644
index 00000000..d1543cfc
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabJsonAdapter.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug
+
+import android.animation.ObjectAnimator
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.isInvisible
+import androidx.recyclerview.widget.RecyclerView
+import com.google.gson.JsonArray
+import com.google.gson.JsonElement
+import com.google.gson.JsonObject
+import com.google.gson.JsonPrimitive
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonArray
+import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonElement
+import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonObject
+import pl.szczodrzynski.edziennik.ui.modules.debug.viewholder.JsonArrayViewHolder
+import pl.szczodrzynski.edziennik.ui.modules.debug.viewholder.JsonElementViewHolder
+import pl.szczodrzynski.edziennik.ui.modules.debug.viewholder.JsonObjectViewHolder
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import kotlin.coroutines.CoroutineContext
+
+class LabJsonAdapter(
+ val activity: AppCompatActivity,
+ var onJsonElementClick: ((item: LabJsonElement) -> Unit)? = null
+) : RecyclerView.Adapter(), CoroutineScope {
+ companion object {
+ private const val TAG = "AttendanceAdapter"
+ private const val ITEM_TYPE_OBJECT = 0
+ private const val ITEM_TYPE_ARRAY = 1
+ private const val ITEM_TYPE_ELEMENT = 2
+ const val STATE_CLOSED = 0
+ const val STATE_OPENED = 1
+
+ fun expand(item: Any, level: Int): MutableList {
+ val json = when (item) {
+ is LabJsonObject -> item.jsonObject
+ is LabJsonArray -> item.jsonArray
+ is JsonObject -> item
+ is JsonArray -> item
+ is JsonPrimitive -> item
+ else -> return mutableListOf()
+ }
+
+ return when (json) {
+ is JsonObject -> json.entrySet().mapNotNull { wrap(it.key, it.value, level) }
+ is JsonArray -> json.mapIndexedNotNull { index, jsonElement -> wrap(index.toString(), jsonElement, level) }
+ else -> listOf(LabJsonElement("?", json, level))
+ }.toMutableList()
+ }
+ fun wrap(key: String, item: JsonElement, level: Int = 0): Any? {
+ return when (item) {
+ is JsonObject -> LabJsonObject(key, item, level + 1)
+ is JsonArray -> LabJsonArray(key, item, level + 1)
+ is JsonElement -> LabJsonElement(key, item, level + 1)
+ else -> null
+ }
+ }
+ }
+
+ private val app = activity.applicationContext as App
+
+ private val job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Main
+
+ var items = mutableListOf()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ return when (viewType) {
+ ITEM_TYPE_OBJECT -> JsonObjectViewHolder(inflater, parent)
+ ITEM_TYPE_ARRAY -> JsonArrayViewHolder(inflater, parent)
+ ITEM_TYPE_ELEMENT -> JsonElementViewHolder(inflater, parent)
+ else -> throw IllegalArgumentException("Incorrect viewType")
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return when (items[position]) {
+ is LabJsonObject -> ITEM_TYPE_OBJECT
+ is LabJsonArray -> ITEM_TYPE_ARRAY
+ is LabJsonElement -> ITEM_TYPE_ELEMENT
+ else -> throw IllegalArgumentException("Incorrect viewType")
+ }
+ }
+
+ private val onClickListener = View.OnClickListener { view ->
+ val model = view.getTag(R.string.tag_key_model)
+ if (model is LabJsonElement) {
+ onJsonElementClick?.invoke(model)
+ return@OnClickListener
+ }
+ if (model !is ExpandableItemModel<*>)
+ return@OnClickListener
+ expandModel(model, view)
+ }
+
+ fun expandModel(model: ExpandableItemModel<*>?, view: View?, notifyAdapter: Boolean = true) {
+ model ?: return
+ val position = items.indexOf(model)
+ if (position == -1)
+ return
+
+ view?.findViewById(R.id.dropdownIcon)?.let { dropdownIcon ->
+ ObjectAnimator.ofFloat(
+ dropdownIcon,
+ View.ROTATION,
+ if (model.state == STATE_CLOSED) 0f else 180f,
+ if (model.state == STATE_CLOSED) 180f else 0f
+ ).setDuration(200).start();
+ }
+
+ // hide the preview, show summary
+ val preview = view?.findViewById(R.id.previewContainer)
+ val summary = view?.findViewById(R.id.summaryContainer)
+ preview?.isInvisible = model.state == STATE_CLOSED
+ summary?.isInvisible = model.state != STATE_CLOSED
+
+ if (model.state == STATE_CLOSED) {
+
+ val subItems = when {
+ //model.items.isEmpty() -> listOf(AttendanceEmpty())
+ else -> expand(model, model.level)
+ }
+
+ model.state = STATE_OPENED
+ items.addAll(position + 1, subItems)
+ if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size)
+ }
+ else {
+ val start = position + 1
+ var end: Int = items.size
+ for (i in start until items.size) {
+ val model1 = items[i]
+ val level = (model1 as? ExpandableItemModel<*>)?.level ?: 3
+ if (level <= model.level) {
+ end = i
+ break
+ } else {
+ if (model1 is ExpandableItemModel<*> && model1.state == STATE_OPENED) {
+ model1.state = STATE_CLOSED
+ }
+ }
+ }
+
+ if (end != -1) {
+ items.subList(start, end).clear()
+ if (notifyAdapter) notifyItemRangeRemoved(start, end - start)
+ }
+
+ model.state = STATE_CLOSED
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ val item = items[position]
+ if (holder !is BindableViewHolder<*, *>)
+ return
+
+ val viewType = when (holder) {
+ is JsonObjectViewHolder -> ITEM_TYPE_OBJECT
+ is JsonArrayViewHolder -> ITEM_TYPE_ARRAY
+ is JsonElementViewHolder -> ITEM_TYPE_ELEMENT
+ else -> throw IllegalArgumentException("Incorrect viewType")
+ }
+ holder.itemView.setTag(R.string.tag_key_view_type, viewType)
+ holder.itemView.setTag(R.string.tag_key_position, position)
+ holder.itemView.setTag(R.string.tag_key_model, item)
+
+ when {
+ holder is JsonObjectViewHolder && item is LabJsonObject -> holder.onBind(activity, app, item, position, this)
+ holder is JsonArrayViewHolder && item is LabJsonArray -> holder.onBind(activity, app, item, position, this)
+ holder is JsonElementViewHolder && item is LabJsonElement -> holder.onBind(activity, app, item, position, this)
+ }
+
+ holder.itemView.setOnClickListener(onClickListener)
+ }
+
+ override fun getItemCount() = items.size
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt
new file mode 100644
index 00000000..bad9fd9f
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.sqlite.db.SimpleSQLiteQuery
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.data.db.entity.Event
+import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding
+import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
+import pl.szczodrzynski.fslogin.decode
+import kotlin.coroutines.CoroutineContext
+
+class LabPageFragment : LazyFragment(), CoroutineScope {
+ companion object {
+ private const val TAG = "LabPageFragment"
+ }
+
+ private lateinit var app: App
+ private lateinit var activity: MainActivity
+ private lateinit var b: LabFragmentBinding
+
+ private val job: Job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Main
+
+ // local/private variables go here
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ activity = (getActivity() as MainActivity?) ?: return null
+ context ?: return null
+ app = activity.application as App
+ b = LabFragmentBinding.inflate(inflater)
+ return b.root
+ }
+
+ override fun onPageCreated(): Boolean {
+ b.last10unseen.onClick {
+ launch(Dispatchers.Default) {
+ val events = app.db.eventDao().getAllNow(App.profileId)
+ val ids = events.sortedBy { it.date }.filter { it.type == Event.TYPE_HOMEWORK }.takeLast(10)
+ ids.forEach {
+ app.db.metadataDao().setSeen(App.profileId, it, false)
+ }
+ }
+ }
+
+ b.rodo.onClick {
+ app.db.teacherDao().query(SimpleSQLiteQuery("UPDATE teachers SET teacherSurname = \"\" WHERE profileId = ${App.profileId}"))
+ }
+
+ b.removeHomework.onClick {
+ app.db.eventDao().getRawNow("UPDATE events SET homeworkBody = NULL WHERE profileId = ${App.profileId}")
+ }
+
+ val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
+ startCoroutineTimer(500L, 300L) {
+ val text = app.cookieJar.sessionCookies
+ .map { it.cookie }
+ .sortedBy { it.domain() }
+ .groupBy { it.domain() }
+ .map {
+ listOf(
+ it.key.asBoldSpannable(),
+ ":\n",
+ it.value
+ .sortedBy { it.name() }
+ .map {
+ listOf(
+ " ",
+ it.name(),
+ "=",
+ it.value().decode().take(40).asItalicSpannable().asColoredSpannable(colorSecondary)
+ ).concat("")
+ }.concat("\n")
+ ).concat("")
+ }.concat("\n\n")
+ b.cookies.text = text
+ }
+
+ return true
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabProfileFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabProfileFragment.kt
new file mode 100644
index 00000000..cea59402
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabProfileFragment.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.gson.JsonObject
+import com.google.gson.JsonParser
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.MainActivity
+import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
+import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding
+import pl.szczodrzynski.edziennik.startCoroutineTimer
+import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
+import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
+import kotlin.coroutines.CoroutineContext
+
+class LabProfileFragment : LazyFragment(), CoroutineScope {
+ companion object {
+ private const val TAG = "LabProfileFragment"
+ }
+
+ private lateinit var app: App
+ private lateinit var activity: MainActivity
+ private lateinit var b: TemplateListPageFragmentBinding
+
+ private val job: Job = Job()
+ override val coroutineContext: CoroutineContext
+ get() = job + Dispatchers.Main
+
+ // local/private variables go here
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ activity = (getActivity() as MainActivity?) ?: return null
+ context ?: return null
+ app = activity.application as App
+ b = TemplateListPageFragmentBinding.inflate(inflater)
+ return b.root
+ }
+
+ override fun onPageCreated(): Boolean { startCoroutineTimer(100L) {
+ val adapter = LabJsonAdapter(activity)
+ val json = JsonObject().also { json ->
+ json.add("app.profile", app.profile.studentData)
+ json.add("app.config", JsonParser().parse(app.gson.toJson(app.config.values)))
+ EdziennikTask.profile?.let {
+ json.add("API.profile", it.studentData)
+ } ?: {
+ json.addProperty("API.profile", "null")
+ }()
+ EdziennikTask.loginStore?.let {
+ json.add("API.loginStore", it.data)
+ } ?: {
+ json.addProperty("API.loginStore", "null")
+ }()
+ }
+ adapter.items = LabJsonAdapter.expand(json, 0)
+
+ b.list.adapter = adapter
+ b.list.apply {
+ setHasFixedSize(true)
+ layoutManager = LinearLayoutManager(context)
+ addItemDecoration(SimpleDividerItemDecoration(context))
+ addOnScrollListener(onScrollListener)
+ }
+
+ // show/hide relevant views
+ b.progressBar.isVisible = false
+ b.list.isVisible = true
+ b.noData.isVisible = false
+
+ }; return true }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonArray.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonArray.kt
new file mode 100644
index 00000000..a606e238
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonArray.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug.models
+
+import com.google.gson.JsonArray
+import com.google.gson.JsonElement
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+
+data class LabJsonArray(
+ val key: String,
+ val jsonArray: JsonArray,
+ override var level: Int
+) : ExpandableItemModel(jsonArray.toMutableList())
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonElement.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonElement.kt
new file mode 100644
index 00000000..e0e0e686
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonElement.kt
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug.models
+
+import com.google.gson.JsonElement
+
+data class LabJsonElement(
+ val key: String,
+ val jsonElement: JsonElement,
+ var level: Int
+)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonObject.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonObject.kt
new file mode 100644
index 00000000..43a69201
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/models/LabJsonObject.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug.models
+
+import com.google.gson.JsonElement
+import com.google.gson.JsonObject
+import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
+
+data class LabJsonObject(
+ val key: String,
+ val jsonObject: JsonObject,
+ override var level: Int
+) : ExpandableItemModel(jsonObject.entrySet().map { it.value }.toMutableList())
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonArrayViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonArrayViewHolder.kt
new file mode 100644
index 00000000..08d4cd3c
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonArrayViewHolder.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug.viewholder
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.core.view.isInvisible
+import androidx.recyclerview.widget.RecyclerView
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.databinding.LabItemObjectBinding
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
+import pl.szczodrzynski.edziennik.ui.modules.debug.LabJsonAdapter
+import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonArray
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import pl.szczodrzynski.edziennik.utils.Themes
+
+class JsonArrayViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: LabItemObjectBinding = LabItemObjectBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "JsonArrayViewHolder"
+ }
+
+ @SuppressLint("SetTextI18n")
+ override fun onBind(activity: AppCompatActivity, app: App, item: LabJsonArray, position: Int, adapter: LabJsonAdapter) {
+ val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
+
+ b.dropdownIcon.rotation = when (item.state) {
+ AttendanceAdapter.STATE_CLOSED -> 0f
+ else -> 180f
+ }
+ b.previewContainer.isInvisible = item.state != AttendanceAdapter.STATE_CLOSED
+ b.summaryContainer.isInvisible = item.state == AttendanceAdapter.STATE_CLOSED
+
+ b.key.text = item.key
+ b.previewContainer.text = item.jsonArray.toString().take(200)
+ b.summaryContainer.text = item.jsonArray.size().toString() + " elements"
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonElementViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonElementViewHolder.kt
new file mode 100644
index 00000000..8ce6b461
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonElementViewHolder.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug.viewholder
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.recyclerview.widget.RecyclerView
+import com.google.gson.JsonNull
+import com.google.gson.JsonPrimitive
+import pl.szczodrzynski.edziennik.*
+import pl.szczodrzynski.edziennik.databinding.LabItemElementBinding
+import pl.szczodrzynski.edziennik.ui.modules.debug.LabJsonAdapter
+import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonElement
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import pl.szczodrzynski.edziennik.utils.Themes
+
+class JsonElementViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: LabItemElementBinding = LabItemElementBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "JsonObjectViewHolder"
+ }
+
+ @SuppressLint("SetTextI18n")
+ override fun onBind(activity: AppCompatActivity, app: App, item: LabJsonElement, position: Int, adapter: LabJsonAdapter) {
+ val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
+
+ b.type.text = when (item.jsonElement) {
+ is JsonPrimitive -> when {
+ item.jsonElement.isNumber -> "Number"
+ item.jsonElement.isString -> "String"
+ item.jsonElement.isBoolean -> "Boolean"
+ else -> "Primitive"
+ }
+ is JsonNull -> "null"
+ else -> null
+ }
+
+ val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
+ b.key.text = listOf(
+ item.key.asColoredSpannable(colorSecondary),
+ ": ",
+ item.jsonElement.toString().asItalicSpannable()
+ ).concat("")
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonObjectViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonObjectViewHolder.kt
new file mode 100644
index 00000000..3131b0ea
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/viewholder/JsonObjectViewHolder.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-5-12.
+ */
+
+package pl.szczodrzynski.edziennik.ui.modules.debug.viewholder
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.core.view.isInvisible
+import androidx.recyclerview.widget.RecyclerView
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.databinding.LabItemObjectBinding
+import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
+import pl.szczodrzynski.edziennik.ui.modules.debug.LabJsonAdapter
+import pl.szczodrzynski.edziennik.ui.modules.debug.models.LabJsonObject
+import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
+import pl.szczodrzynski.edziennik.utils.Themes
+
+class JsonObjectViewHolder(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ val b: LabItemObjectBinding = LabItemObjectBinding.inflate(inflater, parent, false)
+) : RecyclerView.ViewHolder(b.root), BindableViewHolder {
+ companion object {
+ private const val TAG = "JsonObjectViewHolder"
+ }
+
+ @SuppressLint("SetTextI18n")
+ override fun onBind(activity: AppCompatActivity, app: App, item: LabJsonObject, position: Int, adapter: LabJsonAdapter) {
+ val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
+
+ b.dropdownIcon.rotation = when (item.state) {
+ AttendanceAdapter.STATE_CLOSED -> 0f
+ else -> 180f
+ }
+ b.previewContainer.isInvisible = item.state != AttendanceAdapter.STATE_CLOSED
+ b.summaryContainer.isInvisible = item.state == AttendanceAdapter.STATE_CLOSED
+
+ b.key.text = item.key
+ b.previewContainer.text = item.jsonObject.toString().take(200)
+ b.summaryContainer.text = item.jsonObject.size().toString() + " elements"
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/editor/GradesEditorFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/editor/GradesEditorFragment.kt
index 3e5978ee..6520ce54 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/editor/GradesEditorFragment.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/editor/GradesEditorFragment.kt
@@ -206,7 +206,7 @@ class GradesEditorFragment : Fragment() {
gradeCountSemester = 0f
averageSemester = 0f
- app.db.gradeDao().getAllWhere(App.profileId, "subjectId = " + subject.id).observe(this, Observer { grades ->
+ app.db.gradeDao().getAllBySubject(App.profileId, subject.id).observe(this, Observer { grades ->
for (grade in grades) {
if (grade.type == Grade.TYPE_NORMAL) {
if (grade.weight < 0) {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/ExpandableItemModel.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/ExpandableItemModel.kt
index c52ba310..20cb3ffc 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/ExpandableItemModel.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/ExpandableItemModel.kt
@@ -6,7 +6,7 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.models
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter.Companion.STATE_CLOSED
-abstract class ExpandableItemModel(val items: MutableList) {
+abstract class ExpandableItemModel(open val items: MutableList) {
open var level: Int = 3
var state: Int = STATE_CLOSED
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt
index e71b7819..2af79ba3 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt
@@ -52,7 +52,7 @@ class GradeViewHolder(
b.gradeWeight.text = weightText
b.gradeWeight.isVisible = weightText != null
- b.gradeTeacherName.text = grade.teacherFullName
+ b.gradeTeacherName.text = grade.teacherName
b.gradeAddedDate.text = Date.fromMillis(grade.addedDate).let {
it.getRelativeString(app, 5) ?: it.formattedStringShort
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/CounterActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/CounterActivity.kt
index 36002444..e337f720 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/CounterActivity.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/CounterActivity.kt
@@ -51,7 +51,7 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
withContext(Dispatchers.Default) {
lessonList.apply {
clear()
- addAll(app.db.timetableDao().getForDateNow(App.profileId, Date.getToday())
+ addAll(app.db.timetableDao().getAllForDateNow(App.profileId, Date.getToday())
.filter {
it.type != Lesson.TYPE_NO_LESSONS && it.type != Lesson.TYPE_CANCELLED &&
it.type != Lesson.TYPE_SHIFTED_SOURCE
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeGradesCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeGradesCard.kt
index 0e26eb2c..f1c3373e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeGradesCard.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeGradesCard.kt
@@ -35,6 +35,7 @@ import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
import pl.szczodrzynski.edziennik.utils.Utils
+import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel
import kotlin.coroutines.CoroutineContext
@@ -63,7 +64,7 @@ class HomeGradesCard(
}
holder.root += b.root
- val sevenDaysAgo = System.currentTimeMillis() - 7 * 24 * 60 * 60 * 1000
+ val sevenDaysAgo = Date.getToday().stepForward(0, 0, -7)
app.db.gradeDao().getAllFromDate(profile.id, sevenDaysAgo).observe(fragment, Observer {
grades.apply {
@@ -120,7 +121,8 @@ class HomeGradesCard(
val gradeName = GradeView(
gradeItem.context,
grade,
- app.gradesManager
+ app.gradesManager,
+ periodGradesTextual = true
)
totalWidth += gradeName.measuredWidth + 5.dp
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeLuckyNumberCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeLuckyNumberCard.kt
index c5e4aede..31611e48 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeLuckyNumberCard.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeLuckyNumberCard.kt
@@ -60,7 +60,7 @@ class HomeLuckyNumberCard(
R.string.home_lucky_number_details
b.subText.setText(subTextRes, profile.name ?: "", profile.studentNumber)
- app.db.luckyNumberDao().getNearestFuture(profile.id, todayValue).observe(fragment, Observer { luckyNumber ->
+ app.db.luckyNumberDao().getNearestFuture(profile.id, today).observe(fragment, Observer { luckyNumber ->
val isYours = luckyNumber?.number == profile.studentNumber
val res: Pair> = when {
luckyNumber == null -> R.string.home_lucky_number_no_info to emptyArray()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.kt
index 7f65279f..c686b857 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.kt
@@ -1,3 +1,7 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2020-4-16.
+ */
+
package pl.szczodrzynski.edziennik.ui.modules.login
import android.app.Activity
@@ -14,20 +18,19 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
-import pl.szczodrzynski.edziennik.databinding.ActivityLoginBinding
+import pl.szczodrzynski.edziennik.databinding.LoginActivityBinding
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
import kotlin.coroutines.CoroutineContext
class LoginActivity : AppCompatActivity(), CoroutineScope {
companion object {
private const val TAG = "LoginActivity"
- @JvmField
- var navOptions: NavOptions? = null
var thisOneIsTricky = 0
}
private val app: App by lazy { applicationContext as App }
- private lateinit var b: ActivityLoginBinding
+ private lateinit var b: LoginActivityBinding
+ lateinit var navOptions: NavOptions
val nav by lazy { Navigation.findNavController(this, R.id.nav_host_fragment) }
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
@@ -36,7 +39,7 @@ class LoginActivity : AppCompatActivity(), CoroutineScope {
get() = job + Dispatchers.Main
var lastError: ApiError? = null
- val profiles = mutableListOf()
+ val profiles = mutableListOf()
val loginStores = mutableListOf