From a2804d813a73014e8b8d27bdc381b7124f1322d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 May 2022 08:41:23 +0000 Subject: [PATCH 001/164] Bump firebase-bom from 30.0.1 to 30.0.2 (#1872) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7db0acebb..57ad5c903 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -233,7 +233,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.7.0' - playImplementation platform('com.google.firebase:firebase-bom:30.0.1') + playImplementation platform('com.google.firebase:firebase-bom:30.0.2') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From dc717c9fb55a8c7d7305be76e19f68e2553ac31f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 May 2022 08:43:32 +0000 Subject: [PATCH 002/164] Bump core-splashscreen from 1.0.0-beta02 to 1.0.0-rc01 (#1871) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 57ad5c903..2cfb7bc6d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" implementation "androidx.core:core-ktx:1.7.0" - implementation 'androidx.core:core-splashscreen:1.0.0-beta02' + implementation 'androidx.core:core-splashscreen:1.0.0-rc01' implementation "androidx.activity:activity-ktx:1.4.0" implementation "androidx.appcompat:appcompat:1.4.1" implementation "androidx.fragment:fragment-ktx:1.4.1" From 808927a58a2e90ee7ddea790d645e407c26d71dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 May 2022 08:43:52 +0000 Subject: [PATCH 003/164] Bump coil from 2.0.0 to 2.1.0 (#1870) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 2cfb7bc6d..b05e0501c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -228,7 +228,7 @@ dependencies { implementation "at.favre.lib:slf4j-timber:1.0.1" implementation 'com.github.bastienpaulfr:Treessence:1.0.5' implementation "com.mikepenz:aboutlibraries-core:$about_libraries" - implementation "io.coil-kt:coil:2.0.0" + implementation "io.coil-kt:coil:2.1.0" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.7.0' From fa48b033af2fa28f53acdea7d6c35eda4615fb35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 May 2022 08:44:17 +0000 Subject: [PATCH 004/164] Bump constraintlayout from 2.1.3 to 2.1.4 (#1869) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b05e0501c..ab1593f82 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "androidx.recyclerview:recyclerview:1.2.1" implementation "androidx.viewpager2:viewpager2:1.1.0-beta01" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" - implementation "androidx.constraintlayout:constraintlayout:2.1.3" + implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" implementation "com.google.android.material:material:1.5.0" implementation "com.github.wulkanowy:material-chips-input:2.3.1" From c42a47ac48007e2db05f8f7a1c563414fcad0e7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 May 2022 04:18:37 +0000 Subject: [PATCH 005/164] Bump material from 1.5.0 to 1.6.0 (#1846) --- app/build.gradle | 2 +- .../ui/modules/timetablewidget/TimetableWidgetProvider.kt | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ab1593f82..57ba0f591 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,7 +198,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" - implementation "com.google.android.material:material:1.5.0" + implementation "com.google.android.material:material:1.6.0" implementation "com.github.wulkanowy:material-chips-input:2.3.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation 'com.github.lopspower:CircularImageView:4.2.0' diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt index 07e717eaf..745769864 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.timetablewidget -import android.annotation.SuppressLint import android.app.PendingIntent import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager.* @@ -132,7 +131,6 @@ class TimetableWidgetProvider : BroadcastReceiver() { } } - @SuppressLint("DefaultLocale") private fun updateWidget( context: Context, appWidgetId: Int, From fcf0adfd807015178cdd9bb631c4784cf8f0380f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 May 2022 05:26:52 +0000 Subject: [PATCH 006/164] Bump gradle from 7.1.3 to 7.2.0 (#1857) --- app/build.gradle | 7 +++++-- app/src/main/AndroidManifest.xml | 1 - build.gradle | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 57ba0f591..2a36b19a9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,6 +15,7 @@ apply from: 'sonarqube.gradle' apply from: 'hooks.gradle' android { + namespace 'io.github.wulkanowy' compileSdkVersion 31 defaultConfig { @@ -136,8 +137,10 @@ android { } packagingOptions { - exclude 'META-INF/library_release.kotlin_module' - exclude 'META-INF/library-core_release.kotlin_module' + resources { + excludes += ['META-INF/library_release.kotlin_module', + 'META-INF/library-core_release.kotlin_module'] + } } aboutLibraries { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 72fee08a7..7835db902 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ diff --git a/build.gradle b/build.gradle index c87ec1506..d778e9a63 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath 'com.android.tools.build:gradle:7.1.3' + classpath 'com.android.tools.build:gradle:7.2.1' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.10' classpath 'com.huawei.agconnect:agcp:1.6.6.200' From 5c4a3d578bad1b5ace29a395b94a150529ca9c8b Mon Sep 17 00:00:00 2001 From: Michael <5672750+mibac138@users.noreply.github.com> Date: Fri, 27 May 2022 22:19:22 +0200 Subject: [PATCH 007/164] Add grade sorting by average (#1863) --- .../wulkanowy/data/enums/GradeSortingMode.kt | 5 +++-- .../grade/details/GradeDetailsPresenter.kt | 5 +++-- .../grade/summary/GradeSummaryPresenter.kt | 16 +++++++++++++++- app/src/main/res/values/preferences_values.xml | 2 ++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt index c5c0196c9..a7aa4cc2f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt +++ b/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt @@ -2,9 +2,10 @@ package io.github.wulkanowy.data.enums enum class GradeSortingMode(val value: String) { ALPHABETIC("alphabetic"), - DATE("date"); + DATE("date"), + AVERAGE("average"); companion object { fun getByValue(value: String) = values().find { it.value == value } ?: ALPHABETIC } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 746601a68..8cde5d6b9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.grade.details import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.enums.GradeExpandMode -import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC -import io.github.wulkanowy.data.enums.GradeSortingMode.DATE +import io.github.wulkanowy.data.enums.GradeSortingMode +import io.github.wulkanowy.data.enums.GradeSortingMode.* import io.github.wulkanowy.data.repositories.GradeRepository import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository @@ -204,6 +204,7 @@ class GradeDetailsPresenter @Inject constructor( ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage -> gradeDetailsWithAverage.subject.lowercase() } + AVERAGE -> gradeSubjects.sortedByDescending { it.average } } } .map { (subject, average, points, _, grades) -> diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index b07570cb2..4d5a43d8f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -2,6 +2,9 @@ package io.github.wulkanowy.ui.modules.grade.summary import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.enums.GradeSortingMode +import io.github.wulkanowy.data.enums.GradeSortingMode.* +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler @@ -14,6 +17,7 @@ import javax.inject.Inject class GradeSummaryPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, + private val preferencesRepository: PreferencesRepository, private val averageProvider: GradeAverageProvider, private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -127,7 +131,17 @@ class GradeSummaryPresenter @Inject constructor( private fun createGradeSummaryItems(items: List): List { return items .filter { !checkEmpty(it) } - .sortedBy { it.subject } + .let { gradeSubjects -> + when (preferencesRepository.gradeSortingMode) { + DATE -> gradeSubjects.sortedByDescending { gradeDetailsWithAverage -> + gradeDetailsWithAverage.grades.maxByOrNull { it.date }?.date + } + ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage -> + gradeDetailsWithAverage.subject.lowercase() + } + AVERAGE -> gradeSubjects.sortedByDescending { it.average } + } + } .map { it.summary.copy(average = it.average) } } diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml index 1d777bdb6..312f0b878 100644 --- a/app/src/main/res/values/preferences_values.xml +++ b/app/src/main/res/values/preferences_values.xml @@ -82,10 +82,12 @@ Alphabetically By date + By average alphabetic date + average From 891e241d1a7cb516a9233d0e5c270d0593e35600 Mon Sep 17 00:00:00 2001 From: Michael <5672750+mibac138@users.noreply.github.com> Date: Sat, 28 May 2022 02:03:42 +0200 Subject: [PATCH 008/164] Hide account selector in MessagePreviewView and SendMessageView (#1866) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rafał Borcz --- .../message/preview/MessagePreviewFragment.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 860ecc571..4b2685c6d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -57,7 +57,8 @@ class MessagePreviewFragment : get() = getString(R.string.message_no_subject) override val printHTML: String - get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() } + get() = requireContext().assets.open("message-print-page.html").bufferedReader() + .use { it.readText() } override val messageNotExists: String get() = getString(R.string.message_not_exists) @@ -81,7 +82,10 @@ class MessagePreviewFragment : super.onViewCreated(view, savedInstanceState) binding = FragmentMessagePreviewBinding.bind(view) messageContainer = binding.messagePreviewContainer - presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message) + presenter.onAttachView( + this, + (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message + ) } override fun initView() { @@ -101,6 +105,8 @@ class MessagePreviewFragment : menuShareButton = menu.findItem(R.id.messagePreviewMenuShare) menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint) presenter.onCreateOptionsMenu() + + menu.findItem(R.id.mainMenuAccount).isVisible = false } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -173,7 +179,8 @@ class MessagePreviewFragment : val webView = WebView(requireContext()) webView.webViewClient = object : WebViewClient() { - override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = + false override fun onPageFinished(view: WebView, url: String) { createWebPrintJob(view, jobName) From d2d1d1dba71a7e6b03a78fe921fc15534cffd080 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 13:50:03 +0000 Subject: [PATCH 009/164] Bump firebase-bom from 30.0.2 to 30.1.0 (#1878) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 2a36b19a9..691d00f64 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -236,7 +236,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.7.0' - playImplementation platform('com.google.firebase:firebase-bom:30.0.2') + playImplementation platform('com.google.firebase:firebase-bom:30.1.0') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From d074e5c9b322e317e010ffb0744b643f2df2541a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 13:50:22 +0000 Subject: [PATCH 010/164] Bump play-services-ads from 20.6.0 to 21.0.0 (#1877) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 691d00f64..c9c7a9ef9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -242,7 +242,7 @@ dependencies { playImplementation 'com.google.firebase:firebase-crashlytics:' playImplementation 'com.google.android.play:core:1.10.3' playImplementation 'com.google.android.play:core-ktx:1.8.1' - playImplementation 'com.google.android.gms:play-services-ads:20.6.0' + playImplementation 'com.google.android.gms:play-services-ads:21.0.0' hmsImplementation 'com.huawei.hms:hianalytics:6.5.0.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.6.200' From 4f3f24ac104a986a8dd42425668ed9ee576b825e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 13:50:52 +0000 Subject: [PATCH 011/164] Bump about_libraries from 10.2.0 to 10.3.0 (#1876) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d778e9a63..5a498be90 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { kotlin_version = '1.6.21' - about_libraries = '10.2.0' + about_libraries = '10.3.0' hilt_version = "2.42" } repositories { From 8dcb3ed45d2625b4208df856099ad58c29a29d3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 13:51:12 +0000 Subject: [PATCH 012/164] Bump firebase-crashlytics-gradle from 2.8.1 to 2.9.0 (#1874) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5a498be90..7640fbc48 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ buildscript { classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.10' classpath 'com.huawei.agconnect:agcp:1.6.6.200' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3" From 8c515bd03f73f41d82f69c29a2d52f5537ae53c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 13:52:09 +0000 Subject: [PATCH 013/164] Bump coroutines from 1.6.1 to 1.6.2 (#1875) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c9c7a9ef9..612cb9536 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -177,7 +177,7 @@ ext { room = "2.4.2" chucker = "3.5.2" mockk = "1.12.4" - coroutines = "1.6.1" + coroutines = "1.6.2" } dependencies { From cce736410bd54e0f186191623be47c09582b89b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 04:59:31 +0000 Subject: [PATCH 014/164] Bump material from 1.6.0 to 1.6.1 (#1884) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 612cb9536..1038184f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -201,7 +201,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" - implementation "com.google.android.material:material:1.6.0" + implementation "com.google.android.material:material:1.6.1" implementation "com.github.wulkanowy:material-chips-input:2.3.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation 'com.github.lopspower:CircularImageView:4.2.0' From 03ad5527f8d83526d751042a5ef32d77a6056392 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 04:59:50 +0000 Subject: [PATCH 015/164] Bump appcompat from 1.4.1 to 1.4.2 (#1883) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1038184f2..1f680e265 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -191,7 +191,7 @@ dependencies { implementation "androidx.core:core-ktx:1.7.0" implementation 'androidx.core:core-splashscreen:1.0.0-rc01' implementation "androidx.activity:activity-ktx:1.4.0" - implementation "androidx.appcompat:appcompat:1.4.1" + implementation "androidx.appcompat:appcompat:1.4.2" implementation "androidx.fragment:fragment-ktx:1.4.1" implementation "androidx.annotation:annotation:1.3.0" From f61d820d6fd33babb7d85cee7dff22ba80eb5d42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 05:07:54 +0000 Subject: [PATCH 016/164] Bump core-ktx from 1.7.0 to 1.8.0 (#1882) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1f680e265..aa7f6b37b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -188,7 +188,7 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" - implementation "androidx.core:core-ktx:1.7.0" + implementation "androidx.core:core-ktx:1.8.0" implementation 'androidx.core:core-splashscreen:1.0.0-rc01' implementation "androidx.activity:activity-ktx:1.4.0" implementation "androidx.appcompat:appcompat:1.4.2" From c3cbaa6ac2f4a58b36572ed64e0d2b68049c795f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Mon, 13 Jun 2022 07:43:12 +0200 Subject: [PATCH 017/164] Update dependencies (#1887) --- app/build.gradle | 4 ++-- .../wulkanowy/ui/modules/login/LoginActivity.kt | 1 + .../ui/modules/login/recover/LoginRecoverFragment.kt | 11 ++++------- .../github/wulkanowy/ui/modules/main/MainActivity.kt | 1 + build.gradle | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index aa7f6b37b..230afdfe9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,13 +16,13 @@ apply from: 'hooks.gradle' android { namespace 'io.github.wulkanowy' - compileSdkVersion 31 + compileSdkVersion 32 defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 32 versionCode 108 versionName "1.6.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt index d7d77f73d..aac60b56d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -93,6 +93,7 @@ class LoginActivity : BaseActivity(), Logi } //https://developer.android.com/guide/playcore/in-app-updates#status_callback + @Deprecated("Deprecated in Java") @Suppress("DEPRECATION") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt index c1c111d46..786bbfce8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt @@ -6,9 +6,7 @@ import android.os.Bundle import android.view.View import android.view.View.GONE import android.view.View.VISIBLE -import android.webkit.JavascriptInterface -import android.webkit.WebView -import android.webkit.WebViewClient +import android.webkit.* import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import com.yariksoffice.lingver.Lingver @@ -206,10 +204,9 @@ class LoginRecoverFragment : } override fun onReceivedError( - view: WebView, - errorCode: Int, - description: String, - failingUrl: String + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError? ) { recoverWebViewSuccess = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index 1bfc8ba58..260cf76c5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -100,6 +100,7 @@ class MainActivity : BaseActivity(), MainVie } //https://developer.android.com/guide/playcore/in-app-updates#status_callback + @Deprecated("Deprecated in Java") @Suppress("DEPRECATION") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) diff --git a/build.gradle b/build.gradle index 7640fbc48..3a8ae20cc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - kotlin_version = '1.6.21' + kotlin_version = '1.7.0' about_libraries = '10.3.0' hilt_version = "2.42" } From 6b705835735892114de3d666817ee00a3b69a0f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Mon, 13 Jun 2022 07:43:40 +0200 Subject: [PATCH 018/164] New Crowdin updates (#1873) --- app/src/main/res/values-cs/preferences_values.xml | 1 + app/src/main/res/values-de/preferences_values.xml | 1 + app/src/main/res/values-pl/preferences_values.xml | 1 + app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-ru/preferences_values.xml | 1 + app/src/main/res/values-sk/preferences_values.xml | 1 + app/src/main/res/values-uk/preferences_values.xml | 1 + 7 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-cs/preferences_values.xml b/app/src/main/res/values-cs/preferences_values.xml index 5252f79b8..c8731372f 100644 --- a/app/src/main/res/values-cs/preferences_values.xml +++ b/app/src/main/res/values-cs/preferences_values.xml @@ -34,6 +34,7 @@ Abecedně Podle data + By average Dzienniczek+ diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml index 08b9d240b..097e90e96 100644 --- a/app/src/main/res/values-de/preferences_values.xml +++ b/app/src/main/res/values-de/preferences_values.xml @@ -34,6 +34,7 @@ Alphabetisch Nach Datum + By average Dzienniczek+ diff --git a/app/src/main/res/values-pl/preferences_values.xml b/app/src/main/res/values-pl/preferences_values.xml index c823e9608..456005747 100644 --- a/app/src/main/res/values-pl/preferences_values.xml +++ b/app/src/main/res/values-pl/preferences_values.xml @@ -34,6 +34,7 @@ Alfabetycznie Według daty + Według średniej Dzienniczek+ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 1607b17c6..91e9fe7f3 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -94,7 +94,7 @@ Przewidywana ocena Obliczona średnia Jak działa obliczona średnia? - Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semetrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej z zsumowanych średnich + Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej z zsumowanych średnich Jak działa końcowa średnia? Średnią końcową jest średnia arytmetyczna obliczona na podstawie wszystkich obecnie dostępnych ocen końcowych w danym semestrze.\n\nSchemat obliczeń składa się z następujących kroków:\n1. Sumowanie końcowych ocen wpisanych przez nauczycieli\n2. Dzielenie przez liczbę przedmiotów, z których oceny zostały już wystawione Końcowa średnia diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml index 9cc37620e..8a8c260da 100644 --- a/app/src/main/res/values-ru/preferences_values.xml +++ b/app/src/main/res/values-ru/preferences_values.xml @@ -34,6 +34,7 @@ В алфавитном порядке По дате + По средней Dzienniczek+ diff --git a/app/src/main/res/values-sk/preferences_values.xml b/app/src/main/res/values-sk/preferences_values.xml index e64f5606a..ab0a43b64 100644 --- a/app/src/main/res/values-sk/preferences_values.xml +++ b/app/src/main/res/values-sk/preferences_values.xml @@ -34,6 +34,7 @@ Abecedne Podľa dátumu + By average Dzienniczek+ diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml index 82c5b6ecc..44acd18e9 100644 --- a/app/src/main/res/values-uk/preferences_values.xml +++ b/app/src/main/res/values-uk/preferences_values.xml @@ -34,6 +34,7 @@ За алфавітом За датою + За середньою Dzienniczek+ From 06ed5f6079ae17c2a63b2a9088fff476c5a4b4b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jun 2022 21:53:21 +0000 Subject: [PATCH 019/164] Bump logging-interceptor from 4.9.3 to 4.10.0 (#1889) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 230afdfe9..f3f39258c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -225,7 +225,7 @@ dependencies { implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0" - implementation "com.squareup.okhttp3:logging-interceptor:4.9.3" + implementation "com.squareup.okhttp3:logging-interceptor:4.10.0" implementation "com.jakewharton.timber:timber:5.0.1" implementation "at.favre.lib:slf4j-timber:1.0.1" From cd59166efbd4bea7f79f2cdfc262212e0fcee8d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jun 2022 22:15:18 +0000 Subject: [PATCH 020/164] Bump sonarqube-gradle-plugin from 3.3 to 3.4.0.2513 (#1888) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3a8ae20cc..9dde0b283 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3" - classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3" + classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513" classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries" } From bfab265ccf4f5a0809788d92f535c7106bef8a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 18 Jun 2022 11:54:08 +0200 Subject: [PATCH 021/164] Fix case sensitive domain checker (#1894) --- app/build.gradle | 2 +- .../wulkanowy/ui/modules/login/form/LoginFormPresenter.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f3f39258c..e629134e0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -181,7 +181,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.6.4" + implementation "io.github.wulkanowy:sdk:16811fbe90" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index b4291ff47..0acb0ea6d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -172,7 +172,7 @@ class LoginFormPresenter @Inject constructor( if ("@" in login && "||" !in login && "login" !in host && "email" !in host) { val emailHost = login.substringAfter("@") val emailDomain = URL(host).host - if (emailHost != emailDomain) { + if (!emailHost.equals(emailDomain, true)) { view?.setErrorEmailInvalid(domain = emailDomain) isCorrect = false } From 0a2eb0784469afd19c837af49a1e7d5e45001567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 18 Jun 2022 12:11:46 +0200 Subject: [PATCH 022/164] Fix date in attendance and timetable when day is changing (#1893) --- .../modules/attendance/AttendancePresenter.kt | 18 +++++++++++------- .../ui/modules/timetable/TimetablePresenter.kt | 18 +++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 7fcbd002e..26bfaf19f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -91,15 +91,19 @@ class AttendancePresenter @Inject constructor( fun onViewReselected() { Timber.i("Attendance view is reselected") - view?.also { view -> + view?.let { view -> if (view.currentStackSize == 1) { - baseDate.also { - if (currentDate != it) { - reloadView(it) - loadData() - } else if (!view.isViewEmpty) view.resetView() + baseDate = now().previousOrSameSchoolDay + + if (currentDate != baseDate) { + reloadView(baseDate) + loadData() + } else if (!view.isViewEmpty) { + view.resetView() } - } else view.popView() + } else { + view.popView() + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index dc6c89213..d06874082 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -87,15 +87,19 @@ class TimetablePresenter @Inject constructor( fun onViewReselected() { Timber.i("Timetable view is reselected") - view?.also { view -> + view?.let { view -> if (view.currentStackSize == 1) { - baseDate.also { - if (currentDate != it) { - reloadView(it) - loadData() - } else if (!view.isViewEmpty) view.resetView() + baseDate = now().nextOrSameSchoolDay + + if (currentDate != baseDate) { + reloadView(baseDate) + loadData() + } else if (!view.isViewEmpty) { + view.resetView() } - } else view.popView() + } else { + view.popView() + } } } From a264abf8144f8ce4ba8f9370333ef8163a0e87d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 18 Jun 2022 12:12:04 +0200 Subject: [PATCH 023/164] Fix typo in average description (#1892) --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ff25da7f5..2ca516adf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,7 +104,7 @@ Predicted grade Calculated average How does Calculated Average work? - The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n3. Adding calculated averages\n4. Calculating the arithmetic average of summed averages + The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages How does the Final Average work? The Final Average is the arithmetic average calculated from all currently available final grades in the given semester.\n\nThe calculation scheme consists of the following steps:\n1. Summing up the final grades given by teachers\n2. Divide by the number of subjects that have already been graded Final average From e9ba65f8f67e0afab0a1f27740abb863810cbac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 18 Jun 2022 12:12:21 +0200 Subject: [PATCH 024/164] Fix multiline address in student info (#1891) --- app/src/main/res/layout/fragment_student_info.xml | 4 ++-- app/src/main/res/layout/item_student_info.xml | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/fragment_student_info.xml b/app/src/main/res/layout/fragment_student_info.xml index d270da4a0..f72fed2ff 100644 --- a/app/src/main/res/layout/fragment_student_info.xml +++ b/app/src/main/res/layout/fragment_student_info.xml @@ -37,7 +37,7 @@ android:padding="10dp" android:visibility="gone" tools:ignore="UseCompoundDrawables" - tools:visibility="visible"> + tools:visibility="gone"> + tools:visibility="gone"> + android:focusable="true" + android:minHeight="64dp"> Date: Sun, 19 Jun 2022 21:04:05 +0200 Subject: [PATCH 025/164] Fix jumping point in notes on refresh (#1898) --- app/src/main/res/layout/fragment_note.xml | 4 +++- app/src/main/res/layout/item_note.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/fragment_note.xml b/app/src/main/res/layout/fragment_note.xml index f62a11a64..969003cc0 100644 --- a/app/src/main/res/layout/fragment_note.xml +++ b/app/src/main/res/layout/fragment_note.xml @@ -19,7 +19,9 @@ + android:layout_height="match_parent" + tools:itemCount="4" + tools:listitem="@layout/item_note" /> Date: Thu, 23 Jun 2022 12:09:25 +0000 Subject: [PATCH 026/164] Bump coroutines from 1.6.2 to 1.6.3 (#1902) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index e629134e0..7c8046dad 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -177,7 +177,7 @@ ext { room = "2.4.2" chucker = "3.5.2" mockk = "1.12.4" - coroutines = "1.6.2" + coroutines = "1.6.3" } dependencies { From c5dfea788c1a492b3cf06f4d00c99ac1fb4899a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 12:09:50 +0000 Subject: [PATCH 027/164] Bump annotation from 1.3.0 to 1.4.0 (#1900) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7c8046dad..3e4912063 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -193,7 +193,7 @@ dependencies { implementation "androidx.activity:activity-ktx:1.4.0" implementation "androidx.appcompat:appcompat:1.4.2" implementation "androidx.fragment:fragment-ktx:1.4.1" - implementation "androidx.annotation:annotation:1.3.0" + implementation "androidx.annotation:annotation:1.4.0" implementation "androidx.preference:preference-ktx:1.2.0" implementation "androidx.recyclerview:recyclerview:1.2.1" From 0fb55bd6c64c78f1b4e110c444f84c9094a27089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sun, 26 Jun 2022 12:12:11 +0200 Subject: [PATCH 028/164] Fix doubled announcements (#1897) --- .../49.json | 2445 +++++++++++++++++ .../github/wulkanowy/data/db/AppDatabase.kt | 3 +- .../data/db/dao/SchoolAnnouncementDao.kt | 4 +- .../data/db/entities/SchoolAnnouncement.kt | 4 +- .../data/db/migrations/Migration49.kt | 23 + .../data/mappers/DirectorInformationMapper.kt | 2 +- .../SchoolAnnouncementRepository.kt | 7 +- .../sync/works/SchoolAnnouncementWork.kt | 12 +- .../notification/mock/schoolAnnouncement.kt | 2 +- 9 files changed, 2488 insertions(+), 14 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json new file mode 100644 index 000000000..5472fb78a --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json @@ -0,0 +1,2445 @@ +{ + "formatVersion": 1, + "database": { + "version": 49, + "identityHash": "790d4dc0e11f38349c49af85fabf9b7b", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "kindergartenDiaryId", + "columnName": "kindergarten_diary_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "kindergarten_diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "destination", + "columnName": "destination", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '790d4dc0e11f38349c49af85fabf9b7b')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 379b8738f..17fd7d696 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -55,7 +55,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 48 + const val VERSION_SCHEMA = 49 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), @@ -102,6 +102,7 @@ abstract class AppDatabase : RoomDatabase() { Migration43(), Migration44(), Migration46(), + Migration49() ) fun newInstance( diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt index 15655f4ae..c32e4aba3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt @@ -10,6 +10,6 @@ import javax.inject.Singleton @Singleton interface SchoolAnnouncementDao : BaseDao { - @Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId ORDER BY date DESC") - fun loadAll(studentId: Int): Flow> + @Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :userLoginId ORDER BY date DESC") + fun loadAll(userLoginId: Int): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt index c8731bded..25e27ef18 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt @@ -9,8 +9,8 @@ import java.time.LocalDate @Entity(tableName = "SchoolAnnouncements") data class SchoolAnnouncement( - @ColumnInfo(name = "student_id") - val studentId: Int, + @ColumnInfo(name = "user_login_id") + val userLoginId: Int, val date: LocalDate, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt new file mode 100644 index 000000000..6e1de19d4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration49 : Migration(48, 49) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS SchoolAnnouncements") + + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` ( + `user_login_id` INTEGER NOT NULL, + `date` INTEGER NOT NULL, + `subject` TEXT NOT NULL, + `content` TEXT NOT NULL, + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `is_notified` INTEGER NOT NULL) + """.trimIndent() + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt index d059db816..16f1bbac0 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio fun List.mapToEntities(student: Student) = map { SchoolAnnouncement( - studentId = student.userLoginId, + userLoginId = student.userLoginId, date = it.date, subject = it.subject, content = it.content, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt index cf7ac86cd..4c42d092f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt @@ -28,7 +28,8 @@ class SchoolAnnouncementRepository @Inject constructor( fun getSchoolAnnouncements( student: Student, - forceRefresh: Boolean, notify: Boolean = false + forceRefresh: Boolean, + notify: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, isResultEmpty = { it.isEmpty() }, @@ -37,7 +38,7 @@ class SchoolAnnouncementRepository @Inject constructor( it.isEmpty() || forceRefresh || isExpired }, query = { - schoolAnnouncementDb.loadAll(student.studentId) + schoolAnnouncementDb.loadAll(student.userLoginId) }, fetch = { sdk.init(student) @@ -56,7 +57,7 @@ class SchoolAnnouncementRepository @Inject constructor( ) fun getSchoolAnnouncementFromDatabase(student: Student): Flow> { - return schoolAnnouncementDb.loadAll(student.studentId) + return schoolAnnouncementDb.loadAll(student.userLoginId) } suspend fun updateSchoolAnnouncement(schoolAnnouncement: List) = diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt index 805ceb3e4..1aedc8399 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt @@ -6,6 +6,7 @@ import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository import io.github.wulkanowy.data.waitForResult import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification import kotlinx.coroutines.flow.first +import java.time.LocalDate import javax.inject.Inject class SchoolAnnouncementWork @Inject constructor( @@ -20,10 +21,13 @@ class SchoolAnnouncementWork @Inject constructor( notify = notify, ).waitForResult() - - schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student).first() - .filter { !it.isNotified }.let { - if (it.isNotEmpty()) newSchoolAnnouncementNotification.notify(it, student) + schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student) + .first() + .filter { !it.isNotified && it.date >= LocalDate.now() } + .let { + if (it.isNotEmpty()) { + newSchoolAnnouncementNotification.notify(it, student) + } schoolAnnouncementRepository.updateSchoolAnnouncement(it.onEach { schoolAnnouncement -> schoolAnnouncement.isNotified = true diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt index 9b21f08e6..e2dc5cd84 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt @@ -19,6 +19,6 @@ val debugSchoolAnnouncementItems = listOf( private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement( subject = subject, content = content, - studentId = 0, + userLoginId = 0, date = LocalDate.now() ) From c808bf2e616eb3abf36c21f22a3e022768469ed5 Mon Sep 17 00:00:00 2001 From: Michael <5672750+mibac138@users.noreply.github.com> Date: Sun, 26 Jun 2022 13:06:43 +0200 Subject: [PATCH 029/164] Fix timetable widget day reset (#1862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rafał Borcz --- .../TimetableWidgetConfigureActivity.kt | 6 ++--- .../TimetableWidgetProvider.kt | 24 ++++++++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt index a27dba882..6ef6cfc98 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.ui.modules.timetablewidget -import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE -import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID -import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.appwidget.AppWidgetManager.* import android.content.Intent import android.os.Build import android.os.Bundle @@ -17,6 +15,7 @@ import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.WidgetConfigureAdapter import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_CONFIGURE import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER import io.github.wulkanowy.utils.AppInfo import javax.inject.Inject @@ -92,6 +91,7 @@ class TimetableWidgetConfigureActivity : .apply { action = ACTION_APPWIDGET_UPDATE putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + putExtra(EXTRA_FROM_CONFIGURE, true) }) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt index 745769864..3ba2ae946 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -60,6 +60,8 @@ class TimetableWidgetProvider : BroadcastReceiver() { private const val BUTTON_RESET = "buttonReset" + const val EXTRA_FROM_CONFIGURE = "extraFromConfigure" + const val EXTRA_FROM_PROVIDER = "extraFromProvider" fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId" @@ -86,12 +88,22 @@ class TimetableWidgetProvider : BroadcastReceiver() { } private suspend fun onUpdate(context: Context, intent: Intent) { - if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) { - intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> + if (intent.getStringExtra(EXTRA_BUTTON_TYPE) == null) { + val isFromConfigure = intent.getBooleanExtra(EXTRA_FROM_CONFIGURE, false) + val appWidgetIds = intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS) ?: return + + appWidgetIds.forEach { appWidgetId -> val student = getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) + val savedDataEpochDay = sharedPref.getLong(getDateWidgetKey(appWidgetId), 0) - updateWidget(context, appWidgetId, getWidgetDateToLoad(appWidgetId), student) + val dateToLoad = if (isFromConfigure && savedDataEpochDay != 0L) { + LocalDate.ofEpochDay(savedDataEpochDay) + } else { + getWidgetDefaultDateToLoad(appWidgetId) + } + + updateWidget(context, appWidgetId, dateToLoad, student) } } else { val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE) @@ -103,10 +115,10 @@ class TimetableWidgetProvider : BroadcastReceiver() { val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0)) val date = when (buttonType) { - BUTTON_RESET -> getWidgetDateToLoad(toggledWidgetId) + BUTTON_RESET -> getWidgetDefaultDateToLoad(toggledWidgetId) BUTTON_NEXT -> savedDate.nextSchoolDay BUTTON_PREV -> savedDate.previousSchoolDay - else -> getWidgetDateToLoad(toggledWidgetId) + else -> getWidgetDefaultDateToLoad(toggledWidgetId) } if (!buttonType.isNullOrBlank()) { analytics.logEvent( @@ -271,7 +283,7 @@ class TimetableWidgetProvider : BroadcastReceiver() { return avatarBitmap } - private fun getWidgetDateToLoad(appWidgetId: Int): LocalDate { + private fun getWidgetDefaultDateToLoad(appWidgetId: Int): LocalDate { val lastLessonEndTimestamp = sharedPref.getLong(getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), 0) val lastLessonEndDateTime = From d8f644c5b4081d44e76a53a60067b31cb7baadf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sun, 26 Jun 2022 13:28:35 +0200 Subject: [PATCH 030/164] Add ads to dashboard (#1815) --- app/build.gradle | 3 + .../io/github/wulkanowy/utils/AdsHelper.kt | 28 +++++++ .../io/github/wulkanowy/utils/AdsHelper.kt | 28 +++++++ .../java/io/github/wulkanowy/WulkanowyApp.kt | 4 + .../repositories/PreferencesRepository.kt | 38 +++++++-- .../ui/modules/dashboard/DashboardFragment.kt | 9 +++ .../ui/modules/dashboard/DashboardItem.kt | 23 +++--- .../dashboard/DashboardItemMoveCallback.kt | 3 +- .../modules/dashboard/DashboardPresenter.kt | 35 +++++++- .../ui/modules/dashboard/DashboardView.kt | 4 +- .../{ => adapters}/DashboardAdapter.kt | 48 ++++++----- .../DashboardAnnouncementsAdapter.kt | 4 +- .../DashboardConferencesAdapter.kt | 4 +- .../{ => adapters}/DashboardExamsAdapter.kt | 4 +- .../{ => adapters}/DashboardGradesAdapter.kt | 2 +- .../DashboardHomeworkAdapter.kt | 4 +- .../wulkanowy/ui/modules/main/MainActivity.kt | 46 +++++++++++ .../ui/modules/main/MainPresenter.kt | 63 ++++++++++++--- .../wulkanowy/ui/modules/main/MainView.kt | 6 ++ .../wulkanowy/utils/ContextExtension.kt | 1 + .../main/res/layout/dialog_ads_consent.xml | 79 +++++++++++++++++++ .../main/res/layout/item_dashboard_ads.xml | 15 ++++ .../main/res/values/preferences_defaults.xml | 2 + app/src/main/res/values/preferences_keys.xml | 4 + app/src/main/res/values/strings.xml | 7 ++ .../ui/modules/settings/ads/AdsFragment.kt | 70 ++++++++++++++-- .../ui/modules/settings/ads/AdsPresenter.kt | 64 ++++++++++++--- .../ui/modules/settings/ads/AdsView.kt | 8 +- .../io/github/wulkanowy/utils/AdsHelper.kt | 59 ++++++++++++-- .../play/res/xml/scheme_preferences_ads.xml | 27 ++++++- .../ui/modules/main/MainPresenterTest.kt | 14 +++- 31 files changed, 621 insertions(+), 85 deletions(-) create mode 100644 app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt create mode 100644 app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardAdapter.kt (96%) rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardAnnouncementsAdapter.kt (95%) rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardConferencesAdapter.kt (95%) rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardExamsAdapter.kt (97%) rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardGradesAdapter.kt (96%) rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardHomeworkAdapter.kt (97%) create mode 100644 app/src/main/res/layout/dialog_ads_consent.xml create mode 100644 app/src/main/res/layout/item_dashboard_ads.xml diff --git a/app/build.gradle b/app/build.gradle index 3e4912063..29325abff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,6 +43,7 @@ android { } buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null" + buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null" if (System.env.SET_BUILD_TIMESTAMP) { buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis()) @@ -99,6 +100,8 @@ android { admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713" ] buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\"" + buildConfigField "String", "DASHBOARD_TILE_AD_ID", "\"${System.getenv("DASHBOARD_TILE_AD_ID") ?: "ca-app-pub-3940256099942544/6300978111"}\"" + } fdroid { diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt new file mode 100644 index 000000000..461d29951 --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.utils + +import android.content.Context +import android.view.View +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.ui.modules.dashboard.DashboardItem +import javax.inject.Inject + +@Suppress("unused") +class AdsHelper @Inject constructor( + @ApplicationContext private val context: Context, + private val preferencesRepository: PreferencesRepository +) { + + fun initialize() { + preferencesRepository.isAdsEnabled = false + preferencesRepository.isAgreeToProcessData = false + preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS + } + + @Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER") + suspend fun getDashboardTileAdBanner(width: Int): AdBanner { + throw IllegalStateException("Can't get ad banner (F-droid)") + } +} + +data class AdBanner(val view: View) diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt new file mode 100644 index 000000000..0e9227022 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.utils + +import android.content.Context +import android.view.View +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.ui.modules.dashboard.DashboardItem +import javax.inject.Inject + +@Suppress("unused") +class AdsHelper @Inject constructor( + @ApplicationContext private val context: Context, + private val preferencesRepository: PreferencesRepository +) { + + fun initialize() { + preferencesRepository.isAdsEnabled = false + preferencesRepository.isAgreeToProcessData = false + preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS + } + + @Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER") + suspend fun getDashboardTileAdBanner(width: Int): AdBanner { + throw IllegalStateException("Can't get ad banner (HMS)") + } +} + +data class AdBanner(val view: View) diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt index b5103e3ec..7d2eeb1ec 100644 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -31,10 +31,14 @@ class WulkanowyApp : Application(), Configuration.Provider { @Inject lateinit var analyticsHelper: AnalyticsHelper + @Inject + lateinit var adsHelper: AdsHelper + override fun onCreate() { super.onCreate() initializeAppLanguage() themeManager.applyDefaultTheme() + adsHelper.initialize() initLogging() } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt index 4cd85586f..237fb1a0a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt @@ -222,16 +222,14 @@ class PreferencesRepository @Inject constructor( get() = selectedDashboardTilesPreference.asFlow() .map { set -> set.map { DashboardItem.Tile.valueOf(it) } - .plus(DashboardItem.Tile.ACCOUNT) - .plus(DashboardItem.Tile.ADMIN_MESSAGE) + .plus(listOf(DashboardItem.Tile.ACCOUNT, DashboardItem.Tile.ADMIN_MESSAGE)) .toSet() } var selectedDashboardTiles: Set get() = selectedDashboardTilesPreference.get() .map { DashboardItem.Tile.valueOf(it) } - .plus(DashboardItem.Tile.ACCOUNT) - .plus(DashboardItem.Tile.ADMIN_MESSAGE) + .plus(listOf(DashboardItem.Tile.ACCOUNT, DashboardItem.Tile.ADMIN_MESSAGE)) .toSet() set(value) { val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT } @@ -271,7 +269,33 @@ class PreferencesRepository @Inject constructor( var isAppReviewDone: Boolean get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false) - set(value) = sharedPref.edit().putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value).apply() + set(value) = sharedPref.edit { putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value) } + + var isAppSupportShown: Boolean + get() = sharedPref.getBoolean(PREF_KEY_APP_SUPPORT_SHOWN, false) + set(value) = sharedPref.edit { putBoolean(PREF_KEY_APP_SUPPORT_SHOWN, value) } + + var isAgreeToProcessData: Boolean + get() = getBoolean( + R.string.pref_key_ads_consent_data_processing, + R.bool.pref_default_ads_consent_data_processing + ) + set(value) = sharedPref.edit { + putBoolean(context.getString(R.string.pref_key_ads_consent_data_processing), value) + } + + var isPersonalizedAdsEnabled: Boolean + get() = sharedPref.getBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, false) + set(value) = sharedPref.edit { putBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, value) } + + var isAdsEnabled: Boolean + get() = getBoolean( + R.string.pref_key_ads_enabled, + R.bool.pref_default_ads_enabled + ) + set(value) = sharedPref.edit { + putBoolean(context.getString(R.string.pref_key_ads_enabled), value) + } private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default) @@ -301,6 +325,10 @@ class PreferencesRepository @Inject constructor( private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done" + private const val PREF_KEY_APP_SUPPORT_SHOWN = "app_support_shown" + + private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled" + private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids" } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt index 65832bdb1..de0b4a6c9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt @@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment import io.github.wulkanowy.ui.modules.conference.ConferenceFragment +import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.homework.HomeworkFragment @@ -47,6 +48,14 @@ class DashboardFragment : BaseFragment(R.layout.fragme override var subtitleString = LocalDate.now().toFormattedString("EEEE, d MMMM yyyy").capitalise() + override val tileWidth: Int + get() { + val recyclerWidth = binding.dashboardRecycler.width + val margin = requireContext().dpToPx(24f).toInt() + + return ((recyclerWidth - margin) / resources.displayMetrics.density).toInt() + } + companion object { fun newInstance() = DashboardFragment() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt index c20bae7fa..e220ae236 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt @@ -1,13 +1,9 @@ package io.github.wulkanowy.ui.modules.dashboard -import io.github.wulkanowy.data.db.entities.AdminMessage -import io.github.wulkanowy.data.db.entities.Conference -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.SchoolAnnouncement -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.* import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.data.pojos.TimetableFull +import io.github.wulkanowy.utils.AdBanner import io.github.wulkanowy.data.db.entities.Homework as EntitiesHomework sealed class DashboardItem(val type: Type) { @@ -106,17 +102,26 @@ sealed class DashboardItem(val type: Type) { override val isDataLoaded get() = conferences != null } + data class Ads( + val adBanner: AdBanner? = null, + override val error: Throwable? = null, + override val isLoading: Boolean = false + ) : DashboardItem(Type.ADS) { + + override val isDataLoaded get() = adBanner != null + } + enum class Type { ADMIN_MESSAGE, ACCOUNT, HORIZONTAL_GROUP, LESSONS, + ADS, GRADES, HOMEWORK, ANNOUNCEMENTS, EXAMS, CONFERENCES, - ADS } enum class Tile { @@ -126,12 +131,12 @@ sealed class DashboardItem(val type: Type) { MESSAGES, ATTENDANCE, LESSONS, + ADS, GRADES, HOMEWORK, ANNOUNCEMENTS, EXAMS, CONFERENCES, - ADS } } @@ -148,4 +153,4 @@ fun DashboardItem.Tile.toDashboardItemType() = when (this) { DashboardItem.Tile.EXAMS -> DashboardItem.Type.EXAMS DashboardItem.Tile.CONFERENCES -> DashboardItem.Type.CONFERENCES DashboardItem.Tile.ADS -> DashboardItem.Type.ADS -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt index b9625570f..9c15acc35 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt @@ -2,7 +2,8 @@ package io.github.wulkanowy.ui.modules.dashboard import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import java.util.Collections +import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter +import java.util.* class DashboardItemMoveCallback( private val dashboardAdapter: DashboardAdapter, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt index c33955bc7..e963a0205 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -8,6 +8,7 @@ import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.data.repositories.* import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AdsHelper import io.github.wulkanowy.utils.calculatePercentage import io.github.wulkanowy.utils.nextOrSameSchoolDay import kotlinx.coroutines.flow.* @@ -31,7 +32,8 @@ class DashboardPresenter @Inject constructor( private val conferenceRepository: ConferenceRepository, private val preferencesRepository: PreferencesRepository, private val schoolAnnouncementRepository: SchoolAnnouncementRepository, - private val adminMessageRepository: AdminMessageRepository + private val adminMessageRepository: AdminMessageRepository, + private val adsHelper: AdsHelper ) : BasePresenter(errorHandler, studentRepository) { private val dashboardItemLoadedList = mutableListOf() @@ -166,7 +168,7 @@ class DashboardPresenter @Inject constructor( DashboardItem.Type.CONFERENCES -> { loadConferences(student, forceRefresh) } - DashboardItem.Type.ADS -> TODO() + DashboardItem.Type.ADS -> loadAds(forceRefresh) DashboardItem.Type.ADMIN_MESSAGE -> loadAdminMessage(student, forceRefresh) } } @@ -595,6 +597,23 @@ class DashboardPresenter @Inject constructor( .launchWithUniqueRefreshJob("dashboard_admin_messages", forceRefresh) } + private fun loadAds(forceRefresh: Boolean) { + presenterScope.launch { + if (!forceRefresh) { + updateData(DashboardItem.Ads(), forceRefresh) + } + + val dashboardAdItem = + runCatching { + DashboardItem.Ads(adsHelper.getDashboardTileAdBanner(view!!.tileWidth)) + } + .onFailure { Timber.e(it) } + .getOrElse { DashboardItem.Ads(error = it) } + + updateData(dashboardAdItem, forceRefresh) + } + } + private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) { val isForceRefreshError = forceRefresh && dashboardItem.error != null val isFirstRunDataLoadedError = @@ -619,6 +638,18 @@ class DashboardPresenter @Inject constructor( } } + if (dashboardItem is DashboardItem.Ads) { + if (!dashboardItem.isDataLoaded) { + dashboardItemsToLoad = dashboardItemsToLoad - DashboardItem.Type.ADS + dashboardTileLoadedList = dashboardTileLoadedList - DashboardItem.Tile.ADS + + dashboardItemLoadedList.removeAll { it.type == DashboardItem.Type.ADS } + } else { + dashboardItemsToLoad = dashboardItemsToLoad + DashboardItem.Type.ADS + dashboardTileLoadedList = dashboardTileLoadedList + DashboardItem.Tile.ADS + } + } + if (forceRefresh) { updateForceRefreshData(dashboardItem) } else { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt index 2cc2f1d2d..767885434 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt @@ -4,6 +4,8 @@ import io.github.wulkanowy.ui.base.BaseView interface DashboardView : BaseView { + val tileWidth: Int + fun initView() fun updateData(data: List) @@ -27,4 +29,4 @@ interface DashboardView : BaseView { fun openNotificationsCenterView() fun openInternetBrowser(url: String) -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt similarity index 96% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt index 9191d43c9..a3c423a8b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard +package io.github.wulkanowy.ui.modules.dashboard.adapters import android.annotation.SuppressLint import android.content.res.ColorStateList @@ -22,24 +22,15 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.TimetableHeader import io.github.wulkanowy.data.enums.GradeColorTheme -import io.github.wulkanowy.databinding.ItemDashboardAccountBinding -import io.github.wulkanowy.databinding.ItemDashboardAdminMessageBinding -import io.github.wulkanowy.databinding.ItemDashboardAnnouncementsBinding -import io.github.wulkanowy.databinding.ItemDashboardConferencesBinding -import io.github.wulkanowy.databinding.ItemDashboardExamsBinding -import io.github.wulkanowy.databinding.ItemDashboardGradesBinding -import io.github.wulkanowy.databinding.ItemDashboardHomeworkBinding -import io.github.wulkanowy.databinding.ItemDashboardHorizontalGroupBinding -import io.github.wulkanowy.databinding.ItemDashboardLessonsBinding -import io.github.wulkanowy.utils.createNameInitialsDrawable -import io.github.wulkanowy.utils.dpToPx -import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.left -import io.github.wulkanowy.utils.nickOrName -import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.databinding.* +import io.github.wulkanowy.ui.modules.dashboard.DashboardItem +import io.github.wulkanowy.utils.* import timber.log.Timber -import java.time.* -import java.util.Timer +import java.time.Duration +import java.time.Instant +import java.time.LocalDate +import java.time.LocalDateTime +import java.util.* import javax.inject.Inject import kotlin.concurrent.timer @@ -120,6 +111,9 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter AdminMessageViewHolder( ItemDashboardAdminMessageBinding.inflate(inflater, parent, false) ) + DashboardItem.Type.ADS.ordinal -> AdsViewHolder( + ItemDashboardAdsBinding.inflate(inflater, parent, false) + ) else -> throw IllegalArgumentException() } } @@ -135,6 +129,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter bindExamsViewHolder(holder, position) is ConferencesViewHolder -> bindConferencesViewHolder(holder, position) is AdminMessageViewHolder -> bindAdminMessage(holder, position) + is AdsViewHolder -> bindAdsViewHolder(holder, position) } } @@ -746,6 +741,20 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter, private val oldList: List diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt similarity index 95% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt index 7a4c2b257..da5880153 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard +package io.github.wulkanowy.ui.modules.dashboard.adapters import android.view.LayoutInflater import android.view.ViewGroup @@ -33,4 +33,4 @@ class DashboardAnnouncementsAdapter : class ViewHolder(val binding: SubitemDashboardAnnouncementsBinding) : RecyclerView.ViewHolder(binding.root) -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt similarity index 95% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt index 64cf599c8..1244ff60f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard +package io.github.wulkanowy.ui.modules.dashboard.adapters import android.view.LayoutInflater import android.view.ViewGroup @@ -33,4 +33,4 @@ class DashboardConferencesAdapter : class ViewHolder(val binding: SubitemDashboardConferencesBinding) : RecyclerView.ViewHolder(binding.root) -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt similarity index 97% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt index 060f224b3..583bf29da 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard +package io.github.wulkanowy.ui.modules.dashboard.adapters import android.annotation.SuppressLint import android.view.LayoutInflater @@ -56,4 +56,4 @@ class DashboardExamsAdapter : class ViewHolder(val binding: SubitemDashboardExamsBinding) : RecyclerView.ViewHolder(binding.root) -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt similarity index 96% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt index afffcc511..d00df9d41 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard +package io.github.wulkanowy.ui.modules.dashboard.adapters import android.view.LayoutInflater import android.view.ViewGroup diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardHomeworkAdapter.kt similarity index 97% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardHomeworkAdapter.kt index 55ec90294..8105ff9c8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardHomeworkAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard +package io.github.wulkanowy.ui.modules.dashboard.adapters import android.annotation.SuppressLint import android.view.LayoutInflater @@ -53,4 +53,4 @@ class DashboardHomeworkAdapter : RecyclerView.Adapter(), MainVie inAppReviewHelper.showInAppReview(this) } + override fun showAppSupport() { + MaterialAlertDialogBuilder(this) + .setTitle(R.string.main_support_title) + .setMessage(R.string.main_support_description) + .setPositiveButton(R.string.main_support_positive) { _, _ -> presenter.onEnableAdsSelected() } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .setOnDismissListener { } + .show() + } + + override fun showPrivacyPolicyDialog() { + val dialogAdsConsentBinding = DialogAdsConsentBinding.inflate(layoutInflater) + + val dialog = MaterialAlertDialogBuilder(this) + .setTitle(R.string.pref_ads_consent_title) + .setMessage(R.string.pref_ads_consent_description) + .setView(dialogAdsConsentBinding.root) + .show() + + dialogAdsConsentBinding.adsConsentOver.setOnCheckedChangeListener { _, isChecked -> + dialogAdsConsentBinding.adsConsentPersonalised.isEnabled = isChecked + } + + dialogAdsConsentBinding.adsConsentPersonalised.setOnClickListener { + presenter.onPrivacyAgree(true) + dialog.dismiss() + } + + dialogAdsConsentBinding.adsConsentNonPersonalised.setOnClickListener { + presenter.onPrivacyAgree(false) + dialog.dismiss() + } + + dialogAdsConsentBinding.adsConsentPrivacy.setOnClickListener { presenter.onPrivacySelected() } + dialogAdsConsentBinding.adsConsentCancel.setOnClickListener { dialog.cancel() } + } + + override fun openPrivacyPolicy() { + openInternetBrowser( + "https://wulkanowy.github.io/polityka-prywatnosci.html", + ::showMessage + ) + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) navController.onSaveInstanceState(outState) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index a07bdb371..8f457d925 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -14,11 +14,15 @@ import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.account.AccountView import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView +import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.ui.modules.message.MessageView import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView +import io.github.wulkanowy.utils.AdsHelper import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.AppInfo +import kotlinx.coroutines.launch import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import timber.log.Timber @@ -29,10 +33,12 @@ import javax.inject.Inject class MainPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, - private val prefRepository: PreferencesRepository, + private val preferencesRepository: PreferencesRepository, private val syncManager: SyncManager, private val analytics: AnalyticsHelper, - private val json: Json + private val json: Json, + private val adsHelper: AdsHelper, + private val appInfo: AppInfo ) : BasePresenter(errorHandler, studentRepository) { private var studentsWitSemesters: List? = null @@ -47,7 +53,7 @@ class MainPresenter @Inject constructor( private val Destination?.startMenuIndex get() = when { - this == null -> prefRepository.startMenuIndex + this == null -> preferencesRepository.startMenuIndex destinationType in rootDestinationTypeList -> { rootDestinationTypeList.indexOf(destinationType) } @@ -71,6 +77,8 @@ class MainPresenter @Inject constructor( syncManager.startPeriodicSyncWorker() + checkAppSupport() + analytics.logEvent("app_open", "destination" to initDestination.toString()) Timber.i("Main view was initialized with $initDestination") } @@ -155,18 +163,53 @@ class MainPresenter @Inject constructor( } == true } - private fun checkInAppReview() { - prefRepository.inAppReviewCount++ + fun onEnableAdsSelected() { + view?.showPrivacyPolicyDialog() + } - if (prefRepository.inAppReviewDate == null) { - prefRepository.inAppReviewDate = Instant.now() + fun onPrivacyAgree(isPersonalizedAds: Boolean) { + preferencesRepository.isAdsEnabled = true + preferencesRepository.isAgreeToProcessData = true + preferencesRepository.isPersonalizedAdsEnabled = isPersonalizedAds + + adsHelper.initialize() + + preferencesRepository.selectedDashboardTiles += DashboardItem.Tile.ADS + } + + fun onPrivacySelected() { + view?.openPrivacyPolicy() + } + + private fun checkInAppReview() { + preferencesRepository.inAppReviewCount++ + + if (preferencesRepository.inAppReviewDate == null) { + preferencesRepository.inAppReviewDate = Instant.now() } - if (!prefRepository.isAppReviewDone && prefRepository.inAppReviewCount >= 50 && - Instant.now().minus(Duration.ofDays(14)).isAfter(prefRepository.inAppReviewDate) + if (!preferencesRepository.isAppReviewDone && preferencesRepository.inAppReviewCount >= 50 && + Instant.now().minus(Duration.ofDays(14)).isAfter(preferencesRepository.inAppReviewDate) ) { view?.showInAppReview() - prefRepository.isAppReviewDone = true + preferencesRepository.isAppReviewDone = true + } + } + + private fun checkAppSupport() { + if (!preferencesRepository.isAppSupportShown && !preferencesRepository.isAdsEnabled + && appInfo.buildFlavor == "play" + ) { + presenterScope.launch { + val student = runCatching { studentRepository.getCurrentStudent(false) } + .onFailure { Timber.e(it) } + .getOrElse { return@launch } + + if (Instant.now().minus(Duration.ofDays(28)).isAfter(student.registrationDate)) { + view?.showAppSupport() + preferencesRepository.isAppSupportShown = true + } + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt index 3a57fcc6b..3d018e3d6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt @@ -41,6 +41,12 @@ interface MainView : BaseView { fun showInAppReview() + fun showAppSupport() + + fun showPrivacyPolicyDialog() + + fun openPrivacyPolicy() + fun openMoreDestination(destination: Destination) interface MainChildView { diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index 323e1e477..dd91d36d4 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -13,6 +13,7 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.graphics.drawable.toBitmap + @ColorInt fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int { val array = obtainStyledAttributes(null, intArrayOf(colorAttr)) diff --git a/app/src/main/res/layout/dialog_ads_consent.xml b/app/src/main/res/layout/dialog_ads_consent.xml new file mode 100644 index 000000000..395101627 --- /dev/null +++ b/app/src/main/res/layout/dialog_ads_consent.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_dashboard_ads.xml b/app/src/main/res/layout/item_dashboard_ads.xml new file mode 100644 index 000000000..b75bb27e0 --- /dev/null +++ b/app/src/main/res/layout/item_dashboard_ads.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml index deeb36961..9c092ec5e 100644 --- a/app/src/main/res/values/preferences_defaults.xml +++ b/app/src/main/res/values/preferences_defaults.xml @@ -37,4 +37,6 @@ GRADES ANNOUNCEMENTS + false + false diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index 849d989ee..f29080b33 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -37,4 +37,8 @@ notifications_piggyback notifications_piggyback_cancel_original single_ad_support + ads_enabled + ads_privacy_policy + ads_consent_data_processing + ads_over_eighteen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2ca516adf..db591309f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -85,6 +85,9 @@ Log in Session expired Session expired, log in again + Application support + Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time + Enable ads @@ -715,6 +718,10 @@ Privacy policy Ad is loading Thank you for your support, come back later for more ads + Can we use your data to display ads? + You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details + Personalized ads + Non-personalized ads Advanced Appearance & Behavior diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt index 8d31928b7..48a6fc3ec 100644 --- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt +++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt @@ -2,12 +2,15 @@ package io.github.wulkanowy.ui.modules.settings.ads import android.os.Bundle import android.view.View +import androidx.preference.CheckBoxPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreferenceCompat import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.DialogAdsConsentBinding import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.ErrorDialog import io.github.wulkanowy.ui.modules.main.MainView @@ -36,6 +39,22 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView { presenter.onWatchSingleAdSelected() true } + + findPreference(getString(R.string.pref_key_ads_privacy_policy))?.setOnPreferenceClickListener { + presenter.onPrivacySelected() + true + } + + findPreference(getString(R.string.pref_key_ads_consent_data_processing)) + ?.setOnPreferenceChangeListener { _, newValue -> + presenter.onConsentSelected(newValue as Boolean) + true + } + + findPreference(getString(R.string.pref_key_ads_enabled))?.setOnPreferenceChangeListener { _, newValue -> + presenter.onAddEnabled(newValue as Boolean) + true + } } override fun showAd(ad: RewardedInterstitialAd) { @@ -45,13 +64,50 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView { } override fun showPrivacyPolicyDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.pref_ads_privacy_title)) - .setMessage(getString(R.string.pref_ads_privacy_description)) - .setPositiveButton(getString(R.string.pref_ads_privacy_agree)) { _, _ -> presenter.onAgreedPrivacy() } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .setNeutralButton(getString(R.string.pref_ads_privacy_link)) { _, _ -> presenter.onPrivacySelected() } + val dialogAdsConsentBinding = DialogAdsConsentBinding.inflate(layoutInflater) + + val dialog = MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.pref_ads_consent_title) + .setMessage(R.string.pref_ads_consent_description) + .setView(dialogAdsConsentBinding.root) + .setOnCancelListener { presenter.onPrivacyDialogCanceled() } .show() + + dialogAdsConsentBinding.adsConsentOver.setOnCheckedChangeListener { _, isChecked -> + dialogAdsConsentBinding.adsConsentPersonalised.isEnabled = isChecked + } + + dialogAdsConsentBinding.adsConsentPersonalised.setOnClickListener { + presenter.onPersonalizedAgree() + dialog.dismiss() + } + + dialogAdsConsentBinding.adsConsentNonPersonalised.setOnClickListener { + presenter.onNonPersonalizedAgree() + dialog.dismiss() + } + + dialogAdsConsentBinding.adsConsentPrivacy.setOnClickListener { presenter.onPrivacySelected() } + dialogAdsConsentBinding.adsConsentCancel.setOnClickListener { dialog.cancel() } + } + + override fun showProcessingDataSummary(isPersonalized: Boolean?) { + val summaryText = isPersonalized?.let { + getString(if (it) R.string.pref_ads_summary_personalized else R.string.pref_ads_summary_non_personalized) + } + + findPreference(getString(R.string.pref_key_ads_consent_data_processing)) + ?.summary = summaryText + } + + override fun setCheckedProcessingData(checked: Boolean) { + findPreference(getString(R.string.pref_key_ads_consent_data_processing)) + ?.isChecked = checked + } + + override fun setCheckedAdsEnabled(checked: Boolean) { + findPreference(getString(R.string.pref_key_ads_enabled)) + ?.isChecked = checked } override fun openPrivacyPolicy() { @@ -98,4 +154,4 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView { override fun showErrorDetailsDialog(error: Throwable) { ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } -} \ No newline at end of file +} diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt index 5ccbce1e5..85c14c0a0 100644 --- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt +++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt @@ -1,8 +1,10 @@ package io.github.wulkanowy.ui.modules.settings.ads +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.utils.AdsHelper import kotlinx.coroutines.launch import timber.log.Timber @@ -11,24 +13,22 @@ import javax.inject.Inject class AdsPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, - private val adsHelper: AdsHelper + private val adsHelper: AdsHelper, + private val preferencesRepository: PreferencesRepository ) : BasePresenter(errorHandler, studentRepository) { override fun onAttachView(view: AdsView) { super.onAttachView(view) view.initView() Timber.i("Settings ads view was initialized") + + view.showProcessingDataSummary( + preferencesRepository.isPersonalizedAdsEnabled.takeIf { + preferencesRepository.isAgreeToProcessData + }) } fun onWatchSingleAdSelected() { - view?.showPrivacyPolicyDialog() - } - - fun onPrivacySelected() { - view?.openPrivacyPolicy() - } - - fun onAgreedPrivacy() { view?.showLoadingSupportAd(true) presenterScope.launch { runCatching { adsHelper.getSupportAd() } @@ -41,4 +41,48 @@ class AdsPresenter @Inject constructor( } } } -} \ No newline at end of file + + fun onConsentSelected(isChecked: Boolean) { + if (isChecked) { + view?.showPrivacyPolicyDialog() + } else { + view?.showProcessingDataSummary(null) + view?.setCheckedAdsEnabled(false) + onAddEnabled(false) + } + } + + fun onPrivacySelected() { + view?.openPrivacyPolicy() + } + + fun onPrivacyDialogCanceled() { + view?.setCheckedProcessingData(false) + } + + fun onNonPersonalizedAgree() { + preferencesRepository.isPersonalizedAdsEnabled = false + + adsHelper.initialize() + + view?.setCheckedProcessingData(true) + view?.showProcessingDataSummary(false) + } + + fun onPersonalizedAgree() { + preferencesRepository.isPersonalizedAdsEnabled = true + + adsHelper.initialize() + + view?.setCheckedProcessingData(true) + view?.showProcessingDataSummary(true) + } + + fun onAddEnabled(isEnabled: Boolean) { + if (isEnabled) { + preferencesRepository.selectedDashboardTiles += DashboardItem.Tile.ADS + } else { + preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS + } + } +} diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt index 89de7bd1d..8de6e60d3 100644 --- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt +++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt @@ -16,4 +16,10 @@ interface AdsView : BaseView { fun showLoadingSupportAd(show: Boolean) fun showWatchAdOncePerVisit(show: Boolean) -} \ No newline at end of file + + fun setCheckedAdsEnabled(checked: Boolean) + + fun setCheckedProcessingData(checked: Boolean) + + fun showProcessingDataSummary(isPersonalized: Boolean?) +} diff --git a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt index dde4d0121..6be8e924c 100644 --- a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt +++ b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt @@ -2,27 +2,39 @@ package io.github.wulkanowy.utils import android.content.Context import android.os.Bundle +import android.view.View import com.google.ads.mediation.admob.AdMobAdapter -import com.google.android.gms.ads.AdRequest -import com.google.android.gms.ads.LoadAdError -import com.google.android.gms.ads.MobileAds +import com.google.android.gms.ads.* import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.BuildConfig +import io.github.wulkanowy.data.repositories.PreferencesRepository import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine -class AdsHelper @Inject constructor(@ApplicationContext private val context: Context) { + +class AdsHelper @Inject constructor( + @ApplicationContext private val context: Context, + private val preferencesRepository: PreferencesRepository +) { + + fun initialize() { + if (preferencesRepository.isAgreeToProcessData) { + MobileAds.initialize(context) + } + } suspend fun getSupportAd(): RewardedInterstitialAd? { - MobileAds.initialize(context) - val extra = Bundle().apply { putString("npa", "1") } val adRequest = AdRequest.Builder() - .addNetworkExtrasBundle(AdMobAdapter::class.java, extra) + .apply { + if (!preferencesRepository.isPersonalizedAdsEnabled) { + addNetworkExtrasBundle(AdMobAdapter::class.java, extra) + } + } .build() return suspendCoroutine { @@ -41,4 +53,35 @@ class AdsHelper @Inject constructor(@ApplicationContext private val context: Con }) } } -} \ No newline at end of file + + suspend fun getDashboardTileAdBanner(width: Int): AdBanner { + val extra = Bundle().apply { putString("npa", "1") } + val adRequest = AdRequest.Builder() + .apply { + if (!preferencesRepository.isPersonalizedAdsEnabled) { + addNetworkExtrasBundle(AdMobAdapter::class.java, extra) + } + } + .build() + + return suspendCoroutine { + val adView = AdView(context).apply { + adSize = AdSize.getPortraitAnchoredAdaptiveBannerAdSize(context, width) + adUnitId = BuildConfig.DASHBOARD_TILE_AD_ID + adListener = object : AdListener() { + override fun onAdFailedToLoad(loadAdError: LoadAdError) { + it.resumeWithException(IllegalArgumentException(loadAdError.message)) + } + + override fun onAdLoaded() { + it.resume(AdBanner(this@apply)) + } + } + } + + adView.loadAd(adRequest) + } + } +} + +data class AdBanner(val view: View) diff --git a/app/src/play/res/xml/scheme_preferences_ads.xml b/app/src/play/res/xml/scheme_preferences_ads.xml index 52a3df58d..d5e93a355 100644 --- a/app/src/play/res/xml/scheme_preferences_ads.xml +++ b/app/src/play/res/xml/scheme_preferences_ads.xml @@ -2,11 +2,34 @@ + app:title="Agreements"> + + + + + - \ No newline at end of file + diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt index 720239e62..6cfab1995 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt @@ -4,7 +4,9 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AdsHelper import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.AppInfo import io.mockk.* import io.mockk.impl.annotations.MockK import kotlinx.serialization.json.Json @@ -31,6 +33,12 @@ class MainPresenterTest { @MockK(relaxed = true) lateinit var analytics: AnalyticsHelper + @MockK(relaxed = true) + lateinit var appInfo: AppInfo + + @MockK(relaxed = true) + lateinit var adsHelper: AdsHelper + private lateinit var presenter: MainPresenter @Before @@ -42,10 +50,12 @@ class MainPresenterTest { presenter = MainPresenter( errorHandler = errorHandler, studentRepository = studentRepository, - prefRepository = prefRepository, + preferencesRepository = prefRepository, syncManager = syncManager, analytics = analytics, - json = Json + json = Json, + appInfo = appInfo, + adsHelper = adsHelper ) presenter.onAttachView(mainView, null) } From c4689fcbb38f7055f5378d7c6a537a79841e3a96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Jun 2022 12:44:06 +0000 Subject: [PATCH 031/164] Bump agconnect-crash from 1.6.6.200 to 1.7.0.300 (#1899) --- app/build.gradle | 2 +- app/src/debug/agconnect-services.json | 115 +++++++++++++++----- app/src/release/agconnect-services.json.gpg | Bin 608 -> 972 bytes build.gradle | 2 +- 4 files changed, 89 insertions(+), 30 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 29325abff..ab1d0aefd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -248,7 +248,7 @@ dependencies { playImplementation 'com.google.android.gms:play-services-ads:21.0.0' hmsImplementation 'com.huawei.hms:hianalytics:6.5.0.300' - hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.6.200' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.0.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" diff --git a/app/src/debug/agconnect-services.json b/app/src/debug/agconnect-services.json index 48192df01..52426f54e 100644 --- a/app/src/debug/agconnect-services.json +++ b/app/src/debug/agconnect-services.json @@ -1,33 +1,92 @@ { - "agcgw":{ - "backurl":"connect-dre.dbankcloud.cn", - "url":"connect-dre.hispace.hicloud.com" - }, - "client":{ - "cp_id":"890048000024105546", - "product_id":"", - "client_id":"", - "client_secret":"", - "app_id":"101440411", - "package_name":"io.github.wulkanowy.dev", - "api_key":"" - }, - "service":{ - "analytics":{ - "collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", - "resource_id":"p1", - "channel_id":"" - }, + "agcgw": { + "backurl": "connect-dre.hispace.hicloud.com", + "url": "connect-dre.dbankcloud.cn", + "websocketbackurl": "connect-ws-dre.hispace.dbankcloud.com", + "websocketurl": "connect-ws-dre.hispace.dbankcloud.cn" + }, + "agcgw_all": { + "CN": "connect-drcn.dbankcloud.cn", + "CN_back": "connect-drcn.hispace.hicloud.com", + "DE": "connect-dre.dbankcloud.cn", + "DE_back": "connect-dre.hispace.hicloud.com", + "RU": "connect-drru.hispace.dbankcloud.ru", + "RU_back": "connect-drru.hispace.dbankcloud.cn", + "SG": "connect-dra.dbankcloud.cn", + "SG_back": "connect-dra.hispace.hicloud.com" + }, + "websocketgw_all": { + "CN": "connect-ws-drcn.hispace.dbankcloud.cn", + "CN_back": "connect-ws-drcn.hispace.dbankcloud.com", + "DE": "connect-ws-dre.hispace.dbankcloud.cn", + "DE_back": "connect-ws-dre.hispace.dbankcloud.com", + "RU": "connect-ws-drru.hispace.dbankcloud.ru", + "RU_back": "connect-ws-drru.hispace.dbankcloud.cn", + "SG": "connect-ws-dra.hispace.dbankcloud.cn", + "SG_back": "connect-ws-dra.hispace.dbankcloud.com" + }, + "client": { + "cp_id": "890048000024105546", + "product_id": "736430079244736562", + "client_id": "514530959291319360", + "client_secret": "C42522DBF17D3D4BBE9D9C1783A54484B7E6844B388B7A67502D36A633A4186B", + "project_id": "736430079244736562", + "app_id": "106552551", + "api_key": "CgB6e3x9BUNiq+r8ebCHNojjjYsMT4pJSjjNDOkm9owtBb6rVI6LjnASoZBRxbjjhObcrV5gANo99fI/eKZDTbWS", + "package_name": "io.github.wulkanowy.dev" + }, + "oauth_client": { + "client_id": "106552551", + "client_type": 1 + }, + "app_info": { + "app_id": "106552551", + "package_name": "io.github.wulkanowy.dev" + }, + "service": { + "analytics": { + "collector_url": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "collector_url_ru": "datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com", + "collector_url_sg": "datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn", + "collector_url_de": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "collector_url_cn": "datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn", + "resource_id": "p1", + "channel_id": "" + }, "search":{ "url":"https://search-dre.cloud.huawei.com" }, - "cloudstorage":{ - "storage_url":"https://ops-dre.agcstorage.link" - }, - "ml":{ - "mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn" - } - }, - "region":"DE", - "configuration_version":"1.0" + "cloudstorage": { + "storage_url_sg_back": "https://agc-storage-dra.cloud.huawei.asia", + "storage_url_ru_back": "https://agc-storage-drru.cloud.huawei.ru", + "storage_url_ru": "https://agc-storage-drru.cloud.huawei.ru", + "storage_url_de_back": "https://agc-storage-dre.cloud.huawei.eu", + "storage_url_de": "https://ops-dre.agcstorage.link", + "storage_url": "https://agc-storage-drcn.platform.dbankcloud.cn", + "storage_url_sg": "https://ops-dra.agcstorage.link", + "storage_url_cn_back": "https://agc-storage-drcn.cloud.huawei.com.cn", + "storage_url_cn": "https://agc-storage-drcn.platform.dbankcloud.cn" + }, + "ml": { + "mlservice_url": "ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn" + } + }, + "region": "DE", + "configuration_version": "3.0", + "appInfos": [ + { + "package_name": "io.github.wulkanowy.dev", + "client": { + "app_id": "106552551" + }, + "app_info": { + "package_name": "io.github.wulkanowy.dev", + "app_id": "106552551" + }, + "oauth_client": { + "client_type": 1, + "client_id": "106552551" + } + } + ] } diff --git a/app/src/release/agconnect-services.json.gpg b/app/src/release/agconnect-services.json.gpg index 484d8bc72d7a15b689a3fb76cddd91f1992cebf4..149b9b26818f97db4ac68202bbde3c1fde04f422 100644 GIT binary patch literal 972 zcmeC-W#MFI%CvVWn)pF~)2B->8E2?+NK~+wEmA%j+80#$WVzsb?=#N){B2s#L*)Xl zZftqWws;5gwPRWTW0pQkYnQlnt96a~2Ih|&t&Yz4^>Vsy1%Lha8+soXZ;TK;wAb-c zrbc&sd2mi||2EUv+>Ym7Zc%gXTsGnJd1E!n9efv(w#Qj7esS%=ulUx5GvhiIxz#_P zAa8$t|J~n{H%3hk+)<;rJ>W_7&Yi7mBc?8J-Bw@S<{#Nnd-zXuRmZmZPxzC=rd>DG zt~}u2vEbt;!7KMp7@ZQk-uP{z2}AR24dvn+%J%|3UOd$m+kRe?tsiSI56 zGami;DkoQPrL^AiuG+U-!(Wc&MA)@%%}{p^851=% zrr$g3pL@LHZlAr>@WN@kJ@Ow_I4zbIo3<9eIc%x@Xj&!5jQUq`|7{doV&wf#@kMD@ zG}q+tJvq1H@ACiQKY9MSolknG!ancx=JoR?>qkpYlT^ripBuN$E1e;Tb)iaU-O;nF zKFpgPW$%=K=>FN$imm@!!XIinUKLEYx4R-MsOFS&udE<-)*0T92YyEExY57zP@&-F zO`9Cv)OvRcU-Dihozx$pJ>}D-kjC6Q78zz`C7A+`-PfqRJ$ZHU=GN%2{QzB@UgFw)%4P>D`2a!wQ?_ z*t6rbZ}piinpM2|wokEc{NXCQMT%`=R?8yi-LUw{@AjehVSJr|T}orT%!4hCac3Cx z4p%KtU8bn2ov@VA#qW1A$ByD7C&jiUU3GDL^X{z^EL66&iv3sFV(yXorruHL z;FS4MCbdWI6g_k~z;f-%pF8^Anwt`f<4(-p*%JAahnJt7+wqHO!CVvlX6{+%yG1u0 zva4IZZGQcln_j21_lX|jEN{D%z0lypyr3&Po?X(Lf4cVH9K*Psyvnz^{@YGTo#R(E z-)!YF*$SZrk{4~6qIHsHx~5$Y(qDPeNWG3LCrj>*&9acxT8 zenrbN!D{v|T*0w*XS%L^Sb9*_Tk4?jeV1R2JD1dM64L&9A>>KjrzZ`;kK~(Y{Jt+; z)bDJ0?*Ej1c}}9y4D0U}&d9vu6Z%xe-qF&wPk++Yy*F;Zs*2Q7TXvaKwqe?>E3b?G zdK9-XSUz(@jfchUcT{rL0@)a|GUkmJOb{FkC$JQ z<9qd5f0pN$s^e^HO*OcBV|XsGG3abCtw>m8GjY=T)lWH+f*=1sbi*lPLHV{M=biou zOO4-7_h*9V_;&zm=sJ!mvAJ zv*7Qg%cn7C#@vW}SJ9r;-}HOaW}{FS^<({6qQ&2CW+=M^_A?7Hd^j4{7;^OKjISKR7LmZLtn6cQ|L6U~ur$r?+|kZ4&s-FIg{O8v5|;#tTV|E&Ch8nEZvEA51G> zF!yZDzuEVEENhqdm7inF+p=lJozs`nSoU)+3l5pHXw5z|rgldYkJ)}+E2c5G2Bdgw zz0~8>ILTT}KlYGWN?rQT$!WL$)~*a`F5Ta>kSltz^?%p-Nm54^bL|gZ(O44p Date: Sun, 26 Jun 2022 15:50:35 +0200 Subject: [PATCH 032/164] Fix ads tile (#1905) --- .../repositories/PreferencesRepository.kt | 25 ++++++++++++++++--- .../modules/dashboard/DashboardPresenter.kt | 6 ++++- .../ui/modules/main/MainPresenter.kt | 4 +-- .../ui/modules/settings/ads/AdsFragment.kt | 5 ---- .../ui/modules/settings/ads/AdsPresenter.kt | 10 -------- .../io/github/wulkanowy/utils/AdsHelper.kt | 2 +- 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt index 237fb1a0a..486538e0c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt @@ -222,17 +222,31 @@ class PreferencesRepository @Inject constructor( get() = selectedDashboardTilesPreference.asFlow() .map { set -> set.map { DashboardItem.Tile.valueOf(it) } - .plus(listOf(DashboardItem.Tile.ACCOUNT, DashboardItem.Tile.ADMIN_MESSAGE)) + .plus( + listOfNotNull( + DashboardItem.Tile.ACCOUNT, + DashboardItem.Tile.ADMIN_MESSAGE, + DashboardItem.Tile.ADS.takeIf { isAdsEnabled } + ) + ) .toSet() } var selectedDashboardTiles: Set get() = selectedDashboardTilesPreference.get() .map { DashboardItem.Tile.valueOf(it) } - .plus(listOf(DashboardItem.Tile.ACCOUNT, DashboardItem.Tile.ADMIN_MESSAGE)) + .plus( + listOfNotNull( + DashboardItem.Tile.ACCOUNT, + DashboardItem.Tile.ADMIN_MESSAGE, + DashboardItem.Tile.ADS.takeIf { isAdsEnabled } + ) + ) .toSet() set(value) { - val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT } + val filteredValue = value.filterNot { + it == DashboardItem.Tile.ACCOUNT || it == DashboardItem.Tile.ADMIN_MESSAGE + } .map { it.name } .toSet() @@ -288,6 +302,11 @@ class PreferencesRepository @Inject constructor( get() = sharedPref.getBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, false) set(value) = sharedPref.edit { putBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, value) } + val isAdsEnabledFlow = flowSharedPref.getBoolean( + context.getString(R.string.pref_key_ads_enabled), + context.resources.getBoolean(R.bool.pref_default_ads_enabled) + ).asFlow() + var isAdsEnabled: Boolean get() = getBoolean( R.string.pref_key_ads_enabled, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt index e963a0205..5d7c7df4b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -57,7 +57,11 @@ class DashboardPresenter @Inject constructor( showContent(false) } - preferencesRepository.selectedDashboardTilesFlow + merge( + preferencesRepository.selectedDashboardTilesFlow, + preferencesRepository.isAdsEnabledFlow + .map { preferencesRepository.selectedDashboardTiles } + ) .onEach { loadData(tilesToLoad = it) } .launch("dashboard_pref") } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index 8f457d925..9c32d8583 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -14,7 +14,6 @@ import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.account.AccountView import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView -import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.ui.modules.message.MessageView import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView @@ -168,13 +167,12 @@ class MainPresenter @Inject constructor( } fun onPrivacyAgree(isPersonalizedAds: Boolean) { - preferencesRepository.isAdsEnabled = true preferencesRepository.isAgreeToProcessData = true preferencesRepository.isPersonalizedAdsEnabled = isPersonalizedAds adsHelper.initialize() - preferencesRepository.selectedDashboardTiles += DashboardItem.Tile.ADS + preferencesRepository.isAdsEnabled = true } fun onPrivacySelected() { diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt index 48a6fc3ec..de4c591e1 100644 --- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt +++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt @@ -50,11 +50,6 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView { presenter.onConsentSelected(newValue as Boolean) true } - - findPreference(getString(R.string.pref_key_ads_enabled))?.setOnPreferenceChangeListener { _, newValue -> - presenter.onAddEnabled(newValue as Boolean) - true - } } override fun showAd(ad: RewardedInterstitialAd) { diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt index 85c14c0a0..772d616d7 100644 --- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt +++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt @@ -4,7 +4,6 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.utils.AdsHelper import kotlinx.coroutines.launch import timber.log.Timber @@ -48,7 +47,6 @@ class AdsPresenter @Inject constructor( } else { view?.showProcessingDataSummary(null) view?.setCheckedAdsEnabled(false) - onAddEnabled(false) } } @@ -77,12 +75,4 @@ class AdsPresenter @Inject constructor( view?.setCheckedProcessingData(true) view?.showProcessingDataSummary(true) } - - fun onAddEnabled(isEnabled: Boolean) { - if (isEnabled) { - preferencesRepository.selectedDashboardTiles += DashboardItem.Tile.ADS - } else { - preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS - } - } } diff --git a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt index 6be8e924c..c536e2218 100644 --- a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt +++ b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt @@ -66,7 +66,7 @@ class AdsHelper @Inject constructor( return suspendCoroutine { val adView = AdView(context).apply { - adSize = AdSize.getPortraitAnchoredAdaptiveBannerAdSize(context, width) + setAdSize(AdSize.getPortraitAnchoredAdaptiveBannerAdSize(context, width)) adUnitId = BuildConfig.DASHBOARD_TILE_AD_ID adListener = object : AdListener() { override fun onAdFailedToLoad(loadAdError: LoadAdError) { From b70649f13672b45e8574cdc2f92c8ebc6299bfec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:05:51 +0000 Subject: [PATCH 033/164] Bump about_libraries from 10.3.0 to 10.3.1 (#1907) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0391e282c..21964d49d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { kotlin_version = '1.7.0' - about_libraries = '10.3.0' + about_libraries = '10.3.1' hilt_version = "2.42" } repositories { From e70fe6f097039ea0ddd8e92d96627d82ea6ac925 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:06:20 +0000 Subject: [PATCH 034/164] Bump firebase-crashlytics-gradle from 2.9.0 to 2.9.1 (#1910) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 21964d49d..7949ceaac 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ buildscript { classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.10' classpath 'com.huawei.agconnect:agcp:1.7.0.300' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513" From 0f11f14c3e3df07a3a77c7405df8245689831d12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:07:33 +0000 Subject: [PATCH 035/164] Bump firebase-bom from 30.1.0 to 30.2.0 (#1909) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ab1d0aefd..b7a5c04b0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -239,7 +239,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.7.0' - playImplementation platform('com.google.firebase:firebase-bom:30.1.0') + playImplementation platform('com.google.firebase:firebase-bom:30.2.0') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From 1b74bffc06bab18be850a9a2be80a75385fb6166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 2 Jul 2022 19:10:57 +0200 Subject: [PATCH 036/164] Fix no mobile devices on parent account (#1896) --- .../50.json | 2445 +++++++++++++++++ .../github/wulkanowy/data/db/AppDatabase.kt | 5 +- .../wulkanowy/data/db/dao/MobileDeviceDao.kt | 2 +- .../data/db/entities/MobileDevice.kt | 2 +- .../data/db/migrations/Migration50.kt | 21 + .../data/mappers/MobileDeviceMapper.kt | 6 +- .../repositories/MobileDeviceRepository.kt | 4 +- .../MobileDeviceRepositoryTest.kt | 20 +- 8 files changed, 2486 insertions(+), 19 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json new file mode 100644 index 000000000..4361db954 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json @@ -0,0 +1,2445 @@ +{ + "formatVersion": 1, + "database": { + "version": 50, + "identityHash": "87455aae2b15baa976386c833afa9cd9", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "kindergartenDiaryId", + "columnName": "kindergarten_diary_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "kindergarten_diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "destination", + "columnName": "destination", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '87455aae2b15baa976386c833afa9cd9')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 17fd7d696..87915a9ed 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -55,7 +55,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 49 + const val VERSION_SCHEMA = 50 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), @@ -102,7 +102,8 @@ abstract class AppDatabase : RoomDatabase() { Migration43(), Migration44(), Migration46(), - Migration49() + Migration49(), + Migration50() ) fun newInstance( diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt index 081e859a5..96382cc10 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt @@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow @Dao interface MobileDeviceDao : BaseDao { - @Query("SELECT * FROM MobileDevices WHERE student_id = :userLoginId ORDER BY date DESC") + @Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC") fun loadAll(userLoginId: Int): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt index 887e43239..89b04ccc8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt @@ -9,7 +9,7 @@ import java.time.Instant @Entity(tableName = "MobileDevices") data class MobileDevice( - @ColumnInfo(name = "student_id") + @ColumnInfo(name = "user_login_id") val userLoginId: Int, @ColumnInfo(name = "device_id") diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt new file mode 100644 index 000000000..d45a81570 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration50 : Migration(49, 50) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS MobileDevices") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `MobileDevices` ( + `user_login_id` INTEGER NOT NULL, + `device_id` INTEGER NOT NULL, + `name` TEXT NOT NULL, + `date` INTEGER NOT NULL, + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL) + """.trimIndent() + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt index b1e96a27b..1a1c501f6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt @@ -1,14 +1,14 @@ package io.github.wulkanowy.data.mappers import io.github.wulkanowy.data.db.entities.MobileDevice -import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.pojos.MobileDeviceToken import io.github.wulkanowy.sdk.pojo.Device as SdkDevice import io.github.wulkanowy.sdk.pojo.Token as SdkToken -fun List.mapToEntities(semester: Semester) = map { +fun List.mapToEntities(student: Student) = map { MobileDevice( - userLoginId = semester.studentId, + userLoginId = student.userLoginId, date = it.createDateZoned.toInstant(), deviceId = it.id, name = it.name diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt index eda40cac4..07c6959e3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt @@ -39,12 +39,12 @@ class MobileDeviceRepository @Inject constructor( val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) it.isEmpty() || forceRefresh || isExpired }, - query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) }, + query = { mobileDb.loadAll(student.userLoginId) }, fetch = { sdk.init(student) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) .getRegisteredDevices() - .mapToEntities(semester) + .mapToEntities(student) }, saveFetchResult = { old, new -> mobileDb.deleteAll(old uniqueSubtract new) diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt index b9a958d43..6865aa7da 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt @@ -56,8 +56,8 @@ class MobileDeviceRepositoryTest { // prepare coEvery { sdk.getRegisteredDevices() } returns remoteList coEvery { mobileDeviceDb.loadAll(student.studentId) } returnsMany listOf( - flowOf(remoteList.mapToEntities(semester)), - flowOf(remoteList.mapToEntities(semester)) + flowOf(remoteList.mapToEntities(student)), + flowOf(remoteList.mapToEntities(student)) ) coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3) coEvery { mobileDeviceDb.deleteAll(any()) } just Runs @@ -79,9 +79,9 @@ class MobileDeviceRepositoryTest { // prepare coEvery { sdk.getRegisteredDevices() } returns remoteList coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf( - flowOf(remoteList.dropLast(1).mapToEntities(semester)), - flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result - flowOf(remoteList.mapToEntities(semester)) + flowOf(remoteList.dropLast(1).mapToEntities(student)), + flowOf(remoteList.dropLast(1).mapToEntities(student)), // after fetch end before save result + flowOf(remoteList.mapToEntities(student)) ) coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3) coEvery { mobileDeviceDb.deleteAll(any()) } just Runs @@ -96,7 +96,7 @@ class MobileDeviceRepositoryTest { coVerify { mobileDeviceDb.loadAll(1) } coVerify { mobileDeviceDb.insertAll(match { - it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1] + it.size == 1 && it[0] == remoteList.mapToEntities(student)[1] }) } coVerify { mobileDeviceDb.deleteAll(match { it.isEmpty() }) } @@ -107,9 +107,9 @@ class MobileDeviceRepositoryTest { // prepare coEvery { sdk.getRegisteredDevices() } returns remoteList.dropLast(1) coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf( - flowOf(remoteList.mapToEntities(semester)), - flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result - flowOf(remoteList.dropLast(1).mapToEntities(semester)) + flowOf(remoteList.mapToEntities(student)), + flowOf(remoteList.mapToEntities(student)), // after fetch end before save result + flowOf(remoteList.dropLast(1).mapToEntities(student)) ) coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3) coEvery { mobileDeviceDb.deleteAll(any()) } just Runs @@ -125,7 +125,7 @@ class MobileDeviceRepositoryTest { coVerify { mobileDeviceDb.insertAll(match { it.isEmpty() }) } coVerify { mobileDeviceDb.deleteAll(match { - it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1] + it.size == 1 && it[0] == remoteList.mapToEntities(student)[1] }) } } From fcc7dc0913227c595a2575326b80d3ba51ca15d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 Jul 2022 07:33:27 +0000 Subject: [PATCH 037/164] Bump fragment-ktx from 1.4.1 to 1.5.0 (#1915) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b7a5c04b0..50c572ed0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -195,7 +195,7 @@ dependencies { implementation 'androidx.core:core-splashscreen:1.0.0-rc01' implementation "androidx.activity:activity-ktx:1.4.0" implementation "androidx.appcompat:appcompat:1.4.2" - implementation "androidx.fragment:fragment-ktx:1.4.1" + implementation "androidx.fragment:fragment-ktx:1.5.0" implementation "androidx.annotation:annotation:1.4.0" implementation "androidx.preference:preference-ktx:1.2.0" From 89a6a98bbf7e15e808b35bea3d8364c5e1cacdca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 Jul 2022 07:33:46 +0000 Subject: [PATCH 038/164] Bump google-services from 4.3.10 to 4.3.13 (#1913) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7949ceaac..b96c8582e 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath 'com.android.tools.build:gradle:7.2.1' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" - classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.gms:google-services:4.3.13' classpath 'com.huawei.agconnect:agcp:1.7.0.300' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1' classpath "com.github.triplet.gradle:play-publisher:3.6.0" From 344e0d55ffb83c0acd75b1d2ed2755a7c9033329 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 Jul 2022 07:34:03 +0000 Subject: [PATCH 039/164] Bump lifecycle-livedata-ktx from 2.4.1 to 2.5.0 (#1911) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 50c572ed0..83e18e953 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -212,7 +212,7 @@ dependencies { implementation "androidx.work:work-runtime-ktx:$work_manager" playImplementation "androidx.work:work-gcm:$work_manager" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.0" implementation "androidx.room:room-runtime:$room" implementation "androidx.room:room-ktx:$room" From 4b6277abf517162b695788d082126a7e25ba6953 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 Jul 2022 07:34:24 +0000 Subject: [PATCH 040/164] Bump activity-ktx from 1.4.0 to 1.5.0 (#1912) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 83e18e953..91c458761 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -193,7 +193,7 @@ dependencies { implementation "androidx.core:core-ktx:1.8.0" implementation 'androidx.core:core-splashscreen:1.0.0-rc01' - implementation "androidx.activity:activity-ktx:1.4.0" + implementation "androidx.activity:activity-ktx:1.5.0" implementation "androidx.appcompat:appcompat:1.4.2" implementation "androidx.fragment:fragment-ktx:1.5.0" implementation "androidx.annotation:annotation:1.4.0" From f1c217b087e46faef8279ab9b0bd47d415c0745a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jul 2022 10:20:28 +0000 Subject: [PATCH 041/164] Bump kotlin_version from 1.7.0 to 1.7.10 (#1918) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b96c8582e..71eb2def3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - kotlin_version = '1.7.0' + kotlin_version = '1.7.10' about_libraries = '10.3.1' hilt_version = "2.42" } From bdb6c962ea2ac017b3a46c7d7f743b24a6fb7545 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jul 2022 10:20:42 +0000 Subject: [PATCH 042/164] Bump hianalytics from 6.5.0.300 to 6.6.0.300 (#1919) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 91c458761..588f8dccc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -247,7 +247,7 @@ dependencies { playImplementation 'com.google.android.play:core-ktx:1.8.1' playImplementation 'com.google.android.gms:play-services-ads:21.0.0' - hmsImplementation 'com.huawei.hms:hianalytics:6.5.0.300' + hmsImplementation 'com.huawei.hms:hianalytics:6.6.0.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.0.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" From 7c4f1c7b22a8a67df9c5575ab74b7651555e4b59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 12 Jul 2022 12:23:55 +0200 Subject: [PATCH 043/164] Add auto refresh to reporting units (#1916) --- .../repositories/ReportingUnitRepository.kt | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt index b9caf978b..84055cef0 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt @@ -5,6 +5,8 @@ import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.uniqueSubtract import javax.inject.Inject @@ -13,30 +15,39 @@ import javax.inject.Singleton @Singleton class ReportingUnitRepository @Inject constructor( private val reportingUnitDb: ReportingUnitDao, - private val sdk: Sdk + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, ) { + private val cacheKey = "reporting_unit" + suspend fun refreshReportingUnits(student: Student) { val new = sdk.init(student).getReportingUnits().mapToEntities(student) val old = reportingUnitDb.load(student.id.toInt()) reportingUnitDb.deleteAll(old.uniqueSubtract(new)) reportingUnitDb.insertAll(new.uniqueSubtract(old)) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) } suspend fun getReportingUnits(student: Student): List { - return reportingUnitDb.load(student.id.toInt()).ifEmpty { - refreshReportingUnits(student) + val cached = reportingUnitDb.load(student.id.toInt()) + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) + return if (cached.isEmpty() || isExpired) { + refreshReportingUnits(student) reportingUnitDb.load(student.id.toInt()) - } + } else cached } suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? { - return reportingUnitDb.loadOne(student.id.toInt(), unitId) ?: run { - refreshReportingUnits(student) + val cached = reportingUnitDb.loadOne(student.id.toInt(), unitId) + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - return reportingUnitDb.loadOne(student.id.toInt(), unitId) - } + return if (cached == null || isExpired) { + refreshReportingUnits(student) + reportingUnitDb.loadOne(student.id.toInt(), unitId) + } else cached } } From fd18583df2ce86969eeab953754c7f7680448d01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 21:28:20 +0000 Subject: [PATCH 044/164] Bump play-services-ads from 21.0.0 to 21.1.0 (#1922) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 588f8dccc..b51c87f10 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -245,7 +245,7 @@ dependencies { playImplementation 'com.google.firebase:firebase-crashlytics:' playImplementation 'com.google.android.play:core:1.10.3' playImplementation 'com.google.android.play:core-ktx:1.8.1' - playImplementation 'com.google.android.gms:play-services-ads:21.0.0' + playImplementation 'com.google.android.gms:play-services-ads:21.1.0' hmsImplementation 'com.huawei.hms:hianalytics:6.6.0.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.0.300' From 08c9539abee8d52ecd3b16738056333ba1aee665 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 21:28:37 +0000 Subject: [PATCH 045/164] Bump coroutines from 1.6.3 to 1.6.4 (#1921) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b51c87f10..0a848a44c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -180,7 +180,7 @@ ext { room = "2.4.2" chucker = "3.5.2" mockk = "1.12.4" - coroutines = "1.6.3" + coroutines = "1.6.4" } dependencies { From dfc4553fc61540aae4bf84f28b4474f1469ab3c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 21:28:53 +0000 Subject: [PATCH 046/164] Bump firebase-bom from 30.2.0 to 30.3.0 (#1920) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 0a848a44c..d5df65793 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -239,7 +239,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.7.0' - playImplementation platform('com.google.firebase:firebase-bom:30.2.0') + playImplementation platform('com.google.firebase:firebase-bom:30.3.0') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From b9be85d99cc11c526431931bf927c52e9a1d446b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 10:09:28 +0000 Subject: [PATCH 047/164] Bump hilt_version from 2.42 to 2.43 (#1923) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 71eb2def3..bb9afab3b 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { kotlin_version = '1.7.10' about_libraries = '10.3.1' - hilt_version = "2.42" + hilt_version = "2.43" } repositories { mavenCentral() From efa68f50447f3eb66240cebafb5d156a4e57d780 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:16:46 +0000 Subject: [PATCH 048/164] Bump mockk from 1.12.4 to 1.12.5 (#1933) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index d5df65793..2f284d0a3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -179,7 +179,7 @@ ext { android_hilt = "1.0.0" room = "2.4.2" chucker = "3.5.2" - mockk = "1.12.4" + mockk = "1.12.5" coroutines = "1.6.4" } From cf7c6f78eaa7e4f6227b173d30b3bac6b4808539 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:17:00 +0000 Subject: [PATCH 049/164] Bump lifecycle-livedata-ktx from 2.5.0 to 2.5.1 (#1932) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 2f284d0a3..2f4f7021c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -212,7 +212,7 @@ dependencies { implementation "androidx.work:work-runtime-ktx:$work_manager" playImplementation "androidx.work:work-gcm:$work_manager" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.0" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1" implementation "androidx.room:room-runtime:$room" implementation "androidx.room:room-ktx:$room" From d337be0f40c3e1becf59178ff813ff295eef7842 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:17:20 +0000 Subject: [PATCH 050/164] Bump firebase-bom from 30.3.0 to 30.3.1 (#1931) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 2f4f7021c..80e3dab46 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -239,7 +239,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.7.0' - playImplementation platform('com.google.firebase:firebase-bom:30.3.0') + playImplementation platform('com.google.firebase:firebase-bom:30.3.1') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From c23a90f1043fc19aa0b71095bb71d9625e4e2c63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:17:35 +0000 Subject: [PATCH 051/164] Bump fragment-ktx from 1.5.0 to 1.5.1 (#1930) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 80e3dab46..ac8df81b2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -195,7 +195,7 @@ dependencies { implementation 'androidx.core:core-splashscreen:1.0.0-rc01' implementation "androidx.activity:activity-ktx:1.5.0" implementation "androidx.appcompat:appcompat:1.4.2" - implementation "androidx.fragment:fragment-ktx:1.5.0" + implementation "androidx.fragment:fragment-ktx:1.5.1" implementation "androidx.annotation:annotation:1.4.0" implementation "androidx.preference:preference-ktx:1.2.0" From cf87339ac40aca6121a863829933cff8f6af935c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:18:52 +0000 Subject: [PATCH 052/164] Bump hilt_version from 2.43 to 2.43.1 (#1927) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bb9afab3b..c88cd35fc 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { kotlin_version = '1.7.10' about_libraries = '10.3.1' - hilt_version = "2.43" + hilt_version = "2.43.1" } repositories { mavenCentral() From 378ed0100f7847ee7a25b9479cb9f188a17a080e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:26:15 +0000 Subject: [PATCH 053/164] Bump huawei-publish-gradle-plugin from 1.3.3 to 1.3.4 (#1934) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c88cd35fc..c4dc9c279 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ buildscript { classpath 'com.huawei.agconnect:agcp:1.7.0.300' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1' classpath "com.github.triplet.gradle:play-publisher:3.6.0" - classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3" + classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513" classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries" From 1175740ba22860f27075e12f2c56f4cf6710bc7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:26:34 +0000 Subject: [PATCH 054/164] Bump activity-ktx from 1.5.0 to 1.5.1 (#1926) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ac8df81b2..4eaa79f16 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -193,7 +193,7 @@ dependencies { implementation "androidx.core:core-ktx:1.8.0" implementation 'androidx.core:core-splashscreen:1.0.0-rc01' - implementation "androidx.activity:activity-ktx:1.5.0" + implementation "androidx.activity:activity-ktx:1.5.1" implementation "androidx.appcompat:appcompat:1.4.2" implementation "androidx.fragment:fragment-ktx:1.5.1" implementation "androidx.annotation:annotation:1.4.0" From b67ecbba4b74bb1336910fe20f0b8ad2754b787f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:26:37 +0000 Subject: [PATCH 055/164] Bump room from 2.4.2 to 2.4.3 (#1928) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4eaa79f16..28aa85848 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -177,7 +177,7 @@ huaweiPublish { ext { work_manager = "2.7.1" android_hilt = "1.0.0" - room = "2.4.2" + room = "2.4.3" chucker = "3.5.2" mockk = "1.12.5" coroutines = "1.6.4" From dc3a941e24d6cf920a9c52b1a9980e2f76e7230d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:36:28 +0000 Subject: [PATCH 056/164] Bump core-splashscreen from 1.0.0-rc01 to 1.0.0 (#1929) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 28aa85848..542dda109 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -192,7 +192,7 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" implementation "androidx.core:core-ktx:1.8.0" - implementation 'androidx.core:core-splashscreen:1.0.0-rc01' + implementation 'androidx.core:core-splashscreen:1.0.0' implementation "androidx.activity:activity-ktx:1.5.1" implementation "androidx.appcompat:appcompat:1.4.2" implementation "androidx.fragment:fragment-ktx:1.5.1" From b4117aa62eb66497fc43a5ad7574fefc0207229e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 10:56:41 +0000 Subject: [PATCH 057/164] Bump flow-preferences from 1.7.0 to 1.8.0 (#1925) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 542dda109..a4549f08a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -237,7 +237,7 @@ dependencies { implementation "io.coil-kt:coil:2.1.0" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.4.0' - implementation 'com.fredporciuncula:flow-preferences:1.7.0' + implementation 'com.fredporciuncula:flow-preferences:1.8.0' playImplementation platform('com.google.firebase:firebase-bom:30.3.1') playImplementation 'com.google.firebase:firebase-analytics-ktx' From 9339e7d9168a695656fb88c5678f008dfee2308e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:27:24 +0000 Subject: [PATCH 058/164] Bump gradle from 7.2.1 to 7.2.2 (#1942) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c4dc9c279..35665b252 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle:7.2.2' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.13' classpath 'com.huawei.agconnect:agcp:1.7.0.300' From 96067946d034845495ae7ed074ad9a495cb1c494 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:27:46 +0000 Subject: [PATCH 059/164] Bump firebase-bom from 30.3.1 to 30.3.2 (#1940) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index a4549f08a..f8cb8440c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -239,7 +239,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:30.3.1') + playImplementation platform('com.google.firebase:firebase-bom:30.3.2') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From ffc0cd840b37b38630ab2b5ca360b5805421ac0e Mon Sep 17 00:00:00 2001 From: Patryk <43276401+Zaptyp@users.noreply.github.com> Date: Wed, 10 Aug 2022 11:28:22 +0200 Subject: [PATCH 060/164] Update workflow dependency (#1937) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mikołaj Pich --- .github/workflows/deploy-store.yml | 20 +++++---- .github/workflows/deploy-test.yml | 20 +++++---- .github/workflows/test.yml | 66 +++++++++++++++++++++++++++--- app/build.gradle | 16 ++++---- build.gradle | 2 +- 5 files changed, 92 insertions(+), 32 deletions(-) diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml index 12338feff..cfb0fb523 100644 --- a/.github/workflows/deploy-store.yml +++ b/.github/workflows/deploy-store.yml @@ -1,4 +1,4 @@ -name: Deploy to app stores +name: Deploy release on: release: @@ -7,16 +7,17 @@ on: jobs: deploy-google-play: - name: Deploy to google play + name: Google Play runs-on: ubuntu-latest timeout-minutes: 10 environment: google-play steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: 11 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/.gradle/caches @@ -41,16 +42,17 @@ jobs: run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace; deploy-app-gallery: - name: Deploy to AppGallery + name: AppGallery runs-on: ubuntu-latest timeout-minutes: 10 environment: app-gallery steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: 11 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/.gradle/caches diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index 88edca05d..20082590d 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -1,4 +1,4 @@ -name: Deploy to app tests +name: Deploy DEV on: push: @@ -18,11 +18,12 @@ jobs: timeout-minutes: 10 environment: app-center steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: 11 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/.gradle/caches @@ -66,7 +67,7 @@ jobs: BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }} run: ./gradlew assembleFdroidDebug --stacktrace - name: Upload apk to github artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk @@ -87,11 +88,12 @@ jobs: environment: app-distribution if: github.event_name != 'pull_request_target' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: 11 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/.gradle/caches @@ -131,7 +133,7 @@ jobs: BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }} run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace - name: Upload apk to github artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk path: app/build/outputs/apk/play/debug/app-play-debug.apk diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ee16041f6..3def08953 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,18 +8,20 @@ on: branches: [ master, develop ] jobs: - unit-tests: - name: Unit tests + + tests-fdroid: + name: F-Droid runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: fkirc/skip-duplicate-actions@master - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v1 + - uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: 11 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/.gradle/caches @@ -29,6 +31,58 @@ jobs: run: | ./gradlew testFdroidDebugUnitTest --stacktrace ./gradlew jacocoTestReport --stacktrace - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v3 + with: + flags: unit + + tests-play: + name: Play + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: fkirc/skip-duplicate-actions@master + - uses: actions/checkout@v3 + - uses: gradle/wrapper-validation-action@v1 + - uses: actions/setup-java@v2 + with: + distribution: 'zulu' + java-version: 11 + - uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - name: Unit tests + run: | + ./gradlew testPlayDebugUnitTest --stacktrace + ./gradlew jacocoTestReport --stacktrace + - uses: codecov/codecov-action@v3 + with: + flags: unit + + tests-hms: + name: HMS + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: fkirc/skip-duplicate-actions@master + - uses: actions/checkout@v3 + - uses: gradle/wrapper-validation-action@v1 + - uses: actions/setup-java@v2 + with: + distribution: 'zulu' + java-version: 11 + - uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - name: Unit tests + run: | + ./gradlew testHmsDebugUnitTest --stacktrace + ./gradlew jacocoTestReport --stacktrace + - uses: codecov/codecov-action@v3 with: flags: unit diff --git a/app/build.gradle b/app/build.gradle index f8cb8440c..78eebff8f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,14 +30,14 @@ android { resValue "string", "app_name", "Wulkanowy" manifestPlaceholders = [ - firebase_enabled: project.hasProperty("enableFirebase"), - admob_project_id: "" + firebase_enabled: project.hasProperty("enableFirebase"), + admob_project_id: "" ] javaCompileOptions { annotationProcessorOptions { arguments += [ - "room.schemaLocation": "$projectDir/schemas".toString(), - "room.incremental" : "true" + "room.schemaLocation": "$projectDir/schemas".toString(), + "room.incremental" : "true" ] } } @@ -96,8 +96,8 @@ android { play { dimension "platform" manifestPlaceholders = [ - install_channel : "Google Play", - admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713" + install_channel : "Google Play", + admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713" ] buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\"" buildConfigField "String", "DASHBOARD_TILE_AD_ID", "\"${System.getenv("DASHBOARD_TILE_AD_ID") ?: "ca-app-pub-3940256099942544/6300978111"}\"" @@ -126,6 +126,8 @@ android { testOptions.unitTests { includeAndroidResources = true + // workaround HMS test errors https://github.com/robolectric/robolectric/issues/2750 + all { jvmArgs '-noverify' } } compileOptions { @@ -184,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:16811fbe90" + implementation "io.github.wulkanowy:sdk:9032e33686" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' diff --git a/build.gradle b/build.gradle index 35665b252..84ed29b95 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { kotlin_version = '1.7.10' about_libraries = '10.3.1' - hilt_version = "2.43.1" + hilt_version = "2.43.2" } repositories { mavenCentral() From c55fd9817991081e24ee9cf1a49fc28eb406c001 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:37:53 +0000 Subject: [PATCH 061/164] Bump about_libraries from 10.3.1 to 10.4.0 (#1941) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 84ed29b95..bc4ca5199 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { kotlin_version = '1.7.10' - about_libraries = '10.3.1' + about_libraries = '10.4.0' hilt_version = "2.43.2" } repositories { From 5a884a4c56117a04fd21916c2c59c28f70d3439a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:38:15 +0000 Subject: [PATCH 062/164] Bump agcp from 1.7.0.300 to 1.7.1.300 (#1938) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bc4ca5199..98c9dfb84 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.2.2' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.13' - classpath 'com.huawei.agconnect:agcp:1.7.0.300' + classpath 'com.huawei.agconnect:agcp:1.7.1.300' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4" From 274f9dde07249c79277cc6d5cde1452dfb40fc4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:48:02 +0000 Subject: [PATCH 063/164] Bump agconnect-crash from 1.7.0.300 to 1.7.1.300 (#1943) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 78eebff8f..52b2aad33 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -250,7 +250,7 @@ dependencies { playImplementation 'com.google.android.gms:play-services-ads:21.1.0' hmsImplementation 'com.huawei.hms:hianalytics:6.6.0.300' - hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.0.300' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" From d64a21b50c2ddee1cce67a5cd02483504df1e1a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:56:20 +0000 Subject: [PATCH 064/164] Bump hianalytics from 6.6.0.300 to 6.7.0.300 (#1944) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 52b2aad33..1f68b65cc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -249,7 +249,7 @@ dependencies { playImplementation 'com.google.android.play:core-ktx:1.8.1' playImplementation 'com.google.android.gms:play-services-ads:21.1.0' - hmsImplementation 'com.huawei.hms:hianalytics:6.6.0.300' + hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" From 793952cb4479d0838d8dedff5467bf99d60d3ce1 Mon Sep 17 00:00:00 2001 From: Patryk <43276401+Zaptyp@users.noreply.github.com> Date: Sun, 14 Aug 2022 22:16:47 +0200 Subject: [PATCH 065/164] Fix typo in README DE.md (#1936) * Update README.de.md * Change in README-DE.md file --- README.de.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.de.md b/README.de.md index 3f806e9fd..b9e1d1ec1 100644 --- a/README.de.md +++ b/README.de.md @@ -51,7 +51,7 @@ Die aktuelle Version können Sie von der Google Play, F-Droid oder Huawei AppGal alt="Explore it on AppGallery" height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) -Sie können auch ein [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) das beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden +Sie können auch eine [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) die beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden ## Gebaut mit From 9fe1151a043ba337816269fc0e485d2c356a7d34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Aug 2022 22:22:47 +0000 Subject: [PATCH 066/164] Bump fragment-ktx from 1.5.1 to 1.5.2 (#1947) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1f68b65cc..eace462db 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -197,7 +197,7 @@ dependencies { implementation 'androidx.core:core-splashscreen:1.0.0' implementation "androidx.activity:activity-ktx:1.5.1" implementation "androidx.appcompat:appcompat:1.4.2" - implementation "androidx.fragment:fragment-ktx:1.5.1" + implementation "androidx.fragment:fragment-ktx:1.5.2" implementation "androidx.annotation:annotation:1.4.0" implementation "androidx.preference:preference-ktx:1.2.0" From 08a3bd77bde75f066099f276d887b9d44615a7f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 07:00:10 +0000 Subject: [PATCH 067/164] Bump appcompat from 1.4.2 to 1.5.0 (#1946) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index eace462db..6d33ac476 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "androidx.core:core-ktx:1.8.0" implementation 'androidx.core:core-splashscreen:1.0.0' implementation "androidx.activity:activity-ktx:1.5.1" - implementation "androidx.appcompat:appcompat:1.4.2" + implementation "androidx.appcompat:appcompat:1.5.0" implementation "androidx.fragment:fragment-ktx:1.5.2" implementation "androidx.annotation:annotation:1.4.0" From 9d47127921d5a1d8bff07bb708e284f00ef10bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Mon, 22 Aug 2022 14:30:50 +0200 Subject: [PATCH 068/164] Add support for messages plus API (#1945) --- app/build.gradle | 2 +- .../51.json | 2409 +++++++++++++++++ .../io/github/wulkanowy/data/DataModule.kt | 4 +- .../github/wulkanowy/data/db/AppDatabase.kt | 9 +- .../wulkanowy/data/db/dao/MailboxDao.kt | 17 + .../wulkanowy/data/db/dao/MessagesDao.kt | 8 +- .../wulkanowy/data/db/dao/RecipientDao.kt | 5 +- .../wulkanowy/data/db/dao/ReportingUnitDao.kt | 17 - .../wulkanowy/data/db/entities/Mailbox.kt | 25 + .../wulkanowy/data/db/entities/Message.kt | 27 +- .../data/db/entities/MessageAttachment.kt | 7 +- .../data/db/entities/MessageWithAttachment.kt | 2 +- .../wulkanowy/data/db/entities/Recipient.kt | 31 +- .../data/db/entities/ReportingUnit.kt | 32 - .../data/db/migrations/Migration51.kt | 88 + .../wulkanowy/data/mappers/MailboxMapper.kt | 18 + .../wulkanowy/data/mappers/MessageMapper.kt | 46 +- .../wulkanowy/data/mappers/RecipientMapper.kt | 17 +- .../data/mappers/ReportingUnitMapper.kt | 16 - .../data/repositories/MailboxRepository.kt | 48 + .../data/repositories/MessageRepository.kt | 114 +- .../data/repositories/RecipientRepository.kt | 37 +- .../repositories/ReportingUnitRepository.kt | 53 - .../notifications/NewMessageNotification.kt | 2 +- .../services/sync/works/MessageWork.kt | 7 +- .../services/sync/works/RecipientWork.kt | 15 +- .../modules/dashboard/DashboardPresenter.kt | 4 +- .../debug/notification/mock/message.kt | 13 +- .../message/preview/MessagePreviewAdapter.kt | 26 +- .../message/preview/MessagePreviewFragment.kt | 4 +- .../preview/MessagePreviewPresenter.kt | 114 +- .../message/preview/MessagePreviewView.kt | 2 +- .../message/send/SendMessageActivity.kt | 31 +- .../message/send/SendMessagePresenter.kt | 128 +- .../modules/message/send/SendMessageView.kt | 12 +- .../modules/message/tab/MessageTabAdapter.kt | 11 +- .../message/tab/MessageTabPresenter.kt | 22 +- .../main/res/layout/activity_send_message.xml | 3 +- app/src/main/res/values/strings.xml | 5 +- .../io/github/wulkanowy/TestEnityCreator.kt | 12 + .../repositories/MessageRepositoryTest.kt | 116 +- .../data/repositories/RecipientLocalTest.kt | 81 +- 42 files changed, 3065 insertions(+), 575 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt delete mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt delete mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt delete mode 100644 app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt delete mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt diff --git a/app/build.gradle b/app/build.gradle index 6d33ac476..0efb9c303 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:9032e33686" + implementation "io.github.wulkanowy:sdk:dbe87aac" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json new file mode 100644 index 000000000..271b8c90b --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json @@ -0,0 +1,2409 @@ +{ + "formatVersion": 1, + "database": { + "version": 51, + "identityHash": "51f9cb1d80df003c03bb655c0162487c", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "kindergartenDiaryId", + "columnName": "kindergarten_diary_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "kindergarten_diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", + "fields": [ + { + "fieldPath": "messageGlobalKey", + "columnName": "message_global_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mailboxKey", + "columnName": "mailbox_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "correspondents", + "columnName": "correspondents", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recipients", + "columnName": "recipients", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageGlobalKey", + "columnName": "message_global_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Mailboxes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `userLoginId` INTEGER NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", + "fields": [ + { + "fieldPath": "globalKey", + "columnName": "globalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "fullName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "userLoginId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "studentName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolNameShort", + "columnName": "schoolNameShort", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "globalKey" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "mailboxGlobalKey", + "columnName": "mailboxGlobalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentMailboxGlobalKey", + "columnName": "studentMailboxGlobalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "fullName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "schoolShortName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "destination", + "columnName": "destination", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '51f9cb1d80df003c03bb655c0162487c')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt index cac3ffc23..22123cbec 100644 --- a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt @@ -19,7 +19,6 @@ import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AppInfo -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType @@ -110,7 +109,6 @@ internal class DataModule { fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - @OptIn(ExperimentalCoroutinesApi::class) @Singleton @Provides fun provideFlowSharedPref(sharedPreferences: SharedPreferences) = @@ -197,7 +195,7 @@ internal class DataModule { @Singleton @Provides - fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao + fun provideMailboxesDao(database: AppDatabase) = database.mailboxDao @Singleton @Provides diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 87915a9ed..15b38805b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -30,7 +30,7 @@ import javax.inject.Singleton Subject::class, LuckyNumber::class, CompletedLesson::class, - ReportingUnit::class, + Mailbox::class, Recipient::class, MobileDevice::class, Teacher::class, @@ -55,7 +55,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 50 + const val VERSION_SCHEMA = 51 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), @@ -103,7 +103,8 @@ abstract class AppDatabase : RoomDatabase() { Migration44(), Migration46(), Migration49(), - Migration50() + Migration50(), + Migration51(), ) fun newInstance( @@ -154,7 +155,7 @@ abstract class AppDatabase : RoomDatabase() { abstract val completedLessonsDao: CompletedLessonsDao - abstract val reportingUnitDao: ReportingUnitDao + abstract val mailboxDao: MailboxDao abstract val recipientDao: RecipientDao diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt new file mode 100644 index 000000000..8589db311 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Mailbox +import javax.inject.Singleton + +@Singleton +@Dao +interface MailboxDao : BaseDao { + + @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ") + suspend fun loadAll(userLoginId: Int): List + + @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId AND studentName = :studentName ") + suspend fun load(userLoginId: Int, studentName: String): Mailbox? +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt index 729ba6a68..8c730c9bc 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -11,9 +11,9 @@ import kotlinx.coroutines.flow.Flow interface MessagesDao : BaseDao { @Transaction - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId") - fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow + @Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey") + fun loadMessageWithAttachment(messageGlobalKey: String): Flow - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC") - fun loadAll(studentId: Int, folder: Int): Flow> + @Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC") + fun loadAll(mailboxKey: String, folder: Int): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt index c2787ac3b..1956261eb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao import androidx.room.Query +import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Recipient import javax.inject.Singleton @@ -9,6 +10,6 @@ import javax.inject.Singleton @Dao interface RecipientDao : BaseDao { - @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND unit_id = :unitId AND role = :role") - suspend fun loadAll(studentId: Int, unitId: Int, role: Int): List + @Query("SELECT * FROM Recipients WHERE type = :type AND studentMailboxGlobalKey = :studentMailboxGlobalKey") + suspend fun loadAll(type: MailboxType, studentMailboxGlobalKey: String): List } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt deleted file mode 100644 index ca697eda8..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.ReportingUnit -import javax.inject.Singleton - -@Singleton -@Dao -interface ReportingUnitDao : BaseDao { - - @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") - suspend fun load(studentId: Int): List - - @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId") - suspend fun loadOne(studentId: Int, unitId: Int): ReportingUnit? -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt new file mode 100644 index 000000000..7c08e481d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "Mailboxes") +data class Mailbox( + + @PrimaryKey + val globalKey: String, + val fullName: String, + val userName: String, + val userLoginId: Int, + val studentName: String, + val schoolNameShort: String, + val type: MailboxType, +) + +enum class MailboxType { + STUDENT, + PARENT, + GUARDIAN, + EMPLOYEE, + UNKNOWN, +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt index 8782bc765..77874e03d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -9,23 +9,16 @@ import java.time.Instant @Entity(tableName = "Messages") data class Message( - @ColumnInfo(name = "student_id") - val studentId: Long, + @ColumnInfo(name = "message_global_key") + val messageGlobalKey: String, - @ColumnInfo(name = "real_id") - val realId: Int, + @ColumnInfo(name = "mailbox_key") + val mailboxKey: String, @ColumnInfo(name = "message_id") val messageId: Int, - @ColumnInfo(name = "sender_name") - val sender: String, - - @ColumnInfo(name = "sender_id") - val senderId: Int, - - @ColumnInfo(name = "recipient_name") - val recipient: String, + val correspondents: String, val subject: String, @@ -36,8 +29,6 @@ data class Message( var unread: Boolean, - val removed: Boolean, - @ColumnInfo(name = "has_attachments") val hasAttachments: Boolean ) : Serializable { @@ -48,11 +39,7 @@ data class Message( @ColumnInfo(name = "is_notified") var isNotified: Boolean = true - @ColumnInfo(name = "unread_by") - var unreadBy: Int = 0 - - @ColumnInfo(name = "read_by") - var readBy: Int = 0 - var content: String = "" + var sender: String? = null + var recipients: String? = null } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt index d1886e910..93f042999 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt @@ -12,11 +12,8 @@ data class MessageAttachment( @ColumnInfo(name = "real_id") val realId: Int, - @ColumnInfo(name = "message_id") - val messageId: Int, - - @ColumnInfo(name = "one_drive_id") - val oneDriveId: String, + @ColumnInfo(name = "message_global_key") + val messageGlobalKey: String, @ColumnInfo(name = "url") val url: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt index 2e7af0f40..cd468215d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt @@ -7,6 +7,6 @@ data class MessageWithAttachment( @Embedded val message: Message, - @Relation(parentColumn = "message_id", entityColumn = "message_id") + @Relation(parentColumn = "message_global_key", entityColumn = "message_global_key") val attachments: List ) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt index 223322705..d09742cd2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.data.db.entities -import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import java.io.Serializable @@ -8,32 +7,16 @@ import java.io.Serializable @kotlinx.serialization.Serializable @Entity(tableName = "Recipients") data class Recipient( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "real_id") - val realId: String, - - val name: String, - - @ColumnInfo(name = "real_name") - val realName: String, - - @ColumnInfo(name = "login_id") - val loginId: Int, - - @ColumnInfo(name = "unit_id") - val unitId: Int, - - val role: Int, - - val hash: String - + val mailboxGlobalKey: String, + val studentMailboxGlobalKey: String, + val fullName: String, + val userName: String, + val schoolShortName: String, + val type: MailboxType, ) : Serializable { @PrimaryKey(autoGenerate = true) var id: Long = 0 - override fun toString() = name + override fun toString() = userName } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt deleted file mode 100644 index 0570a2ffd..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity(tableName = "ReportingUnits") -data class ReportingUnit( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "real_id") - val unitId: Int, - - @ColumnInfo(name = "short") - val shortName: String, - - @ColumnInfo(name = "sender_id") - val senderId: Int, - - @ColumnInfo(name = "sender_name") - val senderName: String, - - val roles: List - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt new file mode 100644 index 000000000..e78e2e3a7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt @@ -0,0 +1,88 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration51 : Migration(50, 51) { + + override fun migrate(database: SupportSQLiteDatabase) { + createMailboxTable(database) + recreateMessagesTable(database) + recreateMessageAttachmentsTable(database) + recreateRecipientsTable(database) + deleteReportingUnitTable(database) + } + + private fun createMailboxTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Mailboxes") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `Mailboxes` ( + `globalKey` TEXT NOT NULL, + `fullName` TEXT NOT NULL, + `userName` TEXT NOT NULL, + `userLoginId` INTEGER NOT NULL, + `studentName` TEXT NOT NULL, + `schoolNameShort` TEXT NOT NULL, + `type` TEXT NOT NULL, + PRIMARY KEY(`globalKey`) + )""".trimIndent() + ) + } + + private fun recreateMessagesTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `Messages` ( + `message_global_key` TEXT NOT NULL, + `mailbox_key` TEXT NOT NULL, + `message_id` INTEGER NOT NULL, + `correspondents` TEXT NOT NULL, + `subject` TEXT NOT NULL, + `date` INTEGER NOT NULL, + `folder_id` INTEGER NOT NULL, + `unread` INTEGER NOT NULL, + `has_attachments` INTEGER NOT NULL, + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `is_notified` INTEGER NOT NULL, + `content` TEXT NOT NULL, + `sender` TEXT, `recipients` TEXT + )""".trimIndent() + ) + } + + private fun recreateMessageAttachmentsTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS MessageAttachments") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `MessageAttachments` ( + `real_id` INTEGER NOT NULL, + `message_global_key` TEXT NOT NULL, + `url` TEXT NOT NULL, + `filename` TEXT NOT NULL, + PRIMARY KEY(`real_id`) + )""".trimIndent() + ) + } + + private fun recreateRecipientsTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Recipients") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `Recipients` ( + `mailboxGlobalKey` TEXT NOT NULL, + `studentMailboxGlobalKey` TEXT NOT NULL, + `fullName` TEXT NOT NULL, + `userName` TEXT NOT NULL, + `schoolShortName` TEXT NOT NULL, + `type` TEXT NOT NULL, + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL + )""".trimIndent() + ) + } + + private fun deleteReportingUnitTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS ReportingUnits") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt new file mode 100644 index 000000000..2ccca1b90 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.db.entities.MailboxType +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.pojo.Mailbox as SdkMailbox + +fun List.mapToEntities(student: Student) = map { + Mailbox( + globalKey = it.globalKey, + fullName = it.fullName, + userName = it.userName, + userLoginId = student.userLoginId, + studentName = it.studentName, + schoolNameShort = it.schoolNameShort, + type = MailboxType.valueOf(it.type.name), + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt index 13f0ab33e..2e7967f0e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -1,40 +1,31 @@ package io.github.wulkanowy.data.mappers -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.MessageAttachment -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.Student -import java.time.Instant +import io.github.wulkanowy.data.db.entities.* +import io.github.wulkanowy.sdk.pojo.MailboxType import io.github.wulkanowy.sdk.pojo.Message as SdkMessage import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient -fun List.mapToEntities(student: Student) = map { +fun List.mapToEntities(mailbox: Mailbox) = map { Message( - studentId = student.id, - realId = it.id ?: 0, - messageId = it.messageId ?: 0, - sender = it.sender?.name.orEmpty(), - senderId = it.sender?.loginId ?: 0, - recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów", + messageGlobalKey = it.globalKey, + mailboxKey = mailbox.globalKey, + messageId = it.id, + correspondents = it.correspondents, subject = it.subject.trim(), - date = it.dateZoned?.toInstant() ?: Instant.now(), + date = it.dateZoned.toInstant(), folderId = it.folderId, - unread = it.unread ?: false, - removed = it.removed, + unread = it.unread, hasAttachments = it.hasAttachments ).apply { content = it.content.orEmpty() - unreadBy = it.unreadBy ?: 0 - readBy = it.readBy ?: 0 } } -fun List.mapToEntities() = map { +fun List.mapToEntities(messageGlobalKey: String) = map { MessageAttachment( - realId = it.id, - messageId = it.messageId, - oneDriveId = it.oneDriveId, + messageGlobalKey = messageGlobalKey, + realId = it.url.hashCode(), url = it.url, filename = it.filename ) @@ -42,12 +33,11 @@ fun List.mapToEntities() = map { fun List.mapFromEntities() = map { SdkRecipient( - id = it.realId, - name = it.realName, - loginId = it.loginId, - reportingUnitId = it.unitId, - role = it.role, - hash = it.hash, - shortName = it.name + fullName = it.fullName, + userName = it.userName, + studentName = it.userName, + mailboxGlobalKey = it.mailboxGlobalKey, + schoolNameShort = it.schoolShortName, + type = MailboxType.valueOf(it.type.name), ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt index 80bddaab1..eb993a0f0 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt @@ -1,17 +1,16 @@ package io.github.wulkanowy.data.mappers +import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient -fun List.mapToEntities(userLoginId: Int) = map { +fun List.mapToEntities(studentMailboxGlobalKey: String) = map { Recipient( - studentId = userLoginId, - realId = it.id, - realName = it.name, - name = it.shortName, - hash = it.hash, - loginId = it.loginId, - role = it.role, - unitId = it.reportingUnitId ?: 0 + mailboxGlobalKey = it.mailboxGlobalKey, + fullName = it.fullName, + userName = it.userName, + studentMailboxGlobalKey = studentMailboxGlobalKey, + schoolShortName = it.schoolNameShort, + type = MailboxType.valueOf(it.type.name), ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt deleted file mode 100644 index 6a21d59fc..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.data.mappers - -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.pojo.ReportingUnit as SdkReportingUnit - -fun List.mapToEntities(student: Student) = map { - ReportingUnit( - studentId = student.id.toInt(), - unitId = it.id, - roles = it.roles, - senderId = it.senderId, - senderName = it.senderName, - shortName = it.short - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt new file mode 100644 index 000000000..7f5974920 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt @@ -0,0 +1,48 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.MailboxDao +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MailboxRepository @Inject constructor( + private val mailboxDao: MailboxDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + private val cacheKey = "mailboxes" + + suspend fun refreshMailboxes(student: Student) { + val new = sdk.init(student).getMailboxes().mapToEntities(student) + val old = mailboxDao.loadAll(student.userLoginId) + + mailboxDao.deleteAll(old uniqueSubtract new) + mailboxDao.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) + } + + suspend fun getMailbox(student: Student): Mailbox { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) + val mailbox = mailboxDao.load(student.userLoginId, student.studentName) + + return if (isExpired || mailbox == null) { + refreshMailboxes(student) + val newMailbox = mailboxDao.load(student.userLoginId, student.studentName) + + requireNotNull(newMailbox) { + "Mailbox for ${student.userName} - ${student.studentName} not found!" + } + + newMailbox + } else mailbox + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index 05fb97657..00cbffb84 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -10,24 +10,24 @@ import io.github.wulkanowy.data.db.dao.MessagesDao import io.github.wulkanowy.data.db.entities.* import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.enums.MessageFolder.TRASHED import io.github.wulkanowy.data.mappers.mapFromEntities import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.data.pojos.MessageDraft import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Folder -import io.github.wulkanowy.sdk.pojo.SentMessage import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.sync.Mutex import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import timber.log.Timber -import java.time.LocalDateTime.now import javax.inject.Inject import javax.inject.Singleton @@ -49,7 +49,7 @@ class MessageRepository @Inject constructor( @Suppress("UNUSED_PARAMETER") fun getMessages( student: Student, - semester: Semester, + mailbox: Mailbox, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false, @@ -62,42 +62,20 @@ class MessageRepository @Inject constructor( ) it.isEmpty() || forceRefresh || isExpired }, - query = { messagesDb.loadAll(student.id.toInt(), folder.id) }, + query = { messagesDb.loadAll(mailbox.globalKey, folder.id) }, fetch = { - sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()) - .mapToEntities(student) + sdk.init(student).getMessages(Folder.valueOf(folder.name)).mapToEntities(mailbox) }, saveFetchResult = { old, new -> messagesDb.deleteAll(old uniqueSubtract new) messagesDb.insertAll((new uniqueSubtract old).onEach { it.isNotified = !notify }) - messagesDb.updateAll(getMessagesWithReadByChange(old, new, !notify)) refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder)) } ) - private fun getMessagesWithReadByChange( - old: List, - new: List, - setNotified: Boolean - ): List { - val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) } - val newMeta = new.map { Triple(it, it.readBy, it.unreadBy) } - - val updatedItems = newMeta uniqueSubtract oldMeta - - return updatedItems.map { - val oldItem = old.find { item -> item.messageId == it.first.messageId } - it.first.apply { - id = oldItem?.id ?: 0 - isNotified = oldItem?.isNotified ?: setNotified - content = oldItem?.content.orEmpty() - } - } - } - fun getMessage( student: Student, message: Message, @@ -106,34 +84,34 @@ class MessageRepository @Inject constructor( isResultEmpty = { it?.message?.content.isNullOrBlank() }, shouldFetch = { checkNotNull(it) { "This message no longer exist!" } - Timber.d("Message content in db empty: ${it.message.content.isEmpty()}") - it.message.unread || it.message.content.isEmpty() + Timber.d("Message content in db empty: ${it.message.content.isBlank()}") + it.message.unread || it.message.content.isBlank() }, - query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) }, + query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) }, fetch = { - sdk.init(student).getMessageDetails( - messageId = it!!.message.messageId, - folderId = message.folderId, - read = markAsRead, - id = message.realId - ).let { details -> - details.content to details.attachments.mapToEntities() - } + sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey) }, - saveFetchResult = { old, (downloadedMessage, attachments) -> + saveFetchResult = { old, new -> checkNotNull(old) { "Fetched message no longer exist!" } - messagesDb.updateAll(listOf(old.message.apply { - id = old.message.id - unread = !markAsRead - content = content.ifBlank { downloadedMessage } - })) - messageAttachmentDao.insertAttachments(attachments) + messagesDb.updateAll( + listOf(old.message.apply { + id = message.id + unread = !markAsRead + sender = new.sender + recipients = new.recipients.firstOrNull() ?: "Wielu adresoatów" + content = content.ifBlank { new.content } + }) + ) + messageAttachmentDao.insertAttachments( + items = new.attachments.mapToEntities(message.messageGlobalKey), + ) + Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read") } ) - fun getMessagesFromDatabase(student: Student): Flow> { - return messagesDb.loadAll(student.id.toInt(), RECEIVED.id) + fun getMessagesFromDatabase(mailbox: Mailbox): Flow> { + return messagesDb.loadAll(mailbox.globalKey, RECEIVED.id) } suspend fun updateMessages(messages: List) { @@ -145,32 +123,48 @@ class MessageRepository @Inject constructor( subject: String, content: String, recipients: List, - ): SentMessage = sdk.init(student).sendMessage( - subject = subject, - content = content, - recipients = recipients.mapFromEntities() - ) + mailboxId: String, + ) { + sdk.init(student).sendMessage( + subject = subject, + content = content, + recipients = recipients.mapFromEntities(), + mailboxId = mailboxId, + ) + } - suspend fun deleteMessages(student: Student, messages: List) { - val folderId = messages.first().folderId - val isDeleted = sdk.init(student) - .deleteMessages(messages = messages.map { it.messageId }, folderId = folderId) + suspend fun deleteMessages(student: Student, mailbox: Mailbox, messages: List) { + val firstMessage = messages.first() + sdk.init(student).deleteMessages( + messages = messages.map { it.messageGlobalKey }, + removeForever = firstMessage.folderId == TRASHED.id, + ) - if (folderId != MessageFolder.TRASHED.id && isDeleted) { + if (firstMessage.folderId != TRASHED.id) { val deletedMessages = messages.map { - it.copy(folderId = MessageFolder.TRASHED.id) + it.copy(folderId = TRASHED.id) .apply { id = it.id content = it.content + sender = it.sender + recipients = it.recipients } } messagesDb.updateAll(deletedMessages) } else messagesDb.deleteAll(messages) + + getMessages( + student = student, + mailbox = mailbox, + folder = TRASHED, + forceRefresh = true, + ).first() } - suspend fun deleteMessage(student: Student, message: Message) = - deleteMessages(student, listOf(message)) + suspend fun deleteMessage(student: Student, mailbox: Mailbox, message: Message) { + deleteMessages(student, mailbox, listOf(message)) + } var draftMessage: MessageDraft? get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft)) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt index 60e6f248f..e80f028e1 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt @@ -1,10 +1,7 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.db.dao.RecipientDao -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.* import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper @@ -23,9 +20,10 @@ class RecipientRepository @Inject constructor( private val cacheKey = "recipient" - suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) { - val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.studentId) - val old = recipientDb.loadAll(unit.studentId, unit.unitId, role) + suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) { + val new = sdk.init(student).getRecipients(mailbox.globalKey) + .mapToEntities(mailbox.globalKey) + val old = recipientDb.loadAll(type, mailbox.globalKey) recipientDb.deleteAll(old uniqueSubtract new) recipientDb.insertAll(new uniqueSubtract old) @@ -33,18 +31,27 @@ class RecipientRepository @Inject constructor( refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) } - suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List { - val cached = recipientDb.loadAll(unit.studentId, unit.unitId, role) + suspend fun getRecipients( + student: Student, + mailbox: Mailbox, + type: MailboxType + ): List { + val cached = recipientDb.loadAll(type, mailbox.globalKey) val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) return if (cached.isEmpty() || isExpired) { - refreshRecipients(student, unit, role) - recipientDb.loadAll(unit.studentId, unit.unitId, role) + refreshRecipients(student, mailbox, type) + recipientDb.loadAll(type, mailbox.globalKey) } else cached } - suspend fun getMessageRecipients(student: Student, message: Message): List { - return sdk.init(student).getMessageRecipients(message.messageId, message.senderId) - .mapToEntities(student.studentId) - } + suspend fun getMessageSender( + student: Student, + mailbox: Mailbox, + message: Message + ): List = sdk.init(student) + .getMessageReplayDetails(message.messageGlobalKey) + .sender + .let(::listOf) + .mapToEntities(mailbox.globalKey) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt deleted file mode 100644 index 84055cef0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.data.repositories - -import io.github.wulkanowy.data.db.dao.ReportingUnitDao -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.AutoRefreshHelper -import io.github.wulkanowy.utils.getRefreshKey -import io.github.wulkanowy.utils.init -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitRepository @Inject constructor( - private val reportingUnitDb: ReportingUnitDao, - private val sdk: Sdk, - private val refreshHelper: AutoRefreshHelper, -) { - - private val cacheKey = "reporting_unit" - - suspend fun refreshReportingUnits(student: Student) { - val new = sdk.init(student).getReportingUnits().mapToEntities(student) - val old = reportingUnitDb.load(student.id.toInt()) - - reportingUnitDb.deleteAll(old.uniqueSubtract(new)) - reportingUnitDb.insertAll(new.uniqueSubtract(old)) - - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) - } - - suspend fun getReportingUnits(student: Student): List { - val cached = reportingUnitDb.load(student.id.toInt()) - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - - return if (cached.isEmpty() || isExpired) { - refreshReportingUnits(student) - reportingUnitDb.load(student.id.toInt()) - } else cached - } - - suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? { - val cached = reportingUnitDb.loadOne(student.id.toInt(), unitId) - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - - return if (cached == null || isExpired) { - refreshReportingUnits(student) - reportingUnitDb.loadOne(student.id.toInt(), unitId) - } else cached - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt index 5c3c52c5b..3b7bcff05 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt @@ -21,7 +21,7 @@ class NewMessageNotification @Inject constructor( val notificationDataList = items.map { NotificationData( title = context.getPlural(R.plurals.message_new_items, 1), - content = "${it.sender}: ${it.subject}", + content = "${it.correspondents}: ${it.subject}", destination = Destination.Message, ) } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt index 26fac1a2f..180568267 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt @@ -3,6 +3,7 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.waitForResult import io.github.wulkanowy.services.sync.notifications.NewMessageNotification @@ -11,19 +12,21 @@ import javax.inject.Inject class MessageWork @Inject constructor( private val messageRepository: MessageRepository, + private val mailboxRepository: MailboxRepository, private val newMessageNotification: NewMessageNotification, ) : Work { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + val mailbox = mailboxRepository.getMailbox(student) messageRepository.getMessages( student = student, - semester = semester, + mailbox = mailbox, folder = RECEIVED, forceRefresh = true, notify = notify ).waitForResult() - messageRepository.getMessagesFromDatabase(student).first() + messageRepository.getMessagesFromDatabase(mailbox).first() .filter { !it.isNotified && it.unread }.let { if (it.isNotEmpty()) newMessageNotification.notify(it, student) messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt index 425e68b91..b1322ada3 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt @@ -1,23 +1,22 @@ package io.github.wulkanowy.services.sync.works +import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.RecipientRepository -import io.github.wulkanowy.data.repositories.ReportingUnitRepository import javax.inject.Inject class RecipientWork @Inject constructor( - private val reportingUnitRepository: ReportingUnitRepository, + private val mailboxRepository: MailboxRepository, private val recipientRepository: RecipientRepository ) : Work { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - reportingUnitRepository.refreshReportingUnits(student) + mailboxRepository.refreshMailboxes(student) - reportingUnitRepository.getReportingUnits(student).let { units -> - units.map { - recipientRepository.refreshRecipients(student, it, 2) - } - } + val mailbox = mailboxRepository.getMailbox(student) + + recipientRepository.refreshRecipients(student, mailbox, MailboxType.EMPLOYEE) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt index 5d7c7df4b..350300937 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -25,6 +25,7 @@ class DashboardPresenter @Inject constructor( private val gradeRepository: GradeRepository, private val semesterRepository: SemesterRepository, private val messageRepository: MessageRepository, + private val mailboxRepository: MailboxRepository, private val attendanceSummaryRepository: AttendanceSummaryRepository, private val timetableRepository: TimetableRepository, private val homeworkRepository: HomeworkRepository, @@ -227,6 +228,7 @@ class DashboardPresenter @Inject constructor( private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) { flow { val semester = semesterRepository.getCurrentSemester(student) + val mailbox = mailboxRepository.getMailbox(student) val selectedTiles = preferencesRepository.selectedDashboardTiles val flowSuccess = flowOf(Resource.Success(null)) @@ -238,7 +240,7 @@ class DashboardPresenter @Inject constructor( val messageFLow = messageRepository.getMessages( student = student, - semester = semester, + mailbox = mailbox, folder = MessageFolder.RECEIVED, forceRefresh = forceRefresh ).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt index 53d439612..6ff26162b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt @@ -17,16 +17,13 @@ val debugMessageItems = listOf( ) private fun generateMessage(sender: String, subject: String) = Message( - sender = sender, subject = subject, - studentId = 0, - realId = 0, - messageId = 0, - senderId = 0, - recipient = "", + messageId = 123, date = Instant.now(), folderId = 0, unread = true, - removed = false, - hasAttachments = false + hasAttachments = false, + messageGlobalKey = "", + correspondents = sender, + mailboxKey = "", ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt index d75128be1..3c1c53d39 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt @@ -4,6 +4,8 @@ import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT +import androidx.core.text.parseAsHtml import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message @@ -75,29 +77,25 @@ class MessagePreviewAdapter @Inject constructor() : @SuppressLint("SetTextI18n") private fun bindMessage(holder: MessageViewHolder, message: Message) { val context = holder.binding.root.context - val recipientCount = message.unreadBy + message.readBy - val readText = when { - recipientCount > 1 -> { - context.getString(R.string.message_read_by, message.readBy, recipientCount) - } - message.readBy == 1 -> { - context.getString(R.string.message_read, context.getString(R.string.all_yes)) - } - else -> context.getString(R.string.message_read, context.getString(R.string.all_no)) + val readTextValue = when { + !message.unread -> R.string.all_yes + else -> R.string.all_no } + val readText = context.getString(R.string.message_read, context.getString(readTextValue)) with(holder.binding) { - messagePreviewSubject.text = - message.subject.ifBlank { root.context.getString(R.string.message_no_subject) } - messagePreviewDate.text = root.context.getString( + messagePreviewSubject.text = message.subject.ifBlank { + context.getString(R.string.message_no_subject) + } + messagePreviewDate.text = context.getString( R.string.message_date, message.date.toFormattedString("yyyy-MM-dd HH:mm:ss") ) messagePreviewRead.text = readText - messagePreviewContent.text = message.content + messagePreviewContent.text = message.content.parseAsHtml(FROM_HTML_MODE_COMPACT) messagePreviewFromSender.text = message.sender - messagePreviewToRecipient.text = message.recipient + messagePreviewToRecipient.text = message.recipients } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 4b2685c6d..2a5523f4d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -135,8 +135,8 @@ class MessagePreviewFragment : binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE } - override fun showOptions(show: Boolean) { - menuReplyButton?.isVisible = show + override fun showOptions(show: Boolean, isReplayable: Boolean) { + menuReplyButton?.isVisible = isReplayable menuForwardButton?.isVisible = show menuDeleteButton?.isVisible = show menuShareButton?.isVisible = show diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index 39c337bf2..c011f41f3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -1,10 +1,12 @@ package io.github.wulkanowy.ui.modules.message.preview import android.annotation.SuppressLint +import androidx.core.text.parseAsHtml import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageAttachment import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter @@ -19,6 +21,7 @@ class MessagePreviewPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, + private val mailboxRepository: MailboxRepository, private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -52,7 +55,7 @@ class MessagePreviewPresenter @Inject constructor( private fun loadData(messageToLoad: Message) { flatResourceFlow { - val student = studentRepository.getStudentById(messageToLoad.studentId) + val student = studentRepository.getCurrentStudent() messageRepository.getMessage(student, messageToLoad, true) } .logResourceStatus("message ${messageToLoad.messageId} preview") @@ -104,62 +107,69 @@ class MessagePreviewPresenter @Inject constructor( } fun onShare(): Boolean { - message?.let { - var text = - "Temat: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}\n" + when (it.sender.isNotEmpty()) { - true -> "Od: ${it.sender}\n" - false -> "Do: ${it.recipient}\n" - } + "Data: ${it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${it.content}" + val message = message ?: return false + val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() } - attachments?.let { attachments -> - if (attachments.isNotEmpty()) { - text += "\n\nZałączniki:" + val text = buildString { + appendLine("Temat: $subject") + appendLine("Od: ${message.sender}") + appendLine("Do: ${message.recipients}") + appendLine("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}") - attachments.forEach { attachment -> - text += "\n${attachment.filename}: ${attachment.url}" - } - } + appendLine() + + appendLine(message.content.parseAsHtml()) + + if (!attachments.isNullOrEmpty()) { + appendLine() + appendLine("Załączniki:") + + append(attachments.orEmpty().joinToString(separator = "\n") { attachment -> + "${attachment.filename}: ${attachment.url}" + }) } - - view?.shareText( - text, - "FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}" - ) - return true } - return false + + view?.shareText( + subject = "FW: $subject", + text = text, + ) + return true } @SuppressLint("NewApi") fun onPrint(): Boolean { - message?.let { - val dateString = it.date.toFormattedString("yyyy-MM-dd HH:mm:ss") - val infoContent = "

Data wysłania

$dateString
" + when { - it.sender.isNotEmpty() -> "

Od

${it.sender}
" - else -> "

Do

${it.recipient}
" - } + val message = message ?: return false + val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() } - val messageContent = "

${it.content}

" - .replace(Regex("[\\n\\r]{2,}"), "

") - .replace(Regex("[\\n\\r]"), "
") + val dateString = message.date.toFormattedString("yyyy-MM-dd HH:mm:ss") - val jobName = "Wiadomość " + when { - it.sender.isNotEmpty() -> "od ${it.sender}" - else -> "do ${it.recipient}" - } + " $dateString: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }} | Wulkanowy" + val infoContent = buildString { + append("

Data wysłania

$dateString
") - view?.apply { - val html = printHTML - .replace( - "%SUBJECT%", - it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }) - .replace("%CONTENT%", messageContent) - .replace("%INFO%", infoContent) - printDocument(html, jobName) - } - return true + append("

Od

${message.sender}
") + append("

DO

${message.recipients}
") } - return false + val messageContent = "

${message.content}

" + .replace(Regex("[\\n\\r]{2,}"), "

") + .replace(Regex("[\\n\\r]"), "
") + + val jobName = buildString { + append("Wiadomość ") + append("od ${message.correspondents}") + append("do ${message.correspondents}") + append(" $dateString: $subject | Wulkanowy") + } + + view?.apply { + val html = printHTML + .replace("%SUBJECT%", subject) + .replace("%CONTENT%", messageContent) + .replace("%INFO%", infoContent) + printDocument(html, jobName) + } + + return true } private fun deleteMessage() { @@ -168,16 +178,17 @@ class MessagePreviewPresenter @Inject constructor( view?.run { showContent(false) showProgress(true) - showOptions(false) + showOptions(show = false, isReplayable = false) showErrorView(false) } - Timber.i("Delete message ${message?.id}") + Timber.i("Delete message ${message?.messageGlobalKey}") presenterScope.launch { runCatching { - val student = studentRepository.getCurrentStudent() - messageRepository.deleteMessage(student, message!!) + val student = studentRepository.getCurrentStudent(decryptPass = true) + val mailbox = mailboxRepository.getMailbox(student) + messageRepository.deleteMessage(student, mailbox, message!!) } .onFailure { retryCallback = { onMessageDelete() } @@ -211,7 +222,10 @@ class MessagePreviewPresenter @Inject constructor( private fun initOptions() { view?.apply { - showOptions(message != null) + showOptions( + show = message != null, + isReplayable = message?.folderId != MessageFolder.SENT.id, + ) message?.let { when (it.folderId == MessageFolder.TRASHED.id) { true -> setDeletedOptionsLabels() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index 88fe77d94..c5a947939 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -28,7 +28,7 @@ interface MessagePreviewView : BaseView { fun setErrorRetryCallback(callback: () -> Unit) - fun showOptions(show: Boolean) + fun showOptions(show: Boolean, isReplayable: Boolean) fun setDeletedOptionsLabels() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt index 70f9a9b54..334e389e2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -6,6 +6,7 @@ import android.content.Context import android.content.Intent import android.graphics.Rect import android.os.Bundle +import android.text.Spanned import android.view.Menu import android.view.MenuItem import android.view.TouchDelegate @@ -13,11 +14,12 @@ import android.view.View.GONE import android.view.View.VISIBLE import android.widget.Toast import android.widget.Toast.LENGTH_LONG +import androidx.core.text.parseAsHtml +import androidx.core.text.toHtml import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.databinding.ActivitySendMessageBinding import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.utils.dpToPx @@ -72,17 +74,32 @@ class SendMessageActivity : BaseActivity formSubjectValue = binding.sendMessageSubject.text.toString() - formContentValue = binding.sendMessageMessageContent.text.toString() + formContentValue = + binding.sendMessageMessageContent.text.toString().parseAsHtml().toString() presenter.onAttachView( view = this, @@ -110,7 +127,7 @@ class SendMessageActivity : BaseActivity) { @@ -165,7 +182,7 @@ class SendMessageActivity : BaseActivity "Re: " + true -> "RE: " else -> "FW: " } + message.subject ) if (preferencesRepository.fillMessageContent || reply != true) { - setContent( - when (reply) { - true -> "\n\n" - else -> "" - } + when (message.sender.isNotEmpty()) { - true -> "Od: ${message.sender}\n" - false -> "Do: ${message.recipient}\n" - } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}" - ) + setContent(buildString { + if (reply == true) { + append("

") + } + + append("Od: ${message.sender}
") + append("Do: ${message.recipients}
") + append("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}

") + append(message.content) + }) } } } @@ -111,21 +113,24 @@ class SendMessagePresenter @Inject constructor( private fun loadData(message: Message?, reply: Boolean?) { resourceFlow { val student = studentRepository.getCurrentStudent() - val semester = semesterRepository.getCurrentSemester(student) - val unit = reportingUnitRepository.getReportingUnit(student, semester.unitId) + val mailbox = mailboxRepository.getMailbox(student) Timber.i("Loading recipients started") - val recipients = when { - unit != null -> recipientRepository.getRecipients(student, unit, 2) - else -> listOf() - }.let { createChips(it) } + val recipients = createChips( + recipients = recipientRepository.getRecipients( + student = student, + mailbox = mailbox, + type = MailboxType.EMPLOYEE, + ) + ) Timber.i("Loading recipients result: Success, fetched %d recipients", recipients.size) Timber.i("Loading message recipients started") val messageRecipients = when { - message != null && reply == true -> recipientRepository.getMessageRecipients( - student, - message + message != null && reply == true -> recipientRepository.getMessageSender( + student = student, + message = message, + mailbox = mailbox, ) else -> emptyList() }.let { createChips(it) } @@ -134,7 +139,7 @@ class SendMessagePresenter @Inject constructor( messageRecipients.size ) - Triple(unit, recipients, messageRecipients) + Triple(mailbox, recipients, messageRecipients) } .logResourceStatus("load recipients") .onEach { @@ -143,19 +148,14 @@ class SendMessagePresenter @Inject constructor( showProgress(true) showContent(false) } - is Resource.Success -> it.data.let { (reportingUnit, recipientChips, selectedRecipientChips) -> + is Resource.Success -> it.data.let { (mailbox, recipientChips, selectedRecipientChips) -> view?.run { - if (reportingUnit != null) { - setReportingUnit(reportingUnit) - setRecipients(recipientChips) - if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients( - selectedRecipientChips - ) - showContent(true) - } else { - Timber.i("Loading recipients result: Can't find the reporting unit") - view?.showEmpty(true) - } + setMailbox(getMailboxName(mailbox)) + setRecipients(recipientChips) + if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients( + selectedRecipientChips + ) + showContent(true) } } is Resource.Error -> { @@ -171,7 +171,14 @@ class SendMessagePresenter @Inject constructor( private fun sendMessage(subject: String, content: String, recipients: List) { resourceFlow { val student = studentRepository.getCurrentStudent() - messageRepository.sendMessage(student, subject, content, recipients) + val mailbox = mailboxRepository.getMailbox(student) + messageRepository.sendMessage( + student = student, + subject = subject, + content = content, + recipients = recipients, + mailboxId = mailbox.globalKey, + ) }.logResourceStatus("sending message").onEach { when (it) { is Resource.Loading -> view?.run { @@ -201,31 +208,44 @@ class SendMessagePresenter @Inject constructor( } private fun createChips(recipients: List): List { - fun generateCorrectSummary(recipientRealName: String): String { - val substring = recipientRealName.substringBeforeLast("-") - return when { - substring == recipientRealName -> recipientRealName - substring.indexOf("(") != -1 -> { - recipientRealName.indexOf("(") - .let { recipientRealName.substring(if (it != -1) it else 0) } - } - substring.indexOf("[") != -1 -> { - recipientRealName.indexOf("[") - .let { recipientRealName.substring(if (it != -1) it else 0) } - } - else -> recipientRealName.substringAfter("-") - }.trim() - } - return recipients.map { RecipientChipItem( - title = it.name, - summary = generateCorrectSummary(it.realName), + title = it.userName, + summary = buildString { + getMailboxType(it.type)?.let(::append) + if (isNotBlank()) append(" ") + + append("(${it.schoolShortName})") + }, recipient = it ) } } + private fun getMailboxName(mailbox: Mailbox): String { + return buildString { + append(mailbox.userName) + append(" - ") + append(getMailboxType(mailbox.type)) + + if (mailbox.type == MailboxType.PARENT) { + append(" - ") + append(mailbox.studentName) + } + + append(" - ") + append("(${mailbox.schoolNameShort})") + } + } + + private fun getMailboxType(type: MailboxType): String? = when (type) { + MailboxType.STUDENT -> view?.mailboxStudent + MailboxType.PARENT -> view?.mailboxParent + MailboxType.GUARDIAN -> view?.mailboxGuardian + MailboxType.EMPLOYEE -> view?.mailboxEmployee + MailboxType.UNKNOWN -> null + } + fun onMessageContentChange() { presenterScope.launch { messageUpdateChannel.send(Unit) @@ -263,7 +283,7 @@ class SendMessagePresenter @Inject constructor( fun getRecipientsNames(): String { return messageRepository.draftMessage?.recipients.orEmpty() - .joinToString { it.recipient.name } + .joinToString { it.recipient.userName } } fun clearDraft() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt index 21b42e3e4..1057114b8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.message.send -import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.ui.base.BaseView interface SendMessageView : BaseView { @@ -18,9 +18,17 @@ interface SendMessageView : BaseView { val messageSuccess: String + val mailboxStudent: String + + val mailboxParent: String + + val mailboxGuardian: String + + val mailboxEmployee: String + fun initView() - fun setReportingUnit(unit: ReportingUnit) + fun setMailbox(mailbox: String) fun setRecipients(recipients: List) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt index af0923b94..55f03ef84 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt @@ -8,7 +8,6 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R -import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.databinding.ItemMessageBinding import io.github.wulkanowy.databinding.ItemMessageChipsBinding import io.github.wulkanowy.utils.toFormattedString @@ -88,12 +87,8 @@ class MessageTabAdapter @Inject constructor() : with(holder.binding) { val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL - messageItemAuthor.run { - text = if (message.folderId == MessageFolder.SENT.id) { - message.recipient - } else { - message.sender - } + with(messageItemAuthor) { + text = message.correspondents setTypeface(null, style) } messageItemSubject.run { @@ -145,7 +140,7 @@ class MessageTabAdapter @Inject constructor() : val newItem = new[newItemPosition] return if (oldItem is MessageTabDataItem.MessageItem && newItem is MessageTabDataItem.MessageItem) { - oldItem.message.id == newItem.message.id + oldItem.message.messageGlobalKey == newItem.message.messageGlobalKey } else { oldItem.viewType == newItem.viewType } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 870b6433e..54711a689 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.message.tab import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository -import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler @@ -26,7 +26,7 @@ class MessageTabPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val semesterRepository: SemesterRepository, + private val mailboxRepository: MailboxRepository, private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -122,7 +122,8 @@ class MessageTabPresenter @Inject constructor( runCatching { val student = studentRepository.getCurrentStudent(true) - messageRepository.deleteMessages(student, messageList) + val mailbox = mailboxRepository.getMailbox(student) + messageRepository.deleteMessages(student, mailbox, messageList) } .onFailure(errorHandler::dispatch) .onSuccess { view?.showMessagesDeleted() } @@ -159,7 +160,7 @@ class MessageTabPresenter @Inject constructor( } fun onMessageItemSelected(messageItem: MessageTabDataItem.MessageItem, position: Int) { - Timber.i("Select message ${messageItem.message.id} item (position: $position)") + Timber.i("Select message ${messageItem.message.messageGlobalKey} item (position: $position)") if (!isActionMode) { view?.run { @@ -206,8 +207,8 @@ class MessageTabPresenter @Inject constructor( flatResourceFlow { val student = studentRepository.getCurrentStudent() - val semester = semesterRepository.getCurrentSemester(student) - messageRepository.getMessages(student, semester, folder, forceRefresh) + val mailbox = mailboxRepository.getMailbox(student) + messageRepository.getMessages(student, mailbox, folder, forceRefresh) } .logResourceStatus("load $folder message") .onResourceData { @@ -333,7 +334,7 @@ class MessageTabPresenter @Inject constructor( addAll(data.map { message -> MessageTabDataItem.MessageItem( message = message, - isSelected = messagesToDelete.any { it.id == message.id }, + isSelected = messagesToDelete.any { it.messageGlobalKey == message.messageGlobalKey }, isActionMode = isActionMode ) }) @@ -345,10 +346,9 @@ class MessageTabPresenter @Inject constructor( private fun calculateMatchRatio(message: Message, query: String): Int { val subjectRatio = FuzzySearch.tokenSortPartialRatio(query.lowercase(), message.subject) - val senderOrRecipientRatio = FuzzySearch.tokenSortPartialRatio( + val correspondentsRatio = FuzzySearch.tokenSortPartialRatio( query.lowercase(), - if (message.sender.isNotEmpty()) message.sender.lowercase() - else message.recipient.lowercase() + message.correspondents ) val dateRatio = listOf( @@ -364,7 +364,7 @@ class MessageTabPresenter @Inject constructor( return (subjectRatio.toDouble().pow(2) - + senderOrRecipientRatio.toDouble().pow(2) + + correspondentsRatio.toDouble().pow(2) + dateRatio.toDouble().pow(2) * 2 ).toInt() } diff --git a/app/src/main/res/layout/activity_send_message.xml b/app/src/main/res/layout/activity_send_message.xml index 10b581f77..320782bdc 100644 --- a/app/src/main/res/layout/activity_send_message.xml +++ b/app/src/main/res/layout/activity_send_message.xml @@ -16,8 +16,7 @@ app:layout_constraintBottom_toTopOf="@id/sendMessageScroll" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:targetApi="lollipop" /> + app:layout_constraintTop_toTopOf="parent" /> Move to trash Delete permanently Message deleted successfully + student + parent + guardian + employee Share Print Subject @@ -300,7 +304,6 @@ Only unread Only with attachments Read: %s - Read by: %1$d of %2$d people %1$d message %1$d messages diff --git a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt index 225399306..ff0a53135 100644 --- a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt +++ b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt @@ -1,5 +1,7 @@ package io.github.wulkanowy +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.sdk.Sdk @@ -21,6 +23,16 @@ fun getSemesterEntity(diaryId: Int = 1, semesterId: Int = 1, start: LocalDate = end = end ) +fun getMailboxEntity() = Mailbox( + globalKey = "v4", + fullName = "", + userName = "", + userLoginId = 0, + studentName = "", + schoolNameShort = "", + type = MailboxType.UNKNOWN, +) + fun getSemesterPojo(diaryId: Int, semesterId: Int, start: LocalDate, end: LocalDate, semesterName: Int = 1) = SdkSemester( diaryId = diaryId, kindergartenDiaryId = 0, diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt index 2a5d2e2b4..24306bfeb 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt @@ -10,12 +10,10 @@ import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.data.errorOrNull import io.github.wulkanowy.data.toFirstResult -import io.github.wulkanowy.getSemesterEntity +import io.github.wulkanowy.getMailboxEntity import io.github.wulkanowy.getStudentEntity import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Folder -import io.github.wulkanowy.sdk.pojo.MessageDetails -import io.github.wulkanowy.sdk.pojo.Sender import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.Status import io.github.wulkanowy.utils.status @@ -23,7 +21,6 @@ import io.mockk.* import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.SpyK import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking @@ -60,7 +57,7 @@ class MessageRepositoryTest { private val student = getStudentEntity() - private val semester = getSemesterEntity() + private val mailbox = getMailboxEntity() private lateinit var repository: MessageRepository @@ -80,59 +77,18 @@ class MessageRepositoryTest { ) } - @Test - fun `get messages when read by values was changed on already read message`() = runTest { - every { messageDb.loadAll(any(), any()) } returns flow { - val dbMessage = getMessageEntity(3, "", false).apply { - unreadBy = 10 - readBy = 5 - isNotified = true - } - emit(listOf(dbMessage)) - } - coEvery { sdk.getMessages(Folder.RECEIVED, any(), any()) } returns listOf( - getMessageDto(messageId = 3, content = "", unread = false).copy( - unreadBy = 5, - readBy = 10, - ) - ) - coEvery { messageDb.deleteAll(any()) } just Runs - coEvery { messageDb.insertAll(any()) } returns listOf() - - repository.getMessages( - student = student, - semester = semester, - folder = MessageFolder.RECEIVED, - forceRefresh = true, - notify = true, // all new messages will be marked as not notified - ).toFirstResult().dataOrNull.orEmpty() - - coVerify(exactly = 1) { messageDb.deleteAll(emptyList()) } - coVerify(exactly = 1) { messageDb.insertAll(emptyList()) } - coVerify(exactly = 1) { - messageDb.updateAll(withArg { - assertEquals(1, it.size) - assertEquals(5, it.single().unreadBy) - assertEquals(10, it.single().readBy) - }) - } - } - @Test fun `get messages when fetched completely new message without notify`() = runBlocking { every { messageDb.loadAll(any(), any()) } returns flowOf(emptyList()) - coEvery { sdk.getMessages(Folder.RECEIVED, any(), any()) } returns listOf( - getMessageDto(messageId = 4, content = "Test", unread = true).copy( - unreadBy = 5, - readBy = 10, - ) + coEvery { sdk.getMessages(Folder.RECEIVED, any()) } returns listOf( + getMessageDto() ) coEvery { messageDb.deleteAll(any()) } just Runs coEvery { messageDb.insertAll(any()) } returns listOf() repository.getMessages( student = student, - semester = semester, + mailbox = mailbox, folder = MessageFolder.RECEIVED, forceRefresh = true, notify = false, @@ -151,7 +107,7 @@ class MessageRepositoryTest { fun `throw error when message is not in the db`() { val testMessage = getMessageEntity(1, "", false) coEvery { - messageDb.loadMessageWithAttachment(1, 1) + messageDb.loadMessageWithAttachment("v4") } throws NoSuchElementException("No message in database") runBlocking { repository.getMessage(student, testMessage).toFirstResult() } @@ -162,7 +118,7 @@ class MessageRepositoryTest { val testMessage = getMessageEntity(123, "Test", false) val messageWithAttachment = MessageWithAttachment(testMessage, emptyList()) - coEvery { messageDb.loadMessageWithAttachment(1, testMessage.messageId) } returns flowOf( + coEvery { messageDb.loadMessageWithAttachment("v4") } returns flowOf( messageWithAttachment ) @@ -174,7 +130,7 @@ class MessageRepositoryTest { } @Test - fun `get message when content in db is empty`() { + fun `get message when content in db is empty`() = runTest { val testMessage = getMessageEntity(123, "", true) val testMessageWithContent = testMessage.copy().apply { content = "Test" } @@ -182,23 +138,19 @@ class MessageRepositoryTest { val mWaWithContent = MessageWithAttachment(testMessageWithContent, emptyList()) coEvery { - messageDb.loadMessageWithAttachment( - 1, - testMessage.messageId - ) + messageDb.loadMessageWithAttachment("v4") } returnsMany listOf(flowOf(mWa), flowOf(mWaWithContent)) coEvery { - sdk.getMessageDetails( - messageId = testMessage.messageId, - folderId = 1, - read = false, - id = testMessage.realId - ) - } returns MessageDetails("Test", emptyList()) + sdk.getMessageDetails("v4") + } returns mockk { + every { sender } returns "" + every { recipients } returns listOf("") + every { attachments } returns listOf() + } coEvery { messageDb.updateAll(any()) } just Runs coEvery { messageAttachmentDao.insertAttachments(any()) } returns listOf(1) - val res = runBlocking { repository.getMessage(student, testMessage).toFirstResult() } + val res = repository.getMessage(student, testMessage).toFirstResult() assertEquals(null, res.errorOrNull) assertEquals(Status.SUCCESS, res.status) @@ -211,7 +163,7 @@ class MessageRepositoryTest { val testMessage = getMessageEntity(123, "", false) coEvery { - messageDb.loadMessageWithAttachment(1, testMessage.messageId) + messageDb.loadMessageWithAttachment("v4") } throws UnknownHostException() runBlocking { repository.getMessage(student, testMessage).toFirstResult() } @@ -222,7 +174,7 @@ class MessageRepositoryTest { val testMessage = getMessageEntity(123, "", true) coEvery { - messageDb.loadMessageWithAttachment(1, testMessage.messageId) + messageDb.loadMessageWithAttachment("v4") } throws UnknownHostException() runBlocking { repository.getMessage(student, testMessage).toList()[1] } @@ -233,42 +185,30 @@ class MessageRepositoryTest { content: String, unread: Boolean ) = Message( - studentId = 1, - realId = 1, + messageGlobalKey = "v4", + mailboxKey = "", + correspondents = "", messageId = messageId, - sender = "", - senderId = 0, - recipient = "Wielu adresatów", subject = "", date = Instant.EPOCH, folderId = 1, unread = unread, - removed = false, hasAttachments = false ).apply { this.content = content - unreadBy = 1 - readBy = 1 } - private fun getMessageDto( - messageId: Int, - content: String, - unread: Boolean, - ) = io.github.wulkanowy.sdk.pojo.Message( - id = 1, - messageId = messageId, - sender = Sender("", "", 0, 0, 0, ""), + private fun getMessageDto() = io.github.wulkanowy.sdk.pojo.Message( + globalKey = "v4", + mailbox = "", + correspondents = "", + id = 4, recipients = listOf(), subject = "", - content = content, - date = Instant.EPOCH.atZone(ZoneOffset.UTC).toLocalDateTime(), + content = "Test", dateZoned = Instant.EPOCH.atZone(ZoneOffset.UTC), folderId = 1, - unread = unread, - unreadBy = 0, - readBy = 0, - removed = false, + unread = true, hasAttachments = false, ) } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt index 980abac0a..ae73a7958 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt @@ -1,19 +1,15 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.db.dao.RecipientDao -import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.getMailboxEntity import io.github.wulkanowy.getStudentEntity import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.MailboxType import io.github.wulkanowy.utils.AutoRefreshHelper -import io.mockk.MockKAnnotations -import io.mockk.Runs -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.every +import io.mockk.* import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.SpyK -import io.mockk.just import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Before @@ -36,9 +32,30 @@ class RecipientLocalTest { private lateinit var recipientRepository: RecipientRepository private val remoteList = listOf( - SdkRecipient("2rPracownik", "Kowalski Jan", 3, 4, 2, "hash", "Kowalski Jan [KJ] - Pracownik (Fake123456)"), - SdkRecipient("3rPracownik", "Kowalska Karolina", 4, 4, 2, "hash", "Kowalska Karolina [KK] - Pracownik (Fake123456)"), - SdkRecipient("4rPracownik", "Krupa Stanisław", 5, 4, 1, "hash", "Krupa Stanisław [KS] - Uczeń (Fake123456)") + SdkRecipient( + mailboxGlobalKey = "2rPracownik", + userName = "Kowalski Jan", + fullName = "Kowalski Jan [KJ] - Pracownik (Fake123456)", + studentName = "", + schoolNameShort = "", + type = MailboxType.UNKNOWN, + ), + SdkRecipient( + mailboxGlobalKey = "3rPracownik", + userName = "Kowalska Karolina", + fullName = "Kowalska Karolina [KK] - Pracownik (Fake123456)", + studentName = "", + schoolNameShort = "", + type = MailboxType.UNKNOWN, + ), + SdkRecipient( + mailboxGlobalKey = "4rPracownik", + userName = "Krupa Stanisław", + fullName = "Krupa Stanisław [KS] - Uczeń (Fake123456)", + studentName = "", + schoolNameShort = "", + type = MailboxType.UNKNOWN, + ) ) @Before @@ -52,39 +69,61 @@ class RecipientLocalTest { @Test fun `load recipients when items already in database`() { // prepare - coEvery { recipientDb.loadAll(4, 123, 7) } returnsMany listOf( - remoteList.mapToEntities(4), - remoteList.mapToEntities(4) + coEvery { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") } returnsMany listOf( + remoteList.mapToEntities("v4"), + remoteList.mapToEntities("v4") ) coEvery { recipientDb.insertAll(any()) } returns listOf(1, 2, 3) coEvery { recipientDb.deleteAll(any()) } just Runs // execute - val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(4, 123, "", 4, "", listOf()), 7) } + val res = runBlocking { + recipientRepository.getRecipients( + student = student, + mailbox = getMailboxEntity(), + type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, + ) + } // verify assertEquals(3, res.size) - coVerify { recipientDb.loadAll(4, 123, 7) } + coVerify { + recipientDb.loadAll( + type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, + studentMailboxGlobalKey = "v4" + ) + } } @Test fun `load recipients when database is empty`() { // prepare - coEvery { sdk.getRecipients(123, 7) } returns remoteList - coEvery { recipientDb.loadAll(4, 123, 7) } returnsMany listOf( + coEvery { sdk.getRecipients("v4") } returns remoteList + coEvery { + recipientDb.loadAll( + io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, + "v4" + ) + } returnsMany listOf( emptyList(), - remoteList.mapToEntities(4) + remoteList.mapToEntities("v4") ) coEvery { recipientDb.insertAll(any()) } returns listOf(1, 2, 3) coEvery { recipientDb.deleteAll(any()) } just Runs // execute - val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(4, 123, "", 4, "", listOf()), 7) } + val res = runBlocking { + recipientRepository.getRecipients( + student = student, + mailbox = getMailboxEntity(), + type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, + ) + } // verify assertEquals(3, res.size) - coVerify { sdk.getRecipients(123, 7) } - coVerify { recipientDb.loadAll(4, 123, 7) } + coVerify { sdk.getRecipients("v4") } + coVerify { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") } coVerify { recipientDb.insertAll(match { it.isEmpty() }) } coVerify { recipientDb.deleteAll(match { it.isEmpty() }) } } From bc0689a30df9ac9c7b35a802256e1922dbbe8264 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 13:28:27 +0000 Subject: [PATCH 069/164] Bump coil from 2.1.0 to 2.2.0 (#1949) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 0efb9c303..d86f5f430 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -236,7 +236,7 @@ dependencies { implementation "at.favre.lib:slf4j-timber:1.0.1" implementation 'com.github.bastienpaulfr:Treessence:1.0.5' implementation "com.mikepenz:aboutlibraries-core:$about_libraries" - implementation "io.coil-kt:coil:2.1.0" + implementation "io.coil-kt:coil:2.2.0" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' From d9e22af5ef0672de79b122af8dfca08b5743ca49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 13:28:58 +0000 Subject: [PATCH 070/164] Bump desugar_jdk_libs from 1.1.5 to 1.1.6 (#1948) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index d86f5f430..efa9b3e09 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -188,7 +188,7 @@ ext { dependencies { implementation "io.github.wulkanowy:sdk:dbe87aac" - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6' implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" From 71f1a55437e3b447b8d3e6b4fa87713c3f382027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Mon, 22 Aug 2022 16:45:59 +0200 Subject: [PATCH 071/164] New Crowdin updates (#1895) --- .../main/res/values-cs/preferences_values.xml | 2 +- app/src/main/res/values-cs/strings.xml | 14 ++++++++++++-- .../main/res/values-de/preferences_values.xml | 2 +- app/src/main/res/values-de/strings.xml | 14 ++++++++++++-- app/src/main/res/values-pl/strings.xml | 16 +++++++++++++--- app/src/main/res/values-ru/strings.xml | 14 ++++++++++++-- .../main/res/values-sk/preferences_values.xml | 2 +- app/src/main/res/values-sk/strings.xml | 16 +++++++++++++--- app/src/main/res/values-uk/strings.xml | 14 ++++++++++++-- 9 files changed, 77 insertions(+), 17 deletions(-) diff --git a/app/src/main/res/values-cs/preferences_values.xml b/app/src/main/res/values-cs/preferences_values.xml index c8731372f..23073adf1 100644 --- a/app/src/main/res/values-cs/preferences_values.xml +++ b/app/src/main/res/values-cs/preferences_values.xml @@ -34,7 +34,7 @@ Abecedně Podle data - By average + Podle průměru Dzienniczek+ diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 11054a0e5..5f76bb6e1 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -77,6 +77,9 @@ Přihlásit se Relace vypršela Relace vypršela. Přihlaste se prosím znovu + Podpora aplikace + Líbí se Vám tato aplikace? Podpořte její vývoj tím, že povolíte neinvazivní reklamy, které můžete kdykoliv vypnout + Zapnout reklamy Známka Semestr %d @@ -94,7 +97,7 @@ Předpokládaná známka Vypočítaný průměr Jak funguje vypočítaný průměr? - Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\nPrůměr známek pouze z vybraného semestru:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr průměrů z obou semestrů:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru sečtených průměrů\n\nPrůměr známek z celého roku:\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru součtených průměrů + Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\nPrůměr známek pouze z vybraného semestru:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr průměrů z obou semestrů:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru sečtených průměrů\n\nPrůměr známek z celého roku:\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů Jak funguje konečný průměr? Konečný průměr je aritmetický průměr vypočítaný ze všech aktuálně dostupných konečných známek v daném semestru.\n\nSchéma výpočtu se skládá z následujících kroků:\n1. Sčítání konečných známek zadaných učiteli\n2. Děleno počtem předmětů, pro které už byly uděleny známky Konečný průměr @@ -294,6 +297,10 @@ Přesunout do koše Odstranit natrvalo Zpráva byla úspěšně odstraněna + žák + rodič + opatrovník + pracovník Sdílet Vytisknout Předmět @@ -305,7 +312,6 @@ Pouze nepřečtené Pouze s přílohami Přečtena: %s - Přečtena přes: %1$d z %2$d osob %1$d zpráva %1$d zprávy @@ -721,6 +727,10 @@ Ochrana osobních údajů Reklama se načítá Děkujeme za vaši podporu, vraťte se později pro více reklam + Můžeme použít Vaše data k zobrazení reklam? + Volbu můžete kdykoliv změnit v nastavení aplikace. Můžeme použít Vaše data k zobrazení reklam šitých pro vás nebo pomocí méně vašich dat zobrazovat nepřizpůsobené reklamy. Podrobnosti naleznete v našich Zásadách ochrany osobních údajů + Přizpůsobené reklamy + Nepřizpůsobené reklamy Pokročilé Vzhled a chování Oznámení diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml index 097e90e96..d9cac1959 100644 --- a/app/src/main/res/values-de/preferences_values.xml +++ b/app/src/main/res/values-de/preferences_values.xml @@ -34,7 +34,7 @@ Alphabetisch Nach Datum - By average + Nach Durchschnitt Dzienniczek+ diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 86308aa19..ef79cee12 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -77,6 +77,9 @@ Anmelden Die Sitzung ist abgelaufen Die Sitzung ist abgelaufen, bitte loggen Sie sich erneut ein + Anwendungsunterstützung + Gefällt Ihnen diese App? Unterstützen Sie ihre Entwicklung, indem Sie nicht-invasive Werbung aktivieren, die Sie jederzeit deaktivieren können + Werbung aktivieren Note Semester %d @@ -94,7 +97,7 @@ Vorhergesagte Note Berechnender Durchschnitt Wie funktioniert der berechnete Durchschnitt? - Der berechnete Mittelwert ist das arithmetische Mittel, das aus den Durchschnittswerten der Probanden errechnet wird. Es erlaubt Ihnen, den ungefähre endgültigen Durchschnitt zu kennen. Sie wird auf eine vom Anwender in den Anwendungseinstellungen gewählte Weise berechnet. Es wird empfohlen, die entsprechende Option zu wählen. Das liegt daran, dass die Berechnung der Schuldurchschnitte unterschiedlich ist. Wenn Ihre Schule den Durchschnitt der Fächer auf der Vulcan-Seite angibt, lädt die Anwendung diese Fächer herunter und berechnet nicht den Durchschnitt. Dies kann geändert werden, indem die Berechnung des Durchschnitts in den Anwendungseinstellungen erzwungen wird. \n\nDurchschnitt der Noten nur aus dem ausgewählten Semester :\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in einem bestimmten Semester\n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Durchschnitte aus beiden Semestern:\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in Semester 1 und 2\n2. Berechnung des arithmetischen Mittels der berechneten Durchschnitte für Semester 1 und 2 für jedes Fach. \n3. Hinzufügen von berechneten Durchschnittswerten\n4. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Noten aus dem ganzen Jahr:\n1. Berechnung des gewichteten Jahresdurchschnitts für jedes Fach. Der Abschlussdurchschnitt im 1. Semester ist irrelevant. \n3. Addition der berechneten Durchschnittswerte\n4. Berechnung des arithmetischen Mittels der summierten Mittelwerte + Der berechnete Mittelwert ist das arithmetische Mittel, das aus den Durchschnittswerten der Probanden errechnet wird. Es erlaubt Ihnen, den ungefähre endgültigen Durchschnitt zu kennen. Sie wird auf eine vom Anwender in den Anwendungseinstellungen gewählte Weise berechnet. Es wird empfohlen, die entsprechende Option zu wählen. Das liegt daran, dass die Berechnung der Schuldurchschnitte unterschiedlich ist. Wenn Ihre Schule den Durchschnitt der Fächer auf der Vulcan-Seite angibt, lädt die Anwendung diese Fächer herunter und berechnet nicht den Durchschnitt. Dies kann geändert werden, indem die Berechnung des Durchschnitts in den Anwendungseinstellungen erzwungen wird. \n\nDurchschnitt der Noten nur aus dem ausgewählten Semester :\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in einem bestimmten Semester\n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Durchschnitte aus beiden Semestern:\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in Semester 1 und 2\n2. Berechnung des arithmetischen Mittels der berechneten Durchschnitte für Semester 1 und 2 für jedes Fach. \n3. Hinzufügen von berechneten Durchschnittswerten\n4. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Noten aus dem ganzen Jahr:\n1. Berechnung des gewichteten Jahresdurchschnitts für jedes Fach. Der Abschlussdurchschnitt im 1. Semester ist irrelevant. \n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Mittelwerte Wie funktioniert der endgültige Durchschnitt? Der Final Average ist das arithmetische Mittel, das aus allen derzeit verfügbaren Abschlussnoten des jeweiligen Semesters berechnet wird. \n\nDas Berechnungsschema besteht aus folgenden Schritten:\n1. Zusammenfassung der von den Lehrern gegebenen Abschlussnoten\n2. Division durch die Anzahl der Fächer, die bereits bewertet wurden Finaler Durchschnitt @@ -260,6 +263,10 @@ In Papierkorb verschieben Dauerhaft löschen Nachricht erfolgreich gelöscht + schüler + Eltern + Betreuer + Mitarbeiter Teilen Drucken Thema @@ -271,7 +278,6 @@ Nur ungelesen Nur mit Anhängen Lesen: %s - Lesen von: %1$d von %2$d Personen %1$d Nachricht %1$d Nachrichten @@ -633,6 +639,10 @@ Datenschutzerklärung Anzeige wird geladen Vielen Dank für Ihre Unterstützung, kommen Sie später wieder für weitere Anzeigen + Können wir Ihre Daten zur Anzeige von Werbung verwenden? + Sie können Ihre Wahl jederzeit in den App-Einstellungen ändern. Wir verwenden Ihre Daten, um auf Sie zugeschnittene Anzeigen anzuzeigen oder unter Verwendung weniger Ihrer Daten nicht personalisierte Werbung anzuzeigen. Bitte lesen Sie unsere Datenschutzerklärung für Details + Personalisierte Werbung + keine personalisierte Werbung Erweitert Aussehen & Verhalten Benachrichtigungen diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 91e9fe7f3..566519e8c 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -77,6 +77,9 @@ Zaloguj się Sesja wygasła Sesja wygasła, zaloguj się ponownie + Wparcie aplikacji + Podoba Ci się ta aplikacja? Wspieraj jej rozwój poprzez włączenie nieinwazyjnych reklam, które możesz wyłączyć w dowolnym momencie + Włącz reklamy Ocena Semestr %d @@ -94,7 +97,7 @@ Przewidywana ocena Obliczona średnia Jak działa obliczona średnia? - Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej z zsumowanych średnich + Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej z zsumowanych średnich Jak działa końcowa średnia? Średnią końcową jest średnia arytmetyczna obliczona na podstawie wszystkich obecnie dostępnych ocen końcowych w danym semestrze.\n\nSchemat obliczeń składa się z następujących kroków:\n1. Sumowanie końcowych ocen wpisanych przez nauczycieli\n2. Dzielenie przez liczbę przedmiotów, z których oceny zostały już wystawione Końcowa średnia @@ -294,6 +297,10 @@ Przenieś do kosza Usuń trwale Wiadomość usunięta pomyślnie + uczeń + rodzic + opiekun + pracownik Udostępnij Drukuj Temat @@ -305,7 +312,6 @@ Tylko nieprzeczytane Tylko z załącznikami Przeczytana: %s - Przeczytana przez: %1$d z %2$d osób %1$d wiadomość %1$d wiadomości @@ -701,7 +707,7 @@ Przejdź do ustawień Synchronizacja Automatyczna aktualizacja - Zawieszona na wakacjach + Wstrzymana podczas wakacji Interwał aktualizacji Tylko WiFi Synchronizuj teraz @@ -721,6 +727,10 @@ Polityka prywatności Ładowanie reklamy Dziękujemy za wsparcie, wróć później po więcej reklam + Czy możemy używać Twoich danych do wyświetlania reklam? + Możesz zmienić swój wybór w dowolnym momencie w ustawieniach aplikacji. Możemy wykorzystać Twoje dane do wyświetlania reklam dostosowanych do Ciebie lub, przy użyciu mniejszej ilości danych, wyświetlić niepersonalizowane reklamy. Zobacz naszą Politykę Prywatności, aby uzyskać więcej informacji + Spersonalizowane reklamy + Niespersonalizowane reklamy Zaawansowane Wygląd i zachowanie Powiadomienia diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 662e0934f..a3c5a62df 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -77,6 +77,9 @@ Войти Сеанс истёк Сеанс истёк, авторизуйтесь снова + Application support + Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time + Enable ads Оценка %d семестр @@ -94,7 +97,7 @@ Ожидаемая оценка Рассчитанная средняя оценка Как работает \"Рассчитанная средняя оценка\"? - Рассчитанная средняя оценка - это среднее арифметическое, рассчитанное на основе средних оценок по предметам. Это позволяет узнать приблизительную итоговую среднюю оценку. Она рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант, так как каждая школа по разному считает среднюю оценку. Кроме того, если ваша школа выставляет средние оценки по предметам на странице Vulcan, приложение просто загрузит их. Это можно изменить, заставив приложение считать среднюю оценку в настройках.\n\nСредняя из оценок выбранного семестра:\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Суммирование вычисленных значений\n3. Вычисление среднего арифметического суммированных значений\n\nСредняя из средних оценок семестров:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах. \n2. Вычисление среднего арифметического из средневзвешенных значений для каждого предмета в семестрах.\n3. Суммирование средних арифметических\n4. Вычисление среднего арифматического из суммированных значений\n\nСредняя из оценок со всего года:\n1. Расчет средневзвешенного значения по каждому предмету за год. Итоговое среднее значение за 1 семестр не имеет значения.\n2. Суммирование вычисленных средних\n4. Расчет среднего арифметического суммированных чисел + The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages Как работает \"Итоговая средняя оценка\"? Итоговая средняя оценка - это среднее арифметическое, рассчитанное из всех имеющихся на данный момент итоговых оценок в семестре.\n\nРассчет происходит следующим образом:\n1. Суммирование итоговых оценок, выставленных преподавателями\n2. Полученная сумма делится на число предметов, по которым выставлены оценки Итоговая средняя оценка @@ -294,6 +297,10 @@ Перенести в корзину Удалить навсегда Письмо успешно удалено + student + parent + guardian + employee Поделиться Печать Тема @@ -305,7 +312,6 @@ Только непрочитанные Только с вложениями Прочитано: %s - Прочитано: %1$d из %2$d человек %1$d сообщение %1$d сообщения @@ -721,6 +727,10 @@ Политика конфиденциальности Реклама загружается Спасибо за вашу поддержку, возвращайтесь позже для дополнительной рекламы + Can we use your data to display ads? + You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details + Personalized ads + Non-personalized ads Расширенные Внешний вид и поведение Уведомления diff --git a/app/src/main/res/values-sk/preferences_values.xml b/app/src/main/res/values-sk/preferences_values.xml index ab0a43b64..e4331315a 100644 --- a/app/src/main/res/values-sk/preferences_values.xml +++ b/app/src/main/res/values-sk/preferences_values.xml @@ -34,7 +34,7 @@ Abecedne Podľa dátumu - By average + Podľa priemeru Dzienniczek+ diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 5ebd1e76b..6a4505d70 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -77,6 +77,9 @@ Prihlásiť sa Relácia vypršala Relácia vypršala. Prihláste sa prosím znovu + Podpora aplikácie + Páči sa Vám táto aplikácia? Podporte jej vývoj tým, že povolíte neinvazívne reklamy, ktoré môžete kedykoľvek vypnúť + Zapnúť reklamy Známka Semester %d @@ -94,7 +97,7 @@ Predpokladaná známka Vypočítaný priemer Ako funguje vypočítaný priemer? - Vypočítaný priemer je aritmetický priemer vypočítaný z priemerov predmetov. Umožňuje vám to poznať približný konečný priemer. Vypočítava sa spôsobom zvoleným užívateľom v nastaveniach aplikácii. Odporúča sa vybrať príslušnú možnosť. Dôvodom je rozdielny výpočet školských priemerov. Ak vaša škola navyše uvádza priemer predmetov na stránke denníka Vulcan, aplikácia si ich stiahne a tieto priemery nepočíta. To možno zmeniť vynútením výpočtu priemeru v nastavení aplikácii.\n\nPriemer známok iba z vybraného semestra:\n1. Výpočet váženého priemeru pre každý predmet v danom semestri\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer priemerov z oboch semestrov:\n1. Výpočet váženého priemeru pre každý predmet v semestri 1 a 2\n2. Výpočet aritmetického priemeru vypočítaných priemerov za semestre 1 a 2 pre každý predmet.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer známok z celého roka:\n1. Výpočet váženého priemeru za rok pre každý predmet. Konečný priemer v 1. semestri je nepodstatný.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov + Vypočítaný priemer je aritmetický priemer vypočítaný z priemerov predmetov. Umožňuje vám to poznať približný konečný priemer. Vypočítava sa spôsobom zvoleným užívateľom v nastaveniach aplikácii. Odporúča sa vybrať príslušnú možnosť. Dôvodom je rozdielny výpočet školských priemerov. Ak vaša škola navyše uvádza priemer predmetov na stránke denníka Vulcan, aplikácia si ich stiahne a tieto priemery nepočíta. To možno zmeniť vynútením výpočtu priemeru v nastavení aplikácii.\n\nPriemer známok iba z vybraného semestra:\n1. Výpočet váženého priemeru pre každý predmet v danom semestri\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer priemerov z oboch semestrov:\n1. Výpočet váženého priemeru pre každý predmet v semestri 1 a 2\n2. Výpočet aritmetického priemeru vypočítaných priemerov za semestre 1 a 2 pre každý predmet.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer známok z celého roka:\n1. Výpočet váženého priemeru za rok pre každý predmet. Konečný priemer v 1. semestri je nepodstatný.\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov Ako funguje konečný priemer? Konečný priemer je aritmetický priemer vypočítaný zo všetkých aktuálne dostupných konečných známok v danom semestri.\n\nSchéma výpočtu sa skladá z nasledujúcich krokov:\n1. Sčítanie konečných známok zadaných učiteľmi\n2. Delené počtom predmetov, pre ktoré už boli vydané známky Konečný priemer @@ -110,7 +113,7 @@ Váš priemer: %1$s Vaša známka: %1$s Trieda - Žiák + Žiak %d známka %d známky @@ -294,6 +297,10 @@ Presunúť do koša Odstrániť natrvalo Správa bola úspešne odstránená + žiak + rodič + opatrovník + pracovník Zdieľať Vytlačiť Predmet @@ -305,7 +312,6 @@ Iba neprečítané Iba s prílohami Prečítaná: %s - Prečítaná cez: %1$d z %2$d osôb %1$d správa %1$d správy @@ -721,6 +727,10 @@ Ochrana osobných údajov Reklama sa načítava Ďakujeme za vašu podporu, vráťte sa neskôr pre viac reklám + Môžeme použiť Vaše údaje na zobrazenie reklám? + Voľbu môžete kedykoľvek zmeniť v nastavení aplikácie. Môžeme použiť vaše údaje na zobrazenie reklám šitých pre vás alebo pomocou menej vašich dát zobrazovať neprispôsobené reklamy. Podrobnosti nájdete v našich Zásadách ochrany osobných údajov + Prispôsobené reklamy + Neprispôsobené reklamy Pokročilé Vzhľad a správanie Oznámenia diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 0cc50dbe5..742e800f3 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -77,6 +77,9 @@ Увійти Минув термін дії сесії Минув термін дії сесії, авторизуйтеся знову + Application support + Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time + Enable ads Оцінка %d семестр @@ -94,7 +97,7 @@ Передбачувана оцінка Розрахована середня оцінка Як працює \"Розрахована середня оцінка\"? - Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів.Це дозволяє дізнатися приблизну кінцеву середню оцінку.Вона розраховується способом, обраним користувачем у налаштуваннях програми.Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку.Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки.Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх + The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages Як працює \"Підсумкова середня оцінка\"? Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \n\nСхема обчислення складається з таких кроків:\n1. Сумування підсумкових оцінок, виставленних викладачами\n2. Ділення на кількість предметів, з яких виставлені ці оцінки Підсумкова середня оцінка @@ -294,6 +297,10 @@ Перемістити до кошика Видалити назавжди Лист було успішно видалено + student + parent + guardian + employee Поділитись Друк Тема @@ -305,7 +312,6 @@ Лише непрочитані Тільки з вкладеннями Прочитаний: %s - Прочитаний: %1$d з %2$d осіб %1$d лист %1$d листи @@ -721,6 +727,10 @@ Політика конфіденційності Реклама завантажується Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості реклам + Can we use your data to display ads? + You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details + Personalized ads + Non-personalized ads Додатково Вигляд та поведінка Сповіщення From 09e07a17136cc7266706dc6a8764619cb2ab8277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Mon, 22 Aug 2022 17:58:35 +0200 Subject: [PATCH 072/164] Version 1.7.0 --- app/build.gradle | 10 +++++----- app/src/main/play/release-notes/pl-PL/default.txt | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index efa9b3e09..e99e8773f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 108 - versionName "1.6.4" + versionCode 109 + versionName "1.7.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -161,8 +161,8 @@ play { defaultToAppBundles = false track = 'production' releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS - userFraction = 0.50d - updatePriority = 3 + userFraction = 0.05d + updatePriority = 5 enabled.set(false) } @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:dbe87aac" + implementation "io.github.wulkanowy:sdk:1.7.0" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index b6340bb93..3eb42eb97 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,5 +1,9 @@ -Wersja 1.6.4 +Wersja 1.7.0 -- naprawiliśmy błąd ładowania frekwencji na GPE i Lubelskim Portalu Oświatowym +- naprawiliśmy logowanie do aplikacji +- dodaliśmy wsparcie nowego modułu Wiadomości Plus +- dodaliśmy nową możliwość wsparcia naszego projektu przez opcjonalne reklamy +- dodaliśmy sortowanie po średniej +- naprawiliśmy też kilka usterek wpływających na komfort używania aplikacji Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From de1bc4809fe93679e89b944e0feb9771e6d3a8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 23 Aug 2022 00:08:39 +0200 Subject: [PATCH 073/164] Fix ads translations (#1951) --- .github/workflows/deploy-store.yml | 1 + app/src/main/res/layout/dialog_ads_consent.xml | 12 ++++++------ app/src/main/res/values/strings.xml | 7 +++++++ app/src/play/res/xml/scheme_preferences_ads.xml | 8 ++++---- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml index cfb0fb523..3ce618ca7 100644 --- a/.github/workflows/deploy-store.yml +++ b/.github/workflows/deploy-store.yml @@ -38,6 +38,7 @@ jobs: ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }} ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }} SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }} + DASHBOARD_TILE_AD_ID: ${{ secrets.DASHBOARD_TILE_AD_ID }} SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }} run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace; diff --git a/app/src/main/res/layout/dialog_ads_consent.xml b/app/src/main/res/layout/dialog_ads_consent.xml index 395101627..816074783 100644 --- a/app/src/main/res/layout/dialog_ads_consent.xml +++ b/app/src/main/res/layout/dialog_ads_consent.xml @@ -15,7 +15,7 @@ android:insetRight="0dp" android:insetBottom="0dp" android:padding="0dp" - android:text="Privacy Policy" + android:text="@string/pref_ads_privacy_policy" android:textAllCaps="false" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -26,9 +26,9 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="17dp" android:layout_marginTop="8dp" - android:text="I am over 18 years old" + android:text="@string/pref_ads_over_18_years_old" android:textColor="?android:textColorSecondary" - android:textSize="14dp" + android:textSize="14sp" app:layout_constraintTop_toBottomOf="@id/ads_consent_privacy" /> @@ -73,7 +73,7 @@ android:insetTop="0dp" android:insetRight="0dp" android:insetBottom="0dp" - android:text="Cancel" + android:text="@android:string/cancel" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintVertical_bias="0" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2eee4cce4..bcd498036 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -714,6 +714,10 @@ Show arithmetic average when no weights provided Support + Privacy Policy + Agreements + Consent to processing of data related to ads + Show ads in app Watch single ad to support project Consent to data processing To view an advertisement you must agree to the data processing terms of our Privacy Policy @@ -725,6 +729,9 @@ You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details Personalized ads Non-personalized ads + I am over 18 years old + Yes, personalized ads + Yes, non-personalized ads Advanced Appearance & Behavior diff --git a/app/src/play/res/xml/scheme_preferences_ads.xml b/app/src/play/res/xml/scheme_preferences_ads.xml index d5e93a355..4165561a7 100644 --- a/app/src/play/res/xml/scheme_preferences_ads.xml +++ b/app/src/play/res/xml/scheme_preferences_ads.xml @@ -2,18 +2,18 @@ + app:title="@string/pref_ads_agreements"> + app:title="@string/pref_ads_privacy_policy" /> + app:title="@string/pref_ads_consent" /> + app:title="@string/pref_ads_show_in_app" /> Date: Tue, 23 Aug 2022 00:54:19 +0200 Subject: [PATCH 074/164] New Crowdin updates (#1952) --- app/src/main/res/values-cs/strings.xml | 7 +++++++ app/src/main/res/values-de/strings.xml | 7 +++++++ app/src/main/res/values-pl/strings.xml | 7 +++++++ app/src/main/res/values-ru/strings.xml | 7 +++++++ app/src/main/res/values-sk/strings.xml | 7 +++++++ app/src/main/res/values-uk/strings.xml | 7 +++++++ app/src/main/res/values/strings.xml | 4 ++-- 7 files changed, 44 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 5f76bb6e1..d036e7e42 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -720,6 +720,10 @@ Odpovědět s historií zpráv Vypočítat aritmetický průměr, pokud žádná známka nemá váhu Podpora + Ochrana osobních údajů + Souhlasy + Souhlas se zpracováním údajů souvisejících s reklamami + Zobrazit reklamy v aplikaci Podívejte se na jednu reklamu pro podporu projektu Souhlas se zpracováním dat Jestli chcete sledovat reklamu, musíte souhlasit s podmínkami zpracování údajů v našich Zásadách Ochrany Osobních Údajů @@ -731,6 +735,9 @@ Volbu můžete kdykoliv změnit v nastavení aplikace. Můžeme použít Vaše data k zobrazení reklam šitých pro vás nebo pomocí méně vašich dat zobrazovat nepřizpůsobené reklamy. Podrobnosti naleznete v našich Zásadách ochrany osobních údajů Přizpůsobené reklamy Nepřizpůsobené reklamy + Mám ukončené 18 let + Ano, přizpůsobené reklamy + Ano, nepřizpůsobené reklamy Pokročilé Vzhled a chování Oznámení diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index ef79cee12..4dfeee4f6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -632,6 +632,10 @@ Antwort mit Nachrichtenhistorie Arithmetisches Mittel anzeigen, wenn keine Gewichte angegeben sind Unterstützung + Privacy Policy + Agreements + Consent to processing of data related to ads + Show ads in app Einzelanzeige ansehen, um Projekt zu unterstützen Einwilligung in die Datenverarbeitung Um eine Anzeige zu sehen, müssen Sie mit den Datenverarbeitungsbedingungen unserer Datenschutzerklärung einverstanden sein @@ -643,6 +647,9 @@ Sie können Ihre Wahl jederzeit in den App-Einstellungen ändern. Wir verwenden Ihre Daten, um auf Sie zugeschnittene Anzeigen anzuzeigen oder unter Verwendung weniger Ihrer Daten nicht personalisierte Werbung anzuzeigen. Bitte lesen Sie unsere Datenschutzerklärung für Details Personalisierte Werbung keine personalisierte Werbung + I am over 18 years old + Yes, personalized ads + Yes, non-personalized ads Erweitert Aussehen & Verhalten Benachrichtigungen diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 566519e8c..c9abbd022 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -720,6 +720,10 @@ Odpowiadaj z historią wiadomości Licz średnią arytmetyczną, gdy żadna ocena nie ma wagi Wsparcie + Polityka prywatności + Zgody + Zgoda na przetwarzanie danych związanych z reklamami + Pokazuj reklamy w aplikacji Obejrzyj pojedynczą reklamę, aby wesprzeć projekt Zgoda na przetwarzanie danych Aby obejrzeć reklamę, musisz zaakceptować warunki przetwarzania danych zawarte w naszej Polityce Prywatności @@ -731,6 +735,9 @@ Możesz zmienić swój wybór w dowolnym momencie w ustawieniach aplikacji. Możemy wykorzystać Twoje dane do wyświetlania reklam dostosowanych do Ciebie lub, przy użyciu mniejszej ilości danych, wyświetlić niepersonalizowane reklamy. Zobacz naszą Politykę Prywatności, aby uzyskać więcej informacji Spersonalizowane reklamy Niespersonalizowane reklamy + Mam ukończone 18 lat + Tak, spersonalizowane reklamy + Tak, niespersonalizowane reklamy Zaawansowane Wygląd i zachowanie Powiadomienia diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index a3c5a62df..e1abb0293 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -720,6 +720,10 @@ Отвечать с историей сообщений Показывать среднее арифметическое при отсутствии стоимости Поддержка + Privacy Policy + Agreements + Consent to processing of data related to ads + Show ads in app Посмотреть рекламу для поддержки проекта Согласие на обработку данных Для просмотра рекламы вы должны согласиться с условиями обработки данных нашей Политики конфиденциальности @@ -731,6 +735,9 @@ You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details Personalized ads Non-personalized ads + I am over 18 years old + Yes, personalized ads + Yes, non-personalized ads Расширенные Внешний вид и поведение Уведомления diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 6a4505d70..1c6eae8c0 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -720,6 +720,10 @@ Odpovedať s históriou správ Vypočítať aritmetický priemer, ak žiadna známka nemá váhu Podpora + Ochrana osobných údajov + Súhlasy + Súhlas so spracovaním údajov súvisiacich s reklamami + Zobraziť reklamy v aplikácii Pozrite sa na jednu reklamu pre podporu projektu Súhlas so spracovaním dát Ak chcete sledovať reklamu, musíte súhlasiť s podmienkami spracovania údajov v našich Zásadách Ochrany Osobných Údajov @@ -731,6 +735,9 @@ Voľbu môžete kedykoľvek zmeniť v nastavení aplikácie. Môžeme použiť vaše údaje na zobrazenie reklám šitých pre vás alebo pomocou menej vašich dát zobrazovať neprispôsobené reklamy. Podrobnosti nájdete v našich Zásadách ochrany osobných údajov Prispôsobené reklamy Neprispôsobené reklamy + Mám ukončené 18 rokov + Áno, prispôsobené reklamy + Áno, neprispôsobené reklamy Pokročilé Vzhľad a správanie Oznámenia diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 742e800f3..f90e78e7d 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -720,6 +720,10 @@ Відповісти з історією повідомлень Вилічити середню аритметичну, якщо оцінка немає вартості Підтримка + Privacy Policy + Agreements + Consent to processing of data related to ads + Show ads in app Подивіться одну рекламу для підтримки проєкту Згода в обробці даних Щоб переглянути рекламу, ви повинні погодитися з умовами обробки даних нашої Політики конфіденційності @@ -731,6 +735,9 @@ You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details Personalized ads Non-personalized ads + I am over 18 years old + Yes, personalized ads + Yes, non-personalized ads Додатково Вигляд та поведінка Сповіщення diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bcd498036..e4542547d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -714,8 +714,8 @@ Show arithmetic average when no weights provided Support - Privacy Policy - Agreements + Privacy Policy + Agreements Consent to processing of data related to ads Show ads in app Watch single ad to support project From 10c36f19bf13234c758b2ac2e96d65a71df8e82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 23 Aug 2022 00:56:12 +0200 Subject: [PATCH 075/164] Version 1.7.1 --- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e99e8773f..5fb07377a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 109 - versionName "1.7.0" + versionCode 110 + versionName "1.7.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -161,7 +161,7 @@ play { defaultToAppBundles = false track = 'production' releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS - userFraction = 0.05d + userFraction = 0.95d updatePriority = 5 enabled.set(false) } diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 3eb42eb97..4b6dcaf28 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,4 +1,4 @@ -Wersja 1.7.0 +Wersja 1.7.1 - naprawiliśmy logowanie do aplikacji - dodaliśmy wsparcie nowego modułu Wiadomości Plus From 70c2cb7dbf717c508bbec08b69931019b2430699 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Aug 2022 13:30:00 +0000 Subject: [PATCH 076/164] Bump desugar_jdk_libs from 1.1.6 to 1.1.8 (#1957) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 5fb07377a..52ae02ddc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -188,7 +188,7 @@ ext { dependencies { implementation "io.github.wulkanowy:sdk:1.7.0" - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" From f68a8e4215e719ecdfedeb72553ce58df1fcfbf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Aug 2022 13:30:28 +0000 Subject: [PATCH 077/164] Bump robolectric from 4.8.1 to 4.8.2 (#1956) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 52ae02ddc..1dba83c5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -263,7 +263,7 @@ dependencies { testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - testImplementation 'org.robolectric:robolectric:4.8.1' + testImplementation 'org.robolectric:robolectric:4.8.2' testImplementation "androidx.test:runner:1.4.0" testImplementation "androidx.test.ext:junit:1.1.3" testImplementation "androidx.test:core:1.4.0" From 5c17c38d1d16214db4cc423ffe8556e7b7c64d8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Aug 2022 13:30:56 +0000 Subject: [PATCH 078/164] Bump mockk from 1.12.5 to 1.12.7 (#1955) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1dba83c5d..10648c15f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -181,7 +181,7 @@ ext { android_hilt = "1.0.0" room = "2.4.3" chucker = "3.5.2" - mockk = "1.12.5" + mockk = "1.12.7" coroutines = "1.6.4" } From 54372e0a55e8e903f868d4e47897e9c1a868b505 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Aug 2022 13:40:14 +0000 Subject: [PATCH 079/164] Bump kotlinx-serialization-json from 1.3.3 to 1.4.0 (#1950) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 10648c15f..5977b347b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -190,7 +190,7 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" implementation "androidx.core:core-ktx:1.8.0" From f2cb3b4f9edd69a1936d3ccb52b3b8eab0e0e320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 30 Aug 2022 09:06:29 +0200 Subject: [PATCH 080/164] Change message draft key in shared preferences (#1959) --- .../github/wulkanowy/data/repositories/MessageRepository.kt | 4 ++-- app/src/main/res/values/preferences_keys.xml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index 00cbffb84..5a43da4e2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -167,10 +167,10 @@ class MessageRepository @Inject constructor( } var draftMessage: MessageDraft? - get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft)) + get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft)) ?.let { json.decodeFromString(it) } set(value) = sharedPrefProvider.putString( - context.getString(R.string.pref_key_message_send_draft), + context.getString(R.string.pref_key_message_draft), value?.let { json.encodeToString(it) } ) } diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index f29080b33..80a71bc71 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -31,8 +31,7 @@ homework_fullscreen subjects_without_grades optional_arithmetic_average - message_send_is_draft - message_send_recipients + message_draft last_sync_date notifications_piggyback notifications_piggyback_cancel_original From 535206056d9b385a5cea1e15a53f0a1c7f37cb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 30 Aug 2022 09:07:07 +0200 Subject: [PATCH 081/164] Fix selecting student mailbox based on studentName field (#1958) --- app/build.gradle | 2 +- .../wulkanowy/data/db/dao/MailboxDao.kt | 3 - .../data/repositories/MailboxRepository.kt | 11 +- .../repositories/MailboxRepositoryTest.kt | 132 ++++++++++++++++++ 4 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt diff --git a/app/build.gradle b/app/build.gradle index 5977b347b..62eb8e315 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.7.0" + implementation "io.github.wulkanowy:sdk:09e5215894" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt index 8589db311..c44ecd0c2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt @@ -11,7 +11,4 @@ interface MailboxDao : BaseDao { @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ") suspend fun loadAll(userLoginId: Int): List - - @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId AND studentName = :studentName ") - suspend fun load(userLoginId: Int, studentName: String): Mailbox? } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt index 7f5974920..8c1097bdb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt @@ -32,17 +32,22 @@ class MailboxRepository @Inject constructor( suspend fun getMailbox(student: Student): Mailbox { val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - val mailbox = mailboxDao.load(student.userLoginId, student.studentName) + val mailboxes = mailboxDao.loadAll(student.userLoginId) + val mailbox = mailboxes.filterByStudent(student) return if (isExpired || mailbox == null) { refreshMailboxes(student) - val newMailbox = mailboxDao.load(student.userLoginId, student.studentName) + val newMailbox = mailboxDao.loadAll(student.userLoginId).filterByStudent(student) requireNotNull(newMailbox) { - "Mailbox for ${student.userName} - ${student.studentName} not found!" + "Mailbox for ${student.userName} - ${student.studentName} not found! Saved mailboxes: $mailboxes" } newMailbox } else mailbox } + + private fun List.filterByStudent(student: Student): Mailbox? = find { + it.studentName.trim() == student.studentName.trim() + } } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt new file mode 100644 index 000000000..56e315638 --- /dev/null +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt @@ -0,0 +1,132 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.MailboxDao +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.db.entities.MailboxType +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.SpyK +import io.mockk.just +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import java.time.Instant + +@OptIn(ExperimentalCoroutinesApi::class) +class MailboxRepositoryTest { + + @SpyK + private var sdk = Sdk() + + @MockK + private lateinit var mailboxDao: MailboxDao + + @MockK + private lateinit var refreshHelper: AutoRefreshHelper + + private lateinit var systemUnderTest: MailboxRepository + + @Before + fun setUp() { + MockKAnnotations.init(this) + + coEvery { refreshHelper.shouldBeRefreshed(any()) } returns false + coEvery { refreshHelper.updateLastRefreshTimestamp(any()) } just Runs + coEvery { mailboxDao.deleteAll(any()) } just Runs + coEvery { mailboxDao.insertAll(any()) } returns emptyList() + coEvery { mailboxDao.loadAll(any()) } returns emptyList() + coEvery { sdk.getMailboxes() } returns emptyList() + + systemUnderTest = MailboxRepository( + mailboxDao = mailboxDao, + sdk = sdk, + refreshHelper = refreshHelper, + ) + } + + @Test(expected = IllegalArgumentException::class) + fun `get mailbox that doesn't exist`() = runTest { + val student = getStudentEntity( + userName = "Stanisław Kowalski", + studentName = "Jan Kowalski", + ) + coEvery { sdk.getMailboxes() } returns emptyList() + + systemUnderTest.getMailbox(student) + } + + @Test + fun `get mailbox for user with additional spaces`() = runTest { + val student = getStudentEntity( + userName = " Stanisław Kowalski ", + studentName = " Jan Kowalski ", + ) + val expectedMailbox = getMailboxEntity("Jan Kowalski ") + coEvery { mailboxDao.loadAll(any()) } returns listOf( + expectedMailbox, + ) + + val selectedMailbox = systemUnderTest.getMailbox(student) + assertEquals(expectedMailbox, selectedMailbox) + } + + @Test(expected = IllegalArgumentException::class) + fun `get mailbox for student with strange name`() = runTest { + val student = getStudentEntity( + userName = "Stanisław Kowalski", + studentName = "J**** K*****", + ) + val expectedMailbox = getMailboxEntity("Jan Kowalski") + coEvery { mailboxDao.loadAll(any()) } returns listOf( + expectedMailbox, + ) + + systemUnderTest.getMailbox(student) + } + + private fun getMailboxEntity( + studentName: String, + ) = Mailbox( + globalKey = "", + fullName = "", + userName = "", + userLoginId = 123, + studentName = studentName, + schoolNameShort = "", + type = MailboxType.STUDENT, + ) + + private fun getStudentEntity( + studentName: String, + userName: String, + ) = Student( + scrapperBaseUrl = "http://fakelog.cf", + email = "jan@fakelog.cf", + certificateKey = "", + classId = 0, + className = "", + isCurrent = false, + isParent = false, + loginMode = Sdk.Mode.API.name, + loginType = Sdk.ScrapperLoginType.STANDARD.name, + mobileBaseUrl = "", + password = "", + privateKey = "", + registrationDate = Instant.now(), + schoolName = "", + schoolShortName = "test", + schoolSymbol = "", + studentId = 1, + studentName = studentName, + symbol = "", + userLoginId = 1, + userName = userName, + ) +} From eed091aad29b76ac3340e84ae90f4ffc01868491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 30 Aug 2022 09:07:24 +0200 Subject: [PATCH 082/164] Update row in mailboxes table on primary key conflict (#1954) --- app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt index 048e9e3cd..056a5cbd1 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt @@ -2,11 +2,12 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Delete import androidx.room.Insert +import androidx.room.OnConflictStrategy import androidx.room.Update interface BaseDao { - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(items: List): List @Update From bf34cb0c1e483b0253e12359c97e332404385e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Tue, 30 Aug 2022 12:11:32 +0200 Subject: [PATCH 083/164] New Crowdin updates (#1953) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mikołaj Pich --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 62eb8e315..b70e77b73 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:09e5215894" + implementation "io.github.wulkanowy:sdk:a1bf69486b" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' From d139bd5b14a3b068f217f4ec107b5484f5539856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 30 Aug 2022 13:34:10 +0200 Subject: [PATCH 084/164] Version 1.7.2 --- app/build.gradle | 8 ++++---- app/src/main/play/release-notes/pl-PL/default.txt | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b70e77b73..759199cad 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 110 - versionName "1.7.1" + versionCode 111 + versionName "1.7.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -161,7 +161,7 @@ play { defaultToAppBundles = false track = 'production' releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS - userFraction = 0.95d + userFraction = 0.05d updatePriority = 5 enabled.set(false) } @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:a1bf69486b" + implementation "io.github.wulkanowy:sdk:1.7.2" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 4b6dcaf28..69699208a 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,9 +1,5 @@ -Wersja 1.7.1 +Wersja 1.7.2 -- naprawiliśmy logowanie do aplikacji -- dodaliśmy wsparcie nowego modułu Wiadomości Plus -- dodaliśmy nową możliwość wsparcia naszego projektu przez opcjonalne reklamy -- dodaliśmy sortowanie po średniej -- naprawiliśmy też kilka usterek wpływających na komfort używania aplikacji +- naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From 558db061f594b709e10a9766ae3cdfccbf8dbed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 31 Aug 2022 18:31:12 +0200 Subject: [PATCH 085/164] Fix marking message as read (#1960) * Fix marking message as read * Update sdk * Update sdk * Fix tests * Use many recipients strings instead of first recipient --- app/build.gradle | 2 +- .../wulkanowy/data/repositories/MessageRepository.kt | 9 ++++++--- .../wulkanowy/data/repositories/MessageRepositoryTest.kt | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 759199cad..ab8add505 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.7.2" + implementation "com.github.wulkanowy:sdk:006a238bda" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index 5a43da4e2..e7428762e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -64,7 +64,10 @@ class MessageRepository @Inject constructor( }, query = { messagesDb.loadAll(mailbox.globalKey, folder.id) }, fetch = { - sdk.init(student).getMessages(Folder.valueOf(folder.name)).mapToEntities(mailbox) + sdk.init(student).getMessages( + folder = Folder.valueOf(folder.name), + mailboxKey = mailbox.globalKey, + ).mapToEntities(mailbox) }, saveFetchResult = { old, new -> messagesDb.deleteAll(old uniqueSubtract new) @@ -89,7 +92,7 @@ class MessageRepository @Inject constructor( }, query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) }, fetch = { - sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey) + sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey, markAsRead) }, saveFetchResult = { old, new -> checkNotNull(old) { "Fetched message no longer exist!" } @@ -98,7 +101,7 @@ class MessageRepository @Inject constructor( id = message.id unread = !markAsRead sender = new.sender - recipients = new.recipients.firstOrNull() ?: "Wielu adresoatów" + recipients = new.recipients.singleOrNull() ?: "Wielu adresatów" content = content.ifBlank { new.content } }) ) diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt index 24306bfeb..4efc9c60a 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt @@ -141,7 +141,7 @@ class MessageRepositoryTest { messageDb.loadMessageWithAttachment("v4") } returnsMany listOf(flowOf(mWa), flowOf(mWaWithContent)) coEvery { - sdk.getMessageDetails("v4") + sdk.getMessageDetails("v4", any()) } returns mockk { every { sender } returns "" every { recipients } returns listOf("") From d566de0282eb5ac77688233aff677673c6ec15ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 31 Aug 2022 19:31:28 +0200 Subject: [PATCH 086/164] Version 1.7.3 --- app/build.gradle | 10 +++++----- app/src/main/play/release-notes/pl-PL/default.txt | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ab8add505..d4a9b9021 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 111 - versionName "1.7.2" + versionCode 112 + versionName "1.7.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -160,8 +160,8 @@ kapt { play { defaultToAppBundles = false track = 'production' - releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS - userFraction = 0.05d +// releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS +// userFraction = 0.05d updatePriority = 5 enabled.set(false) } @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "com.github.wulkanowy:sdk:006a238bda" + implementation "io.github.wulkanowy:sdk:1.7.3" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 69699208a..ac19bc99a 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,4 +1,4 @@ -Wersja 1.7.2 +Wersja 1.7.3 - naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości From 6153c7b97d58f36383f05d4357194c9c3dcb7720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Thu, 1 Sep 2022 14:55:00 +0200 Subject: [PATCH 087/164] Add support for match mailboxes with more different names (#1961) --- .../data/repositories/MailboxRepository.kt | 34 +++++++++- .../repositories/MailboxRepositoryTest.kt | 62 ++++++++++++++++++- 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt index 8c1097bdb..ad4f669e2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt @@ -47,7 +47,37 @@ class MailboxRepository @Inject constructor( } else mailbox } - private fun List.filterByStudent(student: Student): Mailbox? = find { - it.studentName.trim() == student.studentName.trim() + private fun List.filterByStudent(student: Student): Mailbox? { + val normalizedStudentName = student.studentName.normalizeStudentName() + + return find { + it.studentName.normalizeStudentName() == normalizedStudentName + } ?: singleOrNull { + it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart() + } ?: singleOrNull { + it.studentName.getUnauthorizedVersion() == normalizedStudentName + } + } + + private fun String.normalizeStudentName(): String { + return trim().split(" ").joinToString(" ") { part -> + part.lowercase().replaceFirstChar { it.uppercase() } + } + } + + private fun String.getFirstAndLastPart(): String { + val parts = normalizeStudentName().split(" ") + + val endParts = parts.filterIndexed { i, _ -> + i == 0 || parts.size == i - 1 + } + return endParts.joinToString(" ") + } + + private fun String.getUnauthorizedVersion(): String { + return normalizeStudentName().split(" ") + .joinToString(" ") { + it.first() + "*".repeat(it.length - 1) + } } } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt index 56e315638..300662f01 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt @@ -77,20 +77,76 @@ class MailboxRepositoryTest { assertEquals(expectedMailbox, selectedMailbox) } - @Test(expected = IllegalArgumentException::class) - fun `get mailbox for student with strange name`() = runTest { + @Test + fun `get mailbox for unique non-authorized student`() = runTest { val student = getStudentEntity( userName = "Stanisław Kowalski", - studentName = "J**** K*****", + studentName = "J** K*******", ) val expectedMailbox = getMailboxEntity("Jan Kowalski") coEvery { mailboxDao.loadAll(any()) } returns listOf( expectedMailbox, ) + assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + } + + @Test(expected = IllegalArgumentException::class) + fun `get mailbox for not-unique non-authorized student`() = runTest { + val student = getStudentEntity( + userName = "Stanisław Kowalski", + studentName = "J** K*******", + ) + coEvery { mailboxDao.loadAll(any()) } returns listOf( + getMailboxEntity("Jan Kowalski"), + getMailboxEntity("Jan Kurowski"), + ) + systemUnderTest.getMailbox(student) } + @Test + fun `get mailbox for student with uppercase name`() = runTest { + val student = getStudentEntity( + userName = "Mochoń Julia", + studentName = "KLAUDIA MOCHOŃ", + ) + val expectedMailbox = getMailboxEntity("Klaudia Mochoń") + coEvery { mailboxDao.loadAll(any()) } returns listOf( + expectedMailbox, + ) + + assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + } + + @Test + fun `get mailbox for student with second name`() = runTest { + val student = getStudentEntity( + userName = "Fistaszek Karolina", + studentName = "Julia Fistaszek", + ) + val expectedMailbox = getMailboxEntity("Julia Maria Fistaszek") + coEvery { mailboxDao.loadAll(any()) } returns listOf( + expectedMailbox, + ) + + assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + } + + @Test + fun `get mailbox for student with second name and uppercase`() = runTest { + val student = getStudentEntity( + userName = "BEDNAREK KAMIL", + studentName = "ALEKSANDRA BEDNAREK", + ) + val expectedMailbox = getMailboxEntity("Aleksandra Anna Bednarek") + coEvery { mailboxDao.loadAll(any()) } returns listOf( + expectedMailbox, + ) + + assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + } + private fun getMailboxEntity( studentName: String, ) = Mailbox( From e05abb3539c1d0d46d48f0c9528df2009a714723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Thu, 1 Sep 2022 17:48:03 +0200 Subject: [PATCH 088/164] Fix showing empty view in grade details when there is no grades (#1963) --- .../ui/modules/grade/details/GradeDetailsPresenter.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 8cde5d6b9..4261c507d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.grade.details import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.enums.GradeExpandMode -import io.github.wulkanowy.data.enums.GradeSortingMode import io.github.wulkanowy.data.enums.GradeSortingMode.* import io.github.wulkanowy.data.repositories.GradeRepository import io.github.wulkanowy.data.repositories.PreferencesRepository @@ -132,16 +131,17 @@ class GradeDetailsPresenter @Inject constructor( } .logResourceStatus("load grade details") .onResourceData { + val gradeItems = createGradeItems(it) view?.run { enableSwipe(true) showProgress(false) showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) + showContent(gradeItems.isNotEmpty()) + showEmpty(gradeItems.isEmpty()) updateNewGradesAmount(it) updateMarkAsDoneButton() updateData( - data = createGradeItems(it), + data = gradeItems, expandMode = preferencesRepository.gradeExpandMode, preferencesRepository.gradeColorTheme ) From bc22808b0ea2ece030ff3e9ef24d5af231e22b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Thu, 1 Sep 2022 17:48:46 +0200 Subject: [PATCH 089/164] Add support for matching last kingergarten semester if there is no any current (#1962) --- .../wulkanowy/utils/SemesterExtension.kt | 3 + .../utils/SemesterExtensionKtTest.kt | 72 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 app/src/test/java/io/github/wulkanowy/utils/SemesterExtensionKtTest.kt diff --git a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt index 6e11a8b2c..380d6bf6e 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt @@ -15,5 +15,8 @@ fun List.getCurrentOrLast(): Semester { // when there is more than one current semester - find one with higher id singleOrNull { semester -> semester.semesterId == maxByOrNull { it.semesterId }?.semesterId }?.let { return it } + // when there is no active kindergarten semester - get one from last year + singleOrNull { semester -> semester.schoolYear == maxByOrNull { it.schoolYear }?.schoolYear }?.let { return it } + throw IllegalArgumentException("Duplicated last semester! Semesters: ${joinToString(separator = "\n")}") } diff --git a/app/src/test/java/io/github/wulkanowy/utils/SemesterExtensionKtTest.kt b/app/src/test/java/io/github/wulkanowy/utils/SemesterExtensionKtTest.kt new file mode 100644 index 000000000..b7d3ecc94 --- /dev/null +++ b/app/src/test/java/io/github/wulkanowy/utils/SemesterExtensionKtTest.kt @@ -0,0 +1,72 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.getSemesterEntity +import org.junit.Test +import java.time.LocalDate +import kotlin.test.assertEquals + +class SemesterExtensionKtTest { + + @Test(expected = IllegalArgumentException::class) + fun `get current semester when current is doubled`() { + val semesters = listOf( + getSemesterEntity(1, 1, LocalDate.now(), LocalDate.now()), + getSemesterEntity(1, 1, LocalDate.now(), LocalDate.now()) + ) + + semesters.getCurrentOrLast() + } + + @Test(expected = RuntimeException::class) + fun `get current semester when there is empty list`() { + val semesters = listOf() + + semesters.getCurrentOrLast() + } + + @Test + fun `get current kindergarten semester when there is no any current`() { + val semesters = listOf( + createSemesterEntity( + kindergartenDiaryId = 281, + schoolYear = 2020, + semesterId = 0, + start = LocalDate.of(2020, 9, 1), + end = LocalDate.of(2021, 8, 31), + ), + createSemesterEntity( + kindergartenDiaryId = 342, + schoolYear = 2021, + semesterId = 0, + start = LocalDate.of(2021, 9, 1), + end = LocalDate.of(2022, 8, 31), + ), + ) + + val res = semesters.getCurrentOrLast() + + assertEquals(2021, res.schoolYear) + } + + private fun createSemesterEntity( + diaryId: Int = 0, + kindergartenDiaryId: Int = 0, + semesterId: Int = 0, + schoolYear: Int = 0, + start: LocalDate = LocalDate.now(), + end: LocalDate = LocalDate.now().plusMonths(6), + ) = Semester( + studentId = 1, + diaryId = diaryId, + kindergartenDiaryId = kindergartenDiaryId, + semesterId = semesterId, + diaryName = "$semesterId", + schoolYear = schoolYear, + classId = 0, + semesterName = semesterId, + unitId = 1, + start = start, + end = end + ) +} From e67066f3aea1f32c1470b7e43c90cb3da013027e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Thu, 1 Sep 2022 17:57:20 +0200 Subject: [PATCH 090/164] Version 1.7.4 --- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d4a9b9021..e8902fddd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 112 - versionName "1.7.3" + versionCode 113 + versionName "1.7.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.7.3" + implementation "io.github.wulkanowy:sdk:1.7.4" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index ac19bc99a..7fe3b7099 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,5 +1,6 @@ -Wersja 1.7.3 +Wersja 1.7.4 - naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości +- naprawiliśmy wyświetlanie napisu "Brak ocen", jesli uczeń nie zdobył w danym semestrze jeszcze żadnych ocen Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From 157becb017f3502e54ff42ba4f0dbc1bbcd136c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 2 Sep 2022 20:19:19 +0200 Subject: [PATCH 091/164] Fix matching mailboxes when there is more than one space between words (#1964) --- app/build.gradle | 2 +- .../data/repositories/MailboxRepository.kt | 10 ++++++---- .../data/repositories/MailboxRepositoryTest.kt | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e8902fddd..c9425d58f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.7.4" + implementation "io.github.wulkanowy:sdk:dcd8bd8b19" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt index ad4f669e2..c571937ad 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt @@ -60,16 +60,18 @@ class MailboxRepository @Inject constructor( } private fun String.normalizeStudentName(): String { - return trim().split(" ").joinToString(" ") { part -> - part.lowercase().replaceFirstChar { it.uppercase() } - } + return trim().split(" ") + .filter { it.isNotBlank() } + .joinToString(" ") { part -> + part.lowercase().replaceFirstChar { it.uppercase() } + } } private fun String.getFirstAndLastPart(): String { val parts = normalizeStudentName().split(" ") val endParts = parts.filterIndexed { i, _ -> - i == 0 || parts.size == i - 1 + i == 0 || parts.size - 1 == i } return endParts.joinToString(" ") } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt index 300662f01..9198560fe 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt @@ -91,6 +91,20 @@ class MailboxRepositoryTest { assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) } + @Test + fun `get mailbox for unique non-authorized student but with spaces`() = runTest { + val student = getStudentEntity( + userName = "Stanisław Kowalski", + studentName = "J** K*******", + ) + val expectedMailbox = getMailboxEntity("Jan Kowalski") + coEvery { mailboxDao.loadAll(any()) } returns listOf( + expectedMailbox, + ) + + assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + } + @Test(expected = IllegalArgumentException::class) fun `get mailbox for not-unique non-authorized student`() = runTest { val student = getStudentEntity( From 86f8763e697b2976d3b73f9d9f78dc102789191d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 2 Sep 2022 21:30:30 +0200 Subject: [PATCH 092/164] Display lesson number in attendance notification if subject is blank (#1965) --- .../services/sync/notifications/NewAttendanceNotification.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt index 49842c9a6..99473a8ec 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt @@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.pojos.GroupNotificationData import io.github.wulkanowy.data.pojos.NotificationData import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity import io.github.wulkanowy.utils.descriptionRes import io.github.wulkanowy.utils.getPlural import io.github.wulkanowy.utils.toFormattedString @@ -22,8 +21,9 @@ class NewAttendanceNotification @Inject constructor( suspend fun notify(items: List, student: Student) { val lines = items.filterNot { it.presence || it.name == "UNKNOWN" } .map { + val lesson = it.subject.ifBlank { "Lekcja ${it.number}" } val description = context.getString(it.descriptionRes) - "${it.date.toFormattedString("dd.MM")} - ${it.subject}: $description" + "${it.date.toFormattedString("dd.MM")} - $lesson: $description" } .ifEmpty { return } From 59f6f5c2120cfc5ea02bc7e3f1a7d85e6197be51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 2 Sep 2022 21:31:27 +0200 Subject: [PATCH 093/164] Version 1.7.5 --- app/build.gradle | 8 ++++---- app/src/main/play/release-notes/pl-PL/default.txt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c9425d58f..6584b0d28 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 113 - versionName "1.7.4" + versionCode 114 + versionName "1.7.5" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -162,7 +162,7 @@ play { track = 'production' // releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS // userFraction = 0.05d - updatePriority = 5 + updatePriority = 4 enabled.set(false) } @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:dcd8bd8b19" + implementation "io.github.wulkanowy:sdk:1.7.5" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 7fe3b7099..064401abd 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,6 +1,7 @@ -Wersja 1.7.4 +Wersja 1.7.5 - naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości - naprawiliśmy wyświetlanie napisu "Brak ocen", jesli uczeń nie zdobył w danym semestrze jeszcze żadnych ocen +- naprawiliśmy logowanie do aplikacji rodzicom będącym jednocześnie nauczycielami Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From 73a7255d3a003e15de313387bcbf40c73fa17ae5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 19:49:30 +0000 Subject: [PATCH 094/164] Bump firebase-bom from 30.3.2 to 30.4.0 (#1968) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6584b0d28..17a9fe42f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -241,7 +241,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:30.3.2') + playImplementation platform('com.google.firebase:firebase-bom:30.4.0') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From 46c29c438eb1a52824dff8d9b0d1d385c3366013 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:17:19 +0000 Subject: [PATCH 095/164] Bump appcompat from 1.5.0 to 1.5.1 (#1975) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 17a9fe42f..41ead9f3e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "androidx.core:core-ktx:1.8.0" implementation 'androidx.core:core-splashscreen:1.0.0' implementation "androidx.activity:activity-ktx:1.5.1" - implementation "androidx.appcompat:appcompat:1.5.0" + implementation "androidx.appcompat:appcompat:1.5.1" implementation "androidx.fragment:fragment-ktx:1.5.2" implementation "androidx.annotation:annotation:1.4.0" From d3f869c6c2400a5b7fa427a45d89df0add4f5420 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:18:09 +0000 Subject: [PATCH 096/164] Bump firebase-bom from 30.4.0 to 30.4.1 (#1971) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 41ead9f3e..c2dba71fb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -241,7 +241,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:30.4.0') + playImplementation platform('com.google.firebase:firebase-bom:30.4.1') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From d5d45ed1baa7ec0c52abb0fba9e8bec67c6123e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:18:29 +0000 Subject: [PATCH 097/164] Bump play-services-ads from 21.1.0 to 21.2.0 (#1972) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c2dba71fb..7bd4ed15f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -247,7 +247,7 @@ dependencies { playImplementation 'com.google.firebase:firebase-crashlytics:' playImplementation 'com.google.android.play:core:1.10.3' playImplementation 'com.google.android.play:core-ktx:1.8.1' - playImplementation 'com.google.android.gms:play-services-ads:21.1.0' + playImplementation 'com.google.android.gms:play-services-ads:21.2.0' hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300' From a5c636853a9a15563caedf0f5bc90a3a9021d6fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:19:05 +0000 Subject: [PATCH 098/164] Bump coil from 2.2.0 to 2.2.1 (#1973) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7bd4ed15f..7b7251f89 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -236,7 +236,7 @@ dependencies { implementation "at.favre.lib:slf4j-timber:1.0.1" implementation 'com.github.bastienpaulfr:Treessence:1.0.5' implementation "com.mikepenz:aboutlibraries-core:$about_libraries" - implementation "io.coil-kt:coil:2.2.0" + implementation "io.coil-kt:coil:2.2.1" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' From afbfb9761fe61339f364f88ef8fde9d4703cb4e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 20:38:55 +0000 Subject: [PATCH 099/164] Bump mockk from 1.12.7 to 1.12.8 (#1986) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7b7251f89..67b4fd07d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -181,7 +181,7 @@ ext { android_hilt = "1.0.0" room = "2.4.3" chucker = "3.5.2" - mockk = "1.12.7" + mockk = "1.12.8" coroutines = "1.6.4" } From 3625c5c5187786f781ea446433f09f13b809861d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 20:39:08 +0000 Subject: [PATCH 100/164] Bump firebase-bom from 30.4.1 to 30.5.0 (#1991) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 67b4fd07d..6f3f1e989 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -241,7 +241,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:30.4.1') + playImplementation platform('com.google.firebase:firebase-bom:30.5.0') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From 4f0519552e3dbce9e5cdecd10685ed1e85aca540 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 20:39:29 +0000 Subject: [PATCH 101/164] Bump firebase-crashlytics-gradle from 2.9.1 to 2.9.2 (#1988) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 98c9dfb84..d98f6f27e 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ buildscript { classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.13' classpath 'com.huawei.agconnect:agcp:1.7.1.300' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513" From f1db993feedb35e746f309dfa0a11c4d0092a0c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 20:40:36 +0000 Subject: [PATCH 102/164] Bump agconnect-crash from 1.7.1.300 to 1.7.2.300 (#1990) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6f3f1e989..6fddc1a73 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -250,7 +250,7 @@ dependencies { playImplementation 'com.google.android.gms:play-services-ads:21.2.0' hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300' - hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.2.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" From a1dc00af42c73255eeec65704df526c3c877aac4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 20:49:36 +0000 Subject: [PATCH 103/164] Bump agcp from 1.7.1.300 to 1.7.2.300 (#1989) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d98f6f27e..942cf4b66 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.2.2' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.13' - classpath 'com.huawei.agconnect:agcp:1.7.1.300' + classpath 'com.huawei.agconnect:agcp:1.7.2.300' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4" From 5148ff291b7b5c7b2c33061e7e769325c9728aee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 20:59:41 +0000 Subject: [PATCH 104/164] Bump google-services from 4.3.13 to 4.3.14 (#1992) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 942cf4b66..53fcfda5e 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath 'com.android.tools.build:gradle:7.2.2' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" - classpath 'com.google.gms:google-services:4.3.13' + classpath 'com.google.gms:google-services:4.3.14' classpath 'com.huawei.agconnect:agcp:1.7.2.300' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' classpath "com.github.triplet.gradle:play-publisher:3.6.0" From 1bbd249275b97b145011fdd30355664984c03bcb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 20:25:58 +0000 Subject: [PATCH 105/164] Bump fragment-ktx from 1.5.2 to 1.5.3 (#1997) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6fddc1a73..e67747d73 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -197,7 +197,7 @@ dependencies { implementation 'androidx.core:core-splashscreen:1.0.0' implementation "androidx.activity:activity-ktx:1.5.1" implementation "androidx.appcompat:appcompat:1.5.1" - implementation "androidx.fragment:fragment-ktx:1.5.2" + implementation "androidx.fragment:fragment-ktx:1.5.3" implementation "androidx.annotation:annotation:1.4.0" implementation "androidx.preference:preference-ktx:1.2.0" From edbe45332ac522a6d3e78ad76a9d3af09edf5960 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 20:27:43 +0000 Subject: [PATCH 106/164] Bump mockk from 1.12.8 to 1.13.1 (#1996) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index e67747d73..eaa81d233 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -181,7 +181,7 @@ ext { android_hilt = "1.0.0" room = "2.4.3" chucker = "3.5.2" - mockk = "1.12.8" + mockk = "1.13.1" coroutines = "1.6.4" } From 8ca41b5ba348483bf9c10a2af927eb387526748e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 20:28:01 +0000 Subject: [PATCH 107/164] Bump hianalytics from 6.7.0.300 to 6.8.0.300 (#1995) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index eaa81d233..8d33323f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -249,7 +249,7 @@ dependencies { playImplementation 'com.google.android.play:core-ktx:1.8.1' playImplementation 'com.google.android.gms:play-services-ads:21.2.0' - hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300' + hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.2.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" From b271c12ebccd7bb6e8ddaf18f68ed09071f385d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 20:28:31 +0000 Subject: [PATCH 108/164] Bump hilt_version from 2.43.2 to 2.44 (#1994) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 53fcfda5e..e308c579b 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { kotlin_version = '1.7.10' about_libraries = '10.4.0' - hilt_version = "2.43.2" + hilt_version = "2.44" } repositories { mavenCentral() From 354f51dd707807c7c423b275df7ea4e672689443 Mon Sep 17 00:00:00 2001 From: Michael <5672750+mibac138@users.noreply.github.com> Date: Wed, 28 Sep 2022 23:33:05 +0200 Subject: [PATCH 109/164] Fix student average calculation error in grade statistics (#1981) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mikołaj Pich --- app/build.gradle | 2 +- .../grade/summary/GradeSummaryAdapter.kt | 3 ++- .../github/wulkanowy/utils/GradeExtension.kt | 24 ++++++++----------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8d33323f3..1b745f0b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.7.5" + implementation "io.github.wulkanowy:sdk:2840d9d6d0" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt index 082c847e5..8dcade56e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt @@ -10,6 +10,7 @@ import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.ItemGradeSummaryBinding import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding +import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid import io.github.wulkanowy.utils.calcFinalAverage import java.util.Locale import javax.inject.Inject @@ -61,7 +62,7 @@ class GradeSummaryAdapter @Inject constructor( if (items.isEmpty()) return val context = binding.root.context - val finalItemsCount = items.count { it.finalGrade.matches("[0-6][+-]?".toRegex()) } + val finalItemsCount = items.count { isGradeValid(it.finalGrade) } val calculatedItemsCount = items.count { value -> value.average != 0.0 } val allItemsCount = items.count { !it.subject.equals("zachowanie", true) } val finalAverage = items.calcFinalAverage( diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt index ff65d6376..61924d4e9 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -4,6 +4,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.enums.GradeColorTheme +import io.github.wulkanowy.sdk.scrapper.grades.getGradeValueWithModifier import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { @@ -20,20 +21,15 @@ fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { } fun List.calcFinalAverage(plusModifier: Double, minusModifier: Double) = asSequence() - .mapNotNull { - if (it.finalGrade.matches("[0-6][+-]?".toRegex())) { - when { - it.finalGrade.endsWith('+') -> { - it.finalGrade.removeSuffix("+").toDouble() + plusModifier - } - it.finalGrade.endsWith('-') -> { - it.finalGrade.removeSuffix("-").toDouble() - minusModifier - } - else -> { - it.finalGrade.toDouble() - } - } - } else null + .mapNotNull { summary -> + val (gradeValue, gradeModifier) = getGradeValueWithModifier(summary.finalGrade) + if (gradeValue == null || gradeModifier == null) return@mapNotNull null + + when { + gradeModifier > 0 -> gradeValue + plusModifier + gradeModifier < 0 -> gradeValue - minusModifier + else -> gradeValue + 0.0 + } } .average() .let { if (it.isNaN()) 0.0 else it } From a523850216a5d9dc82dc3fda9a73e94507c172f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 23:34:06 +0200 Subject: [PATCH 110/164] Bump annotation from 1.4.0 to 1.5.0 (#1998) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1b745f0b6..3127d55c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,7 +198,7 @@ dependencies { implementation "androidx.activity:activity-ktx:1.5.1" implementation "androidx.appcompat:appcompat:1.5.1" implementation "androidx.fragment:fragment-ktx:1.5.3" - implementation "androidx.annotation:annotation:1.4.0" + implementation "androidx.annotation:annotation:1.5.0" implementation "androidx.preference:preference-ktx:1.2.0" implementation "androidx.recyclerview:recyclerview:1.2.1" From 8114a2376ee3f798e950b4a2deab3ad21c946f19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 21:43:45 +0000 Subject: [PATCH 111/164] Bump gradle from 7.2.2 to 7.3.0 (#1985) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e308c579b..739d5752e 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.3.0' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.14' classpath 'com.huawei.agconnect:agcp:1.7.2.300' From 4dc80595ac9d47e8a07fe998e14dffcee83d5885 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 07:49:22 +0000 Subject: [PATCH 112/164] Bump robolectric from 4.8.2 to 4.9 (#2007) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 3127d55c5..ef5b99500 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -263,7 +263,7 @@ dependencies { testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - testImplementation 'org.robolectric:robolectric:4.8.2' + testImplementation 'org.robolectric:robolectric:4.9' testImplementation "androidx.test:runner:1.4.0" testImplementation "androidx.test.ext:junit:1.1.3" testImplementation "androidx.test:core:1.4.0" From 95a90a7a79f2b2ef0510baaf305257246997f6f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 07:49:41 +0000 Subject: [PATCH 113/164] Bump mockk from 1.13.1 to 1.13.2 (#2006) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ef5b99500..458f963fb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -181,7 +181,7 @@ ext { android_hilt = "1.0.0" room = "2.4.3" chucker = "3.5.2" - mockk = "1.13.1" + mockk = "1.13.2" coroutines = "1.6.4" } From c65303959072a7e4b009973ed593abfd7f6657c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 07:50:05 +0000 Subject: [PATCH 114/164] Bump coil from 2.2.1 to 2.2.2 (#2004) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 458f963fb..cd4f6cc12 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -236,7 +236,7 @@ dependencies { implementation "at.favre.lib:slf4j-timber:1.0.1" implementation 'com.github.bastienpaulfr:Treessence:1.0.5' implementation "com.mikepenz:aboutlibraries-core:$about_libraries" - implementation "io.coil-kt:coil:2.2.1" + implementation "io.coil-kt:coil:2.2.2" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' From 37f7f21a033e03ccb976dc8c6c267ef68061cd25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 4 Oct 2022 09:51:30 +0200 Subject: [PATCH 115/164] Reorder action buttons on the message preview screen to hide the forward button in overflow menu (#2000) --- .../res/menu/action_menu_message_preview.xml | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/res/menu/action_menu_message_preview.xml b/app/src/main/res/menu/action_menu_message_preview.xml index 5011e2356..57cf05ddb 100644 --- a/app/src/main/res/menu/action_menu_message_preview.xml +++ b/app/src/main/res/menu/action_menu_message_preview.xml @@ -8,20 +8,6 @@ android:title="@string/message_reply" app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="ifRoom" /> - - + + From cd037f0ce0f2f2b0107af5114ed3c11f1d03388e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 07:58:58 +0000 Subject: [PATCH 116/164] Bump kotlin_version from 1.7.10 to 1.7.20 (#2003) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 739d5752e..fb58a44d2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - kotlin_version = '1.7.10' + kotlin_version = '1.7.20' about_libraries = '10.4.0' hilt_version = "2.44" } From 3f431022a5b5316babaa89951d13a67d5680299e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 08:08:21 +0000 Subject: [PATCH 117/164] Bump about_libraries from 10.4.0 to 10.5.0 (#2005) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fb58a44d2..9f5b21f4d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { kotlin_version = '1.7.20' - about_libraries = '10.4.0' + about_libraries = '10.5.0' hilt_version = "2.44" } repositories { From ad487e680cf6907eba664d7922c12f184974f830 Mon Sep 17 00:00:00 2001 From: Daniel Olczyk <44818681+MRmlik12@users.noreply.github.com> Date: Wed, 5 Oct 2022 22:25:09 +0200 Subject: [PATCH 118/164] Fix grade weight text truncation in grade dialog with large font set (#2009) --- app/src/main/res/layout/dialog_grade.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml index 9c52c1d0b..94facb232 100644 --- a/app/src/main/res/layout/dialog_grade.xml +++ b/app/src/main/res/layout/dialog_grade.xml @@ -21,7 +21,7 @@ Date: Tue, 18 Oct 2022 19:39:16 +0000 Subject: [PATCH 119/164] Bump play-services-ads from 21.2.0 to 21.3.0 (#2016) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index cd4f6cc12..a0285de83 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -247,7 +247,7 @@ dependencies { playImplementation 'com.google.firebase:firebase-crashlytics:' playImplementation 'com.google.android.play:core:1.10.3' playImplementation 'com.google.android.play:core-ktx:1.8.1' - playImplementation 'com.google.android.gms:play-services-ads:21.2.0' + playImplementation 'com.google.android.gms:play-services-ads:21.3.0' hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.2.300' From 1f11eea9b58e66ce9c7aa6aba7e1dd3ad3ad6078 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:39:36 +0000 Subject: [PATCH 120/164] Bump gradle from 7.3.0 to 7.3.1 (#2015) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9f5b21f4d..5b7b4abbb 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath 'com.android.tools.build:gradle:7.3.0' + classpath 'com.android.tools.build:gradle:7.3.1' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.14' classpath 'com.huawei.agconnect:agcp:1.7.2.300' From e20c232f8fd2797dd83fae0e6dbdeddde6b000d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:39:54 +0000 Subject: [PATCH 121/164] Bump kotlinx-serialization-json from 1.4.0 to 1.4.1 (#2014) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index a0285de83..4ea25d7e2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -190,7 +190,7 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" implementation "androidx.core:core-ktx:1.8.0" From 4c24363599f8dc486a1d7b9a1507977439ef9982 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:40:28 +0000 Subject: [PATCH 122/164] Bump about_libraries from 10.5.0 to 10.5.1 (#2012) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5b7b4abbb..5131796bb 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { kotlin_version = '1.7.20' - about_libraries = '10.5.0' + about_libraries = '10.5.1' hilt_version = "2.44" } repositories { From e91cd188044c94c1022f13f053d74cf243201d5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:41:15 +0000 Subject: [PATCH 123/164] Bump firebase-bom from 30.5.0 to 31.0.0 (#2013) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4ea25d7e2..672a4becc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -241,7 +241,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:30.5.0') + playImplementation platform('com.google.firebase:firebase-bom:31.0.0') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From a14c4b489bf1b413157500666c828fc4a178c7f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:22:16 +0000 Subject: [PATCH 124/164] Bump material from 1.6.1 to 1.7.0 (#2022) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 672a4becc..8848a413b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -206,7 +206,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" - implementation "com.google.android.material:material:1.6.1" + implementation "com.google.android.material:material:1.7.0" implementation "com.github.wulkanowy:material-chips-input:2.3.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation 'com.github.lopspower:CircularImageView:4.2.0' From 4a484dc2ce25472961f0842cb3e05575233b1c00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:22:34 +0000 Subject: [PATCH 125/164] Bump fragment-ktx from 1.5.3 to 1.5.4 (#2020) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 8848a413b..b15eb651d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -197,7 +197,7 @@ dependencies { implementation 'androidx.core:core-splashscreen:1.0.0' implementation "androidx.activity:activity-ktx:1.5.1" implementation "androidx.appcompat:appcompat:1.5.1" - implementation "androidx.fragment:fragment-ktx:1.5.3" + implementation "androidx.fragment:fragment-ktx:1.5.4" implementation "androidx.annotation:annotation:1.5.0" implementation "androidx.preference:preference-ktx:1.2.0" From 49b383fbe51078cc375d972aea6b11458906a35f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:22:53 +0000 Subject: [PATCH 126/164] Bump firebase-bom from 31.0.0 to 31.0.1 (#2019) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b15eb651d..6211db2d8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -241,7 +241,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:31.0.0') + playImplementation platform('com.google.firebase:firebase-bom:31.0.1') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From 3925a6261b9d921f2ea4b0f051b7092af90d9930 Mon Sep 17 00:00:00 2001 From: Damian Czupryn <60961958+Daxxxis@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:27:37 +0200 Subject: [PATCH 127/164] Add missing CS and SK links in German README (#2018) --- README.de.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.de.md b/README.de.md index b9e1d1ec1..e03557a06 100644 --- a/README.de.md +++ b/README.de.md @@ -2,6 +2,10 @@ [English version of README](README.en.md) +[Česká verze README](README.cs.md) + +[Slovenská verzia README](README.sk.md) + # Wulkanowy [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) From 22a4f509dcc49086ea51939d0b820a479a01aa32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Thu, 27 Oct 2022 12:41:33 +0200 Subject: [PATCH 128/164] Add installation id to crashlytics and bug report emails (#2024) --- .../github/wulkanowy/utils/AnalyticsHelper.kt | 14 +++------- .../github/wulkanowy/utils/AnalyticsHelper.kt | 26 ++++++++++++++----- .../github/wulkanowy/utils/CrashLogUtils.kt | 9 ++----- .../repositories/PreferencesRepository.kt | 24 ++++++++--------- .../github/wulkanowy/ui/base/ErrorDialog.kt | 10 ++++--- .../ui/modules/about/AboutFragment.kt | 7 ++++- .../modules/login/form/LoginFormFragment.kt | 7 ++++- .../LoginStudentSelectFragment.kt | 10 +++++-- .../login/symbol/LoginSymbolFragment.kt | 7 ++++- app/src/main/res/values/strings.xml | 4 +-- .../github/wulkanowy/utils/AnalyticsHelper.kt | 26 ++++++++++++++----- .../wulkanowy/utils/CrashlyticsUtils.kt | 3 --- 12 files changed, 90 insertions(+), 57 deletions(-) diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index 3bf7e1693..a3eed484a 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -8,15 +8,7 @@ import javax.inject.Singleton @Suppress("UNUSED_PARAMETER") class AnalyticsHelper @Inject constructor() { - fun logEvent(name: String, vararg params: Pair) { - // do nothing - } - - fun setCurrentScreen(activity: Activity, name: String?) { - // do nothing - } - - fun popCurrentScreen(name: String?) { - // do nothing - } + fun logEvent(name: String, vararg params: Pair) = Unit + fun setCurrentScreen(activity: Activity, name: String?) = Unit + fun popCurrentScreen(name: String?) = Unit } diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index 5d33825f1..1f78931ae 100644 --- a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt +++ b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -3,26 +3,38 @@ package io.github.wulkanowy.utils import android.app.Activity import android.content.Context import android.os.Bundle +import com.huawei.agconnect.crash.AGConnectCrash import com.huawei.hms.analytics.HiAnalytics import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.repositories.PreferencesRepository import javax.inject.Inject import javax.inject.Singleton @Singleton class AnalyticsHelper @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, + preferencesRepository: PreferencesRepository, + appInfo: AppInfo, ) { private val analytics by lazy { HiAnalytics.getInstance(context) } + private val connectCrash by lazy { AGConnectCrash.getInstance() } + + init { + if (!appInfo.isDebug) { + connectCrash.setUserId(preferencesRepository.installationId) + } + } + fun logEvent(name: String, vararg params: Pair) { Bundle().apply { - params.forEach { - if (it.second == null) return@forEach - when (it.second) { - is String, is String? -> putString(it.first, it.second as String) - is Int, is Int? -> putInt(it.first, it.second as Int) - is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) + params.forEach { (key, value) -> + if (value == null) return@forEach + when (value) { + is String -> putString(key, value) + is Int -> putInt(key, value) + is Boolean -> putBoolean(key, value) } } analytics.onEvent(name, this) diff --git a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt index b0c34f413..377e83666 100644 --- a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt +++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -3,6 +3,7 @@ package io.github.wulkanowy.utils import android.util.Log import com.huawei.agconnect.crash.AGConnectCrash import fr.bipi.tressence.base.FormatterPriorityTree +import fr.bipi.tressence.common.StackTraceRecorder class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) { @@ -22,16 +23,10 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter) override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (skipLog(priority, tag, message, t)) return - // Disabled due to a bug in the Huawei library - - /*connectCrash.setCustomKey("priority", priority) - connectCrash.setCustomKey("tag", tag.orEmpty()) - connectCrash.setCustomKey("message", message) - if (t != null) { connectCrash.recordException(t) } else { connectCrash.recordException(StackTraceRecorder(format(priority, tag, message))) - }*/ + } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt index 486538e0c..afc262868 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt @@ -10,17 +10,16 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.enums.* import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.ui.modules.grade.GradeAverageMode -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.time.Instant +import java.util.* import javax.inject.Inject import javax.inject.Singleton -@OptIn(ExperimentalCoroutinesApi::class) @Singleton class PreferencesRepository @Inject constructor( @ApplicationContext val context: Context, @@ -316,6 +315,16 @@ class PreferencesRepository @Inject constructor( putBoolean(context.getString(R.string.pref_key_ads_enabled), value) } + var installationId: String + get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty() + private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) } + + init { + if (installationId.isEmpty()) { + installationId = UUID.randomUUID().toString() + } + } + private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default) private fun getLong(id: String, default: Int) = @@ -331,23 +340,14 @@ class PreferencesRepository @Inject constructor( private fun getBoolean(id: String, default: Int) = sharedPref.getBoolean(id, context.resources.getBoolean(default)) - private fun getBoolean(id: Int, default: Boolean) = - sharedPref.getBoolean(context.getString(id), default) - private companion object { - + private const val PREF_KEY_INSTALLATION_ID = "installation_id" private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position" - private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count" - private const val PREF_KEY_IN_APP_REVIEW_DATE = "in_app_review_date" - private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done" - private const val PREF_KEY_APP_SUPPORT_SHOWN = "app_support_shown" - private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled" - private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids" } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt index 48c003b7e..e979fa8af 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt @@ -4,7 +4,6 @@ import android.app.Dialog import android.content.ClipData import android.content.ClipboardManager import android.os.Bundle -import android.view.LayoutInflater import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.appcompat.app.AlertDialog @@ -15,6 +14,7 @@ import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.DialogErrorBinding import io.github.wulkanowy.utils.* import javax.inject.Inject @@ -25,6 +25,9 @@ class ErrorDialog : DialogFragment() { @Inject lateinit var appInfo: AppInfo + @Inject + lateinit var preferencesRepository: PreferencesRepository + companion object { private const val ARGUMENT_KEY = "error" @@ -36,7 +39,7 @@ class ErrorDialog : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable - val binding = DialogErrorBinding.inflate(LayoutInflater.from(context)) + val binding = DialogErrorBinding.inflate(layoutInflater) binding.bindErrorDetails(error) return getAlertDialog(binding, error).apply { @@ -99,7 +102,8 @@ class ErrorDialog : DialogFragment() { R.string.about_feedback_template, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}" + "${appInfo.versionName}-${appInfo.buildFlavor}", + preferencesRepository.installationId, ) + "\n" + content, onActivityNotFound = { requireContext().openInternetBrowser( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt index 701656b55..d7f39e303 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -6,6 +6,7 @@ import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.FragmentAboutBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment @@ -30,6 +31,9 @@ class AboutFragment : BaseFragment(R.layout.fragment_about @Inject lateinit var appInfo: AppInfo + @Inject + lateinit var preferencesRepository: PreferencesRepository + override val versionRes: Triple? get() = context?.run { val buildTimestamp = @@ -185,7 +189,8 @@ class AboutFragment : BaseFragment(R.layout.fragment_about R.string.about_feedback_template, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}" + "${appInfo.versionName}-${appInfo.buildFlavor}", + preferencesRepository.installationId, ), onActivityNotFound = { requireContext().openInternetBrowser( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt index d31f5cf0f..463e192de 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -10,6 +10,7 @@ import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.FragmentLoginFormBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity @@ -32,6 +33,9 @@ class LoginFormFragment : BaseFragment(R.layout.fragme @Inject lateinit var appInfo: AppInfo + @Inject + lateinit var preferencesRepository: PreferencesRepository + companion object { fun newInstance() = LoginFormFragment() } @@ -260,8 +264,9 @@ class LoginFormFragment : BaseFragment(R.layout.fragme R.string.login_email_text, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - appInfo.versionName, + "${appInfo.versionName}-${appInfo.buildFlavor}", "$formHostValue/$formHostSymbol", + preferencesRepository.installationId, lastError ) ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index 6c910fe03..c42a4e9d1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -9,6 +9,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity @@ -32,6 +33,9 @@ class LoginStudentSelectFragment : @Inject lateinit var appInfo: AppInfo + @Inject + lateinit var preferencesRepository: PreferencesRepository + companion object { const val ARG_STUDENTS = "STUDENTS" @@ -111,10 +115,12 @@ class LoginStudentSelectFragment : email = "wulkanowyinc@gmail.com", subject = requireContext().getString(R.string.login_email_subject), body = requireContext().getString( - R.string.login_email_text, appInfo.systemModel, + R.string.login_email_text, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - appInfo.versionName, + "${appInfo.versionName}-${appInfo.buildFlavor}", "Select users to log in", + preferencesRepository.installationId, lastError ) ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt index 58bdf6cef..36c40d156 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt @@ -13,6 +13,7 @@ import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity @@ -34,6 +35,9 @@ class LoginSymbolFragment : @Inject lateinit var appInfo: AppInfo + @Inject + lateinit var preferencesRepository: PreferencesRepository + companion object { private const val SAVED_LOGIN_DATA = "LOGIN_DATA" @@ -159,8 +163,9 @@ class LoginSymbolFragment : R.string.login_email_text, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - appInfo.versionName, + "${appInfo.versionName}-${appInfo.buildFlavor}", "$host/${binding.loginSymbolName.text}", + preferencesRepository.installationId, lastError ) ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e4542547d..48376d216 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -71,7 +71,7 @@ Discord Send email Zgłoszenie: Problemy z logowaniem - Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nOstatni błąd: %5$s\n\nNazwa szkoły i miejscowość: + Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nIdentyfikator instalacji: %5$s\nOstatni błąd: %6$s\n\nNazwa szkoły i miejscowość: Make sure you select the correct UONET+ register variation! I forgot my password Recover your account @@ -512,7 +512,7 @@ Visit the website and help develop the application Licenses Licenses of libraries used in the application - Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\n\nTreść zgłoszenia: + Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nIdentyfikator instalacji: %4$s\nTreść zgłoszenia: diff --git a/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index b65325790..3215fa20c 100644 --- a/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt +++ b/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -4,25 +4,37 @@ import android.app.Activity import android.content.Context import android.os.Bundle import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.crashlytics.FirebaseCrashlytics import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.repositories.PreferencesRepository import javax.inject.Inject import javax.inject.Singleton @Singleton class AnalyticsHelper @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, + preferencesRepository: PreferencesRepository, + appInfo: AppInfo, ) { private val analytics by lazy { FirebaseAnalytics.getInstance(context) } + private val crashlytics by lazy { FirebaseCrashlytics.getInstance() } + + init { + if (!appInfo.isDebug) { + crashlytics.setUserId(preferencesRepository.installationId) + } + } + fun logEvent(name: String, vararg params: Pair) { Bundle().apply { - params.forEach { - if (it.second == null) return@forEach - when (it.second) { - is String, is String? -> putString(it.first, it.second.toString()) - is Int, is Int? -> putInt(it.first, it.second as Int) - is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) + params.forEach { (key, value) -> + if (value == null) return@forEach + when (value) { + is String -> putString(key, value) + is Int -> putInt(key, value) + is Boolean -> putBoolean(key, value) } } analytics.logEvent(name, this) diff --git a/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt b/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt index 410fddf16..f980bc4bb 100644 --- a/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt +++ b/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt @@ -23,9 +23,6 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter) override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (skipLog(priority, tag, message, t)) return - crashlytics.setCustomKey("priority", priority) - crashlytics.setCustomKey("tag", tag.orEmpty()) - crashlytics.setCustomKey("message", message) if (t != null) { crashlytics.recordException(t) } else { From 7bee10d5ce0aa85becaa5a5b2a8ecd752ae0b1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 28 Oct 2022 11:08:40 +0200 Subject: [PATCH 129/164] Hide room view in timetable item if there is no room in API (#2026) --- .../github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt index d6917672a..2f0d697fc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt @@ -191,7 +191,7 @@ class TimetableAdapter @Inject constructor() : ) } else { timetableItemDescription.visibility = GONE - timetableItemRoom.visibility = VISIBLE + timetableItemRoom.isVisible = lesson.room.isNotBlank() || lesson.roomOld.isNotBlank() timetableItemGroup.isVisible = item.showGroupsInPlan && lesson.group.isNotBlank() timetableItemTeacher.visibility = VISIBLE } From 515a3973b74048daba05783609b5530b59279938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 28 Oct 2022 11:09:38 +0200 Subject: [PATCH 130/164] Use text color, font face and red dot to differentiate unread messages (#2027) --- .../modules/message/tab/MessageTabAdapter.kt | 33 ++++++++++++++----- app/src/main/res/drawable/ic_circle.xml | 10 ++++++ app/src/main/res/layout/item_message.xml | 22 +++++++++++-- 3 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 app/src/main/res/drawable/ic_circle.xml diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt index 55f03ef84..234d17eb2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt @@ -1,15 +1,18 @@ package io.github.wulkanowy.ui.modules.message.tab +import android.content.res.ColorStateList import android.graphics.Typeface import android.view.LayoutInflater import android.view.ViewGroup import android.widget.CompoundButton import androidx.core.view.isVisible +import androidx.core.widget.ImageViewCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.databinding.ItemMessageBinding import io.github.wulkanowy.databinding.ItemMessageChipsBinding +import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject @@ -85,21 +88,35 @@ class MessageTabAdapter @Inject constructor() : val message = item.message with(holder.binding) { - val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL + val normalFont = Typeface.create("sans-serif", Typeface.NORMAL) + val boldFont = Typeface.create("sans-serif-black", Typeface.NORMAL) + + val primaryColor = root.context.getThemeAttrColor(android.R.attr.textColorPrimary) + val secondaryColor = root.context.getThemeAttrColor(android.R.attr.textColorSecondary) + + val currentFont = if (message.unread) boldFont else normalFont + val currentTextColor = if (message.unread) primaryColor else secondaryColor with(messageItemAuthor) { text = message.correspondents - setTypeface(null, style) + setTextColor(currentTextColor) + typeface = currentFont } - messageItemSubject.run { + with(messageItemSubject) { text = message.subject.ifBlank { context.getString(R.string.message_no_subject) } - setTypeface(null, style) + setTextColor(currentTextColor) + typeface = currentFont } - messageItemDate.run { + with(messageItemDate) { text = message.date.toFormattedString() - setTypeface(null, style) + setTextColor(currentTextColor) + typeface = currentFont } - messageItemAttachmentIcon.isVisible = message.hasAttachments + with(messageItemAttachmentIcon) { + ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(currentTextColor)) + isVisible = message.hasAttachments + } + messageItemUnreadIndicator.isVisible = message.unread root.setOnClickListener { holder.bindingAdapterPosition.let { @@ -111,7 +128,7 @@ class MessageTabAdapter @Inject constructor() : root.setOnLongClickListener { onLongItemClickListener(item) - return@setOnLongClickListener true + true } with(messageItemCheckbox) { diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml new file mode 100644 index 000000000..d2932fe62 --- /dev/null +++ b/app/src/main/res/drawable/ic_circle.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml index c25faacc8..39fbaad01 100644 --- a/app/src/main/res/layout/item_message.xml +++ b/app/src/main/res/layout/item_message.xml @@ -30,6 +30,7 @@ android:layout_marginEnd="10dp" android:ellipsize="end" android:singleLine="true" + android:textColor="?android:textColorSecondary" android:textSize="15sp" app:layout_constraintEnd_toStartOf="@+id/messageItemDate" app:layout_constraintStart_toEndOf="@id/messageItemCheckbox" @@ -40,10 +41,13 @@ android:id="@+id/messageItemDate" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginEnd="8dp" android:gravity="end" + android:textColor="?android:textColorSecondary" android:textSize="13sp" - app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintEnd_toStartOf="@id/messageItemUnreadIndicator" app:layout_constraintTop_toTopOf="parent" + app:layout_goneMarginEnd="0dp" tools:text="@tools:sample/date/ddmmyy" /> + + From c5e2b18695c2a1f58afd407d399d927d03573dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 28 Oct 2022 11:10:05 +0200 Subject: [PATCH 131/164] Fix SSL certificate out-of-date detection (#2028) --- .../java/io/github/wulkanowy/utils/ExceptionExtension.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt index 43cecd400..a4c2537ac 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt @@ -15,16 +15,17 @@ import java.net.ConnectException import java.net.SocketException import java.net.SocketTimeoutException import java.net.UnknownHostException +import java.security.cert.CertPathValidatorException import java.security.cert.CertificateExpiredException import java.security.cert.CertificateNotYetValidException import javax.net.ssl.SSLHandshakeException fun Resources.getErrorString(error: Throwable): String = when (error) { is UnknownHostException -> R.string.error_no_internet + is ConnectException, is SocketException, is SocketTimeoutException, is InterruptedIOException, - is ConnectException, is StreamResetException -> R.string.error_timeout is NotLoggedInException -> R.string.error_login_failed is PasswordChangeRequiredException -> R.string.error_password_change_required @@ -42,10 +43,10 @@ fun Resources.getErrorString(error: Throwable): String = when (error) { fun Throwable.isShouldBeReported(): Boolean = when (this) { is UnknownHostException, + is ConnectException, is SocketException, is SocketTimeoutException, is InterruptedIOException, - is ConnectException, is StreamResetException, is ServiceUnavailableException, is FeatureDisabledException, @@ -70,5 +71,6 @@ private fun Throwable?.isCausedByCertificateNotValidNow(): Boolean { private fun Throwable?.isCertificateNotValidNow(): Boolean { val isNotYetValid = this is CertificateNotYetValidException val isExpired = this is CertificateExpiredException - return isNotYetValid || isExpired + val isInvalidPath = this is CertPathValidatorException + return isNotYetValid || isExpired || isInvalidPath } From ffd5addadb3f1250f438ad3cea2e2889649e31b8 Mon Sep 17 00:00:00 2001 From: Damian Czupryn <60961958+Daxxxis@users.noreply.github.com> Date: Fri, 28 Oct 2022 11:10:35 +0200 Subject: [PATCH 132/164] Add Crowdin badges to README (#2025) --- README.cs.md | 1 + README.de.md | 1 + README.en.md | 1 + README.md | 1 + README.sk.md | 1 + 5 files changed, 5 insertions(+) diff --git a/README.cs.md b/README.cs.md index 5c1e5ea71..0d6da9c36 100644 --- a/README.cs.md +++ b/README.cs.md @@ -13,6 +13,7 @@ [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) +[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče diff --git a/README.de.md b/README.de.md index e03557a06..9c25bad65 100644 --- a/README.de.md +++ b/README.de.md @@ -13,6 +13,7 @@ [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) +[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern diff --git a/README.en.md b/README.en.md index 1ac2a6721..708d30bdb 100644 --- a/README.en.md +++ b/README.en.md @@ -13,6 +13,7 @@ [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) +[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Unofficial android VULCAN UONET+ register client for both students and their parents diff --git a/README.md b/README.md index e7c7d4c5e..86b83552a 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) +[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica diff --git a/README.sk.md b/README.sk.md index 2f3ba41dd..d318936aa 100644 --- a/README.sk.md +++ b/README.sk.md @@ -13,6 +13,7 @@ [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) +[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov From b269360ecb852bfaf9eb3b97c762e7e7c689c9e1 Mon Sep 17 00:00:00 2001 From: Damian Czupryn <60961958+Daxxxis@users.noreply.github.com> Date: Sun, 30 Oct 2022 03:00:39 +0100 Subject: [PATCH 133/164] Langs placement in README adjustments (#2029) --- README.cs.md | 10 ++-------- README.de.md | 8 +------- README.en.md | 8 +------- README.md | 8 +------- README.sk.md | 10 ++-------- 5 files changed, 7 insertions(+), 37 deletions(-) diff --git a/README.cs.md b/README.cs.md index 0d6da9c36..2b0dc12ea 100644 --- a/README.cs.md +++ b/README.cs.md @@ -1,10 +1,4 @@ -[English version of README](README.en.md) - -[Deutsche Version von README](README.de.md) - -[Polska wersja README](README.md) - -[Slovenská verzia README](README.sk.md) +Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) # Wulkanowy @@ -58,7 +52,7 @@ Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGal Můžete si také stáhnout [vývojovou verzi](https://wulkanowy.github.io/#download), která zahrnuje nové funkce připravované pro příští vydání -## Postaveno s +## Postaveno s pomocí * [Wulkanowy SDK](https://github.com/wulkanowy/sdk) diff --git a/README.de.md b/README.de.md index 9c25bad65..6df10ecd0 100644 --- a/README.de.md +++ b/README.de.md @@ -1,10 +1,4 @@ -[Polska wersja README](README.md) - -[English version of README](README.en.md) - -[Česká verze README](README.cs.md) - -[Slovenská verzia README](README.sk.md) +[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) # Wulkanowy diff --git a/README.en.md b/README.en.md index 708d30bdb..417b74de0 100644 --- a/README.en.md +++ b/README.en.md @@ -1,10 +1,4 @@ -[Polska wersja README](README.md) - -[Deutsche Version von README](README.de.md) - -[Česká verze README](README.cs.md) - -[Slovenská verzia README](README.sk.md) +[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) # Wulkanowy diff --git a/README.md b/README.md index 86b83552a..75b6cfca2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,4 @@ -[English version of README](README.en.md) - -[Deutsche Version von README](README.de.md) - -[Česká verze README](README.cs.md) - -[Slovenská verzia README](README.sk.md) +[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.md) # Wulkanowy diff --git a/README.sk.md b/README.sk.md index d318936aa..240f8835c 100644 --- a/README.sk.md +++ b/README.sk.md @@ -1,10 +1,4 @@ -[English version of README](README.en.md) - -[Deutsche Version von README](README.de.md) - -[Polska wersja README](README.md) - -[Česká verze README](README.cs.md) +[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia # Wulkanowy @@ -58,7 +52,7 @@ Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGa Môžete si tiež stiahnuť [vývojovú verziu](https://wulkanowy.github.io/#download), ktorá zahrňuje nové funkcie pripravované pre budúce vydanie -## Postavené s +## Postavené s pomocou * [Wulkanowy SDK](https://github.com/wulkanowy/sdk) From d924902dacac95123477dd6410c62430785aaa3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:49:56 +0000 Subject: [PATCH 134/164] Bump CircularImageView from 4.2.0 to 4.3.0 (#2036) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6211db2d8..d7fcfa8f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -209,7 +209,7 @@ dependencies { implementation "com.google.android.material:material:1.7.0" implementation "com.github.wulkanowy:material-chips-input:2.3.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" - implementation 'com.github.lopspower:CircularImageView:4.2.0' + implementation 'com.github.lopspower:CircularImageView:4.3.0' implementation "androidx.work:work-runtime-ktx:$work_manager" playImplementation "androidx.work:work-gcm:$work_manager" From 4113bd9b538428cb6465cc3380b4b4d2f25ba9b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:50:17 +0000 Subject: [PATCH 135/164] Bump agconnect-crash from 1.7.2.300 to 1.7.3.300 (#2035) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index d7fcfa8f7..c250e7ead 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -250,7 +250,7 @@ dependencies { playImplementation 'com.google.android.gms:play-services-ads:21.3.0' hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300' - hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.2.300' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" From 86fe2b61cb9c158e53682974c11484343141e003 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:50:37 +0000 Subject: [PATCH 136/164] Bump agcp from 1.7.2.300 to 1.7.3.300 (#2034) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5131796bb..fa66d9304 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.3.1' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.14' - classpath 'com.huawei.agconnect:agcp:1.7.2.300' + classpath 'com.huawei.agconnect:agcp:1.7.3.300' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4" From 02cd4e4e06850b95a845b45db8c2b7f0194061ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:50:59 +0000 Subject: [PATCH 137/164] Bump sonarqube-gradle-plugin from 3.4.0.2513 to 3.5.0.2730 (#2033) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fa66d9304..3773dbc38 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4" - classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513" + classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.5.0.2730" classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries" } From 21fe20924643fa4c6db0aa05b2bbee30d48ba071 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:51:28 +0000 Subject: [PATCH 138/164] Bump firebase-bom from 31.0.1 to 31.0.2 (#2032) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c250e7ead..a8f68884e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -241,7 +241,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:31.0.1') + playImplementation platform('com.google.firebase:firebase-bom:31.0.2') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From 62b7d42a73d0f422b02995f049449966020f0df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Tue, 1 Nov 2022 20:57:05 +0100 Subject: [PATCH 139/164] New Crowdin updates (#1966) --- app/src/main/res/values-de/strings.xml | 14 +- .../res/values-es-rES/preferences_values.xml | 65 ++ app/src/main/res/values-es-rES/strings.xml | 715 ++++++++++++++++++ app/src/main/res/values-uk/strings.xml | 38 +- 4 files changed, 806 insertions(+), 26 deletions(-) create mode 100644 app/src/main/res/values-es-rES/preferences_values.xml create mode 100644 app/src/main/res/values-es-rES/strings.xml diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 4dfeee4f6..7b544258a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -632,10 +632,10 @@ Antwort mit Nachrichtenhistorie Arithmetisches Mittel anzeigen, wenn keine Gewichte angegeben sind Unterstützung - Privacy Policy - Agreements - Consent to processing of data related to ads - Show ads in app + Datenschutz-Bestimmungen + Vereinbarungen + Zustimmung zur Verarbeitung von Daten im Zusammenhang mit Anzeigen + Anzeigen in der App anzeigen Einzelanzeige ansehen, um Projekt zu unterstützen Einwilligung in die Datenverarbeitung Um eine Anzeige zu sehen, müssen Sie mit den Datenverarbeitungsbedingungen unserer Datenschutzerklärung einverstanden sein @@ -647,9 +647,9 @@ Sie können Ihre Wahl jederzeit in den App-Einstellungen ändern. Wir verwenden Ihre Daten, um auf Sie zugeschnittene Anzeigen anzuzeigen oder unter Verwendung weniger Ihrer Daten nicht personalisierte Werbung anzuzeigen. Bitte lesen Sie unsere Datenschutzerklärung für Details Personalisierte Werbung keine personalisierte Werbung - I am over 18 years old - Yes, personalized ads - Yes, non-personalized ads + Ich bin über 18 Jahre alt + Ja, personalisierte Werbung + Ja, nicht personalisierte Werbung Erweitert Aussehen & Verhalten Benachrichtigungen diff --git a/app/src/main/res/values-es-rES/preferences_values.xml b/app/src/main/res/values-es-rES/preferences_values.xml new file mode 100644 index 000000000..ac2b6e9e5 --- /dev/null +++ b/app/src/main/res/values-es-rES/preferences_values.xml @@ -0,0 +1,65 @@ + + + + Light + Dark + Black (AMOLED) + + + System language + Polski + English + Pусский + Українська + Deutsch + Čeština + Slovenčina + + + 15 minutes + 30 minutes + 1 hour + 2 hours + 6 hours + 12 hours + 24 hours + + + 0,00 + 0,25 + 0,33 + 0,5 + 0,75 + + + Alphabetically + By date + By average + + + Dzienniczek+ + Wulkanowy + Grade colors in register + + + Up to 1 at once + Always expanded + Unlimited expansions + + + Average of grades only from selected semester + Average of averages from both semesters + Average of grades from the whole year + + + Lucky number + Unread messages + Attendance + Lessons + Grades + Homework + School announcements + Exams + Conferences + + diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml new file mode 100644 index 000000000..aecc720b7 --- /dev/null +++ b/app/src/main/res/values-es-rES/strings.xml @@ -0,0 +1,715 @@ + + + + Login + Wulkanowy + Grades + Attendance + Exams + Timetable + Settings + More + About + Log viewer + Debug + Notification debug + Contributors + Licenses + Messages + New message + New homework + Notes and achievements + Homework + Accounts manager + Select account + Account details + Student info + Dashboard + Notifications center + + Semester %1$d, %2$d/%3$d + + Sign in with the student or parent account + Enter the symbol from the register page for account: <b>%1$s</b> + Username + Email + Login, PESEL or e-mail + Password + UONET+ register variant + Mobile API + Scraper + Hybrid + Token + PIN + Symbol + Sign in + Password too short + Login details are incorrect + %1$s. Make sure the correct UONET+ register variation is selected below + Invalid PIN + Invalid token + Token expired + Invalid email + Use the assigned login instead of email + Use the assigned login or email in @%1$s + Invalid symbol + Student not found. Validate the symbol and the chosen variation of the UONET+ register + Selected student is already logged in + The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nMake sure that you have set the appropriate register variant in the UONET+ register variant field on the previous screen + Select students to log in to the application + Other options + In this mode, a lucky number does not work, a class grade stats, summary of attendance, excuse for absence, completed lessons, school information and preview of the list of registered devices + This mode displays the same data as it appears on the register website + The combination of the best features of the other two modes. It works faster than scraper and provides features not available in the Mobile API mode. It is in the experimental phase + Privacy policy + Trouble signing in? Contact us! + Email + Discord + Send email + Make sure you select the correct UONET+ register variation! + I forgot my password + Recover your account + Recover + Student is already signed in + Standard + + Account manager + Log in + Session expired + Session expired, log in again + Application support + Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time + Enable ads + + Grade + Semester %d + Change semester + No grades + Weight + Weight: %s + Comment + Number of new ratings: %1$d + Average: %1$.2f + Points: %s + No average + Total points + Final grade + Predicted grade + Calculated average + How does Calculated Average work? + The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages + How does the Final Average work? + The Final Average is the arithmetic average calculated from all currently available final grades in the given semester.\n\nThe calculation scheme consists of the following steps:\n1. Summing up the final grades given by teachers\n2. Divide by the number of subjects that have already been graded + Final average + from %1$d of %2$d subjects + Summary + Class + Mark as read + Partial + Semester + Points + Legend + Class average: %1$s + Your average: %1$s + Your grade: %1$s + Class + Student + + %d grade + %d grades + + + New grade + New grades + + + New predicted grade + New predicted grades + + + New final grade + New final grades + + + You received %1$d grade + You received %1$d grades + + + You received %1$d predicted grade + You received %1$d predicted grades + + + You received %1$d final grade + You received %1$d final grades + + + Lesson + Room + Group + Hours + Changes + No lessons this day + %s min + %s sec + %1$s left + in %1$s + Finished + Now: %s + Next: %s + Later: %s + %1$s lesson %2$d - %3$s + Change of room from %1$s to %2$s + Change of teacher from %1$s to %2$s + Change of subject from %1$s to %2$s + + Timetable change + Timetable changes + + + %1$s - %2$d change in timetable + %1$s - %2$d changes in timetable + + + %1$d change in timetable + %1$d changes in timetable + + + %d change + %d changes + + + Completed lessons + Show completed lessons + No info about completed lessons + Topic + Absence + Resources + + Additional lessons + Show additional lessons + No info about additional lessons + New lesson + New additional lesson + Additional lesson added successfully + Additional lesson deleted successfully + Repeat weekly + Delete additional lesson + Just this lesson + All in the series + Start time + End time + End time must be greater than start time + + Attendance summary + Absent for school reasons + Excused absence + Unexcused absence + Exemption + Excused lateness + Unexcused lateness + Present + Deleted + Unknown + Number of lesson + No entries + Absence reason (optional) + Send + Absence excuse request sent successfully! + You must select at least one absence! + Excuse + + New attendance + New attendance + + + %1$d new attendance + %1$d attendance + + + %d attendance + %d attendance + + + Total + + No exams this week + Type + Entry date + + New exam + New exams + + + %d new exam + %d new exams + + + %d exam + %d exams + + + Inbox + Sent + Trash + (no subject) + No messages + From: + To: + Date: %1$s + Reply + Forward + Select all + Unselect all + Move to trash + Delete permanently + Message deleted successfully + student + parent + guardian + employee + Share + Print + Subject + Content + Message sent successfully + Message does not exist + You need to choose at least 1 recipient + The message content must be at least 3 characters + Only unread + Only with attachments + Read: %s + + %1$d message + %1$d messages + + + New message + New messages + + Do you want to restore draft message? + Do you want to restore draft message with recipients: %s? + + You received %1$d message + You received %1$d messages + + + %1$d selected + %1$d selected + + Messages deleted + + No info about notes + Points + + %d note + %d notes + + + New note + New notes + + + You received %1$d note + You received %1$d notes + + + + %d praise + %d praises + + + New praise + New praises + + + You received %1$d praise + You received %1$d praises + + + + %d neutral note + %d neutral notes + + + New neutral note + New neutral notes + + + You received %1$d neutral note + You received %1$d neutral notes + + + No info about homework + Mark as done + Mark as undone + Add homework + Homework added successfully + Homework deleted successfully + Attachments + + New homework + New homework + + + You received %d new homework + You received %d new homework + + + %d homework + %d homework + + + Lucky number + Today\'s lucky number is + No info about the lucky number + Lucky number for today + Today\'s lucky number is: %s + Show history + + Lucky number history + No info about lucky numbers + + Mobile devices + No devices + Deregister + Device removed + QR code + Token + Symbol + PIN + + School and teachers + + School + No info about school + School name + School address + Telephone + Name of headmaster + Name of pedagogue + Show on map + Call + + Teachers + No info about teachers + No subject + + Conferences + No info about conferences + + %d conference + %d conferences + + + New conference + New conferences + + + You have %1$d new conference + You have %1$d new conferences + + Present at conference + Agenda + + School announcements + No school announcements + + %d school announcement + %d school announcements + + + New school announcement + New school announcements + + + You have %1$d new school announcement + You have %1$d new school announcements + + + Add account + Logout + Do you want to log out this student? + Student logout + Student account + Parent account + Edit data + Accounts manager + Select student + Family + Contact + Residence details + Personal information + + App version + Contributors + List of Wulkanowy developers + Report a bug + Send a bug report via e-mail + FAQ + Read Frequently Asked Questions + Discord server + Join the Wulkanowy community + Facebook fanpage + Twitter page + Follow us on twitter + Like our facebook fanpage + Privacy policy + Rules for collecting personal data + System settings + Open system settings + Homepage + Visit the website and help develop the application + Licenses + Licenses of libraries used in the application + + License + + Avatar + See more on GitHub + + No info about student or student family + Name + Second name + Gender + Polish citizenship + Family name + Mother\'s and father\'s names + Phone + Cellphone + E-mail + Address of residence + Address of registration + Correspondence address + Surname and first name + Degree of kinship + Address + Phones + Male + Female + Last name + Guardian + + Nick + Add nick + Choose avatar color + + Share logs + Refresh + + Lessons + (Tomorrow) + (Today and tomorrow) + In a moment: + Soon: + First: + Now: + End of lessons + Next: + Later: + + %1$d more lesson + %1$d more lessons + + until %1$s + No upcoming lessons + An error occurred while loading the lessons + Homework + No homework to do + An error occurred while loading the homework + + %1$d more homework + %1$d more homework + + due %1$s + Last grades + No new grades + An error occurred while loading the grades + School announcements + No current announcements + An error occurred while loading the announcements + + %1$d more announcement + %1$d more announcements + + Exams + No upcoming exams + An error occurred while loading the exams + + %1$d more exam + %1$d more exams + + Conferences + No upcoming conferences + An error occurred while loading the conferences + + %1$d more conference + %1$d more conferences + + An error occurred while loading data + None + + Check for updates + Before reporting a bug, check first if an update with the bug fix is available + + Content + Retry + Description + No description + Teacher + Date + Entry date + Color + Details + Category + Close + No data + Subject + Prev + Next + Search + Search… + Yes + No + Save + Title + Add + Copied + Undo + Change + Add to calendar + + No lessons + Choose theme + Light + Dark + System Theme + + App + Default view + Calculated average options + Force average calculation by app + Show presence + Theme + Grades expanding + Mark current lesson + Show groups next to subjects + Show chart list in class grades + Show subjects without grades + Grades color scheme + Subjects sorting + Language + Notifications + Other + Show notifications + Show upcoming lesson notifications + Make upcoming lesson notification persistent + Turn off when notification is not showing in your watch/band + Open system notification settings + Fix synchronization & notifications issues + Your device may have data synchronization issues and with notifications.\n\nTo fix them, you need to add Wulkanowy to the autostart and turn off battery optimization/saving in the phone settings. + Show debug notifications + Synchronization is disabled + Official app notifications + Capture official app notifications + Remove official app notifications after capture + Capture notifications + With this feature you can gain a substitute of push notifications like in the official app. All you need to do is allow Wulkanowy to receive all notifications in your system settings.\n\nHow it works?\nWhen you get a notification in Dziennik VULCAN, Wulkanowy will be notified (that\'s what these extra permissions are for) and will trigger a sync so that can send its own notification.\n\nFOR ADVANCED USERS ONLY + Upcoming lesson notifications + You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature. + Go to settings + Synchronization + Automatic update + Suspended on holidays + Updates interval + Wi-Fi only + Sync now + Synced! + Sync failed + Sync in progress + Last full sync: %s + Value of the plus + Value of the minus + Reply with message history + Show arithmetic average when no weights provided + Support + Privacy Policy + Agreements + Consent to processing of data related to ads + Show ads in app + Watch single ad to support project + Consent to data processing + To view an advertisement you must agree to the data processing terms of our Privacy Policy + Agree + Privacy policy + Ad is loading + Thank you for your support, come back later for more ads + Can we use your data to display ads? + You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details + Personalized ads + Non-personalized ads + I am over 18 years old + Yes, personalized ads + Yes, non-personalized ads + Advanced + Appearance & Behavior + Notifications + Synchronization + Advertisements + Grades + Dashboard + Tiles visibility + Attendance + Timetable + Grades + Calculated average + Messages + Appearance & Behavior + Languages, themes, subjects sorting + App notifications, fix problems + Notifications + Synchronization + Automatic update, synchronization interval + Plus and minus values, average calculation + Advanced + App version, contributors, social portals + Displaying advertisements, project support + + New grades + New homework + New conferences + New exams + Lucky number + New messages + New notes + New school announcements + Push notifications + Upcoming lessons + Debug + Timetable change + New attendance + + Black + Red + Blue + Green + Purple + No color + + Download of updates has started… + An update has just been downloaded. + Restart + Update failed! Wulkanowy may not function properly. Consider updating + + No internet connection + An error occurred. Check your device clock + Connection to register failed. Servers can be overloaded. Please try again later + Loading data failed. Please try again later + Register password change required + Maintenance underway UONET + register. Try again later + Unknown UONET + register error. Try again later + Unknown application error. Please try again later + An unexpected error occurred + Feature disabled by your school + Feature not available. Login in a mode other than Mobile API + This field is required + diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index f90e78e7d..2c104ecbf 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -77,9 +77,9 @@ Увійти Минув термін дії сесії Минув термін дії сесії, авторизуйтеся знову - Application support - Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time - Enable ads + Підтримка додатків + Вам подобається цей додаток? Підтримуйте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете будь-коли вимкнути + Увімкнути рекламу Оцінка %d семестр @@ -97,7 +97,7 @@ Передбачувана оцінка Розрахована середня оцінка Як працює \"Розрахована середня оцінка\"? - The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages + Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів.Це дозволяє дізнатися приблизну кінцеву середню оцінку.Вона розраховується способом, обраним користувачем у налаштуваннях програми.Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку.Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки.Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх Як працює \"Підсумкова середня оцінка\"? Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \n\nСхема обчислення складається з таких кроків:\n1. Сумування підсумкових оцінок, виставленних викладачами\n2. Ділення на кількість предметів, з яких виставлені ці оцінки Підсумкова середня оцінка @@ -297,10 +297,10 @@ Перемістити до кошика Видалити назавжди Лист було успішно видалено - student - parent - guardian - employee + студент + батькові + опікун + працівник Поділитись Друк Тема @@ -720,10 +720,10 @@ Відповісти з історією повідомлень Вилічити середню аритметичну, якщо оцінка немає вартості Підтримка - Privacy Policy - Agreements - Consent to processing of data related to ads - Show ads in app + Політика конфіденційності + Угоди + Згода на обробку даних, пов’язаних з оголошеннями + Показувати рекламу в додатку Подивіться одну рекламу для підтримки проєкту Згода в обробці даних Щоб переглянути рекламу, ви повинні погодитися з умовами обробки даних нашої Політики конфіденційності @@ -731,13 +731,13 @@ Політика конфіденційності Реклама завантажується Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості реклам - Can we use your data to display ads? - You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details - Personalized ads - Non-personalized ads - I am over 18 years old - Yes, personalized ads - Yes, non-personalized ads + Чи можемо ми використовувати ваші дані для відбивання реклами? + Ви можете змінити свій вибір в будь-який час в налаштуваннях додатку. Ми можемо використовувати ваші дані для відбивання оголошень, адаптованої до вас або, використовуючи менше ваших даних, відбивати неперсоналізовані оголошення. Перегляньте нашу Політику конфіденційності для деталей + Персоналізовані оголошення + Неперсоналізована реклама + Мені більше 18 років + Так, персоналізована реклама + Так, неперсоналізована реклама Додатково Вигляд та поведінка Сповіщення From 50b6d380b6e7c9c5537ddcce698e9b8c00c9db0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Wed, 2 Nov 2022 16:44:05 +0100 Subject: [PATCH 140/164] Fix unexpected error in support ad when no internet (#2030) --- .../ui/modules/settings/ads/AdsPresenter.kt | 21 ++++++++++++----- .../io/github/wulkanowy/utils/AdsHelper.kt | 23 +++++++++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt index 772d616d7..28c98e3c3 100644 --- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt +++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt @@ -31,13 +31,22 @@ class AdsPresenter @Inject constructor( view?.showLoadingSupportAd(true) presenterScope.launch { runCatching { adsHelper.getSupportAd() } - .onFailure(errorHandler::dispatch) - .onSuccess { it?.let { view?.showAd(it) } } + .onFailure { + errorHandler.dispatch(it) - view?.run { - showLoadingSupportAd(false) - showWatchAdOncePerVisit(true) - } + view?.run { + showLoadingSupportAd(false) + showWatchAdOncePerVisit(false) + } + } + .onSuccess { + it?.let { view?.showAd(it) } + + view?.run { + showLoadingSupportAd(false) + showWatchAdOncePerVisit(true) + } + } } } diff --git a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt index c536e2218..d5f65b46d 100644 --- a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt +++ b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt @@ -1,8 +1,12 @@ package io.github.wulkanowy.utils import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build import android.os.Bundle import android.view.View +import androidx.core.content.getSystemService import com.google.ads.mediation.admob.AdMobAdapter import com.google.android.gms.ads.* import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd @@ -10,6 +14,7 @@ import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoa import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.BuildConfig import io.github.wulkanowy.data.repositories.PreferencesRepository +import java.net.UnknownHostException import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException @@ -28,6 +33,10 @@ class AdsHelper @Inject constructor( } suspend fun getSupportAd(): RewardedInterstitialAd? { + if (!context.isInternetConnected()) { + throw UnknownHostException() + } + val extra = Bundle().apply { putString("npa", "1") } val adRequest = AdRequest.Builder() .apply { @@ -84,4 +93,18 @@ class AdsHelper @Inject constructor( } } +@Suppress("DEPRECATION") +private fun Context.isInternetConnected(): Boolean { + val connectivityManager = getSystemService() ?: return false + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val currentNetwork = connectivityManager.activeNetwork + val networkCapabilities = connectivityManager.getNetworkCapabilities(currentNetwork) + + networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) == true + } else { + connectivityManager.activeNetworkInfo?.isConnected == true + } +} + data class AdBanner(val view: View) From 1257dc63d3c081ec75dffebfebf2fdc14dacd18f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 18:21:17 +0000 Subject: [PATCH 141/164] Bump runner from 1.4.0 to 1.5.1 (#2047) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index a8f68884e..7142d92fb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -264,7 +264,7 @@ dependencies { testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testImplementation 'org.robolectric:robolectric:4.9' - testImplementation "androidx.test:runner:1.4.0" + testImplementation "androidx.test:runner:1.5.1" testImplementation "androidx.test.ext:junit:1.1.3" testImplementation "androidx.test:core:1.4.0" testImplementation "androidx.room:room-testing:$room" From 66ff14f719bf021b83b43e09e5bca726a843d088 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 18:21:35 +0000 Subject: [PATCH 142/164] Bump agcp from 1.7.3.300 to 1.7.3.301 (#2046) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3773dbc38..ccce1c433 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.3.1' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.14' - classpath 'com.huawei.agconnect:agcp:1.7.3.300' + classpath 'com.huawei.agconnect:agcp:1.7.3.301' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' classpath "com.github.triplet.gradle:play-publisher:3.6.0" classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4" From fded5007c14ecbec95680e5b9c5960cab3e77927 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 18:22:25 +0000 Subject: [PATCH 143/164] Bump firebase-bom from 31.0.2 to 31.0.3 (#2041) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7142d92fb..40f571714 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -241,7 +241,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:31.0.2') + playImplementation platform('com.google.firebase:firebase-bom:31.0.3') playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' From 885319a8854f22cfa91dd097db8492599d09fd25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 18:36:20 +0000 Subject: [PATCH 144/164] Bump hilt_version from 2.44 to 2.44.1 (#2044) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ccce1c433..6f3ac4d63 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { kotlin_version = '1.7.20' about_libraries = '10.5.1' - hilt_version = "2.44" + hilt_version = "2.44.1" } repositories { mavenCentral() From d6385e8cdd06b3636568b32aab5c0064e8bbd8cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 20:31:02 +0000 Subject: [PATCH 145/164] Bump core from 1.4.0 to 1.5.0 (#2045) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 40f571714..1edcc753e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -266,7 +266,7 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.9' testImplementation "androidx.test:runner:1.5.1" testImplementation "androidx.test.ext:junit:1.1.3" - testImplementation "androidx.test:core:1.4.0" + testImplementation "androidx.test:core:1.5.0" testImplementation "androidx.room:room-testing:$room" testImplementation "com.google.dagger:hilt-android-testing:$hilt_version" kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version" From b8296ac02f94e17e972c74980bcf480352abaef6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 20:31:26 +0000 Subject: [PATCH 146/164] Bump kotlin_version from 1.7.20 to 1.7.21 (#2042) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6f3ac4d63..e8e1052b6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - kotlin_version = '1.7.20' + kotlin_version = '1.7.21' about_libraries = '10.5.1' hilt_version = "2.44.1" } From 4d49e956b881125354b86228a12c50e49ea6fd8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 20:48:12 +0000 Subject: [PATCH 147/164] Bump junit from 1.1.3 to 1.1.4 (#2043) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1edcc753e..11f055ea8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -265,7 +265,7 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.9' testImplementation "androidx.test:runner:1.5.1" - testImplementation "androidx.test.ext:junit:1.1.3" + testImplementation "androidx.test.ext:junit:1.1.4" testImplementation "androidx.test:core:1.5.0" testImplementation "androidx.room:room-testing:$room" testImplementation "com.google.dagger:hilt-android-testing:$hilt_version" From db4f172fb83bd4ca4a3f7219ae5e6f40f87d5727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 16 Nov 2022 12:54:55 +0100 Subject: [PATCH 148/164] Fix unread status in sent messages (#2048) --- app/build.gradle | 2 +- .../52.json | 2421 +++++++++++++++++ .../github/wulkanowy/data/db/AppDatabase.kt | 3 +- .../wulkanowy/data/db/entities/Message.kt | 6 + .../wulkanowy/data/mappers/MessageMapper.kt | 4 +- .../debug/notification/mock/message.kt | 2 + .../message/preview/MessagePreviewAdapter.kt | 16 +- app/src/main/res/values/strings.xml | 1 + .../repositories/MessageRepositoryTest.kt | 6 +- 9 files changed, 2451 insertions(+), 10 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json diff --git a/app/build.gradle b/app/build.gradle index 11f055ea8..99672f1f9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:2840d9d6d0" + implementation "io.github.wulkanowy:sdk:701016eda2" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json new file mode 100644 index 000000000..129d1917b --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json @@ -0,0 +1,2421 @@ +{ + "formatVersion": 1, + "database": { + "version": 52, + "identityHash": "8742176f26afcc81279d4a073dca2949", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "kindergartenDiaryId", + "columnName": "kindergarten_diary_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "kindergarten_diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", + "fields": [ + { + "fieldPath": "messageGlobalKey", + "columnName": "message_global_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mailboxKey", + "columnName": "mailbox_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "correspondents", + "columnName": "correspondents", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recipients", + "columnName": "recipients", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageGlobalKey", + "columnName": "message_global_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Mailboxes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `userLoginId` INTEGER NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", + "fields": [ + { + "fieldPath": "globalKey", + "columnName": "globalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "fullName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "userLoginId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "studentName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolNameShort", + "columnName": "schoolNameShort", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "globalKey" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "mailboxGlobalKey", + "columnName": "mailboxGlobalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentMailboxGlobalKey", + "columnName": "studentMailboxGlobalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "fullName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "schoolShortName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "destination", + "columnName": "destination", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8742176f26afcc81279d4a073dca2949')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 15b38805b..e61ffe844 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -47,6 +47,7 @@ import javax.inject.Singleton AutoMigration(from = 44, to = 45), AutoMigration(from = 46, to = 47), AutoMigration(from = 47, to = 48), + AutoMigration(from = 51, to = 52), ], version = AppDatabase.VERSION_SCHEMA, exportSchema = true @@ -55,7 +56,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 51 + const val VERSION_SCHEMA = 52 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt index 77874e03d..323b00bec 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -29,6 +29,12 @@ data class Message( var unread: Boolean, + @ColumnInfo(name = "read_by") + val readBy: Int?, + + @ColumnInfo(name = "unread_by") + val unreadBy: Int?, + @ColumnInfo(name = "has_attachments") val hasAttachments: Boolean ) : Serializable { diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt index 2e7967f0e..c329607f4 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -16,7 +16,9 @@ fun List.mapToEntities(mailbox: Mailbox) = map { date = it.dateZoned.toInstant(), folderId = it.folderId, unread = it.unread, - hasAttachments = it.hasAttachments + unreadBy = it.unreadBy, + readBy = it.readBy, + hasAttachments = it.hasAttachments, ).apply { content = it.content.orEmpty() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt index 6ff26162b..e31bd84a9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt @@ -22,6 +22,8 @@ private fun generateMessage(sender: String, subject: String) = Message( date = Instant.now(), folderId = 0, unread = true, + readBy = 2, + unreadBy = 2, hasAttachments = false, messageGlobalKey = "", correspondents = sender, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt index 3c1c53d39..d3c6b95c7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.message.preview -import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -74,15 +73,20 @@ class MessagePreviewAdapter @Inject constructor() : } } - @SuppressLint("SetTextI18n") private fun bindMessage(holder: MessageViewHolder, message: Message) { val context = holder.binding.root.context + val recipientCount = (message.unreadBy ?: 0) + (message.readBy ?: 0) + val isReceived = message.unreadBy == null - val readTextValue = when { - !message.unread -> R.string.all_yes - else -> R.string.all_no + val readText = when { + recipientCount > 1 -> { + context.getString(R.string.message_read_by, message.readBy, recipientCount) + } + message.readBy == 1 || (isReceived && !message.unread) -> { + context.getString(R.string.message_read, context.getString(R.string.all_yes)) + } + else -> context.getString(R.string.message_read, context.getString(R.string.all_no)) } - val readText = context.getString(R.string.message_read, context.getString(readTextValue)) with(holder.binding) { messagePreviewSubject.text = message.subject.ifBlank { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 48376d216..e65a27fcc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -304,6 +304,7 @@ Only unread Only with attachments Read: %s + Read by: %1$d of %2$d people %1$d message %1$d messages diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt index 4efc9c60a..9fc83a23b 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt @@ -193,7 +193,9 @@ class MessageRepositoryTest { date = Instant.EPOCH, folderId = 1, unread = unread, - hasAttachments = false + readBy = 1, + unreadBy = 1, + hasAttachments = false, ).apply { this.content = content } @@ -209,6 +211,8 @@ class MessageRepositoryTest { dateZoned = Instant.EPOCH.atZone(ZoneOffset.UTC), folderId = 1, unread = true, + readBy = 1, + unreadBy = 1, hasAttachments = false, ) } From 51a1097bb4af2373bcd14ac8189e66f1cb606229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 16 Nov 2022 13:46:47 +0100 Subject: [PATCH 149/164] Add mailbox chooser to messages (#2002) --- .../53.json | 2439 +++++++++++++++++ .../github/wulkanowy/data/db/AppDatabase.kt | 3 +- .../wulkanowy/data/db/dao/MailboxDao.kt | 8 +- .../wulkanowy/data/db/dao/MessagesDao.kt | 3 + .../wulkanowy/data/db/entities/Mailbox.kt | 11 +- .../wulkanowy/data/db/entities/Message.kt | 3 + .../data/db/migrations/Migration53.kt | 57 + .../wulkanowy/data/mappers/MailboxMapper.kt | 4 +- .../wulkanowy/data/mappers/MessageMapper.kt | 7 +- .../data/repositories/MailboxRepository.kt | 85 - .../data/repositories/MessageRepository.kt | 67 +- .../data/repositories/RecipientRepository.kt | 24 +- .../messages/GetMailboxByStudentUseCase.kt | 52 + .../notifications/NewMessageNotification.kt | 1 - .../services/sync/works/MessageWork.kt | 6 +- .../services/sync/works/RecipientWork.kt | 15 +- .../modules/dashboard/DashboardPresenter.kt | 3 +- .../debug/notification/mock/message.kt | 1 + .../mailboxchooser/MailboxChooserAdapter.kt | 81 + .../mailboxchooser/MailboxChooserDialog.kt | 75 + .../mailboxchooser/MailboxChooserItem.kt | 9 + .../mailboxchooser/MailboxChooserPresenter.kt | 38 + .../mailboxchooser/MailboxChooserView.kt | 13 + .../preview/MessagePreviewPresenter.kt | 6 +- .../message/send/SendMessageActivity.kt | 16 + .../message/send/SendMessagePresenter.kt | 107 +- .../modules/message/send/SendMessageView.kt | 1 + .../modules/message/tab/MessageTabAdapter.kt | 32 +- .../modules/message/tab/MessageTabDataItem.kt | 1 + .../modules/message/tab/MessageTabFragment.kt | 20 + .../message/tab/MessageTabPresenter.kt | 39 +- .../ui/modules/message/tab/MessageTabView.kt | 3 + .../io/github/wulkanowy/utils/RefreshUtils.kt | 5 +- .../main/res/layout/activity_send_message.xml | 20 +- .../res/layout/dialog_mailbox_chooser.xml | 40 + .../main/res/layout/item_mailbox_chooser.xml | 43 + .../main/res/layout/item_message_chips.xml | 23 +- app/src/main/res/values/strings.xml | 2 + .../io/github/wulkanowy/TestEnityCreator.kt | 4 +- .../repositories/MessageRepositoryTest.kt | 24 +- .../GetMailboxByStudentUseCaseTest.kt} | 53 +- 41 files changed, 3209 insertions(+), 235 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt delete mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt create mode 100644 app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt create mode 100644 app/src/main/res/layout/dialog_mailbox_chooser.xml create mode 100644 app/src/main/res/layout/item_mailbox_chooser.xml rename app/src/test/java/io/github/wulkanowy/{data/repositories/MailboxRepositoryTest.kt => domain/GetMailboxByStudentUseCaseTest.kt} (77%) diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json new file mode 100644 index 000000000..985617872 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json @@ -0,0 +1,2439 @@ +{ + "formatVersion": 1, + "database": { + "version": 53, + "identityHash": "1dc96a366125ec9f8567da87cdc9c863", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "kindergartenDiaryId", + "columnName": "kindergarten_diary_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "kindergarten_diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", + "fields": [ + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "messageGlobalKey", + "columnName": "message_global_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mailboxKey", + "columnName": "mailbox_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "correspondents", + "columnName": "correspondents", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recipients", + "columnName": "recipients", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageGlobalKey", + "columnName": "message_global_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Mailboxes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", + "fields": [ + { + "fieldPath": "globalKey", + "columnName": "globalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolId", + "columnName": "schoolId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "fullName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "studentName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolNameShort", + "columnName": "schoolNameShort", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "globalKey" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "mailboxGlobalKey", + "columnName": "mailboxGlobalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentMailboxGlobalKey", + "columnName": "studentMailboxGlobalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "fullName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "schoolShortName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "destination", + "columnName": "destination", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1dc96a366125ec9f8567da87cdc9c863')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index e61ffe844..792611a81 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -56,7 +56,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 52 + const val VERSION_SCHEMA = 53 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), @@ -106,6 +106,7 @@ abstract class AppDatabase : RoomDatabase() { Migration49(), Migration50(), Migration51(), + Migration53(), ) fun newInstance( diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt index c44ecd0c2..084192a07 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt @@ -3,12 +3,16 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao import androidx.room.Query import io.github.wulkanowy.data.db.entities.Mailbox +import kotlinx.coroutines.flow.Flow import javax.inject.Singleton @Singleton @Dao interface MailboxDao : BaseDao { - @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ") - suspend fun loadAll(userLoginId: Int): List + @Query("SELECT * FROM Mailboxes WHERE email = :email") + suspend fun loadAll(email: String): List + + @Query("SELECT * FROM Mailboxes WHERE email = :email AND symbol = :symbol AND schoolId = :schoolId") + fun loadAll(email: String, symbol: String, schoolId: String): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt index 8c730c9bc..1709f7636 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -16,4 +16,7 @@ interface MessagesDao : BaseDao { @Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC") fun loadAll(mailboxKey: String, folder: Int): Flow> + + @Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC") + fun loadAll(folder: Int, email: String): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt index 7c08e481d..e65e213dd 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt @@ -1,20 +1,27 @@ package io.github.wulkanowy.data.db.entities +import android.os.Parcelable import androidx.room.Entity import androidx.room.PrimaryKey +import kotlinx.parcelize.Parcelize +@Parcelize @Entity(tableName = "Mailboxes") data class Mailbox( @PrimaryKey val globalKey: String, + + val email: String, + val symbol: String, + val schoolId: String, + val fullName: String, val userName: String, - val userLoginId: Int, val studentName: String, val schoolNameShort: String, val type: MailboxType, -) +) : java.io.Serializable, Parcelable enum class MailboxType { STUDENT, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt index 323b00bec..d1356b33d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -9,6 +9,9 @@ import java.time.Instant @Entity(tableName = "Messages") data class Message( + @ColumnInfo(name = "email") + val email: String, + @ColumnInfo(name = "message_global_key") val messageGlobalKey: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt new file mode 100644 index 000000000..12624a51a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt @@ -0,0 +1,57 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration53 : Migration(52, 53) { + + override fun migrate(database: SupportSQLiteDatabase) { + createMailboxTable(database) + recreateMessagesTable(database) + } + + private fun createMailboxTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Mailboxes") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `Mailboxes` ( + `globalKey` TEXT NOT NULL, + `email` TEXT NOT NULL, + `symbol` TEXT NOT NULL, + `schoolId` TEXT NOT NULL, + `fullName` TEXT NOT NULL, + `userName` TEXT NOT NULL, + `studentName` TEXT NOT NULL, + `schoolNameShort` TEXT NOT NULL, + `type` TEXT NOT NULL, + PRIMARY KEY(`globalKey`) + )""".trimIndent() + ) + } + + private fun recreateMessagesTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `Messages` ( + `email` TEXT NOT NULL, + `message_global_key` TEXT NOT NULL, + `mailbox_key` TEXT NOT NULL, + `message_id` INTEGER NOT NULL, + `correspondents` TEXT NOT NULL, + `subject` TEXT NOT NULL, + `date` INTEGER NOT NULL, + `folder_id` INTEGER NOT NULL, + `unread` INTEGER NOT NULL, + `read_by` INTEGER, + `unread_by` INTEGER, + `has_attachments` INTEGER NOT NULL, + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `is_notified` INTEGER NOT NULL, + `content` TEXT NOT NULL, + `sender` TEXT, + `recipients` TEXT + )""".trimIndent() + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt index 2ccca1b90..0cf547770 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt @@ -10,9 +10,11 @@ fun List.mapToEntities(student: Student) = map { globalKey = it.globalKey, fullName = it.fullName, userName = it.userName, - userLoginId = student.userLoginId, studentName = it.studentName, schoolNameShort = it.schoolNameShort, type = MailboxType.valueOf(it.type.name), + email = student.email, + symbol = student.symbol, + schoolId = student.schoolSymbol, ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt index c329607f4..8825c5744 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -6,10 +6,13 @@ import io.github.wulkanowy.sdk.pojo.Message as SdkMessage import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient -fun List.mapToEntities(mailbox: Mailbox) = map { +fun List.mapToEntities(student: Student, mailbox: Mailbox?, allMailboxes: List) = map { Message( messageGlobalKey = it.globalKey, - mailboxKey = mailbox.globalKey, + mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box -> + box.fullName == it.mailbox + }?.globalKey!!, + email = student.email, messageId = it.id, correspondents = it.correspondents, subject = it.subject.trim(), diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt deleted file mode 100644 index c571937ad..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.wulkanowy.data.repositories - -import io.github.wulkanowy.data.db.dao.MailboxDao -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.AutoRefreshHelper -import io.github.wulkanowy.utils.getRefreshKey -import io.github.wulkanowy.utils.init -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MailboxRepository @Inject constructor( - private val mailboxDao: MailboxDao, - private val sdk: Sdk, - private val refreshHelper: AutoRefreshHelper, -) { - private val cacheKey = "mailboxes" - - suspend fun refreshMailboxes(student: Student) { - val new = sdk.init(student).getMailboxes().mapToEntities(student) - val old = mailboxDao.loadAll(student.userLoginId) - - mailboxDao.deleteAll(old uniqueSubtract new) - mailboxDao.insertAll(new uniqueSubtract old) - - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) - } - - suspend fun getMailbox(student: Student): Mailbox { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - val mailboxes = mailboxDao.loadAll(student.userLoginId) - val mailbox = mailboxes.filterByStudent(student) - - return if (isExpired || mailbox == null) { - refreshMailboxes(student) - val newMailbox = mailboxDao.loadAll(student.userLoginId).filterByStudent(student) - - requireNotNull(newMailbox) { - "Mailbox for ${student.userName} - ${student.studentName} not found! Saved mailboxes: $mailboxes" - } - - newMailbox - } else mailbox - } - - private fun List.filterByStudent(student: Student): Mailbox? { - val normalizedStudentName = student.studentName.normalizeStudentName() - - return find { - it.studentName.normalizeStudentName() == normalizedStudentName - } ?: singleOrNull { - it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart() - } ?: singleOrNull { - it.studentName.getUnauthorizedVersion() == normalizedStudentName - } - } - - private fun String.normalizeStudentName(): String { - return trim().split(" ") - .filter { it.isNotBlank() } - .joinToString(" ") { part -> - part.lowercase().replaceFirstChar { it.uppercase() } - } - } - - private fun String.getFirstAndLastPart(): String { - val parts = normalizeStudentName().split(" ") - - val endParts = parts.filterIndexed { i, _ -> - i == 0 || parts.size - 1 == i - } - return endParts.joinToString(" ") - } - - private fun String.getUnauthorizedVersion(): String { - return normalizeStudentName().split(" ") - .joinToString(" ") { - it.first() + "*".repeat(it.length - 1) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index e7428762e..f8be4296d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -5,6 +5,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.dao.MailboxDao import io.github.wulkanowy.data.db.dao.MessageAttachmentDao import io.github.wulkanowy.data.db.dao.MessagesDao import io.github.wulkanowy.data.db.entities.* @@ -15,6 +16,8 @@ import io.github.wulkanowy.data.mappers.mapFromEntities import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.data.pojos.MessageDraft +import io.github.wulkanowy.data.toFirstResult +import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Folder import io.github.wulkanowy.utils.AutoRefreshHelper @@ -40,16 +43,18 @@ class MessageRepository @Inject constructor( private val refreshHelper: AutoRefreshHelper, private val sharedPrefProvider: SharedPrefProvider, private val json: Json, + private val mailboxDao: MailboxDao, + private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase, ) { private val saveFetchResultMutex = Mutex() - private val cacheKey = "message" + private val messagesCacheKey = "message" + private val mailboxCacheKey = "mailboxes" - @Suppress("UNUSED_PARAMETER") fun getMessages( student: Student, - mailbox: Mailbox, + mailbox: Mailbox?, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false, @@ -58,16 +63,20 @@ class MessageRepository @Inject constructor( isResultEmpty = { it.isEmpty() }, shouldFetch = { val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(cacheKey, student, folder) + key = getRefreshKey(messagesCacheKey, mailbox, folder) ) it.isEmpty() || forceRefresh || isExpired }, - query = { messagesDb.loadAll(mailbox.globalKey, folder.id) }, + query = { + if (mailbox == null) { + messagesDb.loadAll(folder.id, student.email) + } else messagesDb.loadAll(mailbox.globalKey, folder.id) + }, fetch = { sdk.init(student).getMessages( folder = Folder.valueOf(folder.name), - mailboxKey = mailbox.globalKey, - ).mapToEntities(mailbox) + mailboxKey = mailbox?.globalKey, + ).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email)) }, saveFetchResult = { old, new -> messagesDb.deleteAll(old uniqueSubtract new) @@ -75,7 +84,9 @@ class MessageRepository @Inject constructor( it.isNotified = !notify }) - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder)) + refreshHelper.updateLastRefreshTimestamp( + getRefreshKey(messagesCacheKey, mailbox, folder) + ) } ) @@ -90,7 +101,9 @@ class MessageRepository @Inject constructor( Timber.d("Message content in db empty: ${it.message.content.isBlank()}") it.message.unread || it.message.content.isBlank() }, - query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) }, + query = { + messagesDb.loadMessageWithAttachment(message.messageGlobalKey) + }, fetch = { sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey, markAsRead) }, @@ -113,8 +126,10 @@ class MessageRepository @Inject constructor( } ) - fun getMessagesFromDatabase(mailbox: Mailbox): Flow> { - return messagesDb.loadAll(mailbox.globalKey, RECEIVED.id) + fun getMessagesFromDatabase(student: Student, mailbox: Mailbox?): Flow> { + return if (mailbox == null) { + messagesDb.loadAll(RECEIVED.id, student.email) + } else messagesDb.loadAll(mailbox.globalKey, RECEIVED.id) } suspend fun updateMessages(messages: List) { @@ -136,7 +151,7 @@ class MessageRepository @Inject constructor( ) } - suspend fun deleteMessages(student: Student, mailbox: Mailbox, messages: List) { + suspend fun deleteMessages(student: Student, mailbox: Mailbox?, messages: List) { val firstMessage = messages.first() sdk.init(student).deleteMessages( messages = messages.map { it.messageGlobalKey }, @@ -169,6 +184,34 @@ class MessageRepository @Inject constructor( deleteMessages(student, mailbox, listOf(message)) } + suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it.isEmpty() }, + shouldFetch = { + val isExpired = refreshHelper.shouldBeRefreshed( + key = getRefreshKey(mailboxCacheKey, student), + ) + it.isEmpty() || isExpired || forceRefresh + }, + query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) }, + fetch = { sdk.init(student).getMailboxes().mapToEntities(student) }, + saveFetchResult = { old, new -> + mailboxDao.deleteAll(old uniqueSubtract new) + mailboxDao.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(mailboxCacheKey, student)) + } + ) + + suspend fun getMailboxByStudent(student: Student): Mailbox? { + val mailbox = getMailboxByStudentUseCase(student) + + return if (mailbox == null) { + getMailboxes(student, forceRefresh = true).toFirstResult() + getMailboxByStudentUseCase(student) + } else mailbox + } + var draftMessage: MessageDraft? get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft)) ?.let { json.decodeFromString(it) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt index e80f028e1..79984ce6d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt @@ -33,9 +33,11 @@ class RecipientRepository @Inject constructor( suspend fun getRecipients( student: Student, - mailbox: Mailbox, - type: MailboxType + mailbox: Mailbox?, + type: MailboxType, ): List { + mailbox ?: return emptyList() + val cached = recipientDb.loadAll(type, mailbox.globalKey) val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) @@ -47,11 +49,15 @@ class RecipientRepository @Inject constructor( suspend fun getMessageSender( student: Student, - mailbox: Mailbox, - message: Message - ): List = sdk.init(student) - .getMessageReplayDetails(message.messageGlobalKey) - .sender - .let(::listOf) - .mapToEntities(mailbox.globalKey) + mailbox: Mailbox?, + message: Message, + ): List { + mailbox ?: return emptyList() + + return sdk.init(student) + .getMessageReplayDetails(message.messageGlobalKey) + .sender + .let(::listOf) + .mapToEntities(mailbox.globalKey) + } } diff --git a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt new file mode 100644 index 000000000..d96794e51 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.domain.messages + +import io.github.wulkanowy.data.db.dao.MailboxDao +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.db.entities.Student +import javax.inject.Inject + +class GetMailboxByStudentUseCase @Inject constructor( + private val mailboxDao: MailboxDao, +) { + + suspend operator fun invoke(student: Student): Mailbox? { + return mailboxDao.loadAll(student.email) + .filterByStudent(student) + } + + private fun List.filterByStudent(student: Student): Mailbox? { + val normalizedStudentName = student.studentName.normalizeStudentName() + + return find { + it.studentName.normalizeStudentName() == normalizedStudentName + } ?: singleOrNull { + it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart() + } ?: singleOrNull { + it.studentName.getUnauthorizedVersion() == normalizedStudentName + } + } + + private fun String.normalizeStudentName(): String { + return trim().split(" ") + .filter { it.isNotBlank() } + .joinToString(" ") { part -> + part.lowercase().replaceFirstChar { it.uppercase() } + } + } + + private fun String.getFirstAndLastPart(): String { + val parts = normalizeStudentName().split(" ") + + val endParts = parts.filterIndexed { i, _ -> + i == 0 || parts.size - 1 == i + } + return endParts.joinToString(" ") + } + + private fun String.getUnauthorizedVersion(): String { + return normalizeStudentName().split(" ") + .joinToString(" ") { + it.first() + "*".repeat(it.length - 1) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt index 3b7bcff05..45523d51e 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt @@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.pojos.GroupNotificationData import io.github.wulkanowy.data.pojos.NotificationData import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity import io.github.wulkanowy.utils.getPlural import javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt index 180568267..c7824e61f 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt @@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED -import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.waitForResult import io.github.wulkanowy.services.sync.notifications.NewMessageNotification @@ -12,12 +11,11 @@ import javax.inject.Inject class MessageWork @Inject constructor( private val messageRepository: MessageRepository, - private val mailboxRepository: MailboxRepository, private val newMessageNotification: NewMessageNotification, ) : Work { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - val mailbox = mailboxRepository.getMailbox(student) + val mailbox = messageRepository.getMailboxByStudent(student) messageRepository.getMessages( student = student, mailbox = mailbox, @@ -26,7 +24,7 @@ class MessageWork @Inject constructor( notify = notify ).waitForResult() - messageRepository.getMessagesFromDatabase(mailbox).first() + messageRepository.getMessagesFromDatabase(student, mailbox).first() .filter { !it.isNotified && it.unread }.let { if (it.isNotEmpty()) newMessageNotification.notify(it, student) messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt index b1322ada3..90b20651d 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt @@ -1,22 +1,23 @@ package io.github.wulkanowy.services.sync.works +import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.MailboxRepository +import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.RecipientRepository +import io.github.wulkanowy.data.toFirstResult import javax.inject.Inject class RecipientWork @Inject constructor( - private val mailboxRepository: MailboxRepository, + private val messageRepository: MessageRepository, private val recipientRepository: RecipientRepository ) : Work { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - mailboxRepository.refreshMailboxes(student) - - val mailbox = mailboxRepository.getMailbox(student) - - recipientRepository.refreshRecipients(student, mailbox, MailboxType.EMPLOYEE) + val mailboxes = messageRepository.getMailboxes(student, forceRefresh = true).toFirstResult() + mailboxes.dataOrNull?.forEach { + recipientRepository.refreshRecipients(student, it, MailboxType.EMPLOYEE) + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt index 350300937..cb92b0043 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -25,7 +25,6 @@ class DashboardPresenter @Inject constructor( private val gradeRepository: GradeRepository, private val semesterRepository: SemesterRepository, private val messageRepository: MessageRepository, - private val mailboxRepository: MailboxRepository, private val attendanceSummaryRepository: AttendanceSummaryRepository, private val timetableRepository: TimetableRepository, private val homeworkRepository: HomeworkRepository, @@ -228,7 +227,7 @@ class DashboardPresenter @Inject constructor( private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) { flow { val semester = semesterRepository.getCurrentSemester(student) - val mailbox = mailboxRepository.getMailbox(student) + val mailbox = messageRepository.getMailboxByStudent(student) val selectedTiles = preferencesRepository.selectedDashboardTiles val flowSuccess = flowOf(Resource.Success(null)) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt index e31bd84a9..27d8613a3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt @@ -19,6 +19,7 @@ val debugMessageItems = listOf( private fun generateMessage(sender: String, subject: String) = Message( subject = subject, messageId = 123, + email = "", date = Instant.now(), folderId = 0, unread = true, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt new file mode 100644 index 000000000..59f6d288d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt @@ -0,0 +1,81 @@ +package io.github.wulkanowy.ui.modules.message.mailboxchooser + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil.ItemCallback +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.db.entities.MailboxType +import io.github.wulkanowy.databinding.ItemMailboxChooserBinding +import javax.inject.Inject + +class MailboxChooserAdapter @Inject constructor() : + ListAdapter(Differ) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemMailboxChooserBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + class ItemViewHolder( + private val binding: ItemMailboxChooserBinding, + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(item: MailboxChooserItem) { + with(binding) { + mailboxItemName.text = item.mailbox?.getFirstLine() + ?: root.resources.getString(R.string.message_chip_all_mailboxes) + mailboxItemSchool.text = item.mailbox?.getSecondLine() + mailboxItemSchool.isVisible = !item.isAll + + root.setOnClickListener { item.onClickListener(item.mailbox) } + } + } + + private fun Mailbox.getFirstLine() = buildString { + if (studentName.isNotBlank() && studentName != userName) { + append(studentName) + append(" - ") + } + append(userName) + } + + private fun Mailbox.getSecondLine() = buildString { + append(schoolNameShort) + append(" - ") + append(getMailboxType(type)) + } + + private fun getMailboxType(type: MailboxType): String = when (type) { + MailboxType.STUDENT -> R.string.message_mailbox_type_student + MailboxType.PARENT -> R.string.message_mailbox_type_parent + MailboxType.GUARDIAN -> R.string.message_mailbox_type_guardian + MailboxType.EMPLOYEE -> R.string.message_mailbox_type_employee + MailboxType.UNKNOWN -> null + }.let { it?.let { it1 -> binding.root.resources.getString(it1) }.orEmpty() } + } + + private object Differ : ItemCallback() { + override fun areItemsTheSame( + oldItem: MailboxChooserItem, + newItem: MailboxChooserItem + ): Boolean { + return oldItem.mailbox?.globalKey == newItem.mailbox?.globalKey + } + + override fun areContentsTheSame( + oldItem: MailboxChooserItem, + newItem: MailboxChooserItem + ): Boolean { + return oldItem == newItem + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt new file mode 100644 index 000000000..222412ef1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt @@ -0,0 +1,75 @@ +package io.github.wulkanowy.ui.modules.message.mailboxchooser + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.os.bundleOf +import androidx.fragment.app.setFragmentResult +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.databinding.DialogMailboxChooserBinding +import io.github.wulkanowy.ui.base.BaseDialogFragment +import javax.inject.Inject + +@AndroidEntryPoint +class MailboxChooserDialog : BaseDialogFragment(), MailboxChooserView { + + @Inject + lateinit var presenter: MailboxChooserPresenter + + @Inject + lateinit var mailboxAdapter: MailboxChooserAdapter + + companion object { + const val LISTENER_KEY = "mailbox_selected" + const val MAILBOX_KEY = "selected_mailbox" + const val REQUIRED_KEY = "is_mailbox_required" + + fun newInstance(mailboxes: List, isMailboxRequired: Boolean, folder: String) = + MailboxChooserDialog().apply { + arguments = bundleOf( + MAILBOX_KEY to mailboxes.toTypedArray(), + REQUIRED_KEY to isMailboxRequired, + LISTENER_KEY to folder, + ) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogMailboxChooserBinding.inflate(inflater).apply { binding = this }.root + + @Suppress("UNCHECKED_CAST") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + presenter.onAttachView( + view = this, + requireMailbox = requireArguments().getBoolean(REQUIRED_KEY, false), + mailboxes = requireArguments().getParcelableArray(MAILBOX_KEY).orEmpty() + .toList() as List, + ) + } + + override fun initView() { + binding.accountQuickDialogRecycler.adapter = mailboxAdapter + } + + override fun submitData(items: List) { + mailboxAdapter.submitList(items) + } + + override fun onMailboxSelected(item: Mailbox?) { + setFragmentResult( + requestKey = requireArguments().getString(LISTENER_KEY).orEmpty(), + result = bundleOf(MAILBOX_KEY to item), + ) + dismiss() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt new file mode 100644 index 000000000..6923cf085 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.ui.modules.message.mailboxchooser + +import io.github.wulkanowy.data.db.entities.Mailbox + +data class MailboxChooserItem( + val mailbox: Mailbox? = null, + val isAll: Boolean = false, + val onClickListener: (Mailbox?) -> Unit, +) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt new file mode 100644 index 000000000..5bd7c84ab --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.ui.modules.message.mailboxchooser + +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import timber.log.Timber +import javax.inject.Inject + +class MailboxChooserPresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository) { + + fun onAttachView(view: MailboxChooserView, mailboxes: List, requireMailbox: Boolean) { + super.onAttachView(view) + + view.initView() + Timber.i("Mailbox chooser view was initialized") + view.submitData(getMailboxItems(mailboxes, requireMailbox)) + } + + private fun getMailboxItems( + mailboxes: List, + requireMailbox: Boolean, + ): List = buildList { + if (!requireMailbox) { + add(MailboxChooserItem(isAll = true, onClickListener = ::onMailboxSelect)) + } + addAll(mailboxes.map { + MailboxChooserItem(mailbox = it, isAll = false, onClickListener = ::onMailboxSelect) + }) + } + + fun onMailboxSelect(item: Mailbox?) { + view?.onMailboxSelected(item) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt new file mode 100644 index 000000000..2e20ee815 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.ui.modules.message.mailboxchooser + +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.ui.base.BaseView + +interface MailboxChooserView : BaseView { + + fun initView() + + fun submitData(items: List) + + fun onMailboxSelected(item: Mailbox?) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index c011f41f3..fd75f6f3a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -6,7 +6,6 @@ import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageAttachment import io.github.wulkanowy.data.enums.MessageFolder -import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter @@ -21,7 +20,6 @@ class MessagePreviewPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val mailboxRepository: MailboxRepository, private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -187,8 +185,8 @@ class MessagePreviewPresenter @Inject constructor( presenterScope.launch { runCatching { val student = studentRepository.getCurrentStudent(decryptPass = true) - val mailbox = mailboxRepository.getMailbox(student) - messageRepository.deleteMessage(student, mailbox, message!!) + val mailbox = messageRepository.getMailboxByStudent(student) + messageRepository.deleteMessage(student, mailbox!!, message!!) } .onFailure { retryCallback = { onMessageDelete() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt index 334e389e2..b5f687bd4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -19,9 +19,13 @@ import androidx.core.text.toHtml import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.databinding.ActivitySendMessageBinding import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog +import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.MAILBOX_KEY +import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.LISTENER_KEY import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.hideSoftInput import io.github.wulkanowy.utils.showSoftInput @@ -100,6 +104,7 @@ class SendMessageActivity : BaseActivity + presenter.onMailboxSelected(bundle.getSerializable(MAILBOX_KEY) as? Mailbox) + } } @SuppressLint("ClickableViewAccessibility") @@ -205,6 +213,14 @@ class SendMessageActivity : BaseActivity) { + MailboxChooserDialog.newInstance( + mailboxes = mailboxes, + isMailboxRequired = true, + folder = LISTENER_KEY, + ).show(supportFragmentManager, "chooser") + } + override fun popView() { finish() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index 6c07ee6ad..5ab8f8fc9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -1,15 +1,15 @@ package io.github.wulkanowy.ui.modules.message.send -import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceNotLoading import io.github.wulkanowy.data.pojos.MessageDraft -import io.github.wulkanowy.data.repositories.* -import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.RecipientRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper @@ -28,7 +28,6 @@ class SendMessagePresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val mailboxRepository: MailboxRepository, private val recipientRepository: RecipientRepository, private val preferencesRepository: PreferencesRepository, private val analytics: AnalyticsHelper @@ -36,10 +35,19 @@ class SendMessagePresenter @Inject constructor( private val messageUpdateChannel = Channel() + private var message: Message? = null + private var isReplay: Boolean? = null + + private var mailboxes: List = emptyList() + private var selectedMailbox: Mailbox? = null + fun onAttachView(view: SendMessageView, reason: String?, message: Message?, reply: Boolean?) { super.onAttachView(view) view.initView() initializeSubjectStream() + this.message = message + this.isReplay = reply + Timber.i("Send message view was initialized") loadData(message, reply) with(view) { @@ -110,16 +118,31 @@ class SendMessagePresenter @Inject constructor( return false } + fun onOpenMailboxChooser() { + view?.showMailboxChooser(mailboxes) + } + + fun onMailboxSelected(mailbox: Mailbox?) { + selectedMailbox = mailbox + + loadData(message, isReplay) + } + private fun loadData(message: Message?, reply: Boolean?) { resourceFlow { val student = studentRepository.getCurrentStudent() - val mailbox = mailboxRepository.getMailbox(student) + + if (selectedMailbox == null && mailboxes.isEmpty()) { + selectedMailbox = messageRepository.getMailboxByStudent(student) + mailboxes = messageRepository.getMailboxes(student, false).toFirstResult() + .dataOrNull.orEmpty() + } Timber.i("Loading recipients started") val recipients = createChips( recipients = recipientRepository.getRecipients( student = student, - mailbox = mailbox, + mailbox = selectedMailbox, type = MailboxType.EMPLOYEE, ) ) @@ -130,7 +153,7 @@ class SendMessagePresenter @Inject constructor( message != null && reply == true -> recipientRepository.getMessageSender( student = student, message = message, - mailbox = mailbox, + mailbox = selectedMailbox, ) else -> emptyList() }.let { createChips(it) } @@ -139,39 +162,42 @@ class SendMessagePresenter @Inject constructor( messageRecipients.size ) - Triple(mailbox, recipients, messageRecipients) + recipients to messageRecipients } .logResourceStatus("load recipients") - .onEach { - when (it) { - is Resource.Loading -> view?.run { - showProgress(true) - showContent(false) - } - is Resource.Success -> it.data.let { (mailbox, recipientChips, selectedRecipientChips) -> - view?.run { - setMailbox(getMailboxName(mailbox)) - setRecipients(recipientChips) - if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients( - selectedRecipientChips - ) - showContent(true) - } - } - is Resource.Error -> { - view?.showContent(true) - errorHandler.dispatch(it.error) + .onResourceLoading { + view?.run { + showProgress(true) + showContent(false) + } + } + .onResourceNotLoading { + view?.run { showProgress(false) } + } + .onResourceError { + view?.showContent(true) + errorHandler.dispatch(it) + } + .onResourceSuccess { + it.let { (recipientChips, selectedRecipientChips) -> + view?.run { + setMailbox(getMailboxName(selectedMailbox)) + setRecipients(recipientChips) + if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients( + selectedRecipientChips + ) + showContent(true) } } - }.onResourceNotLoading { - view?.run { showProgress(false) } - }.launch() + } + .launch() } private fun sendMessage(subject: String, content: String, recipients: List) { + val mailbox = selectedMailbox ?: return + resourceFlow { val student = studentRepository.getCurrentStudent() - val mailbox = mailboxRepository.getMailbox(student) messageRepository.sendMessage( student = student, subject = subject, @@ -222,18 +248,21 @@ class SendMessagePresenter @Inject constructor( } } - private fun getMailboxName(mailbox: Mailbox): String { + private fun getMailboxName(mailbox: Mailbox?): String { + mailbox ?: return "" + + // username - accountType [\n student name - ] (school short name) return buildString { append(mailbox.userName) append(" - ") append(getMailboxType(mailbox.type)) + appendLine() if (mailbox.type == MailboxType.PARENT) { - append(" - ") append(mailbox.studentName) + append(" - ") } - append(" - ") append("(${mailbox.schoolNameShort})") } } @@ -267,9 +296,9 @@ class SendMessagePresenter @Inject constructor( private fun saveDraftMessage() { messageRepository.draftMessage = MessageDraft( - view?.formRecipientsData!!, - view?.formSubjectValue!!, - view?.formContentValue!! + recipients = view?.formRecipientsData!!, + subject = view?.formSubjectValue!!, + content = view?.formContentValue!!, ) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt index 1057114b8..e27a09d60 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt @@ -61,4 +61,5 @@ interface SendMessageView : BaseView { fun getMessageBackupDialogStringWithRecipients(recipients: String): String fun clearDraft() + fun showMailboxChooser(mailboxes: List) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt index 234d17eb2..6df6153c5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt @@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.databinding.ItemMessageBinding import io.github.wulkanowy.databinding.ItemMessageChipsBinding +import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject @@ -19,13 +20,15 @@ import javax.inject.Inject class MessageTabAdapter @Inject constructor() : RecyclerView.Adapter() { - var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit = { _, _ -> } + lateinit var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit - var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit = {} + lateinit var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit - var onHeaderClickListener: (CompoundButton, Boolean) -> Unit = { _, _ -> } + lateinit var onHeaderClickListener: (CompoundButton, Boolean) -> Unit - var onChangesDetectedListener = {} + lateinit var onMailboxClickListener: () -> Unit + + lateinit var onChangesDetectedListener: () -> Unit private var items = mutableListOf() @@ -49,12 +52,12 @@ class MessageTabAdapter @Inject constructor() : val inflater = LayoutInflater.from(parent.context) return when (MessageItemViewType.values()[viewType]) { - MessageItemViewType.MESSAGE -> ItemViewHolder( - ItemMessageBinding.inflate(inflater, parent, false) - ) MessageItemViewType.FILTERS -> HeaderViewHolder( ItemMessageChipsBinding.inflate(inflater, parent, false) ) + MessageItemViewType.MESSAGE -> ItemViewHolder( + ItemMessageBinding.inflate(inflater, parent, false) + ) } } @@ -69,6 +72,20 @@ class MessageTabAdapter @Inject constructor() : val item = items[position] as MessageTabDataItem.FilterHeader with(holder.binding) { + chipMailbox.text = item.selectedMailbox + ?: root.context.getString(R.string.message_chip_all_mailboxes) + chipMailbox.chipBackgroundColor = ColorStateList.valueOf( + if (item.selectedMailbox == null) { + root.context.getCompatColor(R.color.mtrl_choice_chip_background_color) + } else root.context.getThemeAttrColor(android.R.attr.colorPrimary, 64) + ) + chipMailbox.setTextColor( + if (item.selectedMailbox == null) { + root.context.getThemeAttrColor(android.R.attr.textColorPrimary) + } else root.context.getThemeAttrColor(android.R.attr.colorPrimary) + ) + chipMailbox.setOnClickListener { onMailboxClickListener() } + if (item.onlyUnread == null) { chipUnread.isVisible = false } else { @@ -77,6 +94,7 @@ class MessageTabAdapter @Inject constructor() : chipUnread.setOnCheckedChangeListener(onHeaderClickListener) } chipUnread.isEnabled = item.isEnabled + chipAttachments.isEnabled = item.isEnabled chipAttachments.isChecked = item.onlyWithAttachments chipAttachments.setOnCheckedChangeListener(onHeaderClickListener) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt index 634dfc0e7..c0bd4170e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt @@ -11,6 +11,7 @@ sealed class MessageTabDataItem(val viewType: MessageItemViewType) { ) : MessageTabDataItem(MessageItemViewType.MESSAGE) data class FilterHeader( + val selectedMailbox: String?, val onlyUnread: Boolean?, val onlyWithAttachments: Boolean, val isEnabled: Boolean diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 654b0e226..5d608ad3b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -10,15 +10,18 @@ import android.widget.CompoundButton import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView import androidx.core.view.updatePadding +import androidx.fragment.app.setFragmentResultListener import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.databinding.FragmentMessageTabBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment import io.github.wulkanowy.ui.widgets.DividerItemDecoration import io.github.wulkanowy.utils.dpToPx @@ -104,6 +107,7 @@ class MessageTabFragment : BaseFragment(R.layout.frag onItemClickListener = presenter::onMessageItemSelected onLongItemClickListener = presenter::onMessageItemLongSelected onHeaderClickListener = ::onChipChecked + onMailboxClickListener = presenter::onMailboxFilterSelected onChangesDetectedListener = ::resetListPosition } @@ -123,6 +127,12 @@ class MessageTabFragment : BaseFragment(R.layout.frag messageTabErrorRetry.setOnClickListener { presenter.onRetry() } messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() } } + + setFragmentResultListener(requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!) { _, bundle -> + presenter.onMailboxSelected( + mailbox = bundle.getSerializable(MailboxChooserDialog.MAILBOX_KEY) as? Mailbox, + ) + } } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -246,6 +256,16 @@ class MessageTabFragment : BaseFragment(R.layout.frag ) } + override fun showMailboxChooser(mailboxes: List) { + (activity as? MainActivity)?.showDialogFragment( + MailboxChooserDialog.newInstance( + mailboxes = mailboxes, + isMailboxRequired = false, + folder = requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!, + ) + ) + } + override fun hideKeyboard() { activity?.hideSoftInput() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 54711a689..ea142db2b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -1,9 +1,9 @@ package io.github.wulkanowy.ui.modules.message.tab import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.enums.MessageFolder -import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter @@ -26,7 +26,6 @@ class MessageTabPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val mailboxRepository: MailboxRepository, private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -36,6 +35,9 @@ class MessageTabPresenter @Inject constructor( private var lastSearchQuery = "" + private var mailboxes: List = emptyList() + private var selectedMailbox: Mailbox? = null + private var messages = emptyList() private val searchChannel = Channel() @@ -122,8 +124,7 @@ class MessageTabPresenter @Inject constructor( runCatching { val student = studentRepository.getCurrentStudent(true) - val mailbox = mailboxRepository.getMailbox(student) - messageRepository.deleteMessages(student, mailbox, messageList) + messageRepository.deleteMessages(student, selectedMailbox, messageList) } .onFailure(errorHandler::dispatch) .onSuccess { view?.showMessagesDeleted() } @@ -202,13 +203,28 @@ class MessageTabPresenter @Inject constructor( } } + fun onMailboxFilterSelected() { + view?.showMailboxChooser(mailboxes) + } + + fun onMailboxSelected(mailbox: Mailbox?) { + selectedMailbox = mailbox + loadData(false) + } + private fun loadData(forceRefresh: Boolean) { Timber.i("Loading $folder message data started") flatResourceFlow { val student = studentRepository.getCurrentStudent() - val mailbox = mailboxRepository.getMailbox(student) - messageRepository.getMessages(student, mailbox, folder, forceRefresh) + + if (selectedMailbox == null && mailboxes.isEmpty()) { + selectedMailbox = messageRepository.getMailboxByStudent(student) + mailboxes = messageRepository.getMailboxes(student, forceRefresh).toFirstResult() + .dataOrNull.orEmpty() + } + + messageRepository.getMessages(student, selectedMailbox, folder, forceRefresh) } .logResourceStatus("load $folder message") .onResourceData { @@ -327,7 +343,16 @@ class MessageTabPresenter @Inject constructor( MessageTabDataItem.FilterHeader( onlyUnread = onlyUnread.takeIf { folder != MessageFolder.SENT }, onlyWithAttachments = onlyWithAttachments, - isEnabled = !isActionMode + isEnabled = !isActionMode, + selectedMailbox = selectedMailbox?.let { + buildString { + if (it.studentName.isNotBlank() && it.studentName != it.userName) { + append(it.studentName) + append(" - ") + } + append(it.userName) + } + }, ) ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt index bfa43b209..6ece6621b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.message.tab +import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.ui.base.BaseView @@ -46,4 +47,6 @@ interface MessageTabView : BaseView { fun showActionMode(show: Boolean) fun showRecyclerBottomPadding(show: Boolean) + + fun showMailboxChooser(mailboxes: List) } diff --git a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt index c69fec65c..93e67be01 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt @@ -4,6 +4,7 @@ import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder @@ -25,8 +26,8 @@ fun getRefreshKey(name: String, student: Student): String { return "${name}_${student.userLoginId}" } -fun getRefreshKey(name: String, student: Student, folder: MessageFolder): String { - return "${name}_${student.id}_${folder.id}" +fun getRefreshKey(name: String, mailbox: Mailbox?, folder: MessageFolder): String { + return "${name}_${mailbox?.globalKey ?: "all"}_${folder.id}" } class AutoRefreshHelper @Inject constructor( diff --git a/app/src/main/res/layout/activity_send_message.xml b/app/src/main/res/layout/activity_send_message.xml index 320782bdc..a8041d61c 100644 --- a/app/src/main/res/layout/activity_send_message.xml +++ b/app/src/main/res/layout/activity_send_message.xml @@ -55,17 +55,29 @@ android:id="@+id/sendMessageFrom" android:layout_width="0dp" android:layout_height="58dp" - android:layout_marginStart="16dp" - android:layout_marginLeft="16dp" - android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" + android:layout_marginStart="8dp" + android:background="?selectableItemBackground" android:gravity="center_vertical" + android:paddingStart="8dp" + android:paddingEnd="32dp" android:textSize="18sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/sendMessageFromHint" app:layout_constraintTop_toTopOf="parent" tools:text="Jan Kowalski" /> + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_mailbox_chooser.xml b/app/src/main/res/layout/item_mailbox_chooser.xml new file mode 100644 index 000000000..7c93199bc --- /dev/null +++ b/app/src/main/res/layout/item_mailbox_chooser.xml @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_message_chips.xml b/app/src/main/res/layout/item_message_chips.xml index 481a94835..da2e20311 100644 --- a/app/src/main/res/layout/item_message_chips.xml +++ b/app/src/main/res/layout/item_message_chips.xml @@ -1,21 +1,30 @@ - + app:layout_constraintTop_toTopOf="parent" + app:singleLine="true"> + + - + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e65a27fcc..e365a00ca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -301,6 +301,7 @@ Message does not exist You need to choose at least 1 recipient The message content must be at least 3 characters + Wszystkie skrzynki Only unread Only with attachments Read: %s @@ -324,6 +325,7 @@ %1$d selected Messages deleted + Choose mailbox diff --git a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt index ff0a53135..84a0cb405 100644 --- a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt +++ b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt @@ -27,7 +27,9 @@ fun getMailboxEntity() = Mailbox( globalKey = "v4", fullName = "", userName = "", - userLoginId = 0, + email = "test", + symbol = "powiatwulkanowy", + schoolId = "123456", studentName = "", schoolNameShort = "", type = MailboxType.UNKNOWN, diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt index 9fc83a23b..9a2c22fd6 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt @@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories import android.content.Context import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.dao.MailboxDao import io.github.wulkanowy.data.db.dao.MessageAttachmentDao import io.github.wulkanowy.data.db.dao.MessagesDao import io.github.wulkanowy.data.db.entities.Message @@ -10,6 +11,7 @@ import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.data.errorOrNull import io.github.wulkanowy.data.toFirstResult +import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase import io.github.wulkanowy.getMailboxEntity import io.github.wulkanowy.getStudentEntity import io.github.wulkanowy.sdk.Sdk @@ -55,6 +57,12 @@ class MessageRepositoryTest { @MockK private lateinit var sharedPrefProvider: SharedPrefProvider + @MockK + private lateinit var mailboxDao: MailboxDao + + @MockK + private lateinit var getMailboxByStudentUseCase: GetMailboxByStudentUseCase + private val student = getStudentEntity() private val mailbox = getMailboxEntity() @@ -74,26 +82,33 @@ class MessageRepositoryTest { refreshHelper = refreshHelper, sharedPrefProvider = sharedPrefProvider, json = Json, + mailboxDao = mailboxDao, + getMailboxByStudentUseCase = getMailboxByStudentUseCase, ) } @Test fun `get messages when fetched completely new message without notify`() = runBlocking { - every { messageDb.loadAll(any(), any()) } returns flowOf(emptyList()) + coEvery { mailboxDao.loadAll(any()) } returns listOf(mailbox) + every { messageDb.loadAll(mailbox.globalKey, any()) } returns flowOf(emptyList()) coEvery { sdk.getMessages(Folder.RECEIVED, any()) } returns listOf( - getMessageDto() + getMessageDto().copy( + unreadBy = 5, + readBy = 10, + ) ) coEvery { messageDb.deleteAll(any()) } just Runs coEvery { messageDb.insertAll(any()) } returns listOf() - repository.getMessages( + val res = repository.getMessages( student = student, mailbox = mailbox, folder = MessageFolder.RECEIVED, forceRefresh = true, notify = false, - ).toFirstResult().dataOrNull.orEmpty() + ).toFirstResult() + assertEquals(null, res.errorOrNull) coVerify(exactly = 1) { messageDb.deleteAll(withArg { checkEquals(emptyList()) }) } coVerify { messageDb.insertAll(withArg { @@ -187,6 +202,7 @@ class MessageRepositoryTest { ) = Message( messageGlobalKey = "v4", mailboxKey = "", + email = "", correspondents = "", messageId = messageId, subject = "", diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt similarity index 77% rename from app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt rename to app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt index 9198560fe..96a84a5a6 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt @@ -1,65 +1,52 @@ -package io.github.wulkanowy.data.repositories +package io.github.wulkanowy.domain import io.github.wulkanowy.data.db.dao.MailboxDao import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.AutoRefreshHelper import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.coEvery import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK import io.mockk.just import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import java.time.Instant +import kotlin.test.assertEquals +import kotlin.test.assertNull @OptIn(ExperimentalCoroutinesApi::class) -class MailboxRepositoryTest { - - @SpyK - private var sdk = Sdk() +class GetMailboxByStudentUseCaseTest { @MockK private lateinit var mailboxDao: MailboxDao - @MockK - private lateinit var refreshHelper: AutoRefreshHelper - - private lateinit var systemUnderTest: MailboxRepository + private lateinit var systemUnderTest: GetMailboxByStudentUseCase @Before fun setUp() { MockKAnnotations.init(this) - coEvery { refreshHelper.shouldBeRefreshed(any()) } returns false - coEvery { refreshHelper.updateLastRefreshTimestamp(any()) } just Runs coEvery { mailboxDao.deleteAll(any()) } just Runs coEvery { mailboxDao.insertAll(any()) } returns emptyList() coEvery { mailboxDao.loadAll(any()) } returns emptyList() - coEvery { sdk.getMailboxes() } returns emptyList() - systemUnderTest = MailboxRepository( - mailboxDao = mailboxDao, - sdk = sdk, - refreshHelper = refreshHelper, - ) + systemUnderTest = GetMailboxByStudentUseCase(mailboxDao = mailboxDao) } - @Test(expected = IllegalArgumentException::class) + @Test fun `get mailbox that doesn't exist`() = runTest { val student = getStudentEntity( userName = "Stanisław Kowalski", studentName = "Jan Kowalski", ) - coEvery { sdk.getMailboxes() } returns emptyList() + coEvery { mailboxDao.loadAll(any()) } returns emptyList() - systemUnderTest.getMailbox(student) + assertNull(systemUnderTest(student)) } @Test @@ -73,7 +60,7 @@ class MailboxRepositoryTest { expectedMailbox, ) - val selectedMailbox = systemUnderTest.getMailbox(student) + val selectedMailbox = systemUnderTest(student) assertEquals(expectedMailbox, selectedMailbox) } @@ -88,7 +75,7 @@ class MailboxRepositoryTest { expectedMailbox, ) - assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + assertEquals(expectedMailbox, systemUnderTest(student)) } @Test @@ -102,10 +89,10 @@ class MailboxRepositoryTest { expectedMailbox, ) - assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + assertEquals(expectedMailbox, systemUnderTest(student)) } - @Test(expected = IllegalArgumentException::class) + @Test fun `get mailbox for not-unique non-authorized student`() = runTest { val student = getStudentEntity( userName = "Stanisław Kowalski", @@ -116,7 +103,7 @@ class MailboxRepositoryTest { getMailboxEntity("Jan Kurowski"), ) - systemUnderTest.getMailbox(student) + assertNull(systemUnderTest(student)) } @Test @@ -130,7 +117,7 @@ class MailboxRepositoryTest { expectedMailbox, ) - assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + assertEquals(expectedMailbox, systemUnderTest(student)) } @Test @@ -144,7 +131,7 @@ class MailboxRepositoryTest { expectedMailbox, ) - assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + assertEquals(expectedMailbox, systemUnderTest(student)) } @Test @@ -158,7 +145,7 @@ class MailboxRepositoryTest { expectedMailbox, ) - assertEquals(expectedMailbox, systemUnderTest.getMailbox(student)) + assertEquals(expectedMailbox, systemUnderTest(student)) } private fun getMailboxEntity( @@ -167,7 +154,9 @@ class MailboxRepositoryTest { globalKey = "", fullName = "", userName = "", - userLoginId = 123, + email = "", + schoolId = "", + symbol = "", studentName = studentName, schoolNameShort = "", type = MailboxType.STUDENT, From d3e276d6fc79d4806befce5c9f43826b4f4ec4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Wed, 16 Nov 2022 20:13:48 +0100 Subject: [PATCH 150/164] New Crowdin updates (#2049) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mikołaj Pich --- app/src/main/res/values-cs/strings.xml | 5 +- app/src/main/res/values-de/strings.xml | 3 + app/src/main/res/values-es-rES/strings.xml | 3 + app/src/main/res/values-pl/strings.xml | 3 + app/src/main/res/values-ru/strings.xml | 41 +++++----- app/src/main/res/values-sk/strings.xml | 5 +- app/src/main/res/values-uk/strings.xml | 25 +++--- app/src/main/res/values/strings.xml | 92 +--------------------- 8 files changed, 55 insertions(+), 122 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index d036e7e42..f41cb17f9 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -309,9 +309,11 @@ Zpráva neexistuje Musíte vybrat alespoň 1 příjemce Obsah zprávy musí mít alespoň 3 znaky + Všechny poštovní schránky Pouze nepřečtené Pouze s přílohami Přečtena: %s + Přečtena přes: %1$d z %2$d osob %1$d zpráva %1$d zprávy @@ -339,6 +341,7 @@ %1$d vybraných Zprávy odstraněné + Vyberte poštovní schránku Žádné informace o poznámkách Body @@ -735,7 +738,7 @@ Volbu můžete kdykoliv změnit v nastavení aplikace. Můžeme použít Vaše data k zobrazení reklam šitých pro vás nebo pomocí méně vašich dat zobrazovat nepřizpůsobené reklamy. Podrobnosti naleznete v našich Zásadách ochrany osobních údajů Přizpůsobené reklamy Nepřizpůsobené reklamy - Mám ukončené 18 let + Je mi více než 18 let Ano, přizpůsobené reklamy Ano, nepřizpůsobené reklamy Pokročilé diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7b544258a..6107fbb96 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -275,9 +275,11 @@ Nachricht nicht vorhanden Sie müssen mindestens 1 Empfänger auswählen. Der Inhalt der Nachricht muss mindestens 3 Zeichen lang sein. + All mailboxes Nur ungelesen Nur mit Anhängen Lesen: %s + Read by: %1$d of %2$d people %1$d Nachricht %1$d Nachrichten @@ -297,6 +299,7 @@ %1$d ausgewählt Nachrichten gelöscht + Choose mailbox Keine Informationen über Eintragen Punkte diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index aecc720b7..95a00a602 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -275,9 +275,11 @@ Message does not exist You need to choose at least 1 recipient The message content must be at least 3 characters + All mailboxes Only unread Only with attachments Read: %s + Read by: %1$d of %2$d people %1$d message %1$d messages @@ -297,6 +299,7 @@ %1$d selected Messages deleted + Choose mailbox No info about notes Points diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index c9abbd022..f612d826d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -309,9 +309,11 @@ Wiadomość nie istnieje Musisz wybrać co najmniej 1 adresata Treść wiadomości musi zawierać co najmniej 3 znaki + Wszystkie skrzynki Tylko nieprzeczytane Tylko z załącznikami Przeczytana: %s + Przeczytana przez: %1$d z %2$d osób %1$d wiadomość %1$d wiadomości @@ -339,6 +341,7 @@ %1$d wybranych Wiadomości zostały usunięte + Wybierz skrzynkę Brak informacji o uwagach Punkty diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e1abb0293..195371fd0 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -77,9 +77,9 @@ Войти Сеанс истёк Сеанс истёк, авторизуйтесь снова - Application support - Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time - Enable ads + Поддержка приложения + Вам нравится это приложение? Поддержите его разработку, включив неинвазивную рекламу, которую можно отключить в любое время + Включить рекламу Оценка %d семестр @@ -97,7 +97,7 @@ Ожидаемая оценка Рассчитанная средняя оценка Как работает \"Рассчитанная средняя оценка\"? - The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages + Рассчитанная средняя оценка - это среднее арифметическое, рассчитанное на основе средних оценок по предметам. Это позволяет узнать приблизительную итоговую среднюю оценку. Она рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант, так как каждая школа по разному считает среднюю оценку. Кроме того, если ваша школа выставляет средние оценки по предметам на странице Vulcan, приложение просто загрузит их. Это можно изменить, заставив приложение считать среднюю оценку в настройках.\n\nСредняя из оценок выбранного семестра:\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Суммирование вычисленных значений\n3. Вычисление среднего арифметического суммированных значений\n\nСредняя из средних оценок семестров:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах. \n2. Вычисление среднего арифметического из средневзвешенных значений для каждого предмета в семестрах.\n3. Суммирование средних арифметических\n4. Вычисление среднего арифматического из суммированных значений\n\nСредняя из оценок со всего года:\n1. Расчет средневзвешенного значения по каждому предмету за год. Итоговое среднее значение за 1 семестр не имеет значения.\n2. Суммирование вычисленных средних\n3. Расчет среднего арифметического суммированных чисел Как работает \"Итоговая средняя оценка\"? Итоговая средняя оценка - это среднее арифметическое, рассчитанное из всех имеющихся на данный момент итоговых оценок в семестре.\n\nРассчет происходит следующим образом:\n1. Суммирование итоговых оценок, выставленных преподавателями\n2. Полученная сумма делится на число предметов, по которым выставлены оценки Итоговая средняя оценка @@ -297,10 +297,10 @@ Перенести в корзину Удалить навсегда Письмо успешно удалено - student - parent - guardian - employee + ученик + родитель + опекун + работник Поделиться Печать Тема @@ -309,9 +309,11 @@ Письма не существует Вы должны выбрать как минимум одного получателя Текст сообщения должен содержать как минимум 3 знака + Все почтовые ящики Только непрочитанные Только с вложениями Прочитано: %s + Прочитано: %1$d из %2$d человек %1$d сообщение %1$d сообщения @@ -339,6 +341,7 @@ %1$d выбрано Сообщение удалено + Выбрать почтовый ящик Нет записей о замечаниях и свершениях Баллы @@ -720,10 +723,10 @@ Отвечать с историей сообщений Показывать среднее арифметическое при отсутствии стоимости Поддержка - Privacy Policy - Agreements - Consent to processing of data related to ads - Show ads in app + Политика приватности + Соглашения + Согласие на обработку данных, связанных с объявлениями + Показать рекламу в приложении Посмотреть рекламу для поддержки проекта Согласие на обработку данных Для просмотра рекламы вы должны согласиться с условиями обработки данных нашей Политики конфиденциальности @@ -731,13 +734,13 @@ Политика конфиденциальности Реклама загружается Спасибо за вашу поддержку, возвращайтесь позже для дополнительной рекламы - Can we use your data to display ads? - You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details - Personalized ads - Non-personalized ads - I am over 18 years old - Yes, personalized ads - Yes, non-personalized ads + Можем ли мы использовать ваши данные для показа рекламы? + Вы можете изменить свой выбор в любое время в настройках приложения. Мы можем использовать ваши данные для показа объявлений в соответствии с вашими пожеланиями или, используя меньше данных, отображать неперсональную рекламу. Пожалуйста, ознакомьтесь с нашей политикой конфиденциальности для подробностей + Персонализированная реклама + Неперсонализированная реклама + Я старше 18 лет + Да, персонализировать рекламу + Да, не персонализировать рекламу Расширенные Внешний вид и поведение Уведомления diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 1c6eae8c0..9fd06bcbe 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -309,9 +309,11 @@ Správa neexistuje Musíte vybrať aspoň 1 príjemca Obsah správy musí mať aspoň 3 znaky + Všetky poštové schránky Iba neprečítané Iba s prílohami Prečítaná: %s + Prečítaná cez: %1$d z %2$d osôb %1$d správa %1$d správy @@ -339,6 +341,7 @@ %1$d vybraných Správy odstránené + Vyberte poštovú schránku Žiadne informácie o poznámkach Body @@ -735,7 +738,7 @@ Voľbu môžete kedykoľvek zmeniť v nastavení aplikácie. Môžeme použiť vaše údaje na zobrazenie reklám šitých pre vás alebo pomocou menej vašich dát zobrazovať neprispôsobené reklamy. Podrobnosti nájdete v našich Zásadách ochrany osobných údajov Prispôsobené reklamy Neprispôsobené reklamy - Mám ukončené 18 rokov + Mám viac ako 18 rokov Áno, prispôsobené reklamy Áno, neprispôsobené reklamy Pokročilé diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2c104ecbf..2d3bf3724 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -77,8 +77,8 @@ Увійти Минув термін дії сесії Минув термін дії сесії, авторизуйтеся знову - Підтримка додатків - Вам подобається цей додаток? Підтримуйте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете будь-коли вимкнути + Підтримка додатку + Вам подобається цей додаток? Підтримайте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете відключити в будь-який час Увімкнути рекламу Оцінка @@ -97,7 +97,7 @@ Передбачувана оцінка Розрахована середня оцінка Як працює \"Розрахована середня оцінка\"? - Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів.Це дозволяє дізнатися приблизну кінцеву середню оцінку.Вона розраховується способом, обраним користувачем у налаштуваннях програми.Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку.Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки.Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх + Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів. Це дозволяє дізнатися приблизну кінцеву середню оцінку. Вона розраховується спосібом, обраним користувачем у налаштуваннях програми. Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку. Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки і не розраховує їх самостійно. Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх Як працює \"Підсумкова середня оцінка\"? Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \n\nСхема обчислення складається з таких кроків:\n1. Сумування підсумкових оцінок, виставленних викладачами\n2. Ділення на кількість предметів, з яких виставлені ці оцінки Підсумкова середня оцінка @@ -220,7 +220,7 @@ Всі в серії Час початку Час завершення - Час завершення має бути більшим, ніж час початку + Час завершення має бути пізніше часу початку Підсумок відвідуваності Відсутність зі шкільних причин @@ -297,8 +297,8 @@ Перемістити до кошика Видалити назавжди Лист було успішно видалено - студент - батькові + учень + родич опікун працівник Поділитись @@ -309,9 +309,11 @@ Такого листа не існує Необхідно обрати принаймні 1 адресата Зміст листа повинен складатися принаймні з 3 знаків + Усі поштові скриньки Лише непрочитані Тільки з вкладеннями Прочитаний: %s + Прочитано: %1$d з %2$d осіб %1$d лист %1$d листи @@ -338,7 +340,8 @@ %1$d вибрано %1$d вибрано - Листи видалені + Листи видалено + Вибрати поштову скриньку Немає інформації о зауваженнях Бали @@ -722,7 +725,7 @@ Підтримка Політика конфіденційності Угоди - Згода на обробку даних, пов’язаних з оголошеннями + Згода на обробку даних, пов\'язаних з рекламою Показувати рекламу в додатку Подивіться одну рекламу для підтримки проєкту Згода в обробці даних @@ -731,9 +734,9 @@ Політика конфіденційності Реклама завантажується Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості реклам - Чи можемо ми використовувати ваші дані для відбивання реклами? - Ви можете змінити свій вибір в будь-який час в налаштуваннях додатку. Ми можемо використовувати ваші дані для відбивання оголошень, адаптованої до вас або, використовуючи менше ваших даних, відбивати неперсоналізовані оголошення. Перегляньте нашу Політику конфіденційності для деталей - Персоналізовані оголошення + Чи можемо ми використовувати ваші дані для висвітлювання реклами? + Ви можете змінити свій вибір в будь-який час в налаштуваннях додатку. Ми можемо використовувати ваші дані для висвітлювання реклами, адаптованої до вас або, використовуючи менше ваших даних, висвітлювати неперсоналізовану рекламу. Перегляньте нашу Політику конфіденційності для подробиць + Персоналізована реклама Неперсоналізована реклама Мені більше 18 років Так, персоналізована реклама diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e365a00ca..71d1767ed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,15 +26,11 @@ Student info Dashboard Notifications center - - Semester %1$d, %2$d/%3$d - - Sign in with the student or parent account - Enter the symbol from the register page for account: <b>%1$s</b> + Enter the symbol from the register page for account: <b>%1$s</b> Username Email Login, PESEL or e-mail @@ -78,8 +74,6 @@ Recover Student is already signed in Standard - - Account manager Log in @@ -88,8 +82,6 @@ Application support Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time Enable ads - - Grade Semester %d @@ -152,8 +144,6 @@ You received %1$d final grade You received %1$d final grades - - Lesson Room @@ -189,8 +179,6 @@ %d change %d changes - - Completed lessons Show completed lessons @@ -198,8 +186,6 @@ Topic Absence Resources - - Additional lessons Show additional lessons @@ -215,8 +201,6 @@ Start time End time End time must be greater than start time - - Attendance summary Absent for school reasons @@ -249,12 +233,8 @@ %d attendance %d attendance - - Total - - No exams this week Type @@ -271,8 +251,6 @@ %d exam %d exams - - Inbox Sent @@ -301,7 +279,7 @@ Message does not exist You need to choose at least 1 recipient The message content must be at least 3 characters - Wszystkie skrzynki + All mailboxes Only unread Only with attachments Read: %s @@ -326,8 +304,6 @@ Messages deleted Choose mailbox - - No info about notes Points @@ -343,7 +319,6 @@ You received %1$d note You received %1$d notes - %d praise @@ -357,7 +332,6 @@ You received %1$d praise You received %1$d praises - %d neutral note @@ -371,8 +345,6 @@ You received %1$d neutral note You received %1$d neutral notes - - No info about homework Mark as done @@ -393,8 +365,6 @@ %d homework %d homework - - Lucky number Today\'s lucky number is @@ -402,13 +372,9 @@ Lucky number for today Today\'s lucky number is: %s Show history - - Lucky number history No info about lucky numbers - - Mobile devices No devices @@ -418,12 +384,8 @@ Token Symbol PIN - - School and teachers - - School No info about school @@ -434,14 +396,10 @@ Name of pedagogue Show on map Call - - Teachers No info about teachers No subject - - Conferences No info about conferences @@ -459,7 +417,6 @@ Present at conference Agenda - School announcements No school announcements @@ -475,8 +432,6 @@ You have %1$d new school announcement You have %1$d new school announcements - - Add account Logout @@ -491,8 +446,6 @@ Contact Residence details Personal information - - App version Contributors @@ -516,17 +469,11 @@ Licenses Licenses of libraries used in the application Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nIdentyfikator instalacji: %4$s\nTreść zgłoszenia: - - License - - Avatar See more on GitHub - - No info about student or student family Name @@ -549,19 +496,13 @@ Female Last name Guardian - - Nick Add nick Choose avatar color - - Share logs Refresh - - Lessons (Tomorrow) @@ -580,7 +521,6 @@ until %1$s No upcoming lessons An error occurred while loading the lessons - Homework No homework to do An error occurred while loading the homework @@ -589,11 +529,9 @@ %1$d more homework due %1$s - Last grades No new grades An error occurred while loading the grades - School announcements No current announcements An error occurred while loading the announcements @@ -601,7 +539,6 @@ %1$d more announcement %1$d more announcements - Exams No upcoming exams An error occurred while loading the exams @@ -609,7 +546,6 @@ %1$d more exam %1$d more exams - Conferences No upcoming conferences An error occurred while loading the conferences @@ -617,16 +553,11 @@ %1$d more conference %1$d more conferences - An error occurred while loading data None - - Check for updates Before reporting a bug, check first if an update with the bug fix is available - - Content Retry @@ -654,16 +585,12 @@ Undo Change Add to calendar - - No lessons Choose theme Light Dark System Theme - - App Default view @@ -679,7 +606,6 @@ Grades color scheme Subjects sorting Language - Notifications Other Show notifications @@ -699,7 +625,6 @@ Upcoming lesson notifications You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature. Go to settings - Synchronization Automatic update Suspended on holidays @@ -710,12 +635,10 @@ Sync failed Sync in progress Last full sync: %s - Value of the plus Value of the minus Reply with message history Show arithmetic average when no weights provided - Support Privacy Policy Agreements @@ -735,13 +658,11 @@ I am over 18 years old Yes, personalized ads Yes, non-personalized ads - Advanced Appearance & Behavior Notifications Synchronization Advertisements - Grades Dashboard Tiles visibility @@ -750,7 +671,6 @@ Grades Calculated average Messages - Appearance & Behavior Languages, themes, subjects sorting App notifications, fix problems @@ -761,8 +681,6 @@ Advanced App version, contributors, social portals Displaying advertisements, project support - - New grades New homework @@ -777,8 +695,6 @@ Debug Timetable change New attendance - - Black Red @@ -786,15 +702,11 @@ Green Purple No color - - Download of updates has started… An update has just been downloaded. Restart Update failed! Wulkanowy may not function properly. Consider updating - - No internet connection An error occurred. Check your device clock From 2d83218f618fcb927e9a6247817623ed5562e301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 16 Nov 2022 20:31:02 +0100 Subject: [PATCH 151/164] Version 1.8.0 --- app/build.gradle | 10 +++++----- app/src/main/play/release-notes/pl-PL/default.txt | 10 ++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 99672f1f9..62a5a21fb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 114 - versionName "1.7.5" + versionCode 115 + versionName "1.8.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -160,8 +160,8 @@ kapt { play { defaultToAppBundles = false track = 'production' -// releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS -// userFraction = 0.05d + releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS + userFraction = 0.25d updatePriority = 4 enabled.set(false) } @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:701016eda2" + implementation "io.github.wulkanowy:sdk:1.8.0" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 064401abd..996d5eebc 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,7 +1,9 @@ -Wersja 1.7.5 +Wersja 1.8.0 -- naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości -- naprawiliśmy wyświetlanie napisu "Brak ocen", jesli uczeń nie zdobył w danym semestrze jeszcze żadnych ocen -- naprawiliśmy logowanie do aplikacji rodzicom będącym jednocześnie nauczycielami +- naprawiliśmy liczenie średniej ucznia w ocenach klasy dla wykresu "Wszystkie" +- zmieniliśmy kolejność przycisków akcji w podglądzie wiadomości +- ulepszyliśmy oznaczenie nieodczytanych wiadomości +- naprawiliśmy pokazywanie informacji o odczytanej wiadomości w wysłanych +- dodaliśmy opcję ręcznego wybierania skrzynki pocztowej Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From 6c115fb915cfcc29c1a25e3f94476c473a4e1ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 18 Nov 2022 16:32:04 +0100 Subject: [PATCH 152/164] Fallback to subject name from timetable when attendance item doesn't have it (#2052) * Fallback to subject name from timetable when attendance item doesn't have it * Fix tests * Add some unit tests --- .../java/io/github/wulkanowy/data/Resource.kt | 2 +- .../wulkanowy/data/db/dao/TimetableDao.kt | 3 + .../data/mappers/AttendanceMapper.kt | 9 +- .../data/repositories/AttendanceRepository.kt | 11 +- .../data/mappers/AttendanceMapperKtTest.kt | 143 ++++++++++++++++++ .../repositories/AttendanceRepositoryTest.kt | 27 ++-- 6 files changed, 180 insertions(+), 15 deletions(-) create mode 100644 app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt diff --git a/app/src/main/java/io/github/wulkanowy/data/Resource.kt b/app/src/main/java/io/github/wulkanowy/data/Resource.kt index 44f8a1b48..6b611e477 100644 --- a/app/src/main/java/io/github/wulkanowy/data/Resource.kt +++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt @@ -49,8 +49,8 @@ fun Resource.mapData(block: (T) -> U) = when (this) { fun Flow>.logResourceStatus(name: String, showData: Boolean = false) = onEach { val description = when (it) { - is Resource.Loading -> "started" is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else "" + is Resource.Loading -> "started" is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else "" is Resource.Error -> "exception occurred: ${it.error}" } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt index 5e6eec668..b4b7379f2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt @@ -13,4 +13,7 @@ interface TimetableDao : BaseDao { @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> + + @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt index 46e67fdaa..c0ed0c8c2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt @@ -3,17 +3,22 @@ package io.github.wulkanowy.data.mappers import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary -fun List.mapToEntities(semester: Semester) = map { +fun List.mapToEntities(semester: Semester, lessons: List) = map { Attendance( studentId = semester.studentId, diaryId = semester.diaryId, date = it.date, timeId = it.timeId, number = it.number, - subject = it.subject, + subject = it.subject.ifBlank { + lessons.find { lesson -> + lesson.date == it.date && lesson.number == it.number + }?.subject.orEmpty() + }, name = it.name, presence = it.presence, absence = it.absence, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt index 9aa6562a6..fd5d8bd16 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt @@ -1,6 +1,7 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.db.dao.AttendanceDao +import io.github.wulkanowy.data.db.dao.TimetableDao import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student @@ -9,8 +10,10 @@ import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Absent import io.github.wulkanowy.utils.* +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.withContext import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime @@ -20,6 +23,7 @@ import javax.inject.Singleton @Singleton class AttendanceRepository @Inject constructor( private val attendanceDb: AttendanceDao, + private val timetableDb: TimetableDao, private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -48,10 +52,15 @@ class AttendanceRepository @Inject constructor( attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) }, fetch = { + val lessons = withContext(Dispatchers.IO) { + timetableDb.load( + semester.diaryId, semester.studentId, start.monday, end.sunday + ) + } sdk.init(student) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) .getAttendance(start.monday, end.sunday, semester.semesterId) - .mapToEntities(semester) + .mapToEntities(semester, lessons) }, saveFetchResult = { old, new -> attendanceDb.deleteAll(old uniqueSubtract new) diff --git a/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt b/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt new file mode 100644 index 000000000..a35e5d303 --- /dev/null +++ b/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt @@ -0,0 +1,143 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.sdk.pojo.Attendance +import io.github.wulkanowy.sdk.scrapper.attendance.SentExcuse +import org.junit.Test +import java.time.Instant +import java.time.LocalDate +import kotlin.test.assertEquals + +class AttendanceMapperTest { + + @Test + fun `map attendance when fallback is not necessary`() { + val attendance = listOf( + getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"), + getSdkAttendance(2, LocalDate.of(2022, 11, 17), "Oryginalna 2"), + ) + val lessons = listOf( + getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"), + getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"), + ) + + val result = attendance.mapToEntities(getEntitySemester(), lessons) + assertEquals("Oryginalna 1", result[0].subject) + assertEquals("Oryginalna 2", result[1].subject) + } + + @Test + fun `map attendance when fallback is not always necessary`() { + val attendance = listOf( + getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"), + getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""), + ) + val lessons = listOf( + getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"), + getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"), + ) + + val result = attendance.mapToEntities(getEntitySemester(), lessons) + assertEquals("Oryginalna 1", result[0].subject) + assertEquals("Druga", result[1].subject) + } + + @Test + fun `map attendance when fallback is sometimes empty`() { + val attendance = listOf( + getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"), + getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""), + ) + val lessons = listOf( + getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"), + ) + + val result = attendance.mapToEntities(getEntitySemester(), lessons) + assertEquals("Oryginalna 1", result[0].subject) + assertEquals("", result[1].subject) + } + + @Test + fun `map attendance when fallback is empty`() { + val attendance = listOf( + getSdkAttendance(1, LocalDate.of(2022, 11, 17), ""), + getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""), + ) + val lessons = listOf( + getEntityTimetable(1, LocalDate.of(2022, 11, 18), "Pierwsza"), + getEntityTimetable(2, LocalDate.of(2022, 10, 17), "Druga"), + ) + + val result = attendance.mapToEntities(getEntitySemester(), lessons) + assertEquals("", result[0].subject) + assertEquals("", result[1].subject) + } + + @Test + fun `map attendance with all subject fallback`() { + val attendance = listOf( + getSdkAttendance(1, LocalDate.of(2022, 11, 17)), + getSdkAttendance(2, LocalDate.of(2022, 11, 17)), + ) + val lessons = listOf( + getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"), + getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"), + ) + + val result = attendance.mapToEntities(getEntitySemester(), lessons) + assertEquals("Pierwsza", result[0].subject) + assertEquals("Druga", result[1].subject) + } + + private fun getSdkAttendance(number: Int, date: LocalDate, subject: String = "") = Attendance( + number = number, + name = "ABSENCE", + subject = subject, + date = date, + timeId = 1, + categoryId = 1, + deleted = false, + excuseStatus = SentExcuse.Status.WAITING, + excusable = false, + absence = false, + excused = false, + exemption = false, + lateness = false, + presence = false, + ) + + private fun getEntityTimetable(number: Int, date: LocalDate, subject: String = "") = Timetable( + number = number, + start = Instant.now(), + end = Instant.now(), + date = date, + subject = subject, + subjectOld = "", + group = "", + room = "", + roomOld = "", + teacher = "", + teacherOld = "", + info = "", + changes = false, + canceled = false, + studentId = 0, + diaryId = 0, + isStudentPlan = false, + ) + + private fun getEntitySemester() = Semester( + studentId = 0, + diaryId = 0, + kindergartenDiaryId = 0, + diaryName = "", + schoolYear = 0, + semesterId = 0, + semesterName = 0, + start = LocalDate.now(), + end = LocalDate.now(), + classId = 0, + unitId = 0 + ) +} diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt index 7d22f7265..896491ef0 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.db.dao.AttendanceDao +import io.github.wulkanowy.data.db.dao.TimetableDao import io.github.wulkanowy.data.errorOrNull import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.toFirstResult @@ -29,6 +30,9 @@ class AttendanceRepositoryTest { @MockK private lateinit var attendanceDb: AttendanceDao + @MockK + private lateinit var timetableDb: TimetableDao + @MockK(relaxUnitFun = true) private lateinit var refreshHelper: AutoRefreshHelper @@ -51,8 +55,9 @@ class AttendanceRepositoryTest { fun setUp() { MockKAnnotations.init(this) every { refreshHelper.shouldBeRefreshed(any()) } returns false + coEvery { timetableDb.load(any(), any(), any(), any()) } returns emptyList() - attendanceRepository = AttendanceRepository(attendanceDb, sdk, refreshHelper) + attendanceRepository = AttendanceRepository(attendanceDb, timetableDb, sdk, refreshHelper) } @Test @@ -60,8 +65,8 @@ class AttendanceRepositoryTest { // prepare coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf( - flowOf(remoteList.mapToEntities(semester)), - flowOf(remoteList.mapToEntities(semester)) + flowOf(remoteList.mapToEntities(semester, emptyList())), + flowOf(remoteList.mapToEntities(semester, emptyList())) ) coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3) coEvery { attendanceDb.deleteAll(any()) } just Runs @@ -83,9 +88,9 @@ class AttendanceRepositoryTest { // prepare coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf( - flowOf(remoteList.dropLast(1).mapToEntities(semester)), - flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result - flowOf(remoteList.mapToEntities(semester)) + flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())), + flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())), // after fetch end before save result + flowOf(remoteList.mapToEntities(semester, emptyList())) ) coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3) coEvery { attendanceDb.deleteAll(any()) } just Runs @@ -100,7 +105,7 @@ class AttendanceRepositoryTest { coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) } coVerify { attendanceDb.insertAll(match { - it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1] + it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1] }) } coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) } @@ -111,9 +116,9 @@ class AttendanceRepositoryTest { // prepare coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList.dropLast(1) coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf( - flowOf(remoteList.mapToEntities(semester)), - flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result - flowOf(remoteList.dropLast(1).mapToEntities(semester)) + flowOf(remoteList.mapToEntities(semester, emptyList())), + flowOf(remoteList.mapToEntities(semester, emptyList())), // after fetch end before save result + flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())) ) coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3) coEvery { attendanceDb.deleteAll(any()) } just Runs @@ -129,7 +134,7 @@ class AttendanceRepositoryTest { coVerify { attendanceDb.insertAll(match { it.isEmpty() }) } coVerify { attendanceDb.deleteAll(match { - it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1] + it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1] }) } } From b16036774460ab500303ab7cb8093ee1584aa8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 19 Nov 2022 08:52:35 +0100 Subject: [PATCH 153/164] Add support for reversed student name mailbox matching (#2051) * Add support for reversed student name mailbox matching * Add additional log stmt to mailbox matching in all mailbox load * Revert changes in mapper --- .../github/wulkanowy/data/mappers/MessageMapper.kt | 2 +- .../domain/messages/GetMailboxByStudentUseCase.kt | 10 ++++++++++ .../domain/GetMailboxByStudentUseCaseTest.kt | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt index 8825c5744..87111dd4d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.Message as SdkMessage import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient -fun List.mapToEntities(student: Student, mailbox: Mailbox?, allMailboxes: List) = map { +fun List.mapToEntities(student: Student, mailbox: Mailbox?, allMailboxes: List): List = map { Message( messageGlobalKey = it.globalKey, mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box -> diff --git a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt index d96794e51..a696d9b2f 100644 --- a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt +++ b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt @@ -21,6 +21,8 @@ class GetMailboxByStudentUseCase @Inject constructor( it.studentName.normalizeStudentName() == normalizedStudentName } ?: singleOrNull { it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart() + } ?: singleOrNull { + it.studentName.getReversedName() == normalizedStudentName } ?: singleOrNull { it.studentName.getUnauthorizedVersion() == normalizedStudentName } @@ -43,6 +45,14 @@ class GetMailboxByStudentUseCase @Inject constructor( return endParts.joinToString(" ") } + private fun String.getReversedName(): String { + val parts = normalizeStudentName().split(" ") + + return parts + .asReversed() + .joinToString(" ") + } + private fun String.getUnauthorizedVersion(): String { return normalizeStudentName().split(" ") .joinToString(" ") { diff --git a/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt index 96a84a5a6..029800266 100644 --- a/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt +++ b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt @@ -64,6 +64,18 @@ class GetMailboxByStudentUseCaseTest { assertEquals(expectedMailbox, selectedMailbox) } + @Test + fun `get mailbox for user with reversed name`() = runTest { + val student = getStudentEntity( + userName = "Kowalski Jan", + studentName = "Jan Kowalski", + ) + val expectedMailbox = getMailboxEntity("Kowalski Jan") + coEvery { mailboxDao.loadAll(any()) } returns listOf(expectedMailbox) + + assertEquals(expectedMailbox, systemUnderTest(student)) + } + @Test fun `get mailbox for unique non-authorized student`() = runTest { val student = getStudentEntity( From 650cbd5a10f8d6e27242f7306daa546cab7ed4ad Mon Sep 17 00:00:00 2001 From: Damian Czupryn <60961958+Daxxxis@users.noreply.github.com> Date: Sat, 19 Nov 2022 14:11:26 +0100 Subject: [PATCH 154/164] Add info about optional ads in README (#2054) --- README.cs.md | 2 +- README.de.md | 4 ++-- README.en.md | 2 +- README.md | 2 +- README.sk.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.cs.md b/README.cs.md index 2b0dc12ea..d3d4e2557 100644 --- a/README.cs.md +++ b/README.cs.md @@ -34,7 +34,7 @@ Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče * podpora více účtů s možností přejmenování žáků * tmavý a černý (AMOLED) motiv * offline režim -* žádné reklamy +* volitelné reklamy na podporu projektu ## Stáhnout diff --git a/README.de.md b/README.de.md index 6df10ecd0..853abd13e 100644 --- a/README.de.md +++ b/README.de.md @@ -21,7 +21,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre * Prozentsatz der Anwesenheit * Prüfungen * Stundenplan - * Unterricht abgeschlossen + * abgeschlossene Unterrichtsstunden * Nachrichten * Hausaufgaben * Anmerkungen @@ -34,7 +34,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre * Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern * dunkles und schwarzes (AMOLED) Thema * Offline-Modus -* keine Werbung +* optionale Werbungen, die es uns ermöglichen das Projekt zu unterstützen ## Herunterladen diff --git a/README.en.md b/README.en.md index 417b74de0..7877bf377 100644 --- a/README.en.md +++ b/README.en.md @@ -34,7 +34,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par * support for multiple accounts with the ability to rename students * dark and black (AMOLED) theme * offline mode -* no ads +* optional ads which allow to support the project ## Download diff --git a/README.md b/README.md index 75b6cfca2..09480e7d7 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica * obsługa wielu kont wraz z możliwością zmiany nazwy ucznia * ciemny i czarny (AMOLED) motyw * tryb offline -* brak reklam +* opcjonalne reklamy umożliwiające wsparcie projektu ## Pobierz diff --git a/README.sk.md b/README.sk.md index 240f8835c..64786556e 100644 --- a/README.sk.md +++ b/README.sk.md @@ -34,7 +34,7 @@ Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov * podpora viacerých účtov s možnosťou premenovania žiakov * tmavý a čierny (AMOLED) motív * offline režim -* žiadne reklamy +* voliteľné reklamy na podporu projektu ## Stiahnuť From fdce2cf477cc98d4035431e1419e046d1db6d2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 19 Nov 2022 22:50:51 +0100 Subject: [PATCH 155/164] Improve error handling in horizontal tile on dashboard (#2053) --- .../wulkanowy/data/mappers/MessageMapper.kt | 10 +- .../data/repositories/MessageRepository.kt | 14 +- .../ui/modules/dashboard/DashboardItem.kt | 19 ++- .../modules/dashboard/DashboardPresenter.kt | 82 ++++++----- .../dashboard/adapters/DashboardAdapter.kt | 136 ++++++++++-------- .../wulkanowy/utils/ContextExtension.kt | 7 + app/src/main/res/drawable/ic_error_filled.xml | 9 ++ .../item_dashboard_horizontal_group.xml | 53 ++++++- 8 files changed, 228 insertions(+), 102 deletions(-) create mode 100644 app/src/main/res/drawable/ic_error_filled.xml diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt index 87111dd4d..120eb183a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -6,12 +6,18 @@ import io.github.wulkanowy.sdk.pojo.Message as SdkMessage import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient -fun List.mapToEntities(student: Student, mailbox: Mailbox?, allMailboxes: List): List = map { +fun List.mapToEntities( + student: Student, + mailbox: Mailbox?, + allMailboxes: List +): List = map { Message( messageGlobalKey = it.globalKey, mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box -> box.fullName == it.mailbox - }?.globalKey!!, + }?.globalKey.let { mailboxKey -> + requireNotNull(mailboxKey) { "Can't find ${it.mailbox} in $allMailboxes" } + }, email = student.email, messageId = it.id, correspondents = it.correspondents, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index f8be4296d..f95b8dbec 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -3,7 +3,7 @@ package io.github.wulkanowy.data.repositories import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R -import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.dao.MailboxDao import io.github.wulkanowy.data.db.dao.MessageAttachmentDao @@ -14,9 +14,7 @@ import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED import io.github.wulkanowy.data.enums.MessageFolder.TRASHED import io.github.wulkanowy.data.mappers.mapFromEntities import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.data.pojos.MessageDraft -import io.github.wulkanowy.data.toFirstResult import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Folder @@ -194,7 +192,9 @@ class MessageRepository @Inject constructor( it.isEmpty() || isExpired || forceRefresh }, query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) }, - fetch = { sdk.init(student).getMailboxes().mapToEntities(student) }, + fetch = { + sdk.init(student).getMailboxes().mapToEntities(student) + }, saveFetchResult = { old, new -> mailboxDao.deleteAll(old uniqueSubtract new) mailboxDao.insertAll(new uniqueSubtract old) @@ -207,7 +207,11 @@ class MessageRepository @Inject constructor( val mailbox = getMailboxByStudentUseCase(student) return if (mailbox == null) { - getMailboxes(student, forceRefresh = true).toFirstResult() + getMailboxes(student, forceRefresh = true) + .onResourceError { throw it } + .onResourceSuccess { Timber.i("Found ${it.size} new mailboxes") } + .waitForResult() + getMailboxByStudentUseCase(student) } else mailbox } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt index e220ae236..d019dea68 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt @@ -33,18 +33,27 @@ sealed class DashboardItem(val type: Type) { } data class HorizontalGroup( - val unreadMessagesCount: Int? = null, - val attendancePercentage: Double? = null, - val luckyNumber: Int? = null, + val unreadMessagesCount: Cell? = null, + val attendancePercentage: Cell? = null, + val luckyNumber: Cell? = null, override val error: Throwable? = null, override val isLoading: Boolean = false ) : DashboardItem(Type.HORIZONTAL_GROUP) { + data class Cell( + val data: T?, + val error: Boolean, + val isLoading: Boolean, + ) { + val isHidden: Boolean + get() = data == null && !error && !isLoading + } + override val isDataLoaded - get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null + get() = unreadMessagesCount?.isLoading == false || attendancePercentage?.isLoading == false || luckyNumber?.isLoading == false val isFullDataLoaded - get() = luckyNumber != -1 && attendancePercentage != -1.0 && unreadMessagesCount != -1 + get() = luckyNumber?.isLoading != true && attendancePercentage?.isLoading != true && unreadMessagesCount?.isLoading != true } data class Grades( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt index cb92b0043..22b0d267e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -226,50 +226,71 @@ class DashboardPresenter @Inject constructor( private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) { flow { - val semester = semesterRepository.getCurrentSemester(student) - val mailbox = messageRepository.getMailboxByStudent(student) val selectedTiles = preferencesRepository.selectedDashboardTiles - val flowSuccess = flowOf(Resource.Success(null)) + val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh) .mapResourceData { it ?: LuckyNumber(0, LocalDate.now(), 0) } + .onResourceError { errorHandler.dispatch(it) } .takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess - val messageFLow = messageRepository.getMessages( - student = student, - mailbox = mailbox, - folder = MessageFolder.RECEIVED, - forceRefresh = forceRefresh - ).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess + val messageFLow = flatResourceFlow { + val mailbox = messageRepository.getMailboxByStudent(student) - val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary( - student = student, - semester = semester, - subjectId = -1, - forceRefresh = forceRefresh - ).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess + messageRepository.getMessages( + student = student, + mailbox = mailbox, + folder = MessageFolder.RECEIVED, + forceRefresh = forceRefresh + ) + } + .onResourceError { errorHandler.dispatch(it) } + .takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess + + val attendanceFlow = flatResourceFlow { + val semester = semesterRepository.getCurrentSemester(student) + attendanceSummaryRepository.getAttendanceSummary( + student = student, + semester = semester, + subjectId = -1, + forceRefresh = forceRefresh + ) + } + .onResourceError { errorHandler.dispatch(it) } + .takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess emitAll( combine( - luckyNumberFlow, - messageFLow, - attendanceFlow + flow = luckyNumberFlow, + flow2 = messageFLow, + flow3 = attendanceFlow, ) { luckyNumberResource, messageResource, attendanceResource -> val resList = listOf(luckyNumberResource, messageResource, attendanceResource) - resList.firstNotNullOfOrNull { it.errorOrNull }?.let { throw it } - val isLoading = resList.any { it is Resource.Loading } - - val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber - val messageCount = messageResource.dataOrNull?.count { it.unread } - val attendancePercentage = attendanceResource.dataOrNull?.calculatePercentage() DashboardItem.HorizontalGroup( - isLoading = isLoading, - attendancePercentage = if (attendancePercentage == 0.0 && isLoading) -1.0 else attendancePercentage, - unreadMessagesCount = if (messageCount == 0 && isLoading) -1 else messageCount, - luckyNumber = if (luckyNumber == 0 && isLoading) -1 else luckyNumber + isLoading = resList.any { it is Resource.Loading }, + error = resList.map { it.errorOrNull }.let { errors -> + if (errors.all { it != null }) { + errors.firstOrNull() + } else null + }, + attendancePercentage = DashboardItem.HorizontalGroup.Cell( + data = attendanceResource.dataOrNull?.calculatePercentage(), + error = attendanceResource.errorOrNull != null, + isLoading = attendanceResource is Resource.Loading, + ), + unreadMessagesCount = DashboardItem.HorizontalGroup.Cell( + data = messageResource.dataOrNull?.count { it.unread }, + error = messageResource.errorOrNull != null, + isLoading = messageResource is Resource.Loading, + ), + luckyNumber = DashboardItem.HorizontalGroup.Cell( + data = luckyNumberResource.dataOrNull?.luckyNumber, + error = luckyNumberResource.errorOrNull != null, + isLoading = luckyNumberResource is Resource.Loading, + ) ) }) } @@ -280,11 +301,8 @@ class DashboardPresenter @Inject constructor( if (it.isLoading) { Timber.i("Loading horizontal group data started") - - if (it.isFullDataLoaded) { - firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP - } } else { + firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP Timber.i("Loading horizontal group result: Success") } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt index a3c423a8b..2c06e45fd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt @@ -171,81 +171,105 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { + updateMarginsRelative( + end = if (isAttendanceHidden && isMessagesHidden && !isLuckyNumberHidden) { + 0 + } else context.dpToPx(8f).toInt() + ) + } + } + } + + private fun ItemDashboardHorizontalGroupBinding.bindMessages( + item: DashboardItem.HorizontalGroup, + isWideErrorShow: Boolean + ) { + dashboardHorizontalGroupItemMessageError.isVisible = item.unreadMessagesCount?.error == true + with(dashboardHorizontalGroupItemMessageValue) { + isVisible = item.unreadMessagesCount?.error != true + text = item.unreadMessagesCount?.data.toString() + } + with(dashboardHorizontalGroupItemMessageContainer) { + isVisible = item.unreadMessagesCount?.isHidden == false && !isWideErrorShow + setOnClickListener { onMessageTileClickListener() } + } + } + + private fun ItemDashboardHorizontalGroupBinding.bindAttendance( + item: DashboardItem.HorizontalGroup, + isWideErrorShow: Boolean + ) { + val attendancePercentage = item.attendancePercentage?.data val attendanceColor = when { attendancePercentage == null || attendancePercentage == .0 -> { - context.getThemeAttrColor(R.attr.colorOnSurface) + root.context.getThemeAttrColor(R.attr.colorOnSurface) } attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> { - context.getThemeAttrColor(R.attr.colorPrimary) + root.context.getThemeAttrColor(R.attr.colorPrimary) } attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> { - context.getThemeAttrColor(R.attr.colorTimetableChange) + root.context.getThemeAttrColor(R.attr.colorTimetableChange) } - else -> context.getThemeAttrColor(R.attr.colorOnSurface) + else -> root.context.getThemeAttrColor(R.attr.colorOnSurface) } val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) { - context.getString(R.string.dashboard_horizontal_group_no_data) + root.context.getString(R.string.dashboard_horizontal_group_no_data) } else { "%.2f%%".format(attendancePercentage) } - with(binding.dashboardHorizontalGroupItemAttendanceValue) { + dashboardHorizontalGroupItemAttendanceError.isVisible = + item.attendancePercentage?.error == true + with(dashboardHorizontalGroupItemAttendanceValue) { + isVisible = item.attendancePercentage?.error != true text = attendanceString setTextColor(attendanceColor) } - - with(binding) { - dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString() - dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == 0) { - context.getString(R.string.dashboard_horizontal_group_no_data) - } else luckyNumber?.toString() - - dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoadingVisible - dashboardHorizontalGroupItemInfoProgress.isVisible = isLoadingVisible - dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null - - with(dashboardHorizontalGroupItemLuckyContainer) { - isVisible = luckyNumber != null && luckyNumber != -1 && !isLoadingVisible - setOnClickListener { onLuckyNumberTileClickListener() } - - updateLayoutParams { - updateMarginsRelative( - end = if (attendancePercentage == null && unreadMessagesCount == null && luckyNumber != null) { - 0 - } else { - context.dpToPx(8f).toInt() - } - ) + with(dashboardHorizontalGroupItemAttendanceContainer) { + isVisible = item.attendancePercentage?.isHidden == false && !isWideErrorShow + setOnClickListener { onAttendanceTileClickListener() } + updateLayoutParams { + matchConstraintPercentWidth = when { + item.luckyNumber?.isHidden == true && item.unreadMessagesCount?.isHidden == true -> 1.0f + item.luckyNumber?.isHidden == true || item.unreadMessagesCount?.isHidden == true -> 0.5f + else -> 0.4f } } - - with(dashboardHorizontalGroupItemAttendanceContainer) { - isVisible = - attendancePercentage != null && attendancePercentage != -1.0 && !isLoadingVisible - updateLayoutParams { - matchConstraintPercentWidth = when { - luckyNumber == null && unreadMessagesCount == null -> 1.0f - luckyNumber == null || unreadMessagesCount == null -> 0.5f - else -> 0.4f - } - } - setOnClickListener { onAttendanceTileClickListener() } - } - - with(dashboardHorizontalGroupItemMessageContainer) { - isVisible = - unreadMessagesCount != null && unreadMessagesCount != -1 && !isLoadingVisible - setOnClickListener { onMessageTileClickListener() } - } } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index dd91d36d4..cc4c5aaa4 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -2,9 +2,11 @@ package io.github.wulkanowy.utils import android.annotation.SuppressLint import android.content.Context +import android.content.res.ColorStateList import android.graphics.* import android.text.TextPaint import android.util.DisplayMetrics.DENSITY_DEFAULT +import android.widget.ImageView import androidx.annotation.* import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils @@ -12,6 +14,7 @@ import androidx.core.graphics.applyCanvas import androidx.core.graphics.drawable.RoundedBitmapDrawable import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.graphics.drawable.toBitmap +import androidx.core.widget.ImageViewCompat @ColorInt @@ -85,3 +88,7 @@ fun Context.createNameInitialsDrawable( return RoundedBitmapDrawableFactory.create(this.resources, bitmap) .apply { isCircular = true } } + +fun ImageView.setTint(@ColorInt color: Int) { + ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(color)) +} diff --git a/app/src/main/res/drawable/ic_error_filled.xml b/app/src/main/res/drawable/ic_error_filled.xml new file mode 100644 index 000000000..61b575dc6 --- /dev/null +++ b/app/src/main/res/drawable/ic_error_filled.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/item_dashboard_horizontal_group.xml b/app/src/main/res/layout/item_dashboard_horizontal_group.xml index 1d43d5115..0c59d1ebf 100644 --- a/app/src/main/res/layout/item_dashboard_horizontal_group.xml +++ b/app/src/main/res/layout/item_dashboard_horizontal_group.xml @@ -37,9 +37,25 @@ app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" + app:layout_goneMarginEnd="16dp" app:tint="?colorOnSurface" tools:ignore="ContentDescription" /> + + + + + tools:text="16" + tools:visibility="visible" /> @@ -145,9 +178,25 @@ app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" + app:layout_goneMarginEnd="16dp" app:tint="?colorOnSurface" tools:ignore="ContentDescription" /> + + - \ No newline at end of file + From 9c60ce688b48226e7ffe44f5029a89de8744f7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 20 Nov 2022 00:05:34 +0100 Subject: [PATCH 156/164] Version 1.8.1 --- app/build.gradle | 8 ++++---- app/src/main/play/release-notes/pl-PL/default.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 62a5a21fb..f6a82ad20 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 115 - versionName "1.8.0" + versionCode 116 + versionName "1.8.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -161,7 +161,7 @@ play { defaultToAppBundles = false track = 'production' releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS - userFraction = 0.25d + userFraction = 0.10d updatePriority = 4 enabled.set(false) } @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.8.0" + implementation "io.github.wulkanowy:sdk:1.8.1" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 996d5eebc..7b2fda861 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,4 +1,4 @@ -Wersja 1.8.0 +Wersja 1.8.1 - naprawiliśmy liczenie średniej ucznia w ocenach klasy dla wykresu "Wszystkie" - zmieniliśmy kolejność przycisków akcji w podglądzie wiadomości From 277ffd22be786cfa429ea1d61827e8cb62d1ea56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Wed, 14 Dec 2022 22:41:57 +0100 Subject: [PATCH 157/164] Fix app name in french (#2072) --- app/build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f6a82ad20..fdb844aca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -237,7 +237,7 @@ dependencies { implementation 'com.github.bastienpaulfr:Treessence:1.0.5' implementation "com.mikepenz:aboutlibraries-core:$about_libraries" implementation "io.coil-kt:coil:2.2.2" - implementation "io.github.wulkanowy:AppKillerManager:3.0.0" + implementation "io.github.wulkanowy:AppKillerManager:3.0.1" implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 8958 zcmbPmk$LT9<_#=XtSSLBqeCZiTItn?SJsGx%l_Z@#_)7XgUOT&thbhl-9GCmma$Mp zfJLb5qCf^y%MuQsGWFS>Y3bK)MlyX}a4V|6>bmw?EPjKcDQaeE;{|{qNta^Uwc%ez2Wk%|ZKT3nj()HRa+?G@o^z z#a{33#%(0TcbX$H!*E`x+8lid*+u?#Xa*<_c|Vv{-}1ZvRL)q!RQ$u*6yi^R!yp? zR(({#&Y!G!=k)FOQ$FhLi9W^dfF5oWYsIVdY#^byTbI z>C-JGGcw&xd6wN?F?-i$&j;sD% zWqivkZTK8;_hjbH)witH7tgNy7Ub~Kz*i`L#mg;jr&p(aOgWlWbj!bfl7@Ha#9f!3 zXzg;pX13(0n@Y{4skVEICoXxKDn4nF!A;L)%L<-!8^^HJ>dp(gwXyj2#2M30J&o{N z^{peV`pIWs-|pqN*9KnRaK*y#oA9+uuNGX3>oPsMF=+0s#Ip)ElRk&eE3q)FS(@s8aqVMtZj)|dU zW7=#<<25FZneESbA`3a^hkal;CYp2jESK4$(1U7n^P10cZA)0)WVS{7MxV|}t&pV; zOzO)xbu?RFT}jw-?Z3w z?MRhZb?kp!E>ySv0soK1P3j-iS?iC*ckJLX;0(+SJJxZ1*0kW+hWVF+w}v=u?Q@-+ zA;eLdopsA7+T&IH&4>FRzUP{EWXXX`FJF|V`?&jO1v^=WJvrpZ_vzEkuBlt5C%HX1 z73rv4@K<$OhF$&3o!4A*ckhr{@zN*1#6rqc@ANtMlUr9PczNrun-Ul#E2n=^k8hd%w!nl`a;OYACRm-@!QWJ6tF@vB<9 zRtXjFYwEf?!#Vxj%Gk46(&@p`XHMRGQhD7V|7Xm!=elQ888=n;EtBnCS2REEcB}Xi zy-#Np|C^XbCVb^xv$OQvtZv1PD|+~xg1W-l9#$4wE(vIAF3i)IawawPivRs^q5GlL zIdk(KdwgVGxb92tl=9cd(s?rKf6A0cb+&L{Q!03_H{<33*?lVvPPHVeF7u5(y)1Xm z?o8RA4LLhJ=FjGf(6IYjS?YPDtMRZWN8sDwz3;cZNR14d)+!MD{Y=_)gYz{uEnik1 z3m4Y3IdAT=Xky!ylIdkFRkQDxEZ@UuzL8P;d6Gl@&u_8otgHSm`CuFJVaaUH^)%s_KVBXFSNP-g;l@IiSBDQ4vb^?p-c>(eL;Hh`RPz!3OFwF)vNQTs76`{S z{60GCh2uegCM#vzea##j=5hVhajN~wzd)(GGF|ceT(_m$gEq~G-`mipBQ|eYx>< zn|9>zyq4ayLhf+ZErC5RWp!p9cxo-BzM+CgaaNY!yhB2>)}K)F`Qlw|yYT9Fr%&?j z_pf_p|7V$2TCXdA_{17Do%u!!kIH|0?$XH_GgEo(zAlciv_ezC)WFxWafcrr{%2e< zjX!a}`rBLg_8b+CxcF$EUGxG~0lqm0OPP{0p00n*YOC+8dr(MKFi41R((HMrmaY#U zu)M13|6gvTZaKlVR*ZK||G$?T49f!^u8ggeX^&!3U9?!`(&h>Et>NoGP1|>;sC42X zrfsG(4H%EhChg*~g~Gv2fnJ ztNqCyHk`44gbzPrnxwakU2*cVRFztD#UCtJugI;^vp<=qpu3i1(vs+l-rHCuu3Elv zKa%`nad2Z`A^+8DYmZd>-0A)MWGcUZXVxADc8^n)Cc7pvTX@89U93)T*AUXGf4iow zhVi#bi%N-oFY9OX$+3MC3t)l+!mfOrf>jS)* zS-2P&I5-#>7#tbaJa}-tmWhEul8u2uck*&4+xojq*Yf{3t-K_ba=KlGCD8e5LeLkc z%PjAgoemJ&rh7B@YW)*l@mU@wvnpP;+MlXOE6em=xzP8radFx6+H=)qY2VM^m*3Bz zlW@~PrcyFZNF*(_LgTWfEUV=?X^#ZQ<4VneUw`yzn@O1-G>E$O{Qb_SCTphmX@|W$ zq*s1tnz~ti>-*1IYdzStW8&6FMf;v#8J;;+Ze5Z={MEl|<#!*=+rS?ZB_4P2=|26M zwykd>OV?fbuqrI!(60v{4?91MR8+m}yVxpf<3?xKV_tr-d-ASo9jZR9y{_o$hMm#R z<|%*vuqe*jaP4p1+sj%KRjYR$PF!_d-nw-8&z8hh?3b3WG@4nT`_3|?a0XY~ z-&iHJE6zH*H`ycCbZ6~`ll+I5@vO5tc}ARzXYwf>GD& z!mSGnE~{%)UYho5^<2%#$HEprz3DXVt;_~XPQyahaNnLsO`O7-!fcb&@*Yj=Q*C?6 zC_8gU+iN46B#HMG)xkH#%jzd5KKd`Q)ic%SlJwLC;`&WnN*p$C+2WksR8+)nwQ!2A z<6_B#KQ1-1pYC`3za~Un^Vn0_ZC&0b=FOoy)=0GFY?4cI)THS?vANzh-D?RTF_OiKq?@gYewZF`p z;;^GK$F9D+tlq7#CGbB3QkoQCaAXKO)6%(5lz~B9pMgPPvY?ghlVeK9j{gW?rUY?Qm+_KjDXjHa%b|&|=sOetfQ3tQF z^l)XpzE*p6TiN!t+qQ14-kP!R|IYk0BTu%*e|Gg1?`Kz^xBag3zxdrwbN~JS4jgs~ zc|2>)WjQKN_R! z+k4KtVx?dCXQRw%*?XLp&#c*ZVf~dI*Upz(*&bPKQa9Nz{bSZ#xo6xdPtJY{)sN3M z$-AI=BGqdBQ@6QN6?6aW37GZul8Nu4{s%|^{;P%p;F0*0}9iOsPw(i}O ziF&=dy}SS3dHg(1lH=u;HvOs3jONyzPwZ8ea(`;P?6T{f<(Jv&+K&~^HRJrwJLkh@ zUHeIwOfqYB`rWSBe(bLm{|C`aF)M>-_v{H?J~!ss^K#Gn|9P3SO7?ZF|MuC`D*p8Q zlaF6l^sgw{JO94+UtXmPzfJZ9E|1%orN6&k$oRg>S$30u3;YECP00A1`16+P74z&r zg1z|{H5IuZ{RxcR8lGXdY_oUGMeVb4|8^Xk|KYRU+;va7Q}t)m?fAx+<$_*raFSg{?w;kr)8}>Z)Rr-}K^OmzNhrA3}ubHh|egE2?uama=u8rPq zvi9b-t(n26RcF86zApV#{=O@F?yTXOeR1BZFl)K4Z&wU`^Ex>9uh@H+M_t$c{>dlY z$E&K=SX`d-EMm{{RqA89t;p53|m(R9gm>&MeFqqChk>t|=C`tjbHBggla@2kq=WwKqauf1fJ zwiE%t_;+$)b$?{hyITo!4sy|QWbw%kl6Pe(J)?DdhGb1Jiq z4w=~MtXh9|)|DT6Jm&rKX;D>P+WIp#a9L+>`g|+EM)}H$b8WY3+*kP59iDce^Q!u3 zA*uVk+Y{el(alVX5v~`ndJvL)0WRQ zIn1S1*%@JUFLPzXjFhk6BkSjGySg;(Pa^x|0Mi-L{x?@uv+63$-Bql!((V3nwY3Z9 zt;ke=e52;0W`*wtwuVU?-W;n-I4{PWx9!oy03}7i1__z-yj{Ckx;Gm?=3B95o$j=o zPgO3r+!gh{CAnwS+UrLj30~eN+Nj?1gx#)I*5gU!u{$#To>6g=d0J)t?i@Esu2*V2 z6OnuC{Y^vEMwf2Q1E*haIy7y`?Fzo%4FTRNS3fdoems5uCU@y0u?E}Z^mR55+3uKD zN$UBzt>%nVB`Vfv)z(Q*MaOJ+VwdT>3{=R{ENJ1?}xdp?$xUte-zKTPQ43 zuqFE7{d!rk<*%199d0_>EpGhviOrnL>OcRdSCxL%pSCP&)~rCyXGQKRa%+Ui8rFM?MvAC1Pv#>C~Oh)Ums~`QX+4DJ9dV zZt}h>sjO(t=;O6>EEyUcI^LMK{}BCwbEsX@^)}c(dI<-Ogio=6mL! z)Sn+ko8LcoY;La*2z@WTU~kk6S;HS1_6(w%OkJX~V&mf*gU&uktP|L&S#74>agcG> zC+9yK4j%up<6!>>$7cEZr^hEK6^r{x)n9$E{^Mgd`-A>ce?;tc&tB*K$96ycgZWF> zWwGK~mCZMg`W6HRCIxQLPE&2VI78N@H+S~Jj0-({4LW^I3Tlztjc&|X#n)7(bE9{& zddAnq6FB%;RvVWvPxa_td?@$T4w1ub_1*h(Ka~I2%~sz(Kc(unXRF8Cx%CIcHYPl& zpM2oK`_KF){}vv!f0RDMY)({i;ao}aWs-NBEyEf=Z~l|q%y&tg_aEo~D-WJ01iibf z*BZ4Vc&_xpi1Kwy59SGnnGPzS*!Q--UA$KBVrT#PvAYMHOy-BkNgqF(G4v*Qaxg=$NIwH}UJj7<5P*_Nhpw+RJU z>s@O;y*tJ8LXY#75IL4zSISmCJ@~Fdhw1l?k10+z52y4`T@+!kTZMnoqBXo?s$Gj- zwn$B>idZ)1VUNCb@YLM3pSW(Yybi6jN<8#n{>R!CC)_@6Rh8W8ET|o9>3QL=OzD}7 zZ)^MOkDq1qJjnLHDbD1_^gr6o^}6x<8<}6Sc5?1j7V$m0(V#-E^z_NBeTFam?kDe= zyLX~Qm&y08)YClwbdz{v>e4^F|H;}MFLL1gsn~8K)*_jE=jI%&e_+qG*xvK=C5fd* zTUQ;d7wfJ#GEXSA-g9#AT1(!#$P>@@7v5a?F`oCI^#A(159&SM>CBmt9Hgd^+o5_m zR;wsrhBDjbsg)b@x0vvk9e=dd@udC#D?j4d{zuC!J}NXd=tssM`R4e<{$uN<{>0lQ ze#rh~wXED)bnQW9J)@X6MGHkvMedONvg&m14x54{r(b_ss%x__$~b1$5qjKrvtS)$_WL|$fk zO0QU$eeTafrCz#6~ke+z6HCt@?+Pxod$qOqdhq@&E~@Tg5kT%X0p{;Er@G4EE#sSy23XW3}kE zRR4{0ZG_Kg=FRY!Um2opzxH%y+nxO5mx?kLx+E_-f2{Z3^|j~ZO5JB4$a(I<{anPp z*t&kjwA%+~7j>2XGWYaXDq&sox=md@Lg(@Gn8f{RVw(k-((b!Wsad;Ca{E!{;H4XK z%q9m;k^ji6=JxD)-A1Y2Nj-L^D{sHH_;mUD(e%B{Q~i_fa68hl$#u4qH^StQr@=3Zt>@~+)X>-*4lj7;b>Zzn*JHDfaLfY>Cfxf zCjL$9@mm_lka|Tw@b;Q{Kf~q)>l&@66`+OF^mMX5+k#p|2TUee0O~(`@-Vu1u!q52k*0nrFX#Yudke z(@X=GPYNl{G2gUx=83A$r8-Strl5O;_{qhO0|fT+Lrz*SEb+%{IfJ?emvs-7(MVZQirH6&gPj zT&orR)Np;XI`fKk0rNAsJ##9`AFFJ9A#=-F)#b@jhQ0lHD$Bk{`_=I(e_{V3eTKJjza(z;KfZqT!2`$r3radZd9{1ypPz3scb<&W-dLZ$EHuNuS97Hfc@$nk6$scqOwk>~t4?&d_{&*k|S8$=@!ob$R8T zlJr$IXy=vbC!QXAq5jgWr^CeEL!I~Ut4)rxb_t%{GVRj(3nDc>>%X$tG*5|tJvGm5 zE&t=m%=;2&-Hem^z`EyYs;F~_fyd^);D-wfi`F&tJ#Q}7Oy#AXYSB}iG&9_dsW%zYh?@a$?QgtEzweE)hg^DH-4&jwAy{m*yrFkMXTl4UdO%D z{=jO(f8Vov`DKp-!4IN8y;Cm9TPt>f?WN86<(WYTtaCpq=w@(Nq^?-;)=FMp;^(xS z?H>eM=5QU3VPMo>P<3_p)SKIwRb0%jcr_)NFSyJv&VS)%39IfR?Ui%xb(V%7o^UeVY9b+&ukqh45LG1nVdzo>e(_1VpUUz$T;n}!fTs*xJ%*%gg2Ar(3yK-RpScs~`ffO{ z$8y1L>H0|q$(!?7gTvzc4{p;dxLNNSzjXbm-|Hf{U1YVD}ojiG^?uUY$O-(tR}X!ZZbL!MVMzV91c$}g(yl4kraY4KjJ=YIE< zqI!OT@)_3{Sbv8s_!Z(&&m2|hxVLFvr@p5ZujE_l7tvaVr>3*4^scRqX8qi(Rr;y^ zpuz3C_ZPljW+LOLKXG^Bj4-8ZS*ns(-!2Y)5w_LgwW{c&L+#V86(b((oqTTMht-8n z?|pxt(BRK-&)nKQXTtBwpB2;j8vhH<_^BaM+mOawzvZexwyG`5%r-&4{$^xydiae}+CL@ekgf z^J)0euGRUEG=1O1yQ!&PROf>G#hRZ&|qLZawjAcEank z8y4nIxOXY;b^RPJ=BR&S>P}4mt~qAqI){k}=jU2F6w7YA{ju}&Lyoo|bEpI{Ys{O1h4>vyKn^oWXtuW#@=Z*K8 zvlzFCGh}!(Y>aZq;#Ii9w&YEdx?Z#Xx~7%iE)+lb!n5G3j^Fm@2i_j%xIF)Wy*=xK zdNzgsGXMT8{gfg9;8t7l=i@V`PtayMTYO{f^k8%52kg-^wFTYFV7+UuWHVi_`j|Pvn-Ke!{WwiT0TQYo*SO z8}`ODDOI&31+2fOZvSh7zlz#D#xM4^K7}oQi)1=7cS_bmWYGj|E*G!)?ahCqzFdDKu-|O*>TL?o zPZZ8{|L?l4inpHgXQ`Icp@_ycg^asbOuV~gUhS6VzgNy$napwt{NnlLZ_b6OHm&O( ztt`>~C4FK4{NFM$Pp|V?6_+7om|woW7)hd*E6&pc_-KPp1ZEW6jR@y@cB)^1kZH0 z=Aa)o|Kv|qPmNjSaY9D$w#W464@7)!D=y(Ml0W`U^moQLgV>w%{`;wiPd#O5?(*I$ ztV*N#h~}him3o;r{m(w#JjK&?xKZ_hXXk4%q1bsH!M9^LWm{M5Dw%L^?|iYT&*Duz z!rvd?_VfCR{-#TRvIUEOpVT;8U(av%ZQ?Ua2f6PVJS&W5oM*nxYSP@j;kT1Tp`h-u z8!JSvMh5*j5&Nvp_)w7Cnqc=wxf>c=QZJTBT)Q8{rgJto{@u>0%cg{{X!^Q~Sy^B4 z$1~-N-{j@CS4#feF=<(`sM&hQom*SPK3rO}XsO2Ihe`{ccL=TEkf?GM)%BbBqK%Wg zzHw!B@I2EKrA}SDlhs!=xX(2?dwz1G+3~902-W3F!=l=Zj@nn5epspNHHE{mo#jJ` z%LRrY_7o}q>AN^}K3BSRrHWR~{CtXW^6Z7l0qYM7bf*_N_8r}KIohV1JGeVLSn~Sv ztP6Km&t-Xi`o*K-62|#AWlkJDUi^XQ=bWR(K^>_#c$&Q{ekI1l?7!1qTvVjj{EtW7 z-omnUru?@`tG62Bo5nsuBgkJztOMc>Ah#Yb2VWD@YWj5l?#3_Y1MdB21dReucDN(8IUtI$5xOcO?6$WI${Ym) z0xW6lcA8wMBRYAnlhoulw*|Nm)=Z05O1!DE5RKF_3>9&Q2)bwBf9lHN9X;Yy{+-=7&Um7bW~cwT9;_j3cLrVEoB&+AQ& gc_1)(_j4(xWtS&kf1buvb#=1+3vV{x>!5H30K|+;4gdfE delta 8722 zcmZ2`nR&uR<_#=Xtd=<A22h#!CTC;u*7}a*FjIYPB48 zax%;nQc!GRSu*zx-)zOr#;@P#G*$)Vu65u0dTQ1D2-l@Ia-1$6`|XguH~(VTT6f#m z^5248YxC30Z{F3>G@O6_-?{R?_kQpD{@eH5-{%Y48P^=Lf0oFslQ-$ko(a;kXN#Ek z_0)?kQI(k}c&MXgu~EvB%Ay|k?d>0w-o>BM)_?eH=^dHp&-f%c_xQM($DKPn_j7@G z+`+}WQ}+n1Pnr~dx`Ou|=fkZ&zjg`4o!@j%;c;(;@cKPFWxhYs_4yGtZ{J#(z&(3q z4EHq8z9{hS@ze5C72@}FlP9e8cwl>HPxztVP8IbW^7R}Kn-yz3+=T7UNXsW?s;kt5 z9J9Kkf0@%>?crk~Kd&Dx4o3yAMkGcAUX{2o+5Baj+J*&>H&nUwz6fi1F3oysrrNUQ zbIk6FMNFA}^H;*Hi@KYyn=PB+bGGwK*>>T}sXk&`&p21v^Q%=`Usqk?Z1nPz}n@J$v?Ta5mq%t|Td7 z^Y<0aGVQ1M_)f=Ny;eCd_RD8Y)+uF|RX(=O^2#*Xn>B6nnv7-6zdeQOT~{tzw#{Nk z*Sm>Z`7$pia?USF^|2?+F=V!BlGHRQd|%irp)^hCZzss}?` z*I7?Hvvd8fj|>rE`oTa=w5D(N`Omk+Ke)2SADS*& z!x}&JgDGqLaZA?w2h3G%7Cnfr2syajQ1hWR*S_xeoGh~@t$M5R!>-Q3?J0x9uGNtb z`BndT9PFR@TK&h$mid}LEbA6Nu&G=5AiToo;Nu_lTbu50_;+8)Z${>2p0kN%hvX~o zZ0eu5K>dR?=l<{y?0*6ePW~~u$^NmvOAOPDmK9;zxkpy{o?huUd;9B?SzZgWjxX{v z;cVGuYMMGzSLN&ang{zIzUP{EXvu*~neR)N`?~vQ1UuafFKL8|s@j?Q$9fJ- z-Q48%;Fq`0#h45Ao0oQ4(ld=QnO`jGQqq)#5D|^-BXX_5M zO`5tbrFTx?{2J33mFLGz)2E+W{_T^)qRQ#BE_7`!_+^%GQ*G+RW3z-(%Z~+#Jv$;% zq?7OY;MT05)crf^roUdfP3A&O@ya~buT#>WJ8dd{op8$NQr|Z3(zX-zhFK=7mcCdN zR$>O&BX<~UV^h~tXw565Z$uryE%uCjEU%dLx@|$J< zST}b2J-hJJ#B^f%*Q{%MD$mX0R@}Ivh0iHyQaani%0kU00S(QDdKw~UQdO_`-wzkM zAG&v&-}XY^hs+DN{m7kSeQj2HX-54^iSme#JGieg6{PFWxOqT!-wK1Smc*mWeWOn= z%bm45^Q~oj&K{4sv&$kh%uM&}I+5IZ;Gsf`#&^y8-y^uKSetXfOAceyAF9 zdAc9>2|E^Ttsj~!0sB>NUv56}_NDKWZ(nqZs;kT|nlI|Fv;I)?C~x^ihEocG^|7&k zC0t?;OPqQob^O6Gm%S^?}nKre!rB;a_t^_e$7BDNhIX zT@2+%X1#Dc=+AUZ*>+#FV1jMxr^uFHujO1K**>3hdMEF`(>&N{M*Pl(Sv+EWmv>JO ztCfr~>-zdtGNfd%E7;&}KE~>E%8dAKU*%1|~6ur+I3xS86)OvrcUh&#Jv=vm$##%>TMEdp4jX9!tNVwd>dtBA1Rb|vwWMhEH81f`<;H17Mm2aq@ zTO4oG$oNs@TX+4Vh8+#ju|D!mKEI|Xntu~$@(=R+I;qV5b^|OWppbQ z`)&HD@XO_O2Re34FUq$6C@22?0Yl`Q!MR3yi{?g>LCv&f?F-}03GSJZF0 z#PgHsnFjb(MZEd@%$zN{vf4v-mj>=~CoK_(lr(V)~ zw^UXsPpYZNqWSA`ue9}#1XKJCRNHPTPLDgDxLv;O;XBoYMU&m8ZrR3oFW!9ZH-~@n z0p83kTnr2x91IK$jttG|8B=SR7#Jkk7#Jomb+VJo;l9Q%BGMJ2$$InCLe9XRiAOSm zzA#;8dB5!TLLHq+&LZ`{J$Z9y&6~N|`hD@e&BnXy|NZ;HnjqWHb7bQ5L`%m+*EV*e z*sFmP9?xoIxmKEEAQ9Rh9`kWiosvY*QRRR0qW$hxJ>2ogFxKKh-|a6))_tgrTPpf8 zq4jijX!iPtmRq&fUOF7g^KA93`w!ObF4lOHJoQ@3_Qyr{#qAE4y<1eZGNS(bs<4DZ zzaD(N9Q-g+QT4X(Vyjzzd!1a>y8L|i3(K9`8(d z)b2U$e|QI=akCdy7X68 z`l%%w!}F`QO-Qw7&k1$%xpQDjr^vb)1`|yZ6YdoHn$qg1ul!eU;z1!M(Cd=mM zpK?ExXW;W+ezDog7M{I!mjqV;uG;t4f&!B061iObprc66R#b3SC38Yrc{;!i}~PnoE?r!p7y zbA4ucHJo)yF80{K6Oq)l z{~LM5(hKt&EUf1IsH;A1{pfv1jGCX;$E51byN|vxD%^11Qgph(%Ch-(kt_>)f3c&c zz___Oj~9wCFgWWnFepqGw340N?aEiLzIo5d4>yz)d1fmMo$$Ib-C?6AhY(Mr&@{HF zud0=vZfT#-N>9Gv8ZA0~+qG+#x?*$IPTjUhbSjIl>)NYP+pp!7UAhrFAw960@?bf<~- zl1R(GzK^RIu4nCuT6Ue~rR*Oy$r$MepO@4ZPk6n-b7= z^|ufA3cr}wwVS-Z@*~!<>tkBxJ>8b+*)^@l-g8b;-7@`Cb?U*Xi{6Af{@T4`q5G`; zpRY>2zx+c=ZLj9MO}!ag79M|dV{zQ$Wmg4EgEWngibP%Cu`jRs^S175^`=|%Zr=*o zUmJb-*|hF$S8{J32)R?z(bx87Gp zZsT>M*obddReSfTEf?Ee!#;Vt;>AoO-<-Z9XQ!2{xw~bX7&=bS?YbunQ}MZ%0r3wq;NL_C-FzGT#a>ue-T#E2qlQpDJr@k3C8~9evqw*~!4b z+Sq$bj$KdOQTfH-@}=OY%6BtDZ$wU>d);--oPfhtud_bKPF%9*@U#P+GpD3#_O5S_ zHn=&l@6et~rt_N8CzsyQt(w5uF}q>yx03Mb2R0>?+fH6yfBeni3YL9ugQUfd2&in9 zVbk2Koa_4L-z;AT^#wciF6A!E`S7WPlc%eAx7pFjnbOuX-Fz0U-7A03Jg0f3z#Cc4 z8OwSnu8Q+i>dV{O+h;qwXUUN43gIH~*x`*F4Ja)Ni;fW)@$; z6_>n*alsOw-5dFDY(Cl$?V~N`xm1Nopn365$BRq{A8om_gIQ~H@#@SHtCJ?FrJHc4M2;2e*lT4yfPkbD5WZMmNmqQctMH($@~)4FRNRxG{rs-kg91$W682iems8_wT)J5AlDVYl1Lk@=kBo~WoFJf>FH-Q)+jZtnPijJ`?2KA znWgn*{X(a2XP)}G=$=h__0#eW`YpU(cdvU0AGYii|Mfw=ms>4&nfe|R)?(+}O{8-Ms* zE_S>>>0xZxNxjwQB{+W?TIe0lx_HPqOni0o+E)<;Oq$W#^aYIO&RdvX`iu8(*z)$5 zf^+M<^k09muw8gLv}CK_`=#2YQVSgCu6xd({McBlH1(pdRjl{*ms|SRUHqYJ_5OMP zt3QVOA|EqKop^Kv~{WPwT zHgf%!6DQEjT=Mk%r?*!1eeW;)RH%=5e*UxggbjDH#e4U!_&m49|PHV)f^qv^+V$ z`taBpM%it`5A~M(-*B^iv&}8}xG9TY$2VL`iP&-W?ev6*=f8h;oMX0G@i}nc?3ZuV zMU^&}b49Bh+GwuUc#i)=vlRc!KdE&cnU7bRid~nSW^neUTv;URx#K?)=h``MfBA>Q ze%0sjgrIjf6}JX0aZdeMu;_Gd=8n@1#r4+bHG8KYyUmanK5NEB`FDnT#)sc7ljOhc zrlUF4d53w@cO8x1)&5-CV%w^Dq&!|C z6P?sxi%CtlrbY1Uy`Q3*c(^HJrVU5Q%C|Kx+r?4${&4XxSN*;Sj}1U zMym1Mo8>L*7pqTmnENDhW6sQT{>s-5Je8a~|LOCRe{BqP^{c8aPlyy*?wM;jcmK)r z+>7%)Kfe|9dV0!r?tW#lk6pH^r}oWOm5%)O@{g_J?7zRJ&OfIA;!o|r#&h;l?nD}y z9Z&HIxH!SX-8%Hi0y8(BwC=|RMLVqc%2n6g+VEt)eCD6zx%EnY7ll@`7EbAuI?Sm5 z(ciQ#^7;Ia=S%;wE zw>+PRtA70(c*mxAi?CV02GjDV%OC#H-1vX~5!w1{FXg}Z&;5U>oS{|S|DVri|IK_( zYv<0Lmf$vb=Z`b{A8fV@Uw5iJ-0hGQ`dbL zd>DLl?#o5SJFTmn#SL8-9|@BWwEWtZpW3wG^xcy&U)#91P7JfoeR@aRWBb^LCy7 z^z3CNn=Wry`t48yvt_)y$;3k)`;CPK?`?u8e*FU{dr4{Fy+aY$T>fk$+*`2+V$hCcVNFqpLX%arFZHqrpx)2 zt;+USU6gryN`?GZ75lg|cIuDCPx)T2dj2oVNWsFb*CQ)-+qU{v+g?9~S9gNr&62u3 z1(qG`P=B<-cX6Ld@adSm$PkkyM=mMl?OWWoe#(|O<)p>$4kRu}GS;6w{fX*|iQ(%n z-_ci3*}u7Mxlwqsk<^40xnbYqv(IsyOwPNg;+l6V&hpvS9E+vW3UXj5vN*74Df)!uNJ*kQ zgD=`&9F`Ew>!v@$>b=lcgr~|X2iVatz=)Z;+p#1$gOof{ro@MGrXc* zPso1?4GrTD`pDp0+v4?3QZ?POWDUb}trJnc#uUB{sRA|EWWc#{2!k5-=4HpT-(0a^oLCOmxsR% z#15SKw*?(i{y$vBXtn#Awade8j#kUBJ+F^^r~j>e&+)v)t&?B6 zoQQrC|LNUgljULDnam~YzF#)cdop`VLBq3mJU>%blspfuv#WSyc{A^xtw52|+9jVA z9#n=buj)6t&8Ovdn|13hM?uCCy~ghE7jB9y{VCF{_&6f{qLf$SRM*}Y<^i*!R!lDo zlHVF^ugZ9#u8N)Er`~~e_55o4oM!IfR?}tjvGd-3*h2BcADO$1!D=6C1lq1;i0dvs zpBxsm-TwKkzh77-q!L>v>R+1c^{uv~<6YAk-4eUIyn1nO_D$R8^1WexL#bw_`hjcf zFUIwHnd}N*{5xXt@6NczH(#4t3H{LbJ+|8|(a!I)NmfPgvHpUm47_OzDw*nSYL)6) zZC+%rtZR5?EW1F{HT7Qmialq8uU1Xb>I-^mJ7cBOvz?;BR~Ik0;lI$t#N@(y)_vu( zY=tO`E)O-ayNgo<;?FF0+7j;kzV^ZoDV95>43EBUk=p$H@S@p?{oR62<{GtATP$an zYR=eHWyN({tl{+0FATOEv*+@k=1;ulT_1mJ&pI9LV*W(G9P^1b$2Hen-1zc%rrsL! zLpuDbZ76qDzwNuZ=tKh>q~Q8Pn#@FNX?!t&U`xhcFonb zxejYJVqQxx63tb-udue}M*&}b-?`r#?;g*PY0Bf`kzEumk~M8XJm;A!%*y5~CVYKt z>(O8DWG}xW{6hE#m6yvE)7#mqe3p7tPAbq1@Q)8otc_3&{AVulEc=Ir?Zo1m#;%9G z&Re(w!?iOrzcNNO=lODPi3`#_XS3}N=dq;wxg~upkCN0m?w2%tSvBDs!&+Sh^&fF- zq~6}KKKNgNr%qgC@9bra2f7&^h6=2eJ1@;}Xic$r{lyefHYttim#)9?)L=MS)RlL} z!lq&Bqi0QXisw#jH&EgVpY)t%(u`@mJ;_qFvh_T|XT=s&7YUy_I>~STvH$5WBA?%A zVEpU#<#<8PobrY}?d>a;tljkDY;(x=&gixk`_BFsFx^twb?xA5*WM47G5hzGPB>To z=?B}B!Y2!sF>k4>?=aiOdyzHw(>)uH*=#G9idEJc=j>%Q`@(-A`c@XRe}Jcl_N`Y( zyfiLYIjpswD1Y?G?I^xWKJ9P$Y#uGK@XD86U?zD(RgUZNnhR|i<`(&&3^F3gz1aqk0-MqeA8+8BXh1d|3OTJ+=0JhVRi16CY(Fo9m)J6 z^GxN3m*an%veyeE7wnR$)?2vtOKw2*#`~;aVt>ii6x(LRFL90fM-;FYC za>0qcO=1$i7BW>E27FyCb3ERUE^V3De z<+kId;u?vZdMEv~t`509o<6?%MtwoQ_g*n{n0t1r>obN@(K~*2?-kE4jL8w#ER1h> zalgH?o?*v-56R?JhZPsM`3b*J72T{<)G%S*@=g}kPrtp=t}ixdd9qt{s@55?1@*7* zJ&Ckl&2{{tr6|{g(9L4Z6Puq*UHnz-Qzc*QQ%8<{ESHuZRyrH_@vo51|KOZIMoF*J zKO8+jF|EFF@w?P0pZ%v@6Es)UwOQ)3%vs&1$$#w(^Hcq)Oxi_t5sv$V>I7JiO@3}~ z;`A#>?f?G1us?AxCNYd_ z!oR*c{_5C{N1yn=aQ(3rbo{LKH!$(Vu{kUAkDNPaS+4vy(s7@F`qOxYcJ=8DbEp1q zpZaLR)g^!1)s(K7{JLEjwIuKji4Y!{amCp5E)f+_EYw^!!(On~z)#=F&Hpm;YE3tg2P^p?HGY zXNy@Ug#&thmh80AeD9-w_GMJpt*3fRvo+uR>aw~#SLzhA$FvLkLt{%LGGhy3vgZHp z4>siIw$)^a|Dq8xUzUT{%p$fGxWJmKf+wjSkCb-JP0cw0u$#Nxc+b~U{tU3X4e zY98PTw_=QHj$2VPss3){Yre+AQr)(1N+aLc?>%bwEnVz-n$V7t=e9gPpZT1Wy+pDsWD|On#1(q$rN42Wj;7h*7Uvhv+H zCZi~@OhYu!!eL(L5-VeRBML3#Nkd$usZ!GhHa3{OW!vlVrtY#|PF-9u<=tD~%^# zcqlOW-~%b9%T<#bDiNkNPbf`3Z~+n^H(p3FbzYkM Date: Mon, 5 Dec 2022 15:45:07 +0100 Subject: [PATCH 158/164] Fix a typo in excuse message subject (#2071) --- .../wulkanowy/ui/modules/message/send/SendMessagePresenter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index 5ab8f8fc9..e776e9941 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -55,7 +55,7 @@ class SendMessagePresenter @Inject constructor( view.showMessageBackupDialog() } reason?.let { - setSubject("Usprawiedliwenie") + setSubject("Usprawiedliwienie") setContent(it) } message?.let { From c34c63c128d50e6ef2ffecbf234c125e2f042289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 20 Dec 2022 16:06:55 +0100 Subject: [PATCH 159/164] Add support for new ADFS light instances (#2084) * Update known symbols * Update resman host details * Add adfslight from tomaszowmazowiecki * Bump sdk to 1.8.2-SNAPSHOT * Add migration 54 with tests * Close db in migration tests * Run tests workflow on every pull request --- .github/workflows/test.yml | 1 - app/build.gradle | 2 +- .../54.json | 2439 +++++++++++++++++ .../github/wulkanowy/data/db/AppDatabase.kt | 3 +- .../data/db/migrations/Migration54.kt | 26 + app/src/main/res/values/api_hosts.xml | 9 +- app/src/main/res/values/api_symbols.xml | 666 ++++- .../db/migrations/AbstractMigrationTest.kt | 7 +- .../data/db/migrations/Migration12Test.kt | 9 +- .../data/db/migrations/Migration13Test.kt | 5 + .../data/db/migrations/Migration27Test.kt | 12 +- .../data/db/migrations/Migration35Test.kt | 4 +- .../data/db/migrations/Migration54Test.kt | 130 + build.gradle | 1 + 14 files changed, 3150 insertions(+), 164 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt create mode 100644 app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration54Test.kt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3def08953..13875078a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,6 @@ on: branches: [ master, develop ] tags: [ '*' ] pull_request: - branches: [ master, develop ] jobs: diff --git a/app/build.gradle b/app/build.gradle index fdb844aca..b0865896e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.8.1" + implementation "io.github.wulkanowy:sdk:1.8.2-SNAPSHOT" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json new file mode 100644 index 000000000..7b41672b9 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json @@ -0,0 +1,2439 @@ +{ + "formatVersion": 1, + "database": { + "version": 54, + "identityHash": "1dc96a366125ec9f8567da87cdc9c863", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "user_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "school_short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "nick", + "columnName": "nick", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarColor", + "columnName": "avatar_color", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "kindergartenDiaryId", + "columnName": "kindergarten_diary_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "kindergarten_diary_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isStudentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPredictedGradeNotified", + "columnName": "is_predicted_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFinalGradeNotified", + "columnName": "is_final_grade_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "predictedGradeLastChange", + "columnName": "predicted_grade_last_change", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "finalGradeLastChange", + "columnName": "final_grade_last_change", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAverage", + "columnName": "class_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAverage", + "columnName": "student_average", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classAmounts", + "columnName": "class_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentAmounts", + "columnName": "student_amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amounts", + "columnName": "amounts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentGrade", + "columnName": "student_grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", + "fields": [ + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "messageGlobalKey", + "columnName": "message_global_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mailboxKey", + "columnName": "mailbox_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "correspondents", + "columnName": "correspondents", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recipients", + "columnName": "recipients", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageGlobalKey", + "columnName": "message_global_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryType", + "columnName": "category_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPointsShow", + "columnName": "is_points_show", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "points", + "columnName": "points", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Mailboxes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", + "fields": [ + { + "fieldPath": "globalKey", + "columnName": "globalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolId", + "columnName": "schoolId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "fullName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "studentName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolNameShort", + "columnName": "schoolNameShort", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "globalKey" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "mailboxGlobalKey", + "columnName": "mailboxGlobalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentMailboxGlobalKey", + "columnName": "studentMailboxGlobalKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "fullName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolShortName", + "columnName": "schoolShortName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "agenda", + "columnName": "agenda", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presentOnConference", + "columnName": "present_on_conference", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conferenceId", + "columnName": "conference_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repeatId", + "columnName": "repeat_id", + "affinity": "BLOB", + "notNull": false, + "defaultValue": "NULL" + }, + { + "fieldPath": "isAddedByUser", + "columnName": "is_added_by_user", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StudentInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fullName", + "columnName": "full_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "first_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondName", + "columnName": "second_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "surname", + "columnName": "surname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "birthDate", + "columnName": "birth_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthPlace", + "columnName": "birth_place", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasPolishCitizenship", + "columnName": "has_polish_citizenship", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "familyName", + "columnName": "family_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentsNames", + "columnName": "parents_names", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "registeredAddress", + "columnName": "registered_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "correspondenceAddress", + "columnName": "correspondence_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phoneNumber", + "columnName": "phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellPhoneNumber", + "columnName": "cell_phone_number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstGuardian.fullName", + "columnName": "first_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.kinship", + "columnName": "first_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.address", + "columnName": "first_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.phones", + "columnName": "first_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstGuardian.email", + "columnName": "first_guardian_email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.fullName", + "columnName": "second_guardian_full_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.kinship", + "columnName": "second_guardian_kinship", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.address", + "columnName": "second_guardian_address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.phones", + "columnName": "second_guardian_phones", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secondGuardian.email", + "columnName": "second_guardian_email", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableHeaders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SchoolAnnouncements", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "destination", + "columnName": "destination", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdminMessages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "versionMin", + "columnName": "version_name", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMax", + "columnName": "version_max", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetRegisterHost", + "columnName": "target_register_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetFlavor", + "columnName": "target_flavor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "destinationUrl", + "columnName": "destination_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDismissible", + "columnName": "is_dismissible", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1dc96a366125ec9f8567da87cdc9c863')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 792611a81..cfb53485f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -56,7 +56,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 53 + const val VERSION_SCHEMA = 54 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), @@ -107,6 +107,7 @@ abstract class AppDatabase : RoomDatabase() { Migration50(), Migration51(), Migration53(), + Migration54(), ) fun newInstance( diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt new file mode 100644 index 000000000..678bd32f2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration54 : Migration(53, 54) { + + override fun migrate(database: SupportSQLiteDatabase) { + migrateResman(database) + removeTomaszowMazowieckiStudents(database) + } + + private fun migrateResman(database: SupportSQLiteDatabase) { + database.execSQL(""" + UPDATE Students SET + scrapper_base_url = 'https://vulcan.net.pl', + login_type = 'ADFSLightScoped', + symbol = 'rzeszowprojekt' + WHERE scrapper_base_url = 'https://resman.pl' + """.trimIndent()) + } + + private fun removeTomaszowMazowieckiStudents(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Students WHERE symbol = 'tomaszowmazowiecki'") + } +} diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml index 8413d68e4..a2a08db67 100644 --- a/app/src/main/res/values/api_hosts.xml +++ b/app/src/main/res/values/api_hosts.xml @@ -6,8 +6,9 @@ Gdańska Platforma Edukacyjna Lubelski Portal Oświatowy EduNet Miasta Tarnowa - ResMan Rzeszów Platforma Edukacyjna Koszalina + Gmina-miasto Tomaszów Mazowiecki - System zarządzania oświatą + ResMan Rzeszów Rawa Mazowiecka - Platforma vEdukacja Zduńska Wola - e-Urząd Sieradz - Portal oświatowy @@ -27,7 +28,6 @@ https://edu.gdansk.pl https://edu.lublin.eu https://umt.tarnow.pl - https://resman.pl https://eduportal.koszalin.pl https://vulcan.net.pl/?login https://vulcan.net.pl/?login @@ -40,6 +40,8 @@ https://vulcan.net.pl/?login https://vulcan.net.pl/?login https://vulcan.net.pl/?login + https://vulcan.net.pl/?login + https://vulcan.net.pl/?login http://fakelog.cf/?email @@ -48,8 +50,9 @@ gdansk lublin tarnow - rzeszow koszalin + tomaszowmazowieckiprojekt + rzeszowprojekt rawamazowiecka zdunskawola sieradz diff --git a/app/src/main/res/values/api_symbols.xml b/app/src/main/res/values/api_symbols.xml index 6f9b1739d..4b61db48d 100644 --- a/app/src/main/res/values/api_symbols.xml +++ b/app/src/main/res/values/api_symbols.xml @@ -4,13 +4,11 @@ Andrychów Augustów Baranów Sandomierski - Bartoszyce Będzin Bełchatów Bełżyce Biała Podlaska Biała Rawska - Białogard Biały Bór Białystok Biecz @@ -18,11 +16,9 @@ Bielsko-Biała Bierawa Bierutów - Biskupice Blachownia Błaszki Błonie - Bochnia Bogatynia Boguchwała Boguty-Pianki @@ -45,6 +41,7 @@ Chełmża Chocianów Chodzież + Chojnice Chojnów Chorzów Ciechanów @@ -58,7 +55,9 @@ Dąbrowa Górnicza Dąbrowa Tarnowska Dębica + Dębno Dobrzeń Wielki + Dobrzeń Wielki 2 Dobrzyń Nad Wisłą Dolnośląskie Duszniki-Zdrój @@ -76,6 +75,9 @@ Głogów Małopolski Głowno Głubczyce + Głubczyce 2 + Głuchołazy + Gmina Abramów Gmina Adamówka Gmina Aleksandrów Kujawski Gmina Aleksandrów Łódzki @@ -87,14 +89,17 @@ Gmina Bądkowo Gmina Bałtów Gmina Baranów - Gmina Barciany Gmina Barcin + Gmina Barczewo Gmina Baruchowo + Gmina Batorz Gmina Będzino Gmina Bełchatów + Gmina Besko Gmina Białaczów Gmina Białe Błota - Gmina Bielsk Podlaski + Gmina Białopole + Gmina Bielsk Gmina Bircza Gmina Błażowa Gmina Błędów @@ -104,7 +109,6 @@ Gmina Bobrowniki Gmina Bodzentyn Gmina Bogoria - Gmina Bojanów Gmina Bojanowo Gmina Bojszowy Gmina Bolesławiec @@ -113,10 +117,14 @@ Gmina Borów Gmina Borowa Gmina Borzęcin + Gmina Borzytuchom + Gmina Bralin Gmina Branice Gmina Braniewo + Gmina Brańszczyk Gmina Brąszewice Gmina Brenna + Gmina Brok Gmina Brzeg Dolny Gmina Brzeziny Gmina Brzeźnio @@ -125,13 +133,14 @@ Gmina Brzuze Gmina Brzyska Gmina Buczek - Gmina Buczkowice Gmina Budzów Gmina Budzyń + Gmina Bukowina Tatrzańska Gmina Bukowsko Gmina Byczyna Gmina Bystra-Sidzina - Gmina Bytoń + Gmina Cegłów + Gmina Cekcyn Gmina Ceków-Kolonia Gmina Celestynów Gmina Cewice @@ -139,16 +148,25 @@ Gmina Chełm Gmina Chełmiec Gmina Chełmno + Gmina Chłopice Gmina Chmielnik + Gmina Chociwel Gmina Chocz Gmina Chodel + Gmina Chodów Gmina Chojnice Gmina Chojnów + Gmina Chotcza + Gmina Chrząstowice Gmina Chrzypsko Wielkie Gmina Chybie Gmina Ciasna + Gmina Ciechanów Gmina Ciechocin + Gmina Cielądz + Gmina Cieszanów Gmina Ciężkowice + Gmina Cisek Gmina Cisna Gmina Cmolas Gmina Cyców @@ -156,19 +174,26 @@ Gmina Czarna Gmina Czarnków Gmina Czarny Dunajec + Gmina Czastary Gmina Czechowice-Dziedzice Gmina Czernichów Gmina Czerniejewo + Gmina Czerniewice + Gmina Czernikowo Gmina Czerwionka-Leszczyny - Gmina Czerwonka + Gmina Czerwonak Gmina Człuchów Gmina Czosnów + Gmina Dąbrowa Zielona + Gmina Dąbrowice Gmina Damasławek Gmina Damnica Gmina Darłowo Gmina Dębe Wielkie Gmina Dębica Gmina Dębno + Gmina Dębowa Kłoda + Gmina Debrzno Gmina Dłutów Gmina Dobczyce Gmina Dobra @@ -176,19 +201,31 @@ Gmina Dobrodzień Gmina Dobroń Gmina Dobrzany + Gmina Dobrzyca Gmina Dobrzyniewo Duże + Gmina Dolsk Gmina Dominowo Gmina Dorohusk + Gmina Doruchów + Gmina Dragacz + Gmina Drawsko + Gmina Drużbice Gmina Drzewica Gmina Dubiecko + Gmina Dubienka Gmina Dukla Gmina Dwikozy + Gmina Dydnia Gmina Dynów Gmina Dziadowa Kłoda Gmina Działoszyce + Gmina Dziemiany Gmina Dzierżoniów + Gmina Dzwola Gmina Elbląg - Gmina Fajsławice + Gmina Ełk + Gmina Fredropol + Gmina Garbatka-Letnisko Gmina Garbów Gmina Garwolin Gmina Gąsawa @@ -198,29 +235,37 @@ Gmina Gdów Gmina Gielniów Gmina Gierałtowice - Gmina Glinojeck Gmina Głogów + Gmina Głogówek Gmina Głuchów + Gmina Głusk Gmina Głuszyca Gmina Gniew + Gmina Gniewino Gmina Gniewoszów Gmina Gniezno Gmina Goczałkowice-Zdrój Gmina Godkowo Gmina Godów Gmina Godziesze Wielkie + Gmina Godziszów Gmina Gołańcz + Gmina Gołcza Gmina Goleszów Gmina Golina Gmina Golub-Dobrzyń + Gmina Gołuchów + Gmina Gomunice Gmina Goraj Gmina Gorlice Gmina Górno + Gmina Górzyca Gmina Gościeradów Gmina Gostyń Gmina Gostynin Gmina Goszczyn Gmina Gózd + Gmina Grabica Gmina Grabów Gmina Grabowiec Gmina Grabów Nad Pilicą @@ -239,45 +284,52 @@ Gmina Grudziądz Gmina Gruta Gmina Grybów + Gmina Gryfice + Gmina Grzmiąca Gmina Haczów Gmina Halinów Gmina Hańsk Gmina Harasiuki Gmina Hażlach Gmina Herby + Gmina Horodło Gmina Hrubieszów Gmina Huszlew Gmina Hyżne Gmina Imielno Gmina Inowrocław + Gmina Irządze Gmina Istebna + Gmina Iwanowice Gmina Iwierzyce Gmina Iwonicz-Zdrój Gmina Izabelin Gmina Izbica - Gmina Jadów + Gmina Izbicko + Gmina Jabłoń Gmina Jaktorów + Gmina Jakubów Gmina Janikowo + Gmina Janów Gmina Janowiec Gmina Janów Podlaski - Gmina Jaraczewo + Gmina Jarczów Gmina Jarocin Gmina Jasienica Rosielna + Gmina Jaśliska Gmina Jasło Gmina Jastków Gmina Jastrowie Gmina Jastrząb Gmina Jedlicze - Gmina Jedlińsk - Gmina Jedlnia-Letnisko Gmina Jejkowice Gmina Jemielnica - Gmina Jemielno Gmina Jerzmanowa Gmina Jeżewo Gmina Jeziora Wielkie Gmina Jeziorzany Gmina Jeżowe + Gmina Joniec Gmina Jordanów Gmina Józefów Gmina Józefów Nad Wisłą @@ -285,14 +337,18 @@ Gmina Kąkolewnica Gmina Kamień Gmina Kamienica - Gmina Kamieniec + Gmina Kamiennik Gmina Kamionka Gmina Karczmiska Gmina Kargowa + Gmina Karlino + Gmina Karniewo Gmina Kawęczyn Gmina Kazimierz Biskupi Gmina Kępice + Gmina Kęsowo Gmina Kiełczygłów + Gmina Kietrz Gmina Kikół Gmina Kiszkowo Gmina Kleczew @@ -307,22 +363,30 @@ Gmina Klucze Gmina Kluczewsko Gmina Kobiele Wielkie + Gmina Kobylanka Gmina Kochanowice Gmina Kock Gmina Kodrąb Gmina Kołaczyce Gmina Kołbaskowo + Gmina Kołbiel Gmina Kołczygłowy + Gmina Kołobrzeg Gmina Koluszki Gmina Komańcza + Gmina Komarówka Podlaska Gmina Komorniki Gmina Komprachcice Gmina Konarzyny Gmina Kondratowice + Gmina Koneck Gmina Koniusza Gmina Konopiska Gmina Końskowola + Gmina Konstantynów Gmina Koprzywnica + Gmina Korfantów + Gmina Kórnik Gmina Korsze Gmina Korycin Gmina Korzenna @@ -332,10 +396,14 @@ Gmina Kościerzyna Gmina Kosów Lacki Gmina Kostrzyn - Gmina Koszyce + Gmina Koszęcin Gmina Kotla Gmina Kotuń + Gmina Kowiesy + Gmina Koziegłowy Gmina Kozłów + Gmina Kramsk + Gmina Kraśniczyn Gmina Kraśnik Gmina Krasnobród Gmina Krasnystaw @@ -345,12 +413,16 @@ Gmina Krośnice Gmina Krupski Młyn Gmina Kruszwica + Gmina Krynice Gmina Krynki Gmina Krzanowice Gmina Krzemieniewo + Gmina Krzeszów Gmina Krzymów + Gmina Krzywcza Gmina Krzywiń Gmina Krzyżanowice + Gmina Ksawerów Gmina Książ Wielki Gmina Kunice Gmina Kunów @@ -360,15 +432,19 @@ Gmina Kwilcz Gmina Łabowa Gmina Łabunie + Gmina Łączna + Gmina Lądek Gmina Łambinowice Gmina Lanckorona + Gmina Łańcut + Gmina Łapanów Gmina Łapsze Niżne Gmina Łasin Gmina Łaskarzew Gmina Lasowice Wielkie Gmina Łaszczów - Gmina Laszki Gmina Latowicz + Gmina Łaziska Gmina Łazy Gmina Łęczyca Gmina Łęczyce @@ -378,11 +454,14 @@ Gmina Lelów Gmina Leśna Gmina Leśna Podlaska + Gmina Leśniowice Gmina Lesznowola Gmina Leżajsk Gmina Lichnowy Gmina Limanowa Gmina Linia + Gmina Liniewo + Gmina Lipiany Gmina Lipinki Gmina Lipnik Gmina Lipowa @@ -390,43 +469,51 @@ Gmina Liszki Gmina Liw Gmina Łobez + Gmina Łochów Gmina Łodygowice Gmina Łomazy + Gmina Łomianki + Gmina Łoniów Gmina Łopiennik Górny Gmina Łopuszno + Gmina Łosice Gmina Lubań Gmina Lubartów Gmina Lubasz + Gmina Lubawka Gmina Lubenia Gmina Łubianka Gmina Lubicz + Gmina Lubień Gmina Lubiewo Gmina Lubin Gmina Łubniany Gmina Lubochnia - Gmina Lubomia Gmina Luboń + Gmina Lubsza + Gmina Lubycza Królewska Gmina Łuków Gmina Łukowica Gmina Lutowiska + Gmina Lututów Gmina Luzino Gmina Łużna Gmina Łysomice + Gmina Maciejowice Gmina Magnuszew + Gmina Majdan Królewski Gmina Maków Podhalański - Gmina Mała Wieś Gmina Malbork Gmina Małdyty Gmina Małkinia Górna Gmina Marcinowice Gmina Margonin Gmina Marianowo - Gmina Markusy - Gmina Masłów + Gmina Markuszów + Gmina Męcinka Gmina Medyka Gmina Mełgiew Gmina Michałów - Gmina Michałowo Gmina Miedziana Góra Gmina Miedźna Gmina Miedźno @@ -435,7 +522,10 @@ Gmina Międzyrzec Podlaski Gmina Międzyzdroje Gmina Miejsce Piastowe + Gmina Miękinia Gmina Mielec + Gmina Mielno + Gmina Mieszkowice Gmina Milanów Gmina Milejów Gmina Milicz @@ -444,21 +534,26 @@ Gmina Miłosław Gmina Milówka Gmina Mińsk Mazowiecki + Gmina Mirów Gmina Mirsk Gmina Młynary + Gmina Modliborzyce Gmina Mogielnica Gmina Mogilany + Gmina Mogilno + Gmina Morawica Gmina Mordy Gmina Moryń Gmina Mrocza Gmina Mrozy + Gmina Mściwojów + Gmina Mstów Gmina Mszana Gmina Mszana Dolna Gmina Murów Gmina Mycielin - Gmina Mysłakowice + Gmina Mykanów Gmina Myślibórz - Gmina Nadarzyn Gmina Namysłów Gmina Nasielsk Gmina Nawojowa @@ -469,23 +564,25 @@ Gmina Niedrzwica Duża Gmina Niedźwiada Gmina Niedźwiedź - Gmina Niegosławice - Gmina Niwiska + Gmina Nowa Karczma Gmina Nowa Ruda Gmina Nowa Wieś Lęborska + Gmina Nowe Gmina Nowe Miasto Gmina Nowe Miasto Nad Wartą - Gmina Nowogród Bobrzański + Gmina Nowogród Gmina Nowosolna Gmina Nowy Kawęczyn + Gmina Nowy Korczyn Gmina Nowy Staw Gmina Nowy Targ Gmina Nowy Tomyśl + Gmina Nozdrzec Gmina Nur Gmina Obrazów Gmina Ochotnica Dolna Gmina Ogrodzieniec - Gmina Olecko + Gmina Olszanica Gmina Olsztynek Gmina Olszyna Gmina Opatowiec @@ -495,11 +592,15 @@ Gmina Osiek Jasielski Gmina Osiek Mały Gmina Osielsko + Gmina Osina + Gmina Osjaków + Gmina Ostroróg Gmina Ostrów Gmina Ostrówek Gmina Ostrów Lubelski Gmina Ostrów Mazowiecka Gmina Ostrów Wielkopolski + Gmina Otmuchów Gmina Otyń Gmina Ożarów Gmina Ożarowice @@ -507,27 +608,32 @@ Gmina Ozorków Gmina Pabianice Gmina Pacanów + Gmina Pacyna Gmina Paczków Gmina Padew Narodowa - Gmina Pajęczno Gmina Pakosław Gmina Pakosławice Gmina Pałecznica Gmina Panki Gmina Parchowo Gmina Parczew - Gmina Pawłosiów + Gmina Pasłęk + Gmina Pątnów Gmina Pawłowice Gmina Pawłowiczki + Gmina Pawonków Gmina Pęcław Gmina Pelplin + Gmina Pępowo Gmina Piaski Gmina Piątnica - Gmina Piecki Gmina Piekoszów + Gmina Pieniężno Gmina Pilchowice + Gmina Pińczów Gmina Pionki - Gmina Piotrków Trybunalski + Gmina Płaska + Gmina Platerówka Gmina Pleśna Gmina Pleszew Gmina Płońsk @@ -535,6 +641,7 @@ Gmina Poczesna Gmina Podedwórze Gmina Podegrodzie + Gmina Podgórzyn Gmina Pokój Gmina Połajewo Gmina Połaniec @@ -543,16 +650,18 @@ Gmina Police Gmina Polkowice Gmina Pomiechówek + Gmina Poniatowa Gmina Popielów Gmina Popów - Gmina Poraj Gmina Potęgowo + Gmina Potok Wielki Gmina Praszka - Gmina Prażmów Gmina Prochowice Gmina Promna Gmina Prószków + Gmina Prusice Gmina Pruszcz Gdański + Gmina Przechlewo Gmina Przecław Gmina Przedecz Gmina Przemęt @@ -562,7 +671,9 @@ Gmina Przodkowo Gmina Przykona Gmina Przyłęk + Gmina Przyrów Gmina Przystajń + Gmina Przytoczna Gmina Puchaczów Gmina Puck Gmina Puławy @@ -570,10 +681,10 @@ Gmina Puszcza Mariańska Gmina Pysznica Gmina Pyzdry + Gmina Raba Wyżna Gmina Rachanie Gmina Raciechowice - Gmina Racławice - Gmina Radecznica + Gmina Radgoszcz Gmina Radków Gmina Radłów Gmina Radomin @@ -581,16 +692,29 @@ Gmina Radomyśl Nad Sanem Gmina Radoszyce Gmina Radwanice + Gmina Radymno + Gmina Radziejów Gmina Radziłów + Gmina Rajgród + Gmina Raków + Gmina Rakszawa Gmina Rawa Mazowiecka + Gmina Regnów Gmina Reńska Wieś + Gmina Rogóźno + Gmina Rokitno + Gmina Ropa Gmina Rossosz Gmina Rozprza Gmina Ruciane-Nida Gmina Ruda-Huta Gmina Rudna Gmina Rudniki + Gmina Rudnik Nad Sanem + Gmina Rudziniec Gmina Rusiec + Gmina Rusinów + Gmina Rybczewice Gmina Rychliki Gmina Rychtal Gmina Ryczywół @@ -598,32 +722,39 @@ Gmina Rypin Gmina Rytro Gmina Rytwiany - Gmina Rząśnia Gmina Rzeczyca Gmina Rzepiennik Strzyżewski Gmina Rzepin + Gmina Rzezawa Gmina Rzgów Gmina Sadki Gmina Sadowne Gmina Samborzec Gmina Sanok + Gmina Sawin Gmina Ścinawa Gmina Sędziejowice + Gmina Sejny + Gmina Sękowa Gmina Sępopol Gmina Serokomla Gmina Sianów Gmina Sicienko Gmina Sieciechów Gmina Siedlce + Gmina Siedliszcze Gmina Siemiatycze + Gmina Siemień Gmina Siemyśl Gmina Siennica Gmina Siennica Różana Gmina Sienno Gmina Siepraw Gmina Sieradz + Gmina Sieraków Gmina Sierakowice Gmina Siewierz + Gmina Sitkówka-Nowiny Gmina Sitno Gmina Skarżysko Kościelne Gmina Skępe @@ -631,16 +762,22 @@ Gmina Skoczów Gmina Skoki Gmina Skołyszyn + Gmina Skrwilno Gmina Skrzyszów Gmina Skulsk + Gmina Skwierzyna Gmina Sława + Gmina Śliwice Gmina Słopnice + Gmina Słubice Gmina Słupca Gmina Słupia + Gmina Słupia (Konecka) + Gmina Śmigiel Gmina Sobienie-Jeziory + Gmina Sobolew Gmina Sobótka Gmina Sokółka - Gmina Sokoły Gmina Solina Gmina Sośnicowice Gmina Sośnie @@ -655,11 +792,16 @@ Gmina Stare Miasto Gmina Stare Pole Gmina Starogard Gdański + Gmina Stary Brus + Gmina Stary Dzierzgoń + Gmina Stary Targ Gmina Stawiszyn + Gmina Stepnica Gmina Stoczek Łukowski Gmina Stopnica Gmina Strawczyn Gmina Stryków + Gmina Stryszawa Gmina Stryszów Gmina Strzałkowo Gmina Strzelce Opolskie @@ -668,38 +810,43 @@ Gmina Strzyżewice Gmina Stupsk Gmina Subkowy + Gmina Suchań Gmina Suchedniów Gmina Suchożebry Gmina Suchy Las Gmina Sulechów Gmina Sulęcin + Gmina Sulejów Gmina Sulików Gmina Sulmierzyce Gmina Sułów Gmina Susiec - Gmina Świerklaniec + Gmina Świercze + Gmina Świerczów + Gmina Świerklany Gmina Świerzawa Gmina Świeszyno Gmina Świlcza Gmina Szadek Gmina Szaflary Gmina Szastarka + Gmina Szczawin Kościelny Gmina Szczebrzeszyn Gmina Szczekociny Gmina Szczerców - Gmina Szczutowo Gmina Szczytna Gmina Szczytniki - Gmina Szemud + Gmina Szczytno Gmina Szerzyny Gmina Szlichtyngowa + Gmina Szreńsk + Gmina Szudziałowo Gmina Szydłów Gmina Tarłów Gmina Tarnów Gmina Tarnowiec Gmina Tarnów Opolski Gmina Teresin - Gmina Terespol Gmina Tereszpol Gmina Tłuchowo Gmina Tłuszcz @@ -709,12 +856,15 @@ Gmina Toszek Gmina Trąbki Wielkie Gmina Trzebiatów + Gmina Trzebielino Gmina Trzebinia - Gmina Trzeszczany Gmina Trzyciąż + Gmina Trzydnik Duży Gmina Tuchów + Gmina Tułowice Gmina Turośń Kościelna Gmina Tuszów Narodowy + Gmina Tworóg Gmina Tyczyn Gmina Tymbark Gmina Tyrawa Wołoska @@ -723,15 +873,21 @@ Gmina Ulan-Majorat Gmina Ulanów Gmina Ułęż + Gmina Ulhówek Gmina Urszulin Gmina Urzędów + Gmina Uście Gorlickie Gmina Uścimów Gmina Wąchock + Gmina Wądroże Wielkie Gmina Wągrowiec + Gmina Walce Gmina Wąpielsk Gmina Wasilków + Gmina Wąsosz Gmina Wąwolnica Gmina Wejherowo + Gmina Werbkowice Gmina Wiązów Gmina Wiązowna Gmina Wicko @@ -739,16 +895,21 @@ Gmina Wielbark Gmina Wieleń Gmina Wielgie + Gmina Wielgomłyny Gmina Wieliszew Gmina Wielka Nieszawka Gmina Wieniawa Gmina Wieprz Gmina Wieruszów + Gmina Wierzbinek Gmina Wierzbno + Gmina Wierzchlas Gmina Wierzchosławice Gmina Wietrzychowice Gmina Wijewo + Gmina Wilczyce Gmina Wilczyn + Gmina Wilkołaz Gmina Wilków Gmina Wilkowice Gmina Winnica @@ -759,12 +920,14 @@ Gmina Witkowo Gmina Władysławów Gmina Wleń + Gmina Włocławek Gmina Włodawa Gmina Włoszczowa Gmina Wodzierady Gmina Wodzisław Gmina Wojcieszków Gmina Wojnicz + Gmina Wojsławice Gmina Wola Krzysztoporska Gmina Wolanów Gmina Wolbrom @@ -775,15 +938,18 @@ Gmina Wręczyca Wielka Gmina Wronki Gmina Wyrzysk - Gmina Zabierzów + Gmina Wysokie Gmina Żabno Gmina Żagań - Gmina Zagórz + Gmina Zagórów Gmina Zaklików Gmina Zakroczym Gmina Zakrzówek + Gmina Zalesie Gmina Zaleszany + Gmina Załuski Gmina Zamość + Gmina Żarnów Gmina Żarnowiec Gmina Żarów Gmina Zarszyn @@ -795,20 +961,26 @@ Gmina Zbójno Gmina Zbrosławice Gmina Zduńska Wola - Gmina Zduny Gmina Zdzieszowice + Gmina Zębowice Gmina Zebrzydowice + Gmina Żegocina Gmina Żelazków + Gmina Zembrzyce Gmina Zgierz Gmina Zgorzelec Gmina Ziębice Gmina Zielonki Gmina Zławieś Wielka + Gmina Złota + Gmina Złotniki Kujawskie Gmina Żmudź Gmina Żnin Gmina Żółkiewka Gmina Żołynia Gmina Żukowice + Gmina Żurawica + Gmina Żyraków Gmina Żyrzyn Gmina Żytno Gniezno @@ -817,19 +989,19 @@ Góra Góra Kalwaria Gorlice + Górzno Gorzów Śląski Gorzów Wielkopolski Gostynin Grajewo Grodzisk Mazowiecki - Gronowo Elbląskie Grudziądz + Grybów Gryfino Gryfów Śląski Hel Hrubieszów Inowrocław - Iwanowice Izbica Kujawska Jabłonowo Pomorskie Janowiec Wielkopolski @@ -839,7 +1011,6 @@ Jasło Jastrzębie-Zdrój Jawor - Jaworzno Jedlina-Zdrój Jelcz-Laskowice Jelenia Góra @@ -860,9 +1031,9 @@ Kępno Kętrzyn Kielce - Kiełczygłów Kłodawa Kłodzko + Kluczbork Knurów Kobyłka Koło @@ -892,11 +1063,13 @@ Krzeszowice Krzyż Wielkopolski Książ Wielkopolski - Kudowa-Zdrój Kujawsko-Pomorskie + Kutno Kuźnia Raciborska + Kwidzyn Łabiszyn Lądek-Zdrój + Łańcut Łapy Łask Łaskarzew @@ -908,10 +1081,10 @@ Legnica Leszno Lewin Brzeski + Lewin Brzeski 2 Leżajsk Limanowa Lipno - Lipsko Łódź Łódzkie Łowicz @@ -928,11 +1101,8 @@ Lwówek Śląski Malbork Małopolskie - Marciszów Marki - Masłowice Mazowieckie - Miastko Michałowice Miechów Międzyrzec Podlaski @@ -940,6 +1110,7 @@ Mielec Milanówek Mińsk Mazowiecki + Mniszków Mosina Mrągowo Mrągowski @@ -949,18 +1120,17 @@ Mysłowice Myszków Nakło Nad Notecią - Nasielsk Niemodlin Niepołomice Nisko Nowa Dęba Nowa Sarzyna + Nowa Sól Nowe Miasteczko Nowe Skalmierzyce Nowogard Nowogród Bobrzański Nowogrodziec - Nowosolna Nowy Dwór Mazowiecki Nowy Sącz Nowy Targ @@ -974,6 +1144,8 @@ Opoczno Opole Opole Lubelskie + Opolskie + Orzesze Osieczna Osiecznica Ostróda @@ -991,13 +1163,16 @@ Piekary Śląskie Pieńsk Piła + Pilzno Piotrków Trybunalski Pisz Płock Płońsk Pniewy + Pobiedziska Podkarpackie Podkowa Leśna + Podlaskie Połczyn-Zdrój Pomorskie Poniec @@ -1006,7 +1181,7 @@ Powiat augustowski Powiat będziński Powiat bełchatowski - Powiat białobrzeski + Powiat białostocki Powiat bialski Powiat bielski Powiat bieszczadzki @@ -1032,6 +1207,7 @@ Powiat człuchowski Powiat dąbrowski Powiat dębicki + Powiat drawski Powiat działdowski Powiat dzierżoniowski Powiat elbląski @@ -1046,10 +1222,11 @@ Powiat goleniowski Powiat golubsko-dobrzyński Powiat gorlicki + Powiat górowski Powiat gorzowski Powiat gostyński + Powiat grajewski Powiat grójecki - Powiat grudziądzki Powiat gryficki Powiat gryfiński Powiat hajnowski @@ -1064,6 +1241,7 @@ Powiat jędrzejowski Powiat jeleniogórski Powiat kaliski + Powiat kamiennogórski Powiat kamieński Powiat kartuski Powiat kazimierski @@ -1088,6 +1266,7 @@ Powiat krośnieński Powiat krotoszyński Powiat kutnowski + Powiat łańcucki Powiat łaski Powiat lęborski Powiat łęczycki @@ -1102,6 +1281,7 @@ Powiat lipski Powiat łobeski Powiat łódzki wschodni + Powiat łosicki Powiat łowicki Powiat lubaczowski Powiat lubański @@ -1122,9 +1302,11 @@ Powiat myślenicki Powiat myszkowski Powiat nakielski + Powiat namysłowski Powiat nidzicki Powiat niżański Powiat nowodworski + Powiat nowomiejski Powiat nowosądecki Powiat nowosolski Powiat nowotarski @@ -1160,7 +1342,6 @@ Powiat proszowicki Powiat prudnicki Powiat pruszkowski - Powiat przasnyski Powiat przemyski Powiat przeworski Powiat przysuski @@ -1183,6 +1364,7 @@ Powiat rzeszowski Powiat sandomierski Powiat sanocki + Powiat sejneński Powiat sępoleński Powiat siedlecki Powiat siemiatycki @@ -1210,6 +1392,7 @@ Powiat świdnicki Powiat świdwiński Powiat świebodziński + Powiat świecki Powiat szamotulski Powiat szczycieński Powiat sztumski @@ -1228,6 +1411,7 @@ Powiat wągrowiecki Powiat wałecki Powiat warszawski zachodni + Powiat węgorzewski Powiat węgrowski Powiat wejherowski Powiat wielicki @@ -1247,7 +1431,6 @@ Powiat wyszkowski Powiat ząbkowicki Powiat żagański - Powiat zambrowski Powiat zamojski Powiat żarski Powiat zawierciański @@ -1258,34 +1441,33 @@ Powiat złotoryjski Powiat złotowski Powiat żniński - Powiat żuromiński Powiat żyrardowski Powiat żywiecki Poznań - Prostki Proszowice Prudnik + Pruszcz Gdański Pruszków Przasnysz Przemyśl Przeworsk Przysucha Pszczyna - Pszów Puck Puławy + Pułtusk + Puszczykowo Pyskowice Rabka-Zdrój Raciąż Racibórz - Raciechowice Radom Radomsko + Radomyśl Wielki Radymno Radziejów Radzionków Radzyń Podlaski - Raków Rawa Mazowiecka Rawicz Reda @@ -1299,6 +1481,7 @@ Rymanów Rypin Rzeszów + Rzeszów projekt Sandomierz Sanok Sędziszów Małopolski @@ -1306,7 +1489,6 @@ Siedlce Siemianowice Śląskie Siemiatycze - Sieniawa Sieradz Skarżysko-Kamienna Skawina @@ -1325,17 +1507,15 @@ Środa Śląska Środa Wielkopolska Starachowice + Stargard Starogard Gdański Stary Sącz Staszów Stronie Śląskie - Strzegom Strzyżów - Suchy Las Sulejówek Sułkowice Sulmierzyce - Suwalski Swarzędz Świdnica Świdnik @@ -1358,8 +1538,10 @@ Terespol Tomaszów Lubelski Tomaszów Mazowiecki + Tomaszów Mazowiecki projekt Toruń Trzcianka + Trzcińsko-Zdrój Trzebnica Trzemeszno Tuliszków @@ -1372,7 +1554,7 @@ Ustrzyki Dolne Wadowice Wągrowiec - Wałbrzych + Wałcz Warmińsko-Mazurskie Warszawa Wąsosz @@ -1381,7 +1563,7 @@ Więcbork Wieliczka Wielkopolskie - Wizna + Wieluń Władysławowo Włocławek Włodawa @@ -1410,6 +1592,7 @@ Zielona Góra Zielonka Złotoryja + Złotów Żory Zwoleń Żyrardów @@ -1418,13 +1601,11 @@ andrychow augustow baranowsandomierski - bartoszyce bedzin belchatow belzyce bialapodlaska bialarawska - bialogard bialybor bialystok biecz @@ -1432,11 +1613,9 @@ bielskobiala bierawa bierutow - biskupice blachownia blaszki blonie - bochnia bogatynia boguchwala bogutypianki @@ -1459,6 +1638,7 @@ chelmza chocianow chodziez + chojnice chojnow chorzow ciechanow @@ -1472,7 +1652,9 @@ dabrowagornicza dabrowatarnowska debica + debno dobrzenwielki + dobrzenwielki2 dobrzynnadwisla dolnoslaskie dusznikizdroj @@ -1490,6 +1672,9 @@ glogowmalopolski glowno glubczyce + glubczyce2 + glucholazy + gminaabramow gminaadamowka gminaaleksandrowkujawski gminaaleksandrowlodzki @@ -1501,14 +1686,17 @@ gminabadkowo gminabaltow gminabaranow - gminabarciany gminabarcin + gminabarczewo gminabaruchowo + gminabatorz gminabedzino gminabelchatow + gminabesko gminabialaczow gminabialeblota - gminabielskpodlaski + gminabialopole + gminabielsk gminabircza gminablazowa gminabledow @@ -1518,7 +1706,6 @@ gminabobrowniki gminabodzentyn gminabogoria - gminabojanow gminabojanowo gminabojszowy gminaboleslawiec @@ -1527,10 +1714,14 @@ gminaborow gminaborowa gminaborzecin + gminaborzytuchom + gminabralin gminabranice gminabraniewo + gminabranszczyk gminabraszewice gminabrenna + gminabrok gminabrzegdolny gminabrzeziny gminabrzeznio @@ -1539,13 +1730,14 @@ gminabrzuze gminabrzyska gminabuczek - gminabuczkowice gminabudzow gminabudzyn + gminabukowinatatrzanska gminabukowsko gminabyczyna gminabystrasidzina - gminabyton + gminaceglow + gminacekcyn gminacekowkolonia gminacelestynow gminacewice @@ -1553,16 +1745,25 @@ gminachelm gminachelmiec gminachelmno + gminachlopice gminachmielnik + gminachociwel gminachocz gminachodel + gminachodow gminachojnice gminachojnow + gminachotcza + gminachrzastowice gminachrzypskowielkie gminachybie gminaciasna + gminaciechanow gminaciechocin + gminacieladz + gminacieszanow gminaciezkowice + gminacisek gminacisna gminacmolas gminacycow @@ -1570,19 +1771,26 @@ gminaczarna gminaczarnkow gminaczarnydunajec + gminaczastary gminaczechowicedziedzice gminaczernichow gminaczerniejewo + gminaczerniewice + gminaczernikowo gminaczerwionkaleszczyny - gminaczerwonka + gminaczerwonak gminaczluchow gminaczosnow + gminadabrowazielona + gminadabrowice gminadamaslawek gminadamnica gminadarlowo gminadebewielkie gminadebica gminadebno + gminadebowakloda + gminadebrzno gminadlutow gminadobczyce gminadobra @@ -1590,19 +1798,31 @@ gminadobrodzien gminadobron gminadobrzany + gminadobrzyca gminadobrzyniewoduze + gminadolsk gminadominowo gminadorohusk + gminadoruchow + gminadragacz + gminadrawsko + gminadruzbice gminadrzewica gminadubiecko + gminadubienka gminadukla gminadwikozy + gminadydnia gminadynow gminadziadowakloda gminadzialoszyce + gminadziemiany gminadzierzoniow + gminadzwola gminaelblag - gminafajslawice + gminaelk + gminafredropol + gminagarbatkaletnisko gminagarbow gminagarwolin gminagasawa @@ -1612,29 +1832,37 @@ gminagdow gminagielniow gminagieraltowice - gminaglinojeck gminaglogow + gminaglogowek gminagluchow + gminaglusk gminagluszyca gminagniew + gminagniewino gminagniewoszow gminagniezno gminagoczalkowicezdroj gminagodkowo gminagodow gminagodzieszewielkie + gminagodziszow gminagolancz + gminagolcza gminagoleszow gminagolina gminagolubdobrzyn + gminagoluchow + gminagomunice gminagoraj gminagorlice gminagorno + gminagorzyca gminagoscieradow gminagostyn gminagostynin gminagoszczyn gminagozd + gminagrabica gminagrabow gminagrabowiec gminagrabownadpilica @@ -1653,45 +1881,52 @@ gminagrudziadz gminagruta gminagrybow + gminagryfice + gminagrzmiaca gminahaczow gminahalinow gminahansk gminaharasiuki gminahazlach gminaherby + gminahorodlo gminahrubieszow gminahuszlew gminahyzne gminaimielno gminainowroclaw + gminairzadze gminaistebna + gminaiwanowice gminaiwierzyce gminaiwoniczzdroj gminaizabelin gminaizbica - gminajadow + gminaizbicko + gminajablon gminajaktorow + gminajakubow gminajanikowo + gminajanow gminajanowiec gminajanowpodlaski - gminajaraczewo + gminajarczow gminajarocin gminajasienicarosielna + gminajasliska gminajaslo gminajastkow gminajastrowie gminajastrzab gminajedlicze - gminajedlinsk - gminajedlnialetnisko gminajejkowice gminajemielnica - gminajemielno gminajerzmanowa gminajezewo gminajeziorawielkie gminajeziorzany gminajezowe + gminajoniec gminajordanow gminajozefow gminajozefownadwisla @@ -1699,14 +1934,18 @@ gminakakolewnica gminakamien gminakamienica - gminakamieniec + gminakamiennik gminakamionka gminakarczmiska gminakargowa + gminakarlino + gminakarniewo gminakaweczyn gminakazimierzbiskupi gminakepice + gminakesowo gminakielczyglow + gminakietrz gminakikol gminakiszkowo gminakleczew @@ -1721,22 +1960,30 @@ gminaklucze gminakluczewsko gminakobielewielkie + gminakobylanka gminakochanowice gminakock gminakodrab gminakolaczyce gminakolbaskowo + gminakolbiel gminakolczyglowy + gminakolobrzeg gminakoluszki gminakomancza + gminakomarowkapodlaska gminakomorniki gminakomprachcice gminakonarzyny gminakondratowice + gminakoneck gminakoniusza gminakonopiska gminakonskowola + gminakonstantynow gminakoprzywnica + gminakorfantow + gminakornik gminakorsze gminakorycin gminakorzenna @@ -1746,10 +1993,14 @@ gminakoscierzyna gminakosowlacki gminakostrzyn - gminakoszyce + gminakoszecin gminakotla gminakotun + gminakowiesy + gminakozieglowy gminakozlow + gminakramsk + gminakrasniczyn gminakrasnik gminakrasnobrod gminakrasnystaw @@ -1759,12 +2010,16 @@ gminakrosnice gminakrupskimlyn gminakruszwica + gminakrynice gminakrynki gminakrzanowice gminakrzemieniewo + gminakrzeszow gminakrzymow + gminakrzywcza gminakrzywin gminakrzyzanowice + gminaksawerow gminaksiazwielki gminakunice gminakunow @@ -1774,15 +2029,19 @@ gminakwilcz gminalabowa gminalabunie + gminalaczna + gminaladek gminalambinowice gminalanckorona + gminalancut + gminalapanow gminalapszenizne gminalasin gminalaskarzew gminalasowicewielkie gminalaszczow - gminalaszki gminalatowicz + gminalaziska gminalazy gminaleczyca gminaleczyce @@ -1792,11 +2051,14 @@ gminalelow gminalesna gminalesnapodlaska + gminalesniowice gminalesznowola gminalezajsk gminalichnowy gminalimanowa gminalinia + gminaliniewo + gminalipiany gminalipinki gminalipnik gminalipowa @@ -1804,43 +2066,51 @@ gminaliszki gminaliw gminalobez + gminalochow gminalodygowice gminalomazy + gminalomianki + gminaloniow gminalopiennikgorny gminalopuszno + gminalosice gminaluban gminalubartow gminalubasz + gminalubawka gminalubenia gminalubianka gminalubicz + gminalubien gminalubiewo gminalubin gminalubniany gminalubochnia - gminalubomia gminalubon + gminalubsza + gminalubyczakrolewska gminalukow gminalukowica gminalutowiska + gminalututow gminaluzino gminaluzna gminalysomice + gminamaciejowice gminamagnuszew + gminamajdankrolewski gminamakowpodhalanski - gminamalawies gminamalbork gminamaldyty gminamalkiniagorna gminamarcinowice gminamargonin gminamarianowo - gminamarkusy - gminamaslow + gminamarkuszow + gminamecinka gminamedyka gminamelgiew gminamichalow - gminamichalowo gminamiedzianagora gminamiedzna gminamiedzno @@ -1849,7 +2119,10 @@ gminamiedzyrzecpodlaski gminamiedzyzdroje gminamiejscepiastowe + gminamiekinia gminamielec + gminamielno + gminamieszkowice gminamilanow gminamilejow gminamilicz @@ -1858,21 +2131,26 @@ gminamiloslaw gminamilowka gminaminskmazowiecki + gminamirow gminamirsk gminamlynary + gminamodliborzyce gminamogielnica gminamogilany + gminamogilno + gminamorawica gminamordy gminamoryn gminamrocza gminamrozy + gminamsciwojow + gminamstow gminamszana gminamszanadolna gminamurow gminamycielin - gminamyslakowice + gminamykanow gminamysliborz - gminanadarzyn gminanamyslow gminanasielsk gminanawojowa @@ -1883,23 +2161,25 @@ gminaniedrzwicaduza gminaniedzwiada gminaniedzwiedz - gminaniegoslawice - gminaniwiska + gminanowakarczma gminanowaruda gminanowawiesleborska + gminanowe gminanowemiasto gminanowemiastonadwarta - gminanowogrodbobrzanski + gminanowogrod gminanowosolna gminanowykaweczyn + gminanowykorczyn gminanowystaw gminanowytarg gminanowytomysl + gminanozdrzec gminanur gminaobrazow gminaochotnicadolna gminaogrodzieniec - gminaolecko + gminaolszanica gminaolsztynek gminaolszyna gminaopatowiec @@ -1909,11 +2189,15 @@ gminaosiekjasielski gminaosiekmaly gminaosielsko + gminaosina + gminaosjakow + gminaostrorog gminaostrow gminaostrowek gminaostrowlubelski gminaostrowmazowiecka gminaostrowwielkopolski + gminaotmuchow gminaotyn gminaozarow gminaozarowice @@ -1921,27 +2205,32 @@ gminaozorkow gminapabianice gminapacanow + gminapacyna gminapaczkow gminapadewnarodowa - gminapajeczno gminapakoslaw gminapakoslawice gminapalecznica gminapanki gminaparchowo gminaparczew - gminapawlosiow + gminapaslek + gminapatnow gminapawlowice gminapawlowiczki + gminapawonkow gminapeclaw gminapelplin + gminapepowo gminapiaski gminapiatnica - gminapiecki gminapiekoszow + gminapieniezno gminapilchowice + gminapinczow gminapionki - gminapiotrkowtrybunalski + gminaplaska + gminaplaterowka gminaplesna gminapleszew gminaplonsk @@ -1949,6 +2238,7 @@ gminapoczesna gminapodedworze gminapodegrodzie + gminapodgorzyn gminapokoj gminapolajewo gminapolaniec @@ -1957,16 +2247,18 @@ gminapolice gminapolkowice gminapomiechowek + gminaponiatowa gminapopielow gminapopow - gminaporaj gminapotegowo + gminapotokwielki gminapraszka - gminaprazmow gminaprochowice gminapromna gminaproszkow + gminaprusice gminapruszczgdanski + gminaprzechlewo gminaprzeclaw gminaprzedecz gminaprzemet @@ -1976,7 +2268,9 @@ gminaprzodkowo gminaprzykona gminaprzylek + gminaprzyrow gminaprzystajn + gminaprzytoczna gminapuchaczow gminapuck gminapulawy @@ -1984,10 +2278,10 @@ gminapuszczamarianska gminapysznica gminapyzdry + gminarabawyzna gminarachanie gminaraciechowice - gminaraclawice - gminaradecznica + gminaradgoszcz gminaradkow gminaradlow gminaradomin @@ -1995,16 +2289,29 @@ gminaradomyslnadsanem gminaradoszyce gminaradwanice + gminaradymno + gminaradziejow gminaradzilow + gminarajgrod + gminarakow + gminarakszawa gminarawamazowiecka + gminaregnow gminarenskawies + gminarogozno + gminarokitno + gminaropa gminarossosz gminarozprza gminarucianenida gminarudahuta gminarudna gminarudniki + gminarudniknadsanem + gminarudziniec gminarusiec + gminarusinow + gminarybczewice gminarychliki gminarychtal gminaryczywol @@ -2012,32 +2319,39 @@ gminarypin gminarytro gminarytwiany - gminarzasnia gminarzeczyca gminarzepiennikstrzyzewski gminarzepin + gminarzezawa gminarzgow gminasadki gminasadowne gminasamborzec gminasanok + gminasawin gminascinawa gminasedziejowice + gminasejny + gminasekowa gminasepopol gminaserokomla gminasianow gminasicienko gminasieciechow gminasiedlce + gminasiedliszcze gminasiemiatycze + gminasiemien gminasiemysl gminasiennica gminasiennicarozana gminasienno gminasiepraw gminasieradz + gminasierakow gminasierakowice gminasiewierz + gminasitkowkanowiny gminasitno gminaskarzyskokoscielne gminaskepe @@ -2045,16 +2359,22 @@ gminaskoczow gminaskoki gminaskolyszyn + gminaskrwilno gminaskrzyszow gminaskulsk + gminaskwierzyna gminaslawa + gminasliwice gminaslopnice + gminaslubice gminaslupca gminaslupia + gminaslupiakonecka + gminasmigiel gminasobieniejeziory + gminasobolew gminasobotka gminasokolka - gminasokoly gminasolina gminasosnicowice gminasosnie @@ -2069,11 +2389,16 @@ gminastaremiasto gminastarepole gminastarogardgdanski + gminastarybrus + gminastarydzierzgon + gminastarytarg gminastawiszyn + gminastepnica gminastoczeklukowski gminastopnica gminastrawczyn gminastrykow + gminastryszawa gminastryszow gminastrzalkowo gminastrzelceopolskie @@ -2082,38 +2407,43 @@ gminastrzyzewice gminastupsk gminasubkowy + gminasuchan gminasuchedniow gminasuchozebry gminasuchylas gminasulechow gminasulecin + gminasulejow gminasulikow gminasulmierzyce gminasulow gminasusiec - gminaswierklaniec + gminaswiercze + gminaswierczow + gminaswierklany gminaswierzawa gminaswieszyno gminaswilcza gminaszadek gminaszaflary gminaszastarka + gminaszczawinkoscielny gminaszczebrzeszyn gminaszczekociny gminaszczercow - gminaszczutowo gminaszczytna gminaszczytniki - gminaszemud + gminaszczytno gminaszerzyny gminaszlichtyngowa + gminaszrensk + gminaszudzialowo gminaszydlow gminatarlow gminatarnow gminatarnowiec gminatarnowopolski gminateresin - gminaterespol gminatereszpol gminatluchowo gminatluszcz @@ -2123,12 +2453,15 @@ gminatoszek gminatrabkiwielkie gminatrzebiatow + gminatrzebielino gminatrzebinia - gminatrzeszczany gminatrzyciaz + gminatrzydnikduzy gminatuchow + gminatulowice gminaturosnkoscielna gminatuszownarodowy + gminatworog gminatyczyn gminatymbark gminatyrawawoloska @@ -2137,15 +2470,21 @@ gminaulanmajorat gminaulanow gminaulez + gminaulhowek gminaurszulin gminaurzedow + gminausciegorlickie gminauscimow gminawachock + gminawadrozewielkie gminawagrowiec + gminawalce gminawapielsk gminawasilkow + gminawasosz gminawawolnica gminawejherowo + gminawerbkowice gminawiazow gminawiazowna gminawicko @@ -2153,16 +2492,21 @@ gminawielbark gminawielen gminawielgie + gminawielgomlyny gminawieliszew gminawielkanieszawka gminawieniawa gminawieprz gminawieruszow + gminawierzbinek gminawierzbno + gminawierzchlas gminawierzchoslawice gminawietrzychowice gminawijewo + gminawilczyce gminawilczyn + gminawilkolaz gminawilkow gminawilkowice gminawinnica @@ -2173,12 +2517,14 @@ gminawitkowo gminawladyslawow gminawlen + gminawloclawek gminawlodawa gminawloszczowa gminawodzierady gminawodzislaw gminawojcieszkow gminawojnicz + gminawojslawice gminawolakrzysztoporska gminawolanow gminawolbrom @@ -2189,15 +2535,18 @@ gminawreczycawielka gminawronki gminawyrzysk - gminazabierzow + gminawysokie gminazabno gminazagan - gminazagorz + gminazagorow gminazaklikow gminazakroczym gminazakrzowek + gminazalesie gminazaleszany + gminazaluski gminazamosc + gminazarnow gminazarnowiec gminazarow gminazarszyn @@ -2209,20 +2558,26 @@ gminazbojno gminazbroslawice gminazdunskawola - gminazduny gminazdzieszowice + gminazebowice gminazebrzydowice + gminazegocina gminazelazkow + gminazembrzyce gminazgierz gminazgorzelec gminaziebice gminazielonki gminazlawieswielka + gminazlota + gminazlotnikikujawskie gminazmudz gminaznin gminazolkiewka gminazolynia gminazukowice + gminazurawica + gminazyrakow gminazyrzyn gminazytno gniezno @@ -2231,19 +2586,19 @@ gora gorakalwaria gorlice + gorzno gorzowslaski gorzowwielkopolski gostynin grajewo grodziskmazowiecki - gronowoelblaskie grudziadz + grybow gryfino gryfowslaski hel hrubieszow inowroclaw - iwanowice izbicakujawska jablonowopomorskie janowiecwielkopolski @@ -2253,7 +2608,6 @@ jaslo jastrzebiezdroj jawor - jaworzno jedlinazdroj jelczlaskowice jeleniagora @@ -2274,9 +2628,9 @@ kepno ketrzyn kielce - kielczyglow klodawa klodzko + kluczbork knurow kobylka kolo @@ -2306,11 +2660,13 @@ krzeszowice krzyzwielkopolski ksiazwielkopolski - kudowazdroj kujawskopomorskie + kutno kuzniaraciborska + kwidzyn labiszyn ladekzdroj + lancut lapy lask laskarzew @@ -2322,10 +2678,10 @@ legnica leszno lewinbrzeski + lewinbrzeski2 lezajsk limanowa lipno - lipsko lodz lodzkie lowicz @@ -2342,11 +2698,8 @@ lwowekslaski malbork malopolskie - marciszow marki - maslowice mazowieckie - miastko michalowice miechow miedzyrzecpodlaski @@ -2354,6 +2707,7 @@ mielec milanowek minskmazowiecki + mniszkow mosina mragowo mragowski @@ -2363,18 +2717,17 @@ myslowice myszkow naklonadnotecia - nasielsk niemodlin niepolomice nisko nowadeba nowasarzyna + nowasol nowemiasteczko noweskalmierzyce nowogard nowogrodbobrzanski nowogrodziec - nowosolna nowydwormazowiecki nowysacz nowytarg @@ -2388,6 +2741,8 @@ opoczno opole opolelubelskie + opolskie + orzesze osieczna osiecznica ostroda @@ -2405,13 +2760,16 @@ piekaryslaskie piensk pila + pilzno piotrkowtrybunalski pisz plock plonsk pniewy + pobiedziska podkarpackie podkowalesna + podlaskie polczynzdroj pomorskie poniec @@ -2420,7 +2778,7 @@ powiataugustowski powiatbedzinski powiatbelchatowski - powiatbialobrzeski + powiatbialostocki powiatbialski powiatbielski powiatbieszczadzki @@ -2446,6 +2804,7 @@ powiatczluchowski powiatdabrowski powiatdebicki + powiatdrawski powiatdzialdowski powiatdzierzoniowski powiatelblaski @@ -2460,10 +2819,11 @@ powiatgoleniowski powiatgolubskodobrzynski powiatgorlicki + powiatgorowski powiatgorzowski powiatgostynski + powiatgrajewski powiatgrojecki - powiatgrudziadzki powiatgryficki powiatgryfinski powiathajnowski @@ -2478,6 +2838,7 @@ powiatjedrzejowski powiatjeleniogorski powiatkaliski + powiatkamiennogorski powiatkamienski powiatkartuski powiatkazimierski @@ -2502,6 +2863,7 @@ powiatkrosnienski powiatkrotoszynski powiatkutnowski + powiatlancucki powiatlaski powiatleborski powiatleczycki @@ -2516,6 +2878,7 @@ powiatlipski powiatlobeski powiatlodzkiwschodni + powiatlosicki powiatlowicki powiatlubaczowski powiatlubanski @@ -2536,9 +2899,11 @@ powiatmyslenicki powiatmyszkowski powiatnakielski + powiatnamyslowski powiatnidzicki powiatnizanski powiatnowodworski + powiatnowomiejski powiatnowosadecki powiatnowosolski powiatnowotarski @@ -2574,7 +2939,6 @@ powiatproszowicki powiatprudnicki powiatpruszkowski - powiatprzasnyski powiatprzemyski powiatprzeworski powiatprzysuski @@ -2597,6 +2961,7 @@ powiatrzeszowski powiatsandomierski powiatsanocki + powiatsejnenski powiatsepolenski powiatsiedlecki powiatsiemiatycki @@ -2624,6 +2989,7 @@ powiatswidnicki powiatswidwinski powiatswiebodzinski + powiatswiecki powiatszamotulski powiatszczycienski powiatsztumski @@ -2642,6 +3008,7 @@ powiatwagrowiecki powiatwalecki powiatwarszawskizachodni + powiatwegorzewski powiatwegrowski powiatwejherowski powiatwielicki @@ -2661,7 +3028,6 @@ powiatwyszkowski powiatzabkowicki powiatzaganski - powiatzambrowski powiatzamojski powiatzarski powiatzawiercianski @@ -2672,34 +3038,33 @@ powiatzlotoryjski powiatzlotowski powiatzninski - powiatzurominski powiatzyrardowski powiatzywiecki poznan - prostki proszowice prudnik + pruszczgdanski pruszkow przasnysz przemysl przeworsk przysucha pszczyna - pszow puck pulawy + pultusk + puszczykowo pyskowice rabkazdroj raciaz raciborz - raciechowice radom radomsko + radomyslwielki radymno radziejow radzionkow radzynpodlaski - rakow rawamazowiecka rawicz reda @@ -2713,6 +3078,7 @@ rymanow rypin rzeszow + rzeszowprojekt sandomierz sanok sedziszowmalopolski @@ -2720,7 +3086,6 @@ siedlce siemianowiceslaskie siemiatycze - sieniawa sieradz skarzyskokamienna skawina @@ -2739,17 +3104,15 @@ srodaslaska srodawielkopolska starachowice + stargard starogardgdanski starysacz staszow stronieslaskie - strzegom strzyzow - suchylas sulejowek sulkowice sulmierzyce - suwalski swarzedz swidnica swidnik @@ -2772,8 +3135,10 @@ terespol tomaszowlubelski tomaszowmazowiecki + tomaszowmazowieckiprojekt torun trzcianka + trzcinskozdroj trzebnica trzemeszno tuliszkow @@ -2786,7 +3151,7 @@ ustrzykidolne wadowice wagrowiec - walbrzych + walcz warminskomazurskie warszawa wasosz @@ -2795,7 +3160,7 @@ wiecbork wieliczka wielkopolskie - wizna + wielun wladyslawowo wloclawek wlodawa @@ -2824,6 +3189,7 @@ zielonagora zielonka zlotoryja + zlotow zory zwolen zyrardow diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt index cc31d8933..18249ba8b 100644 --- a/app/src/test/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt @@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.migrations import android.content.Context import androidx.preference.PreferenceManager import androidx.room.Room +import androidx.room.migration.Migration import androidx.room.testing.MigrationTestHelper import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory import androidx.test.core.app.ApplicationProvider @@ -16,7 +17,7 @@ abstract class AbstractMigrationTest { val dbName = "migration-test" - val context: Context get() = ApplicationProvider.getApplicationContext() + private val context: Context get() = ApplicationProvider.getApplicationContext() @get:Rule val helper: MigrationTestHelper = MigrationTestHelper( @@ -25,6 +26,10 @@ abstract class AbstractMigrationTest { FrameworkSQLiteOpenHelperFactory() ) + fun runMigrationsAndValidate(migration: Migration) { + helper.runMigrationsAndValidate(dbName, migration.endVersion, true, migration).close() + } + fun getMigratedRoomDatabase(): AppDatabase { val database = Room.databaseBuilder( ApplicationProvider.getApplicationContext(), diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt index a02904733..f614c8ca9 100644 --- a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt +++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt @@ -33,7 +33,7 @@ class Migration12Test : AbstractMigrationTest() { close() } - helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + runMigrationsAndValidate(Migration12()) val db = getMigratedRoomDatabase() val students = runBlocking { db.studentDao.loadAll() } @@ -49,6 +49,7 @@ class Migration12Test : AbstractMigrationTest() { assertEquals(2, studentId) assertEquals(6, classId) } + db.close() } @Test @@ -62,7 +63,7 @@ class Migration12Test : AbstractMigrationTest() { close() } - helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + runMigrationsAndValidate(Migration12()) val db = getMigratedRoomDatabase() val students = runBlocking { db.studentDao.loadAll() } @@ -73,6 +74,7 @@ class Migration12Test : AbstractMigrationTest() { assertEquals(2, studentId) assertEquals(1, classId) } + db.close() } @Test @@ -88,7 +90,7 @@ class Migration12Test : AbstractMigrationTest() { close() } - helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + runMigrationsAndValidate(Migration12()) val db = getMigratedRoomDatabase() val students = runBlocking { db.studentDao.loadAll() } @@ -107,6 +109,7 @@ class Migration12Test : AbstractMigrationTest() { assertEquals(studentId, 3) assertEquals(true, isCurrent) } + db.close() } private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean) { diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt index bdfb4137d..b0c03fb11 100644 --- a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt +++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt @@ -57,6 +57,8 @@ class Migration13Test : AbstractMigrationTest() { assertEquals("C", className) assertEquals("Publiczna szkoła Wulkanowego-fejka nr 2 w fakelog.cf", schoolName) } + + db.close() } @Test @@ -85,6 +87,8 @@ class Migration13Test : AbstractMigrationTest() { assertEquals("", className) assertEquals("Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", schoolName) } + + db.close() } @Test @@ -148,6 +152,7 @@ class Migration13Test : AbstractMigrationTest() { assertFalse(semesters[2].second) assertTrue(semesters[3].second) } + db.close() } private fun getSemesters(db: SupportSQLiteDatabase, query: String): List> { diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt index 8e744f27a..19eda9ba8 100644 --- a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt +++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt @@ -27,7 +27,7 @@ class Migration27Test : AbstractMigrationTest() { close() } - helper.runMigrationsAndValidate(dbName, 27, true, Migration27()) + runMigrationsAndValidate(Migration27()) val db = getMigratedRoomDatabase() val students = runBlocking { db.studentDao.loadAll() } @@ -39,6 +39,8 @@ class Migration27Test : AbstractMigrationTest() { assertEquals(123, userLoginId) assertEquals("Student Jan", userName) } + + db.close() } @Test @@ -49,7 +51,7 @@ class Migration27Test : AbstractMigrationTest() { close() } - helper.runMigrationsAndValidate(dbName, 27, true, Migration27()) + runMigrationsAndValidate(Migration27()) val db = getMigratedRoomDatabase() val students = runBlocking { db.studentDao.loadAll() } @@ -61,6 +63,8 @@ class Migration27Test : AbstractMigrationTest() { assertEquals(2, userLoginId) assertEquals("Unit Jan", userName) } + + db.close() } @Test @@ -73,7 +77,7 @@ class Migration27Test : AbstractMigrationTest() { close() } - helper.runMigrationsAndValidate(dbName, 27, true, Migration27()) + runMigrationsAndValidate(Migration27()) val db = getMigratedRoomDatabase() val students = runBlocking { db.studentDao.loadAll() } @@ -90,6 +94,8 @@ class Migration27Test : AbstractMigrationTest() { assertEquals(333, userLoginId) assertEquals("Unit Tomasz", userName) } + + db.close() } private fun createStudent(db: SupportSQLiteDatabase, id: Long, userLoginId: Int, studentName: String) { diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration35Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration35Test.kt index 883cdb81c..79c24f2e6 100644 --- a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration35Test.kt +++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration35Test.kt @@ -29,7 +29,7 @@ class Migration35Test : AbstractMigrationTest() { close() } - helper.runMigrationsAndValidate(dbName, 35, true, Migration35(AppInfo())) + runMigrationsAndValidate(Migration35(AppInfo())) val db = getMigratedRoomDatabase() val students = runBlocking { db.studentDao.loadAll() } @@ -38,6 +38,8 @@ class Migration35Test : AbstractMigrationTest() { assertTrue { students[0].avatarColor in AppInfo().defaultColorsForAvatar } assertTrue { students[1].avatarColor in AppInfo().defaultColorsForAvatar } + + db.close() } private fun createStudent(db: SupportSQLiteDatabase, id: Long) { diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration54Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration54Test.kt new file mode 100644 index 000000000..1855e0d50 --- /dev/null +++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration54Test.kt @@ -0,0 +1,130 @@ +package io.github.wulkanowy.data.db.migrations + +import android.content.ContentValues +import android.database.sqlite.SQLiteDatabase +import android.os.Build +import androidx.sqlite.db.SupportSQLiteDatabase +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.* +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import kotlin.random.Random +import kotlin.test.assertEquals + +@HiltAndroidTest +@RunWith(RobolectricTestRunner::class) +@OptIn(ExperimentalCoroutinesApi::class) +@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class) +class Migration54Test : AbstractMigrationTest() { + + @Test + fun `don't touch unrelated students`() = runTest { + with(helper.createDatabase(dbName, 53)) { + createStudent(1, STANDARD, "vulcan.net.pl", "rzeszow", "Jan Michniewicz") + createStudent(2, ADFSLight, "umt.tarnow.pl", "tarnow", "Joanna Marcinkiewicz") + close() + } + + runMigrationsAndValidate(Migration54()) + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll() + + assertEquals(2, students.size) + with(students[0]) { + assertEquals(STANDARD.name, loginType) + assertEquals("https://vulcan.net.pl", scrapperBaseUrl) + assertEquals("rzeszow", symbol) + } + with(students[1]) { + assertEquals(ADFSLight.name, loginType) + assertEquals("https://umt.tarnow.pl", scrapperBaseUrl) + assertEquals("tarnow", symbol) + } + db.close() + } + + @Test + fun `remove tomaszow mazowiecki students`() = runTest { + with(helper.createDatabase(dbName, 53)) { + createStudent(1, STANDARD, "vulcan.net.pl", "rzeszow", "Jan Michniewicz") + createStudent(2, STANDARD, "vulcan.net.pl", "tomaszowmazowiecki", "Joanna Stec") + createStudent(3, STANDARD, "vulcan.net.pl", "tomaszowmazowiecki", "Kacper Morawiecki") + close() + } + + runMigrationsAndValidate(Migration54()) + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll() + assertEquals(1, students.size) + with(students[0]) { + assertEquals("rzeszow", symbol) + } + db.close() + } + + @Test + fun `migrate resman students`() = runTest { + with(helper.createDatabase(dbName, 53)) { + createStudent(1, ADFSLight, "resman.pl", "rzeszow", "Joanna Stec") + createStudent(2, ADFSLight, "resman.pl", "rzeszow", "Kacper Morawiecki") + createStudent(3, STANDARD, "vulcan.net.pl", "rzeszow", "Jan Michniewicz") + close() + } + runMigrationsAndValidate(Migration54()) + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll() + assertEquals(3, students.size) + with(students[0]) { + assertEquals(ADFSLightScoped.name, loginType) + assertEquals("https://vulcan.net.pl", scrapperBaseUrl) + assertEquals("rzeszowprojekt", symbol) + } + with(students[1]) { + assertEquals(ADFSLightScoped.name, loginType) + assertEquals("https://vulcan.net.pl", scrapperBaseUrl) + assertEquals("rzeszowprojekt", symbol) + } + db.close() + } + + private fun SupportSQLiteDatabase.createStudent( + id: Long, + loginType: Sdk.ScrapperLoginType, + host: String, + symbol: String, + studentName: String, + ) { + insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { + put("scrapper_base_url", "https://$host") + put("mobile_base_url", "") + put("login_type", loginType.name) + put("login_mode", "SCRAPPER") + put("certificate_key", "") + put("private_key", "") + put("is_parent", false) + put("email", "jan@fakelog.cf") + put("password", "******") + put("symbol", symbol) + put("student_id", Random.nextInt()) + put("user_login_id", id) + put("user_name", studentName) + put("student_name", studentName) + put("school_id", "123") + put("school_short", "") + put("school_name", "") + put("class_name", "") + put("class_id", Random.nextInt()) + put("is_current", false) + put("registration_date", "0") + put("id", id) + put("nick", "") + put("avatar_color", "") + }) + } +} diff --git a/build.gradle b/build.gradle index e8e1052b6..174a3cb6d 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,7 @@ allprojects { mavenCentral() google() maven { url "https://jitpack.io" } + maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/" } maven { url "https://developer.huawei.com/repo/" } } } From 2fa26c37a95a0fa34765c6de73121eb26dffe094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 20 Dec 2022 21:55:46 +0100 Subject: [PATCH 160/164] Revert "Fix app name in french (#2072)" This reverts commit 277ffd22be786cfa429ea1d61827e8cb62d1ea56. --- app/build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b0865896e..6b496d85a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -237,7 +237,7 @@ dependencies { implementation 'com.github.bastienpaulfr:Treessence:1.0.5' implementation "com.mikepenz:aboutlibraries-core:$about_libraries" implementation "io.coil-kt:coil:2.2.2" - implementation "io.github.wulkanowy:AppKillerManager:3.0.1" + implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'com.fredporciuncula:flow-preferences:1.8.0' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 8722 zcmZ2`nR&uR<_#=Xtd=<A22h#!CTC;u*7}a*FjIYPB48 zax%;nQc!GRSu*zx-)zOr#;@P#G*$)Vu65u0dTQ1D2-l@Ia-1$6`|XguH~(VTT6f#m z^5248YxC30Z{F3>G@O6_-?{R?_kQpD{@eH5-{%Y48P^=Lf0oFslQ-$ko(a;kXN#Ek z_0)?kQI(k}c&MXgu~EvB%Ay|k?d>0w-o>BM)_?eH=^dHp&-f%c_xQM($DKPn_j7@G z+`+}WQ}+n1Pnr~dx`Ou|=fkZ&zjg`4o!@j%;c;(;@cKPFWxhYs_4yGtZ{J#(z&(3q z4EHq8z9{hS@ze5C72@}FlP9e8cwl>HPxztVP8IbW^7R}Kn-yz3+=T7UNXsW?s;kt5 z9J9Kkf0@%>?crk~Kd&Dx4o3yAMkGcAUX{2o+5Baj+J*&>H&nUwz6fi1F3oysrrNUQ zbIk6FMNFA}^H;*Hi@KYyn=PB+bGGwK*>>T}sXk&`&p21v^Q%=`Usqk?Z1nPz}n@J$v?Ta5mq%t|Td7 z^Y<0aGVQ1M_)f=Ny;eCd_RD8Y)+uF|RX(=O^2#*Xn>B6nnv7-6zdeQOT~{tzw#{Nk z*Sm>Z`7$pia?USF^|2?+F=V!BlGHRQd|%irp)^hCZzss}?` z*I7?Hvvd8fj|>rE`oTa=w5D(N`Omk+Ke)2SADS*& z!x}&JgDGqLaZA?w2h3G%7Cnfr2syajQ1hWR*S_xeoGh~@t$M5R!>-Q3?J0x9uGNtb z`BndT9PFR@TK&h$mid}LEbA6Nu&G=5AiToo;Nu_lTbu50_;+8)Z${>2p0kN%hvX~o zZ0eu5K>dR?=l<{y?0*6ePW~~u$^NmvOAOPDmK9;zxkpy{o?huUd;9B?SzZgWjxX{v z;cVGuYMMGzSLN&ang{zIzUP{EXvu*~neR)N`?~vQ1UuafFKL8|s@j?Q$9fJ- z-Q48%;Fq`0#h45Ao0oQ4(ld=QnO`jGQqq)#5D|^-BXX_5M zO`5tbrFTx?{2J33mFLGz)2E+W{_T^)qRQ#BE_7`!_+^%GQ*G+RW3z-(%Z~+#Jv$;% zq?7OY;MT05)crf^roUdfP3A&O@ya~buT#>WJ8dd{op8$NQr|Z3(zX-zhFK=7mcCdN zR$>O&BX<~UV^h~tXw565Z$uryE%uCjEU%dLx@|$J< zST}b2J-hJJ#B^f%*Q{%MD$mX0R@}Ivh0iHyQaani%0kU00S(QDdKw~UQdO_`-wzkM zAG&v&-}XY^hs+DN{m7kSeQj2HX-54^iSme#JGieg6{PFWxOqT!-wK1Smc*mWeWOn= z%bm45^Q~oj&K{4sv&$kh%uM&}I+5IZ;Gsf`#&^y8-y^uKSetXfOAceyAF9 zdAc9>2|E^Ttsj~!0sB>NUv56}_NDKWZ(nqZs;kT|nlI|Fv;I)?C~x^ihEocG^|7&k zC0t?;OPqQob^O6Gm%S^?}nKre!rB;a_t^_e$7BDNhIX zT@2+%X1#Dc=+AUZ*>+#FV1jMxr^uFHujO1K**>3hdMEF`(>&N{M*Pl(Sv+EWmv>JO ztCfr~>-zdtGNfd%E7;&}KE~>E%8dAKU*%1|~6ur+I3xS86)OvrcUh&#Jv=vm$##%>TMEdp4jX9!tNVwd>dtBA1Rb|vwWMhEH81f`<;H17Mm2aq@ zTO4oG$oNs@TX+4Vh8+#ju|D!mKEI|Xntu~$@(=R+I;qV5b^|OWppbQ z`)&HD@XO_O2Re34FUq$6C@22?0Yl`Q!MR3yi{?g>LCv&f?F-}03GSJZF0 z#PgHsnFjb(MZEd@%$zN{vf4v-mj>=~CoK_(lr(V)~ zw^UXsPpYZNqWSA`ue9}#1XKJCRNHPTPLDgDxLv;O;XBoYMU&m8ZrR3oFW!9ZH-~@n z0p83kTnr2x91IK$jttG|8B=SR7#Jkk7#Jomb+VJo;l9Q%BGMJ2$$InCLe9XRiAOSm zzA#;8dB5!TLLHq+&LZ`{J$Z9y&6~N|`hD@e&BnXy|NZ;HnjqWHb7bQ5L`%m+*EV*e z*sFmP9?xoIxmKEEAQ9Rh9`kWiosvY*QRRR0qW$hxJ>2ogFxKKh-|a6))_tgrTPpf8 zq4jijX!iPtmRq&fUOF7g^KA93`w!ObF4lOHJoQ@3_Qyr{#qAE4y<1eZGNS(bs<4DZ zzaD(N9Q-g+QT4X(Vyjzzd!1a>y8L|i3(K9`8(d z)b2U$e|QI=akCdy7X68 z`l%%w!}F`QO-Qw7&k1$%xpQDjr^vb)1`|yZ6YdoHn$qg1ul!eU;z1!M(Cd=mM zpK?ExXW;W+ezDog7M{I!mjqV;uG;t4f&!B061iObprc66R#b3SC38Yrc{;!i}~PnoE?r!p7y zbA4ucHJo)yF80{K6Oq)l z{~LM5(hKt&EUf1IsH;A1{pfv1jGCX;$E51byN|vxD%^11Qgph(%Ch-(kt_>)f3c&c zz___Oj~9wCFgWWnFepqGw340N?aEiLzIo5d4>yz)d1fmMo$$Ib-C?6AhY(Mr&@{HF zud0=vZfT#-N>9Gv8ZA0~+qG+#x?*$IPTjUhbSjIl>)NYP+pp!7UAhrFAw960@?bf<~- zl1R(GzK^RIu4nCuT6Ue~rR*Oy$r$MepO@4ZPk6n-b7= z^|ufA3cr}wwVS-Z@*~!<>tkBxJ>8b+*)^@l-g8b;-7@`Cb?U*Xi{6Af{@T4`q5G`; zpRY>2zx+c=ZLj9MO}!ag79M|dV{zQ$Wmg4EgEWngibP%Cu`jRs^S175^`=|%Zr=*o zUmJb-*|hF$S8{J32)R?z(bx87Gp zZsT>M*obddReSfTEf?Ee!#;Vt;>AoO-<-Z9XQ!2{xw~bX7&=bS?YbunQ}MZ%0r3wq;NL_C-FzGT#a>ue-T#E2qlQpDJr@k3C8~9evqw*~!4b z+Sq$bj$KdOQTfH-@}=OY%6BtDZ$wU>d);--oPfhtud_bKPF%9*@U#P+GpD3#_O5S_ zHn=&l@6et~rt_N8CzsyQt(w5uF}q>yx03Mb2R0>?+fH6yfBeni3YL9ugQUfd2&in9 zVbk2Koa_4L-z;AT^#wciF6A!E`S7WPlc%eAx7pFjnbOuX-Fz0U-7A03Jg0f3z#Cc4 z8OwSnu8Q+i>dV{O+h;qwXUUN43gIH~*x`*F4Ja)Ni;fW)@$; z6_>n*alsOw-5dFDY(Cl$?V~N`xm1Nopn365$BRq{A8om_gIQ~H@#@SHtCJ?FrJHc4M2;2e*lT4yfPkbD5WZMmNmqQctMH($@~)4FRNRxG{rs-kg91$W682iems8_wT)J5AlDVYl1Lk@=kBo~WoFJf>FH-Q)+jZtnPijJ`?2KA znWgn*{X(a2XP)}G=$=h__0#eW`YpU(cdvU0AGYii|Mfw=ms>4&nfe|R)?(+}O{8-Ms* zE_S>>>0xZxNxjwQB{+W?TIe0lx_HPqOni0o+E)<;Oq$W#^aYIO&RdvX`iu8(*z)$5 zf^+M<^k09muw8gLv}CK_`=#2YQVSgCu6xd({McBlH1(pdRjl{*ms|SRUHqYJ_5OMP zt3QVOA|EqKop^Kv~{WPwT zHgf%!6DQEjT=Mk%r?*!1eeW;)RH%=5e*UxggbjDH#e4U!_&m49|PHV)f^qv^+V$ z`taBpM%it`5A~M(-*B^iv&}8}xG9TY$2VL`iP&-W?ev6*=f8h;oMX0G@i}nc?3ZuV zMU^&}b49Bh+GwuUc#i)=vlRc!KdE&cnU7bRid~nSW^neUTv;URx#K?)=h``MfBA>Q ze%0sjgrIjf6}JX0aZdeMu;_Gd=8n@1#r4+bHG8KYyUmanK5NEB`FDnT#)sc7ljOhc zrlUF4d53w@cO8x1)&5-CV%w^Dq&!|C z6P?sxi%CtlrbY1Uy`Q3*c(^HJrVU5Q%C|Kx+r?4${&4XxSN*;Sj}1U zMym1Mo8>L*7pqTmnENDhW6sQT{>s-5Je8a~|LOCRe{BqP^{c8aPlyy*?wM;jcmK)r z+>7%)Kfe|9dV0!r?tW#lk6pH^r}oWOm5%)O@{g_J?7zRJ&OfIA;!o|r#&h;l?nD}y z9Z&HIxH!SX-8%Hi0y8(BwC=|RMLVqc%2n6g+VEt)eCD6zx%EnY7ll@`7EbAuI?Sm5 z(ciQ#^7;Ia=S%;wE zw>+PRtA70(c*mxAi?CV02GjDV%OC#H-1vX~5!w1{FXg}Z&;5U>oS{|S|DVri|IK_( zYv<0Lmf$vb=Z`b{A8fV@Uw5iJ-0hGQ`dbL zd>DLl?#o5SJFTmn#SL8-9|@BWwEWtZpW3wG^xcy&U)#91P7JfoeR@aRWBb^LCy7 z^z3CNn=Wry`t48yvt_)y$;3k)`;CPK?`?u8e*FU{dr4{Fy+aY$T>fk$+*`2+V$hCcVNFqpLX%arFZHqrpx)2 zt;+USU6gryN`?GZ75lg|cIuDCPx)T2dj2oVNWsFb*CQ)-+qU{v+g?9~S9gNr&62u3 z1(qG`P=B<-cX6Ld@adSm$PkkyM=mMl?OWWoe#(|O<)p>$4kRu}GS;6w{fX*|iQ(%n z-_ci3*}u7Mxlwqsk<^40xnbYqv(IsyOwPNg;+l6V&hpvS9E+vW3UXj5vN*74Df)!uNJ*kQ zgD=`&9F`Ew>!v@$>b=lcgr~|X2iVatz=)Z;+p#1$gOof{ro@MGrXc* zPso1?4GrTD`pDp0+v4?3QZ?POWDUb}trJnc#uUB{sRA|EWWc#{2!k5-=4HpT-(0a^oLCOmxsR% z#15SKw*?(i{y$vBXtn#Awade8j#kUBJ+F^^r~j>e&+)v)t&?B6 zoQQrC|LNUgljULDnam~YzF#)cdop`VLBq3mJU>%blspfuv#WSyc{A^xtw52|+9jVA z9#n=buj)6t&8Ovdn|13hM?uCCy~ghE7jB9y{VCF{_&6f{qLf$SRM*}Y<^i*!R!lDo zlHVF^ugZ9#u8N)Er`~~e_55o4oM!IfR?}tjvGd-3*h2BcADO$1!D=6C1lq1;i0dvs zpBxsm-TwKkzh77-q!L>v>R+1c^{uv~<6YAk-4eUIyn1nO_D$R8^1WexL#bw_`hjcf zFUIwHnd}N*{5xXt@6NczH(#4t3H{LbJ+|8|(a!I)NmfPgvHpUm47_OzDw*nSYL)6) zZC+%rtZR5?EW1F{HT7Qmialq8uU1Xb>I-^mJ7cBOvz?;BR~Ik0;lI$t#N@(y)_vu( zY=tO`E)O-ayNgo<;?FF0+7j;kzV^ZoDV95>43EBUk=p$H@S@p?{oR62<{GtATP$an zYR=eHWyN({tl{+0FATOEv*+@k=1;ulT_1mJ&pI9LV*W(G9P^1b$2Hen-1zc%rrsL! zLpuDbZ76qDzwNuZ=tKh>q~Q8Pn#@FNX?!t&U`xhcFonb zxejYJVqQxx63tb-udue}M*&}b-?`r#?;g*PY0Bf`kzEumk~M8XJm;A!%*y5~CVYKt z>(O8DWG}xW{6hE#m6yvE)7#mqe3p7tPAbq1@Q)8otc_3&{AVulEc=Ir?Zo1m#;%9G z&Re(w!?iOrzcNNO=lODPi3`#_XS3}N=dq;wxg~upkCN0m?w2%tSvBDs!&+Sh^&fF- zq~6}KKKNgNr%qgC@9bra2f7&^h6=2eJ1@;}Xic$r{lyefHYttim#)9?)L=MS)RlL} z!lq&Bqi0QXisw#jH&EgVpY)t%(u`@mJ;_qFvh_T|XT=s&7YUy_I>~STvH$5WBA?%A zVEpU#<#<8PobrY}?d>a;tljkDY;(x=&gixk`_BFsFx^twb?xA5*WM47G5hzGPB>To z=?B}B!Y2!sF>k4>?=aiOdyzHw(>)uH*=#G9idEJc=j>%Q`@(-A`c@XRe}Jcl_N`Y( zyfiLYIjpswD1Y?G?I^xWKJ9P$Y#uGK@XD86U?zD(RgUZNnhR|i<`(&&3^F3gz1aqk0-MqeA8+8BXh1d|3OTJ+=0JhVRi16CY(Fo9m)J6 z^GxN3m*an%veyeE7wnR$)?2vtOKw2*#`~;aVt>ii6x(LRFL90fM-;FYC za>0qcO=1$i7BW>E27FyCb3ERUE^V3De z<+kId;u?vZdMEv~t`509o<6?%MtwoQ_g*n{n0t1r>obN@(K~*2?-kE4jL8w#ER1h> zalgH?o?*v-56R?JhZPsM`3b*J72T{<)G%S*@=g}kPrtp=t}ixdd9qt{s@55?1@*7* zJ&Ckl&2{{tr6|{g(9L4Z6Puq*UHnz-Qzc*QQ%8<{ESHuZRyrH_@vo51|KOZIMoF*J zKO8+jF|EFF@w?P0pZ%v@6Es)UwOQ)3%vs&1$$#w(^Hcq)Oxi_t5sv$V>I7JiO@3}~ z;`A#>?f?G1us?AxCNYd_ z!oR*c{_5C{N1yn=aQ(3rbo{LKH!$(Vu{kUAkDNPaS+4vy(s7@F`qOxYcJ=8DbEp1q zpZaLR)g^!1)s(K7{JLEjwIuKji4Y!{amCp5E)f+_EYw^!!(On~z)#=F&Hpm;YE3tg2P^p?HGY zXNy@Ug#&thmh80AeD9-w_GMJpt*3fRvo+uR>aw~#SLzhA$FvLkLt{%LGGhy3vgZHp z4>siIw$)^a|Dq8xUzUT{%p$fGxWJmKf+wjSkCb-JP0cw0u$#Nxc+b~U{tU3X4e zY98PTw_=QHj$2VPss3){Yre+AQr)(1N+aLc?>%bwEnVz-n$V7t=e9gPpZT1Wy+pDsWD|On#1(q$rN42Wj;7h*7Uvhv+H zCZi~@OhYu!!eL(L5-VeRBML3#Nkd$usZ!GhHa3{OW!vlVrtY#|PF-9u<=tD~%^# zcqlOW-~%b9%T<#bDiNkNPbf`3Z~+n^H(p3FbzYkMY3bK)MlyX}a4V|6>bmw?EPjKcDQaeE;{|{qNta^Uwc%ez2Wk%|ZKT3nj()HRa+?G@o^z z#a{33#%(0TcbX$H!*E`x+8lid*+u?#Xa*<_c|Vv{-}1ZvRL)q!RQ$u*6yi^R!yp? zR(({#&Y!G!=k)FOQ$FhLi9W^dfF5oWYsIVdY#^byTbI z>C-JGGcw&xd6wN?F?-i$&j;sD% zWqivkZTK8;_hjbH)witH7tgNy7Ub~Kz*i`L#mg;jr&p(aOgWlWbj!bfl7@Ha#9f!3 zXzg;pX13(0n@Y{4skVEICoXxKDn4nF!A;L)%L<-!8^^HJ>dp(gwXyj2#2M30J&o{N z^{peV`pIWs-|pqN*9KnRaK*y#oA9+uuNGX3>oPsMF=+0s#Ip)ElRk&eE3q)FS(@s8aqVMtZj)|dU zW7=#<<25FZneESbA`3a^hkal;CYp2jESK4$(1U7n^P10cZA)0)WVS{7MxV|}t&pV; zOzO)xbu?RFT}jw-?Z3w z?MRhZb?kp!E>ySv0soK1P3j-iS?iC*ckJLX;0(+SJJxZ1*0kW+hWVF+w}v=u?Q@-+ zA;eLdopsA7+T&IH&4>FRzUP{EWXXX`FJF|V`?&jO1v^=WJvrpZ_vzEkuBlt5C%HX1 z73rv4@K<$OhF$&3o!4A*ckhr{@zN*1#6rqc@ANtMlUr9PczNrun-Ul#E2n=^k8hd%w!nl`a;OYACRm-@!QWJ6tF@vB<9 zRtXjFYwEf?!#Vxj%Gk46(&@p`XHMRGQhD7V|7Xm!=elQ888=n;EtBnCS2REEcB}Xi zy-#Np|C^XbCVb^xv$OQvtZv1PD|+~xg1W-l9#$4wE(vIAF3i)IawawPivRs^q5GlL zIdk(KdwgVGxb92tl=9cd(s?rKf6A0cb+&L{Q!03_H{<33*?lVvPPHVeF7u5(y)1Xm z?o8RA4LLhJ=FjGf(6IYjS?YPDtMRZWN8sDwz3;cZNR14d)+!MD{Y=_)gYz{uEnik1 z3m4Y3IdAT=Xky!ylIdkFRkQDxEZ@UuzL8P;d6Gl@&u_8otgHSm`CuFJVaaUH^)%s_KVBXFSNP-g;l@IiSBDQ4vb^?p-c>(eL;Hh`RPz!3OFwF)vNQTs76`{S z{60GCh2uegCM#vzea##j=5hVhajN~wzd)(GGF|ceT(_m$gEq~G-`mipBQ|eYx>< zn|9>zyq4ayLhf+ZErC5RWp!p9cxo-BzM+CgaaNY!yhB2>)}K)F`Qlw|yYT9Fr%&?j z_pf_p|7V$2TCXdA_{17Do%u!!kIH|0?$XH_GgEo(zAlciv_ezC)WFxWafcrr{%2e< zjX!a}`rBLg_8b+CxcF$EUGxG~0lqm0OPP{0p00n*YOC+8dr(MKFi41R((HMrmaY#U zu)M13|6gvTZaKlVR*ZK||G$?T49f!^u8ggeX^&!3U9?!`(&h>Et>NoGP1|>;sC42X zrfsG(4H%EhChg*~g~Gv2fnJ ztNqCyHk`44gbzPrnxwakU2*cVRFztD#UCtJugI;^vp<=qpu3i1(vs+l-rHCuu3Elv zKa%`nad2Z`A^+8DYmZd>-0A)MWGcUZXVxADc8^n)Cc7pvTX@89U93)T*AUXGf4iow zhVi#bi%N-oFY9OX$+3MC3t)l+!mfOrf>jS)* zS-2P&I5-#>7#tbaJa}-tmWhEul8u2uck*&4+xojq*Yf{3t-K_ba=KlGCD8e5LeLkc z%PjAgoemJ&rh7B@YW)*l@mU@wvnpP;+MlXOE6em=xzP8radFx6+H=)qY2VM^m*3Bz zlW@~PrcyFZNF*(_LgTWfEUV=?X^#ZQ<4VneUw`yzn@O1-G>E$O{Qb_SCTphmX@|W$ zq*s1tnz~ti>-*1IYdzStW8&6FMf;v#8J;;+Ze5Z={MEl|<#!*=+rS?ZB_4P2=|26M zwykd>OV?fbuqrI!(60v{4?91MR8+m}yVxpf<3?xKV_tr-d-ASo9jZR9y{_o$hMm#R z<|%*vuqe*jaP4p1+sj%KRjYR$PF!_d-nw-8&z8hh?3b3WG@4nT`_3|?a0XY~ z-&iHJE6zH*H`ycCbZ6~`ll+I5@vO5tc}ARzXYwf>GD& z!mSGnE~{%)UYho5^<2%#$HEprz3DXVt;_~XPQyahaNnLsO`O7-!fcb&@*Yj=Q*C?6 zC_8gU+iN46B#HMG)xkH#%jzd5KKd`Q)ic%SlJwLC;`&WnN*p$C+2WksR8+)nwQ!2A z<6_B#KQ1-1pYC`3za~Un^Vn0_ZC&0b=FOoy)=0GFY?4cI)THS?vANzh-D?RTF_OiKq?@gYewZF`p z;;^GK$F9D+tlq7#CGbB3QkoQCaAXKO)6%(5lz~B9pMgPPvY?ghlVeK9j{gW?rUY?Qm+_KjDXjHa%b|&|=sOetfQ3tQF z^l)XpzE*p6TiN!t+qQ14-kP!R|IYk0BTu%*e|Gg1?`Kz^xBag3zxdrwbN~JS4jgs~ zc|2>)WjQKN_R! z+k4KtVx?dCXQRw%*?XLp&#c*ZVf~dI*Upz(*&bPKQa9Nz{bSZ#xo6xdPtJY{)sN3M z$-AI=BGqdBQ@6QN6?6aW37GZul8Nu4{s%|^{;P%p;F0*0}9iOsPw(i}O ziF&=dy}SS3dHg(1lH=u;HvOs3jONyzPwZ8ea(`;P?6T{f<(Jv&+K&~^HRJrwJLkh@ zUHeIwOfqYB`rWSBe(bLm{|C`aF)M>-_v{H?J~!ss^K#Gn|9P3SO7?ZF|MuC`D*p8Q zlaF6l^sgw{JO94+UtXmPzfJZ9E|1%orN6&k$oRg>S$30u3;YECP00A1`16+P74z&r zg1z|{H5IuZ{RxcR8lGXdY_oUGMeVb4|8^Xk|KYRU+;va7Q}t)m?fAx+<$_*raFSg{?w;kr)8}>Z)Rr-}K^OmzNhrA3}ubHh|egE2?uama=u8rPq zvi9b-t(n26RcF86zApV#{=O@F?yTXOeR1BZFl)K4Z&wU`^Ex>9uh@H+M_t$c{>dlY z$E&K=SX`d-EMm{{RqA89t;p53|m(R9gm>&MeFqqChk>t|=C`tjbHBggla@2kq=WwKqauf1fJ zwiE%t_;+$)b$?{hyITo!4sy|QWbw%kl6Pe(J)?DdhGb1Jiq z4w=~MtXh9|)|DT6Jm&rKX;D>P+WIp#a9L+>`g|+EM)}H$b8WY3+*kP59iDce^Q!u3 zA*uVk+Y{el(alVX5v~`ndJvL)0WRQ zIn1S1*%@JUFLPzXjFhk6BkSjGySg;(Pa^x|0Mi-L{x?@uv+63$-Bql!((V3nwY3Z9 zt;ke=e52;0W`*wtwuVU?-W;n-I4{PWx9!oy03}7i1__z-yj{Ckx;Gm?=3B95o$j=o zPgO3r+!gh{CAnwS+UrLj30~eN+Nj?1gx#)I*5gU!u{$#To>6g=d0J)t?i@Esu2*V2 z6OnuC{Y^vEMwf2Q1E*haIy7y`?Fzo%4FTRNS3fdoems5uCU@y0u?E}Z^mR55+3uKD zN$UBzt>%nVB`Vfv)z(Q*MaOJ+VwdT>3{=R{ENJ1?}xdp?$xUte-zKTPQ43 zuqFE7{d!rk<*%199d0_>EpGhviOrnL>OcRdSCxL%pSCP&)~rCyXGQKRa%+Ui8rFM?MvAC1Pv#>C~Oh)Ums~`QX+4DJ9dV zZt}h>sjO(t=;O6>EEyUcI^LMK{}BCwbEsX@^)}c(dI<-Ogio=6mL! z)Sn+ko8LcoY;La*2z@WTU~kk6S;HS1_6(w%OkJX~V&mf*gU&uktP|L&S#74>agcG> zC+9yK4j%up<6!>>$7cEZr^hEK6^r{x)n9$E{^Mgd`-A>ce?;tc&tB*K$96ycgZWF> zWwGK~mCZMg`W6HRCIxQLPE&2VI78N@H+S~Jj0-({4LW^I3Tlztjc&|X#n)7(bE9{& zddAnq6FB%;RvVWvPxa_td?@$T4w1ub_1*h(Ka~I2%~sz(Kc(unXRF8Cx%CIcHYPl& zpM2oK`_KF){}vv!f0RDMY)({i;ao}aWs-NBEyEf=Z~l|q%y&tg_aEo~D-WJ01iibf z*BZ4Vc&_xpi1Kwy59SGnnGPzS*!Q--UA$KBVrT#PvAYMHOy-BkNgqF(G4v*Qaxg=$NIwH}UJj7<5P*_Nhpw+RJU z>s@O;y*tJ8LXY#75IL4zSISmCJ@~Fdhw1l?k10+z52y4`T@+!kTZMnoqBXo?s$Gj- zwn$B>idZ)1VUNCb@YLM3pSW(Yybi6jN<8#n{>R!CC)_@6Rh8W8ET|o9>3QL=OzD}7 zZ)^MOkDq1qJjnLHDbD1_^gr6o^}6x<8<}6Sc5?1j7V$m0(V#-E^z_NBeTFam?kDe= zyLX~Qm&y08)YClwbdz{v>e4^F|H;}MFLL1gsn~8K)*_jE=jI%&e_+qG*xvK=C5fd* zTUQ;d7wfJ#GEXSA-g9#AT1(!#$P>@@7v5a?F`oCI^#A(159&SM>CBmt9Hgd^+o5_m zR;wsrhBDjbsg)b@x0vvk9e=dd@udC#D?j4d{zuC!J}NXd=tssM`R4e<{$uN<{>0lQ ze#rh~wXED)bnQW9J)@X6MGHkvMedONvg&m14x54{r(b_ss%x__$~b1$5qjKrvtS)$_WL|$fk zO0QU$eeTafrCz#6~ke+z6HCt@?+Pxod$qOqdhq@&E~@Tg5kT%X0p{;Er@G4EE#sSy23XW3}kE zRR4{0ZG_Kg=FRY!Um2opzxH%y+nxO5mx?kLx+E_-f2{Z3^|j~ZO5JB4$a(I<{anPp z*t&kjwA%+~7j>2XGWYaXDq&sox=md@Lg(@Gn8f{RVw(k-((b!Wsad;Ca{E!{;H4XK z%q9m;k^ji6=JxD)-A1Y2Nj-L^D{sHH_;mUD(e%B{Q~i_fa68hl$#u4qH^StQr@=3Zt>@~+)X>-*4lj7;b>Zzn*JHDfaLfY>Cfxf zCjL$9@mm_lka|Tw@b;Q{Kf~q)>l&@66`+OF^mMX5+k#p|2TUee0O~(`@-Vu1u!q52k*0nrFX#Yudke z(@X=GPYNl{G2gUx=83A$r8-Strl5O;_{qhO0|fT+Lrz*SEb+%{IfJ?emvs-7(MVZQirH6&gPj zT&orR)Np;XI`fKk0rNAsJ##9`AFFJ9A#=-F)#b@jhQ0lHD$Bk{`_=I(e_{V3eTKJjza(z;KfZqT!2`$r3radZd9{1ypPz3scb<&W-dLZ$EHuNuS97Hfc@$nk6$scqOwk>~t4?&d_{&*k|S8$=@!ob$R8T zlJr$IXy=vbC!QXAq5jgWr^CeEL!I~Ut4)rxb_t%{GVRj(3nDc>>%X$tG*5|tJvGm5 zE&t=m%=;2&-Hem^z`EyYs;F~_fyd^);D-wfi`F&tJ#Q}7Oy#AXYSB}iG&9_dsW%zYh?@a$?QgtEzweE)hg^DH-4&jwAy{m*yrFkMXTl4UdO%D z{=jO(f8Vov`DKp-!4IN8y;Cm9TPt>f?WN86<(WYTtaCpq=w@(Nq^?-;)=FMp;^(xS z?H>eM=5QU3VPMo>P<3_p)SKIwRb0%jcr_)NFSyJv&VS)%39IfR?Ui%xb(V%7o^UeVY9b+&ukqh45LG1nVdzo>e(_1VpUUz$T;n}!fTs*xJ%*%gg2Ar(3yK-RpScs~`ffO{ z$8y1L>H0|q$(!?7gTvzc4{p;dxLNNSzjXbm-|Hf{U1YVD}ojiG^?uUY$O-(tR}X!ZZbL!MVMzV91c$}g(yl4kraY4KjJ=YIE< zqI!OT@)_3{Sbv8s_!Z(&&m2|hxVLFvr@p5ZujE_l7tvaVr>3*4^scRqX8qi(Rr;y^ zpuz3C_ZPljW+LOLKXG^Bj4-8ZS*ns(-!2Y)5w_LgwW{c&L+#V86(b((oqTTMht-8n z?|pxt(BRK-&)nKQXTtBwpB2;j8vhH<_^BaM+mOawzvZexwyG`5%r-&4{$^xydiae}+CL@ekgf z^J)0euGRUEG=1O1yQ!&PROf>G#hRZ&|qLZawjAcEank z8y4nIxOXY;b^RPJ=BR&S>P}4mt~qAqI){k}=jU2F6w7YA{ju}&Lyoo|bEpI{Ys{O1h4>vyKn^oWXtuW#@=Z*K8 zvlzFCGh}!(Y>aZq;#Ii9w&YEdx?Z#Xx~7%iE)+lb!n5G3j^Fm@2i_j%xIF)Wy*=xK zdNzgsGXMT8{gfg9;8t7l=i@V`PtayMTYO{f^k8%52kg-^wFTYFV7+UuWHVi_`j|Pvn-Ke!{WwiT0TQYo*SO z8}`ODDOI&31+2fOZvSh7zlz#D#xM4^K7}oQi)1=7cS_bmWYGj|E*G!)?ahCqzFdDKu-|O*>TL?o zPZZ8{|L?l4inpHgXQ`Icp@_ycg^asbOuV~gUhS6VzgNy$napwt{NnlLZ_b6OHm&O( ztt`>~C4FK4{NFM$Pp|V?6_+7om|woW7)hd*E6&pc_-KPp1ZEW6jR@y@cB)^1kZH0 z=Aa)o|Kv|qPmNjSaY9D$w#W464@7)!D=y(Ml0W`U^moQLgV>w%{`;wiPd#O5?(*I$ ztV*N#h~}him3o;r{m(w#JjK&?xKZ_hXXk4%q1bsH!M9^LWm{M5Dw%L^?|iYT&*Duz z!rvd?_VfCR{-#TRvIUEOpVT;8U(av%ZQ?Ua2f6PVJS&W5oM*nxYSP@j;kT1Tp`h-u z8!JSvMh5*j5&Nvp_)w7Cnqc=wxf>c=QZJTBT)Q8{rgJto{@u>0%cg{{X!^Q~Sy^B4 z$1~-N-{j@CS4#feF=<(`sM&hQom*SPK3rO}XsO2Ihe`{ccL=TEkf?GM)%BbBqK%Wg zzHw!B@I2EKrA}SDlhs!=xX(2?dwz1G+3~902-W3F!=l=Zj@nn5epspNHHE{mo#jJ` z%LRrY_7o}q>AN^}K3BSRrHWR~{CtXW^6Z7l0qYM7bf*_N_8r}KIohV1JGeVLSn~Sv ztP6Km&t-Xi`o*K-62|#AWlkJDUi^XQ=bWR(K^>_#c$&Q{ekI1l?7!1qTvVjj{EtW7 z-omnUru?@`tG62Bo5nsuBgkJztOMc>Ah#Yb2VWD@YWj5l?#3_Y1MdB21dReucDN(8IUtI$5xOcO?6$WI${Ym) z0xW6lcA8wMBRYAnlhoulw*|Nm)=Z05O1!DE5RKF_3>9&Q2)bwBf9lHN9X;Yy{+-=7&Um7bW~cwT9;_j3cLrVEoB&+AQ& gc_1)(_j4(xWtS&kf1buvb#=1+3vV{x>!5H30K|+;4gdfE diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f42e62f37..d7e66b5c6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 7588345b6d3260eb9b024b7e2e627a86e79c1f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 20 Dec 2022 23:35:47 +0100 Subject: [PATCH 161/164] Bump sdk --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6b496d85a..08e0ed921 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.8.2-SNAPSHOT" + implementation "io.github.wulkanowy:sdk:c74512daa7" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' From 61240777cf3d70989e0e944f953c87dc594bd564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 21 Dec 2022 00:00:03 +0100 Subject: [PATCH 162/164] Version 1.8.2 --- .github/workflows/test.yml | 5 ++++- README.cs.md | 2 +- README.de.md | 2 +- README.en.md | 2 +- README.md | 2 +- README.sk.md | 2 +- app/build.gradle | 8 ++++---- app/src/main/play/release-notes/pl-PL/default.txt | 10 ++++------ 8 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 13875078a..7f8591bb1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,10 @@ name: Tests on: push: - branches: [ master, develop ] + branches: + - master + - develop + - 'hotfix/**' tags: [ '*' ] pull_request: diff --git a/README.cs.md b/README.cs.md index d3d4e2557..8171b27d6 100644 --- a/README.cs.md +++ b/README.cs.md @@ -2,7 +2,7 @@ # Wulkanowy -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) diff --git a/README.de.md b/README.de.md index 853abd13e..972f66ba9 100644 --- a/README.de.md +++ b/README.de.md @@ -2,7 +2,7 @@ # Wulkanowy -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) diff --git a/README.en.md b/README.en.md index 7877bf377..6e4da4637 100644 --- a/README.en.md +++ b/README.en.md @@ -2,7 +2,7 @@ # Wulkanowy -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) diff --git a/README.md b/README.md index 09480e7d7..f3d2e29a2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Wulkanowy -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) diff --git a/README.sk.md b/README.sk.md index 64786556e..ff0c6e3c9 100644 --- a/README.sk.md +++ b/README.sk.md @@ -2,7 +2,7 @@ # Wulkanowy -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) diff --git a/app/build.gradle b/app/build.gradle index 08e0ed921..a61d0a1d4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 116 - versionName "1.8.1" + versionCode 117 + versionName "1.8.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -162,7 +162,7 @@ play { track = 'production' releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS userFraction = 0.10d - updatePriority = 4 + updatePriority = 5 enabled.set(false) } @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:c74512daa7" + implementation "io.github.wulkanowy:sdk:1.8.2" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 7b2fda861..c4f4c6cfb 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,9 +1,7 @@ -Wersja 1.8.1 +Wersja 1.8.2 -- naprawiliśmy liczenie średniej ucznia w ocenach klasy dla wykresu "Wszystkie" -- zmieniliśmy kolejność przycisków akcji w podglądzie wiadomości -- ulepszyliśmy oznaczenie nieodczytanych wiadomości -- naprawiliśmy pokazywanie informacji o odczytanej wiadomości w wysłanych -- dodaliśmy opcję ręcznego wybierania skrzynki pocztowej +- naprawiliśmy logowanie dla użytkowników systemu Resman Rzeszów +- dodaliśmy wsparcie dla nowej platformy z Tomaszowa Mazowieckiego +- naprawiliśmy literówkę w tytule wiadomości z szablonem usprawiedliwienia Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From aa3d7e37fcf7acadeb5dfd1f1b2f76ac31b8b496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 21 Dec 2022 00:31:29 +0100 Subject: [PATCH 163/164] Automatically show current student mailbox only when there is only one mailbox available (#2085) * Automatically show current student mailbox only when there is only one mailbox for this student available * Fallback to 'unknown' mailbox key if there is no matching mailbox to message --- .../wulkanowy/data/mappers/MessageMapper.kt | 6 ++++- .../messages/GetMailboxByStudentUseCase.kt | 5 +++- .../domain/GetMailboxByStudentUseCaseTest.kt | 26 +++++++++++++++++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt index 120eb183a..2ede5aa1b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.data.mappers import io.github.wulkanowy.data.db.entities.* import io.github.wulkanowy.sdk.pojo.MailboxType +import timber.log.Timber import io.github.wulkanowy.sdk.pojo.Message as SdkMessage import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient @@ -16,7 +17,10 @@ fun List.mapToEntities( mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box -> box.fullName == it.mailbox }?.globalKey.let { mailboxKey -> - requireNotNull(mailboxKey) { "Can't find ${it.mailbox} in $allMailboxes" } + if (mailboxKey == null) { + Timber.e("Can't find ${it.mailbox} in $allMailboxes") + "unknown" + } else mailboxKey }, email = student.email, messageId = it.id, diff --git a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt index a696d9b2f..669514aae 100644 --- a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt +++ b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt @@ -17,8 +17,11 @@ class GetMailboxByStudentUseCase @Inject constructor( private fun List.filterByStudent(student: Student): Mailbox? { val normalizedStudentName = student.studentName.normalizeStudentName() - return find { + return singleOrNull { it.studentName.normalizeStudentName() == normalizedStudentName + } ?: singleOrNull { + it.studentName.normalizeStudentName() == normalizedStudentName + && it.schoolNameShort == student.schoolShortName } ?: singleOrNull { it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart() } ?: singleOrNull { diff --git a/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt index 029800266..6db16d2f5 100644 --- a/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt +++ b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt @@ -160,8 +160,29 @@ class GetMailboxByStudentUseCaseTest { assertEquals(expectedMailbox, systemUnderTest(student)) } + @Test + fun `get mailbox for student with mailboxes from two different schools`() = runTest { + val student = getStudentEntity( + userName = "Kamil Bednarek", + studentName = "Kamil Bednarek", + schoolShortName = "CKZiU", + ) + val mailbox1 = getMailboxEntity( + studentName = "Kamil Bednarek", + schoolShortName = "ZSTiO", + ) + val mailbox2 = getMailboxEntity( + studentName = "Kamil Bednarek", + schoolShortName = "CKZiU", + ) + coEvery { mailboxDao.loadAll(any()) } returns listOf(mailbox1, mailbox2) + + assertEquals(mailbox2, systemUnderTest(student)) + } + private fun getMailboxEntity( studentName: String, + schoolShortName: String = "test", ) = Mailbox( globalKey = "", fullName = "", @@ -170,13 +191,14 @@ class GetMailboxByStudentUseCaseTest { schoolId = "", symbol = "", studentName = studentName, - schoolNameShort = "", + schoolNameShort = schoolShortName, type = MailboxType.STUDENT, ) private fun getStudentEntity( studentName: String, userName: String, + schoolShortName: String = "test", ) = Student( scrapperBaseUrl = "http://fakelog.cf", email = "jan@fakelog.cf", @@ -192,7 +214,7 @@ class GetMailboxByStudentUseCaseTest { privateKey = "", registrationDate = Instant.now(), schoolName = "", - schoolShortName = "test", + schoolShortName = schoolShortName, schoolSymbol = "", studentId = 1, studentName = studentName, From 67875b1a9a230d0a9950718569391d2d39149116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 21 Dec 2022 13:29:32 +0100 Subject: [PATCH 164/164] Version 1.8.3 --- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a61d0a1d4..4cde3c294 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 32 - versionCode 117 - versionName "1.8.2" + versionCode 118 + versionName "1.8.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -186,7 +186,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.8.2" + implementation "io.github.wulkanowy:sdk:1.8.3" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index c4f4c6cfb..5a47ddc7e 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,7 +1,8 @@ -Wersja 1.8.2 +Wersja 1.8.3 - naprawiliśmy logowanie dla użytkowników systemu Resman Rzeszów - dodaliśmy wsparcie dla nowej platformy z Tomaszowa Mazowieckiego +- poprawiliśmy dopasowywanie skrzynek pocztowych do uczniów - naprawiliśmy literówkę w tytule wiadomości z szablonem usprawiedliwienia Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases