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 01/72] 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 7db0aceb..57ad5c90 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 02/72] 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 57ad5c90..2cfb7bc6 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 03/72] 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 2cfb7bc6..b05e0501 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 04/72] 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 b05e0501..ab1593f8 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 05/72] 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 ab1593f8..57ba0f59 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 07e717ea..74576986 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 06/72] 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 57ba0f59..2a36b19a 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 72fee08a..7835db90 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 c87ec150..d778e9a6 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 07/72] 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 c5c0196c..a7aa4cc2 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 746601a6..8cde5d6b 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 b07570cb..4d5a43d8 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 1d777bdb..312f0b87 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 08/72] 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 860ecc57..4b2685c6 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 09/72] 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 2a36b19a..691d00f6 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 10/72] 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 691d00f6..c9c7a9ef 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 11/72] 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 d778e9a6..5a498be9 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 12/72] 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 5a498be9..7640fbc4 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 13/72] 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 c9c7a9ef..612cb953 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 14/72] 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 612cb953..1038184f 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 15/72] 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 1038184f..1f680e26 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 16/72] 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 1f680e26..aa7f6b37 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 17/72] 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 aa7f6b37..230afdfe 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 d7d77f73..aac60b56 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 c1c111d4..786bbfce 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 1bfc8ba5..260cf76c 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 7640fbc4..3a8ae20c 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 18/72] 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 5252f79b..c8731372 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 08b9d240..097e90e9 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 c823e960..45600574 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 1607b17c..91e9fe7f 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 9cc37620..8a8c260d 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 e64f5606..ab0a43b6 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 82c5b6ec..44acd18e 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 19/72] 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 230afdfe..f3f39258 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 20/72] 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 3a8ae20c..9dde0b28 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 21/72] 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 f3f39258..e629134e 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 b4291ff4..0acb0ea6 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 22/72] 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 7fcbd002..26bfaf19 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 dc6c8921..d0687408 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 23/72] 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 ff25da7f..2ca516ad 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 24/72] 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 d270da4a..f72fed2f 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 25/72] 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 f62a11a6..969003cc 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 26/72] 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 e629134e..7c8046da 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 27/72] 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 7c8046da..3e491206 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 28/72] 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 00000000..5472fb78 --- /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 379b8738..17fd7d69 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 15655f4a..c32e4aba 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 c8731bde..25e27ef1 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 00000000..6e1de19d --- /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 d059db81..16f1bbac 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 cf7ac86c..4c42d092 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 805ceb3e..1aedc839 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 9b21f08e..e2dc5cd8 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 29/72] 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 a27dba88..6ef6cfc9 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 74576986..3ba2ae94 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 30/72] 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 3e491206..29325abf 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 00000000..461d2995 --- /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 00000000..0e922702 --- /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 b5103e3e..7d2eeb1e 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 4cd85586..237fb1a0 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 65832bdb..de0b4a6c 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 c20bae7f..e220ae23 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 b9625570..9c15acc3 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 c33955bc..e963a020 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 2cc2f1d2..76788543 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 9191d43c..a3c423a8 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 7a4c2b25..da588015 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 64cf599c..1244ff60 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 060f224b..583bf29d 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 afffcc51..d00df9d4 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 55ec9029..8105ff9c 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 a07bdb37..8f457d92 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 3a57fcc6..3d018e3d 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 323e1e47..dd91d36d 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 00000000..39510162 --- /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 00000000..b75bb27e --- /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 deeb3696..9c092ec5 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 849d989e..f29080b3 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 2ca516ad..db591309 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 8d31928b..48a6fc3e 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 5ccbce1e..85c14c0a 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 89de7bd1..8de6e60d 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 dde4d012..6be8e924 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 52a3df58..d5e93a35 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 720239e6..6cfab199 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 31/72] 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 29325abf..ab1d0aef 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 48192df0..52426f54 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 zcmV;-12g=L4Fm}T0%<=)a*^;avhvdD0hlES7gzW~SxC7S4YX4lN=4OW&+S-MzC$IzYu{y<=`stS~cn^QK*e>y-u~-qpy+P7x zD2rcrQ*4cowl$jzLC)#4CPj&+kn_(oCKN)(aMTlhtDYp?G>jv9~7)%?$Q$h#qs#wJ`Q47 z3iU@=_0SJCMTnK%{8pSB6CaXs6KTsX3|Y8khwiK zu#-ry=?MTegxR1puRHC^kZZ)AvcY7%KZzDjQ3wdt4#o!8sl3ub$oCtIXluy2q^_DG zn-oyi)_!MuB*~}>WQE#GdcM0TPaXuwR@RFtRYwRMG$tkj`?-JTNbU=Vo24<(%Ramx z@gxa2rE@ifbL_)8E8>=U2$+BBUjIBHgj^p_$_`p9c!PXw4&=_L{ipv|`3(L>&tl;u z2cFBbubz`HTN#!aAa3t%UbaeS08#~^B#C~-&8qO8n_53YZ^GZr%OQpTgjeAyLDdmw zKR(nQ5hg-x-gaLH!1-9X*pI2gaS^k!vOw&8ONkTGOR5`Uk60^|^3qg+ZQM9$ zHg~bXl~LYL@lKj7nk#a8rPx?*e_$ zcup00ns%EQ!p<8y^u^`fU~t1AvmOU)UMt#;HlmtytJ_R-EnmZWKB6Ip7CNR`p4d3~ z4@U5f;a`3*K4gJk9N@G;Ud#Y4!+NJ>rXeLOV5I>>PWxjBxO2qG7xitX;76AwqlbgL zwU9U@wuKh|B(yh3Y3zSN62X+8S~Pvc+;ZVWzy#LR{M;`~DY9X6UdWrdgjx9v4G#wk zLG(3nois0l3!2Z16|%xUey6sdf2`R`$}7GV!U=bV(rckG@SalCxaQI>pUZvzoH1Ux z4I|qM|2>ptoKAY5HmRl^coLu)(LDlNEMl2OX46tHsnI$wR@StgHGAt7uka5l=nqu6s|D~DK1238lJ)2aiB_PSRv7bG1BhhT%-SuT-_%C literal 608 zcmV-m0-ybi4Fm@R0@P}-N~a-oyYSNK0WX%~jn^f7`%YvHjG4XGto&><3EHj#NjE{Q zWNyD9Id&0y2lNV4U4G1p*6^jl9ZMR)6W>Jofw`o8vJxxx&{X7p^5lS1;va*U``;UK zk3%`m|CGLNLKRy8uibH&Y1~Xz}NuW4k4FH-u&kWF!eC{7ds%(vf+OT`d}cd_?yj%#6$?z1%v5Jv&yr`8@0>gz9> zN%VTh2CX$H3XNP0&;|f3ur+vKqCAn3udC$zAy8mm3Q6|8tv-|H{5n;tQXN-44qZm<7x(+3(0lU! Date: Sun, 26 Jun 2022 15:50:35 +0200 Subject: [PATCH 32/72] 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 237fb1a0..486538e0 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 e963a020..5d7c7df4 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 8f457d92..9c32d858 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 48a6fc3e..de4c591e 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 85c14c0a..772d616d 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 6be8e924..c536e221 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 33/72] 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 0391e282..21964d49 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 34/72] 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 21964d49..7949ceaa 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 35/72] 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 ab1d0aef..b7a5c04b 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 36/72] 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 00000000..4361db95 --- /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 17fd7d69..87915a9e 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 081e859a..96382cc1 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 887e4323..89b04ccc 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 00000000..d45a8157 --- /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 b1e96a27..1a1c501f 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 eda40cac..07c6959e 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 b9a958d4..6865aa7d 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 37/72] 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 b7a5c04b..50c572ed 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 38/72] 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 7949ceaa..b96c8582 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 39/72] 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 50c572ed..83e18e95 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 40/72] 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 83e18e95..91c45876 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 41/72] 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 b96c8582..71eb2def 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 42/72] 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 91c45876..588f8dcc 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 43/72] 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 b9caf978..84055cef 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 44/72] 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 588f8dcc..b51c87f1 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 45/72] 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 b51c87f1..0a848a44 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 46/72] 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 0a848a44..d5df6579 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 47/72] 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 71eb2def..bb9afab3 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 48/72] 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 d5df6579..2f284d0a 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 49/72] 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 2f284d0a..2f4f7021 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 50/72] 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 2f4f7021..80e3dab4 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 51/72] 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 80e3dab4..ac8df81b 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 52/72] 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 bb9afab3..c88cd35f 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 53/72] 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 c88cd35f..c4dc9c27 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 54/72] 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 ac8df81b..4eaa79f1 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 55/72] 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 4eaa79f1..28aa8584 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 56/72] 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 28aa8584..542dda10 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 57/72] 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 542dda10..a4549f08 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 58/72] 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 c4dc9c27..35665b25 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 59/72] 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 a4549f08..f8cb8440 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 60/72] 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 12338fef..cfb0fb52 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 88edca05..20082590 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 ee16041f..3def0895 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 f8cb8440..78eebff8 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 35665b25..84ed29b9 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 61/72] 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 84ed29b9..bc4ca519 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 62/72] 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 bc4ca519..98c9dfb8 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 63/72] 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 78eebff8..52b2aad3 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 64/72] 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 52b2aad3..1f68b65c 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 65/72] 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 3f806e9f..b9e1d1ec 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 66/72] 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 1f68b65c..eace462d 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 67/72] 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 eace462d..6d33ac47 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 68/72] 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 6d33ac47..0efb9c30 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 00000000..271b8c90 --- /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 cac3ffc2..22123cbe 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 87915a9e..15b38805 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 00000000..8589db31 --- /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 729ba6a6..8c730c9b 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 c2787ac3..1956261e 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 ca697eda..00000000 --- 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 00000000..7c08e481 --- /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 8782bc76..77874e03 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 d1886e91..93f04299 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 2e7af0f4..cd468215 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 22332270..d09742cd 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 0570a2ff..00000000 --- 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 00000000..e78e2e3a --- /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 00000000..2ccca1b9 --- /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 13f0ab33..2e7967f0 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 80bddaab..eb993a0f 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 6a21d59f..00000000 --- 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 00000000..7f597492 --- /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 05fb9765..00cbffb8 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 60e6f248..e80f028e 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 84055cef..00000000 --- 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 5c3c52c5..3b7bcff0 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 26fac1a2..18056826 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 425e68b9..b1322ada 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 5d7c7df4..35030093 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 53d43961..6ff26162 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 d75128be..3c1c53d3 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 4b2685c6..2a5523f4 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 39c337bf..c011f41f 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 88fe77d9..c5a94793 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 70f9a9b5..334e389e 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 21b42e3e..1057114b 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 af0923b9..55f03ef8 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 870b6433..54711a68 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 10b581f7..320782bd 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 22539930..ff0a5313 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 2a5d2e2b..24306bfe 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 980abac0..ae73a795 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 69/72] 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 0efb9c30..d86f5f43 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 70/72] 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 d86f5f43..efa9b3e0 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 71/72] 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 c8731372..23073adf 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 11054a0e..5f76bb6e 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 097e90e9..d9cac195 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 86308aa1..ef79cee1 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 91e9fe7f..566519e8 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 662e0934..a3c5a62d 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 ab0a43b6..e4331315 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 5ebd1e76..6a4505d7 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 0cc50dbe..742e800f 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 72/72] 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 efa9b3e0..e99e8773 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 b6340bb9..3eb42eb9 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