diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1ae402f53..b07c9638c 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -7,11 +7,11 @@ references:
container_config: &container_config
docker:
- - image: circleci/android:api-28-alpha
+ - image: circleci/android:api-28
working_directory: *workspace_root
environment:
environment:
- JVM_OPTS: -Xmx3200m
+ _JAVA_OPTS: -Xmx3072m
attach_workspace: &attach_workspace
attach_workspace:
@@ -159,7 +159,7 @@ jobs:
openssl aes-256-cbc -d -in ./app/upload-key-encrypted.jks -k $ENCRYPT_KEY >> ./app/upload-key.jks
- run:
name: Publish release
- command: ./gradlew publishRelease --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex
+ command: ./gradlew publish --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex
workflows:
version: 2
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index eb4efba71..43e9b544a 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -19,33 +19,8 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.travis.yml b/.travis.yml
index a430c90ef..10824e32f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,6 +11,11 @@ cache:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
+#branches:
+# only:
+# - master
+# - 0.8.x
+
android:
licenses:
- android-sdk-preview-license-.+
@@ -41,7 +46,7 @@ before_script:
- "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash"
script:
- - ./gradlew build -x test -x lint -x fabricGenerateResourcesRelease -x packageRelease --stacktrace --daemon
+ - ./gradlew dependencies --stacktrace --daemon
- fossa --no-ansi || true
- ./gradlew lint -x fabricGenerateResourcesRelease --stacktrace --daemon
- ./gradlew test -x fabricGenerateResourcesRelease --stacktrace --daemon
@@ -56,7 +61,7 @@ script:
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg;
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg;
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg;
- ./gradlew publishRelease -PenableCrashlytics --stacktrace;
+ ./gradlew publish -PenableCrashlytics --stacktrace;
fi
after_success:
diff --git a/README.md b/README.md
index ccbf14ca5..97a4172c4 100644
--- a/README.md
+++ b/README.md
@@ -6,12 +6,12 @@
[](https://codecov.io/gh/wulkanowy/wulkanowy)
[](https://bettercodehub.com/)
[](https://sonarcloud.io/dashboard?id=io.github.wulkanowy%3Aapp)
-[](https://app.fossa.io/projects/custom%2B5644%2Fgit%40github.com%3Awulkanowy%2Fwulkanowy.git?ref=badge_shield)
+[](https://app.fossa.com/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy?ref=badge_shield)
[](https://discord.gg/vccAQBr)
[Pobierz wersję beta z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy&utm_source=vcs)
-[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/0)
+[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/f841f20d8f8b1dc8/builds/master/artifacts/0)
[(Więcej wersji DEV)](https://wulkanowy.github.io/dev.html)
Androidowy klient dziennika VULCAN UONET+.
@@ -19,4 +19,4 @@ Androidowy klient dziennika VULCAN UONET+.
## License
-[](https://app.fossa.io/projects/custom%2B5644%2Fgit%40github.com%3Awulkanowy%2Fwulkanowy.git?ref=badge_large)
+[](https://app.fossa.com/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy?ref=badge_large)
diff --git a/app/build.gradle b/app/build.gradle
index f60cbb008..3043bc651 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -11,28 +11,29 @@ android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
- playAccountConfigs {
- defaultAccountConfig {
- serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL")
- pk12File = file('key.p12')
- }
- }
-
defaultConfig {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 28
- versionCode 22
- versionName "0.6.3"
+ versionCode 36
+ versionName "0.8.3"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
- playAccountConfig = playAccountConfigs.defaultAccountConfig
manifestPlaceholders = [
- fabric_api_key: System.getenv("FABRIC_API_KEY") ?: "null",
+ fabric_api_key : System.getenv("FABRIC_API_KEY") ?: "null",
crashlytics_enabled: project.hasProperty("enableCrashlytics")
]
+ javaCompileOptions {
+ annotationProcessorOptions {
+ arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
+ }
+ }
+ }
+
+ sourceSets {
+ androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
signingConfigs {
@@ -48,7 +49,6 @@ android {
release {
buildConfigField "boolean", "CRASHLYTICS_ENABLED", "true"
minifyEnabled true
- useProguard false
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
@@ -66,6 +66,11 @@ android {
lintOptions {
disable 'HardwareIds'
}
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
}
androidExtensions {
@@ -73,65 +78,76 @@ androidExtensions {
}
play {
+ serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf"
+ serviceAccountCredentials = file('key.p12')
+ defaultToAppBundles = false
track = 'alpha'
- uploadImages = false
}
dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
- implementation('io.github.wulkanowy:api:0.6.3') { exclude module: "threetenbp" }
+ implementation 'io.github.wulkanowy:api:0.8.3'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.0.2"
- implementation "androidx.cardview:cardview:1.0.0"
- implementation "com.google.android.material:material:1.0.0"
implementation 'androidx.multidex:multidex:2.0.1'
- implementation 'com.takisoft.preferencex:preferencex:1.0.0'
- implementation "com.mikepenz:aboutlibraries:6.2.0"
- implementation "com.firebase:firebase-jobdispatcher:0.8.5"
+ implementation "androidx.cardview:cardview:1.0.0"
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation "com.google.android.material:material:1.1.0-alpha05"
+ implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
+ implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
- implementation "com.google.dagger:dagger-android-support:2.19"
- kapt "com.google.dagger:dagger-compiler:2.19"
- kapt "com.google.dagger:dagger-android-processor:2.19"
+ implementation "androidx.work:work-runtime:2.0.1"
+ implementation "androidx.work:work-rxjava2:2.0.1"
- implementation "androidx.room:room-runtime:2.1.0-alpha03"
- implementation "androidx.room:room-rxjava2:2.1.0-alpha03"
- kapt "androidx.room:room-compiler:2.1.0-alpha03"
+ implementation "androidx.room:room-runtime:2.1.0-alpha07"
+ implementation "androidx.room:room-rxjava2:2.1.0-alpha07"
+ kapt "androidx.room:room-compiler:2.1.0-alpha07"
+
+ implementation "com.google.dagger:dagger-android-support:2.22.1"
+ kapt "com.google.dagger:dagger-compiler:2.22.1"
+ kapt "com.google.dagger:dagger-android-processor:2.22.1"
+ implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
+ kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
implementation "eu.davidea:flexible-adapter:5.1.0"
implementation "eu.davidea:flexible-adapter-ui:1.0.0"
+ implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
+ implementation 'com.ncapdevi:frag-nav:3.2.0'
- implementation "com.aurelhubert:ahbottomnavigation:2.2.0"
- implementation 'com.ncapdevi:frag-nav:3.0.0'
-
- implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.1'
- implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
- implementation "io.reactivex.rxjava2:rxjava:2.2.4"
-
- implementation "com.jakewharton.threetenabp:threetenabp:1.1.0"
+ implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2'
+ implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
+ implementation "io.reactivex.rxjava2:rxjava:2.2.8"
+ implementation 'com.google.code.gson:gson:2.8.5'
+ implementation "com.jakewharton.threetenabp:threetenabp:1.2.0"
implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
+ implementation "com.squareup.okhttp3:logging-interceptor:3.12.1"
- implementation 'com.google.firebase:firebase-core:16.0.6'
- implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
+ implementation "com.mikepenz:aboutlibraries:6.2.3"
+ implementation 'com.takisoft.preferencex:preferencex:1.0.0'
- releaseImplementation 'fr.o80.chucker:library-no-op:2.0.3'
- debugImplementation 'fr.o80.chucker:library:2.0.3'
+ implementation 'com.google.firebase:firebase-core:16.0.8'
+ implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'
- debugImplementation "com.amitshekhar.android:debug-db:1.0.4"
+ releaseImplementation 'fr.o80.chucker:library-no-op:2.0.4'
+
+ debugImplementation 'fr.o80.chucker:library:2.0.4'
+ debugImplementation "com.amitshekhar.android:debug-db:1.0.6"
testImplementation "junit:junit:4.12"
- testImplementation "io.mockk:mockk:1.8.13.kotlin13"
- testImplementation "org.mockito:mockito-inline:2.23.4"
+ testImplementation "io.mockk:mockk:1.9.2"
+ testImplementation "org.mockito:mockito-inline:2.27.0"
testImplementation 'org.threeten:threetenbp:1.3.8'
androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
- androidTestImplementation 'org.mockito:mockito-android:2.23.4'
+ androidTestImplementation "io.mockk:mockk-android:1.9.2"
+ androidTestImplementation 'org.mockito:mockito-android:2.27.0'
+ androidTestImplementation "androidx.room:room-testing:2.1.0-alpha07"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}
diff --git a/app/jacoco.gradle b/app/jacoco.gradle
index 4f94ef190..f22c33598 100644
--- a/app/jacoco.gradle
+++ b/app/jacoco.gradle
@@ -1,7 +1,7 @@
apply plugin: "jacoco"
jacoco {
- toolVersion "0.8.2"
+ toolVersion "0.8.3"
reportsDir = file("$buildDir/reports")
}
@@ -31,17 +31,17 @@ task jacocoTestReport(type: JacocoReport) {
'**/*_Provide*Factory*.*',
'**/*_Factory.*']
- classDirectories = fileTree(
+ classDirectories.setFrom(fileTree(
dir: "$buildDir/intermediates/classes/debug",
excludes: excludes
) + fileTree(
dir: "$buildDir/tmp/kotlin-classes/debug",
excludes: excludes
- )
+ ))
- sourceDirectories = files("$project.projectDir/src/main/java")
- executionData = fileTree(
+ sourceDirectories.setFrom(files("$project.projectDir/src/main/java"))
+ executionData.setFrom(fileTree(
dir: project.projectDir,
includes: ["**/*.exec", "**/*.ec"]
- )
+ ))
}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index cebd3d94d..15b628384 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -5,6 +5,7 @@
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
+-dontobfuscate
-allowaccessmodification
-repackageclasses ''
-verbose
@@ -13,7 +14,6 @@
#Config for anallitycs
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
--renamesourcefileattribute SourceFile
-keep class com.crashlytics.** {*;}
-keep public class * extends java.lang.Exception
-dontwarn com.crashlytics.**
@@ -32,5 +32,8 @@
-dontwarn rx.internal.util.**
-dontwarn sun.misc.Unsafe
+#Config for MPAndroidChart
+-keep class com.github.mikephil.charting.** { *; }
+
#Config for API
-keep class io.github.wulkanowy.api.** {*;}
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json
new file mode 100644
index 000000000..e42a4a719
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json
@@ -0,0 +1,1325 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 11,
+ "identityHash": "478af7daed6ac4563e71826fb70cc8c8",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "endpoint",
+ "columnName": "endpoint",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "loginType",
+ "affinity": "TEXT",
+ "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": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Students_email_symbol_student_id_school_id",
+ "unique": true,
+ "columnNames": [
+ "email",
+ "symbol",
+ "student_id",
+ "school_id"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Semesters",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "semester_id"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER 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)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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": "INTEGER",
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "predictedGrade",
+ "columnName": "predictedGrade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGrade",
+ "columnName": "finalGrade",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "grade",
+ "columnName": "grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amount",
+ "columnName": "amount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semester",
+ "columnName": "is_semester",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `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, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "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": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "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, \"478af7daed6ac4563e71826fb70cc8c8\")"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json
new file mode 100644
index 000000000..32f943554
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json
@@ -0,0 +1,1332 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 12,
+ "identityHash": "972ad26e6488d9a8239f6bd8597af61d",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "endpoint",
+ "columnName": "endpoint",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "loginType",
+ "affinity": "TEXT",
+ "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": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_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
+ }
+ ],
+ "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"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `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}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "semester_id"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER 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)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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": "INTEGER",
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "predictedGrade",
+ "columnName": "predictedGrade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGrade",
+ "columnName": "finalGrade",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "grade",
+ "columnName": "grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amount",
+ "columnName": "amount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semester",
+ "columnName": "is_semester",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `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, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "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": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "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, \"972ad26e6488d9a8239f6bd8597af61d\")"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json
new file mode 100644
index 000000000..ab554665e
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json
@@ -0,0 +1,1356 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 13,
+ "identityHash": "18643bb64804b8268ec9395e3dd55ecb",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` 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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "endpoint",
+ "columnName": "endpoint",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "loginType",
+ "affinity": "TEXT",
+ "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": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "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
+ }
+ ],
+ "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"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `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}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "isCurrent",
+ "columnName": "is_current",
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "semester_id"
+ ],
+ "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER 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)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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": "INTEGER",
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "predictedGrade",
+ "columnName": "predictedGrade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGrade",
+ "columnName": "finalGrade",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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": "grade",
+ "columnName": "grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amount",
+ "columnName": "amount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semester",
+ "columnName": "is_semester",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `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, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "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": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "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, \"18643bb64804b8268ec9395e3dd55ecb\")"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt
new file mode 100644
index 000000000..507105f6d
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt
@@ -0,0 +1,31 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.Room
+import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.platform.app.InstrumentationRegistry
+import io.github.wulkanowy.data.db.AppDatabase
+import org.junit.Rule
+
+abstract class AbstractMigrationTest {
+
+ val dbName = "migration-test"
+
+ @get:Rule
+ val helper: MigrationTestHelper = MigrationTestHelper(
+ InstrumentationRegistry.getInstrumentation(),
+ AppDatabase::class.java.canonicalName,
+ FrameworkSQLiteOpenHelperFactory()
+ )
+
+ fun getMigratedRoomDatabase(): AppDatabase {
+ val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
+ AppDatabase::class.java, dbName)
+ .addMigrations(Migration12(), Migration13())
+ .build()
+ // close the database and release any stream resources when the test finishes
+ helper.closeWhenFinished(database)
+ return database
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt
new file mode 100644
index 000000000..0bbcc4271
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt
@@ -0,0 +1,133 @@
+package io.github.wulkanowy.data.db.migrations
+
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL
+import androidx.sqlite.db.SupportSQLiteDatabase
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+class Migration12Test : AbstractMigrationTest() {
+
+ @Test
+ fun twoNotRelatedStudents() {
+ helper.createDatabase(dbName, 11).apply {
+ // user 1
+ createStudent(this, 1, true)
+ createSemester(this, 1, false, 5, 1)
+ createSemester(this, 1, true, 5, 2)
+
+ // user 2
+ createStudent(this, 2, true)
+ createSemester(this, 2, false, 6, 1)
+ createSemester(this, 2, true, 6, 2)
+ close()
+ }
+
+ helper.runMigrationsAndValidate(dbName, 12, true, Migration12())
+
+ val db = getMigratedRoomDatabase()
+ val students = db.studentDao.loadAll().blockingGet()
+
+ assertEquals(2, students.size)
+
+ students[0].run {
+ assertEquals(1, studentId)
+ assertEquals(5, classId)
+ }
+
+ students[1].run {
+ assertEquals(2, studentId)
+ assertEquals(6, classId)
+ }
+ }
+
+ @Test
+ fun removeStudentsWithoutClassId() {
+ helper.createDatabase(dbName, 11).apply {
+ // user 1
+ createStudent(this, 1, true)
+ createSemester(this, 1, false, 0, 2)
+ createStudent(this, 2, true)
+ createSemester(this, 2, true, 1, 2)
+ close()
+ }
+
+ helper.runMigrationsAndValidate(dbName, 12, true, Migration12())
+
+ val db = getMigratedRoomDatabase()
+ val students = db.studentDao.loadAll().blockingGet()
+
+ assertEquals(1, students.size)
+
+ students[0].run {
+ assertEquals(2, studentId)
+ assertEquals(1, classId)
+ }
+ }
+
+ @Test
+ fun ensureThereIsOnlyOneCurrentStudent() {
+ helper.createDatabase(dbName, 11).apply {
+ // user 1
+ createStudent(this, 1, true)
+ createSemester(this, 1, true, 5, 2)
+ createStudent(this, 2, true)
+ createSemester(this, 2, true, 6, 2)
+ createStudent(this, 3, true)
+ createSemester(this, 3, false, 7, 2)
+ close()
+ }
+
+ helper.runMigrationsAndValidate(dbName, 12, true, Migration12())
+
+ val db = getMigratedRoomDatabase()
+ val students = db.studentDao.loadAll().blockingGet()
+
+ assertEquals(3, students.size)
+
+ students[0].run {
+ assertEquals(studentId, 1)
+ assertEquals(false, isCurrent)
+ }
+ students[1].run {
+ assertEquals(studentId, 2)
+ assertEquals(false, isCurrent)
+ }
+ students[2].run {
+ assertEquals(studentId, 3)
+ assertEquals(true, isCurrent)
+ }
+ }
+
+ private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean) {
+ db.insert("Students", CONFLICT_FAIL, ContentValues().apply {
+ put("endpoint", "https://fakelog.cf")
+ put("loginType", "STANDARD")
+ put("email", "jan@fakelog.cf")
+ put("password", "******")
+ put("symbol", "Default")
+ put("student_id", studentId)
+ put("student_name", "Jan Kowalski")
+ put("school_id", "000123")
+ put("school_name", "")
+ put("is_current", isCurrent)
+ put("registration_date", "0")
+ })
+ }
+
+ private fun createSemester(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean, classId: Int, diaryId: Int) {
+ db.insert("Semesters", CONFLICT_FAIL, ContentValues().apply {
+ put("student_id", studentId)
+ put("diary_id", diaryId)
+ put("diary_name", "IA")
+ put("semester_id", diaryId * 5)
+ put("semester_name", "1")
+ put("is_current", isCurrent)
+ put("class_id", classId)
+ put("unit_id", "99")
+ })
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
new file mode 100644
index 000000000..eb9d02a59
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
@@ -0,0 +1,171 @@
+package io.github.wulkanowy.data.db.migrations
+
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+import androidx.sqlite.db.SupportSQLiteDatabase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Test
+import org.threeten.bp.LocalDate.of
+import kotlin.test.assertTrue
+
+class Migration13Test : AbstractMigrationTest() {
+
+ @Test
+ fun studentsWithSchoolNameWithClassName() {
+ helper.createDatabase(dbName, 12).apply {
+ createStudent(this, 1, "Klasa A - Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", 1, 1)
+ createStudent(this, 2, "Klasa B - Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", 2, 1)
+ createStudent(this, 2, "Klasa C - Publiczna szkoła Wulkanowego-fejka nr 2 w fakelog.cf", 1, 2)
+ close()
+ }
+
+ helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
+
+ val db = getMigratedRoomDatabase()
+ val students = db.studentDao.loadAll().blockingGet()
+
+ assertEquals(3, students.size)
+
+ students[0].run {
+ assertEquals(1, studentId)
+ assertEquals("A", className)
+ assertEquals("Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", schoolName)
+ }
+
+ students[1].run {
+ assertEquals(2, studentId)
+ assertEquals("B", className)
+ assertEquals("Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", schoolName)
+ }
+
+ students[2].run {
+ assertEquals(2, studentId)
+ assertEquals("C", className)
+ assertEquals("Publiczna szkoła Wulkanowego-fejka nr 2 w fakelog.cf", schoolName)
+ }
+ }
+
+ @Test
+ fun studentsWithSchoolNameWithoutClassName() {
+ helper.createDatabase(dbName, 12).apply {
+ createStudent(this, 1, "Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", 1)
+ createStudent(this, 2, "Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", 1)
+ close()
+ }
+
+ helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
+
+ val db = getMigratedRoomDatabase()
+ val students = db.studentDao.loadAll().blockingGet()
+
+ assertEquals(2, students.size)
+
+ students[0].run {
+ assertEquals(1, studentId)
+ assertEquals("", className)
+ assertEquals("Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", schoolName)
+ }
+
+ students[1].run {
+ assertEquals(2, studentId)
+ assertEquals("", className)
+ assertEquals("Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", schoolName)
+ }
+ }
+
+ @Test
+ fun markAtLeastAndOnlyOneSemesterAtCurrent() {
+ helper.createDatabase(dbName, 12).apply {
+ createStudent(this, 1, "", 5)
+ createSemester(this, 1, 5, 1, 1, false)
+ createSemester(this, 1, 5, 2, 1, false)
+ createSemester(this, 1, 5, 3, 2, false)
+ createSemester(this, 1, 5, 4, 2, false)
+
+ createStudent(this, 2, "", 5)
+ createSemester(this, 2, 5, 5, 5, true)
+ createSemester(this, 2, 5, 6, 5, true)
+ createSemester(this, 2, 5, 7, 55, true)
+ createSemester(this, 2, 5, 8, 55, true)
+
+ createStudent(this, 3, "", 5)
+ createSemester(this, 3, 5, 11, 99, false)
+ createSemester(this, 3, 5, 12, 99, false)
+ createSemester(this, 3, 5, 13, 100, false)
+ createSemester(this, 3, 5, 14, 100, true)
+ close()
+ }
+
+ helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
+
+ val db = getMigratedRoomDatabase()
+
+ val semesters1 = db.semesterDao.loadAll(1, 5).blockingGet()
+ assertTrue { semesters1.single { it.isCurrent }.isCurrent }
+ semesters1[0].run {
+ assertFalse(isCurrent)
+ assertEquals(1, semesterId)
+ assertEquals(1, diaryId)
+ }
+ semesters1[2].run {
+ assertFalse(isCurrent)
+ assertEquals(3, semesterId)
+ assertEquals(2, diaryId)
+ }
+ semesters1[3].run {
+ assertTrue(isCurrent)
+ assertEquals(4, semesterId)
+ assertEquals(2, diaryId)
+ }
+
+ db.semesterDao.loadAll(2, 5).blockingGet().let {
+ assertTrue { it.single { it.isCurrent }.isCurrent }
+ assertEquals(1970, it[0].schoolYear)
+ assertEquals(of(1970, 1, 1), it[0].end)
+ assertEquals(of(1970, 1, 1), it[0].start)
+ assertFalse(it[0].isCurrent)
+ assertFalse(it[1].isCurrent)
+ assertFalse(it[2].isCurrent)
+ assertTrue(it[3].isCurrent)
+ }
+
+ db.semesterDao.loadAll(2, 5).blockingGet().let {
+ assertTrue { it.single { it.isCurrent }.isCurrent }
+ assertFalse(it[0].isCurrent)
+ assertFalse(it[1].isCurrent)
+ assertFalse(it[2].isCurrent)
+ assertTrue(it[3].isCurrent)
+ }
+ }
+
+ private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, schoolName: String = "", classId: Int = -1, schoolId: Int = 123) {
+ db.insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
+ put("endpoint", "https://fakelog.cf")
+ put("loginType", "STANDARD")
+ put("email", "jan@fakelog.cf")
+ put("password", "******")
+ put("symbol", "Default")
+ put("student_id", studentId)
+ put("class_id", classId)
+ put("student_name", "Jan Kowalski")
+ put("school_id", schoolId)
+ put("school_name", schoolName)
+ put("is_current", false)
+ put("registration_date", "0")
+ })
+ }
+
+ private fun createSemester(db: SupportSQLiteDatabase, studentId: Int, classId: Int, semesterId: Int, diaryId: Int, isCurrent: Boolean = false) {
+ db.insert("Semesters", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
+ put("student_id", studentId)
+ put("diary_id", diaryId)
+ put("diary_name", "IA")
+ put("semester_id", semesterId)
+ put("semester_name", "1")
+ put("is_current", isCurrent)
+ put("class_id", classId)
+ put("unit_id", "99")
+ })
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt
new file mode 100644
index 000000000..7dc93c4a4
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt
@@ -0,0 +1,19 @@
+package io.github.wulkanowy.data.repositories
+
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler
+import io.reactivex.Observable
+import io.reactivex.Single
+
+class TestInternetObservingStrategy : InternetObservingStrategy {
+
+ override fun checkInternetConnectivity(host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Single {
+ return Single.just(true)
+ }
+
+ override fun observeInternetConnectivity(initialIntervalInMs: Int, intervalInMs: Int, host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Observable {
+ return Observable.just(true)
+ }
+
+ override fun getDefaultPingHost() = "localhost"
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt
similarity index 82%
rename from app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt
rename to app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt
index 8cbd13cd3..69502e742 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.attendance
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
@@ -11,6 +11,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.threeten.bp.LocalDate
+import org.threeten.bp.LocalDate.now
import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
@@ -34,13 +35,13 @@ class AttendanceLocalTest {
@Test
fun saveAndReadTest() {
attendanceLocal.saveAttendance(listOf(
- Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", ""),
- Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", ""),
- Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "")
+ Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false),
+ Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false),
+ Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false)
))
val attendance = attendanceLocal
- .getAttendance(Semester(1, 1, 2, "", 3, 1),
+ .getAttendance(Semester(1, 2, "", 1, 3, 2019, true, now(), now(), 1, 1),
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 14)
)
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt
new file mode 100644
index 000000000..356073e8e
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt
@@ -0,0 +1,57 @@
+package io.github.wulkanowy.data.repositories.completedlessons
+
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import io.github.wulkanowy.data.db.AppDatabase
+import io.github.wulkanowy.data.db.entities.CompletedLesson
+import io.github.wulkanowy.data.db.entities.Semester
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.threeten.bp.LocalDate
+import kotlin.test.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+class CompletedLessonsLocalTest {
+
+ private lateinit var completedLessonsLocal: CompletedLessonsLocal
+
+ private lateinit var testDb: AppDatabase
+
+ @Before
+ fun createDb() {
+ testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
+ .build()
+ completedLessonsLocal = CompletedLessonsLocal(testDb.completedLessonsDao)
+ }
+
+ @After
+ fun closeDb() {
+ testDb.close()
+ }
+
+ @Test
+ fun saveAndReadTest() {
+ completedLessonsLocal.saveCompletedLessons(listOf(
+ getCompletedLesson(LocalDate.of(2018, 9, 10), 1),
+ getCompletedLesson(LocalDate.of(2018, 9, 14), 2),
+ getCompletedLesson(LocalDate.of(2018, 9, 17), 3)
+ ))
+
+ val completed = completedLessonsLocal
+ .getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
+ LocalDate.of(2018, 9, 10),
+ LocalDate.of(2018, 9, 14)
+ )
+ .blockingGet()
+ assertEquals(2, completed.size)
+ assertEquals(completed[0].date, LocalDate.of(2018, 9, 10))
+ assertEquals(completed[1].date, LocalDate.of(2018, 9, 14))
+ }
+
+ private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson {
+ return CompletedLesson(1, 2, date, number, "", "", "", "", "", "", "")
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt
similarity index 91%
rename from app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt
rename to app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt
index c60348e3a..fb76306d9 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.exam
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
@@ -40,7 +40,7 @@ class ExamLocalTest {
))
val exams = examLocal
- .getExams(Semester(1, 1, 2, "", 3, 1),
+ .getExams(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 14)
)
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt
new file mode 100644
index 000000000..954d0eea2
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt
@@ -0,0 +1,53 @@
+package io.github.wulkanowy.data.repositories.grade
+
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import io.github.wulkanowy.data.db.AppDatabase
+import io.github.wulkanowy.data.db.entities.Semester
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.threeten.bp.LocalDate
+import org.threeten.bp.LocalDate.now
+import kotlin.test.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+class GradeLocalTest {
+
+ private lateinit var gradeLocal: GradeLocal
+
+ private lateinit var testDb: AppDatabase
+
+ @Before
+ fun createDb() {
+ testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
+ .build()
+ gradeLocal = GradeLocal(testDb.gradeDao)
+ }
+
+ @After
+ fun closeDb() {
+ testDb.close()
+ }
+
+ @Test
+ fun saveAndReadTest() {
+ gradeLocal.saveGrades(listOf(
+ createGradeLocal(5, 3.0, LocalDate.of(2018, 9, 10), "", 1),
+ createGradeLocal(4, 4.0, LocalDate.of(2019, 2, 27), "", 2),
+ createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2)
+ ))
+
+ val semester = Semester(1, 2, "", 2019, 2, 1, true, now(), now(), 1, 1)
+
+ val grades = gradeLocal
+ .getGrades(semester)
+ .blockingGet()
+
+ assertEquals(2, grades.size)
+ assertEquals(grades[0].date, LocalDate.of(2019, 2, 27))
+ assertEquals(grades[1].date, LocalDate.of(2019, 2, 28))
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt
new file mode 100644
index 000000000..a0acb5a76
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt
@@ -0,0 +1,182 @@
+package io.github.wulkanowy.data.repositories.grade
+
+import android.os.Build.VERSION_CODES.P
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.api.Api
+import io.github.wulkanowy.data.db.AppDatabase
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.impl.annotations.SpyK
+import io.reactivex.Single
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.threeten.bp.LocalDate.of
+import org.threeten.bp.LocalDateTime
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import io.github.wulkanowy.api.grades.Grade as GradeApi
+
+@SdkSuppress(minSdkVersion = P)
+@RunWith(AndroidJUnit4::class)
+class GradeRepositoryTest {
+
+ @SpyK
+ private var mockApi = Api()
+
+ private val settings = InternetObservingSettings.builder()
+ .strategy(TestInternetObservingStrategy())
+ .build()
+
+ @MockK
+ private lateinit var semesterMock: Semester
+
+ @MockK
+ private lateinit var studentMock: Student
+
+ private lateinit var gradeRemote: GradeRemote
+
+ private lateinit var gradeLocal: GradeLocal
+
+ private lateinit var testDb: AppDatabase
+
+ @Before
+ fun initApi() {
+ MockKAnnotations.init(this)
+ testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
+ gradeLocal = GradeLocal(testDb.gradeDao)
+ gradeRemote = GradeRemote(mockApi)
+
+ every { mockApi.diaryId } returns 1
+ every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0)
+ every { semesterMock.studentId } returns 1
+ every { semesterMock.semesterId } returns 1
+ every { semesterMock.diaryId } returns 1
+ }
+
+ @After
+ fun closeDb() {
+ testDb.close()
+ }
+
+ @Test
+ fun markOlderThanRegisterDateAsRead() {
+ every { mockApi.getGrades(1) } returns Single.just(listOf(
+ createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"),
+ createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
+ createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"),
+ createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
+ ))
+
+ val grades = GradeRepository(settings, gradeLocal, gradeRemote)
+ .getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date }
+
+ assertFalse { grades[0].isRead }
+ assertFalse { grades[1].isRead }
+ assertTrue { grades[2].isRead }
+ assertTrue { grades[3].isRead }
+ }
+
+ @Test
+ fun mitigateOldGradesNotifications() {
+ gradeLocal.saveGrades(listOf(
+ createGradeLocal(5, 3.0, of(2019, 2, 25), "Jedna ocena"),
+ createGradeLocal(4, 4.0, of(2019, 2, 26), "Druga"),
+ createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia")
+ ))
+
+ every { mockApi.getGrades(1) } returns Single.just(listOf(
+ createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
+ createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
+ createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
+ createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
+ ))
+
+ val grades = GradeRepository(settings, gradeLocal, gradeRemote)
+ .getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date }
+
+ assertFalse { grades[0].isRead }
+ assertFalse { grades[1].isRead }
+ assertTrue { grades[2].isRead }
+ assertTrue { grades[3].isRead }
+ }
+
+ @Test
+ fun subtractLocaleDuplicateGrades() {
+ gradeLocal.saveGrades(listOf(
+ createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
+ ))
+
+ every { mockApi.getGrades(1) } returns Single.just(listOf(
+ createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
+ ))
+
+ val grades = GradeRepository(settings, gradeLocal, gradeRemote)
+ .getGrades(studentMock, semesterMock, true).blockingGet()
+
+ assertEquals(2, grades.size)
+ }
+
+ @Test
+ fun subtractRemoteDuplicateGrades() {
+ gradeLocal.saveGrades(listOf(
+ createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
+ ))
+
+ every { mockApi.getGrades(1) } returns Single.just(listOf(
+ createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
+ ))
+
+ val grades = GradeRepository(settings, gradeLocal, gradeRemote)
+ .getGrades(studentMock, semesterMock, true).blockingGet()
+
+ assertEquals(3, grades.size)
+ }
+
+ @Test
+ fun emptyLocal() {
+ gradeLocal.saveGrades(listOf())
+
+ every { mockApi.getGrades(1) } returns Single.just(listOf(
+ createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
+ ))
+
+ val grades = GradeRepository(settings, gradeLocal, gradeRemote)
+ .getGrades(studentMock, semesterMock, true).blockingGet()
+
+ assertEquals(3, grades.size)
+ }
+
+ @Test
+ fun emptyRemote() {
+ gradeLocal.saveGrades(listOf(
+ createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
+ createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
+ ))
+
+ every { mockApi.getGrades(1) } returns Single.just(listOf())
+
+ val grades = GradeRepository(settings, gradeLocal, gradeRemote)
+ .getGrades(studentMock, semesterMock, true).blockingGet()
+
+ assertEquals(0, grades.size)
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt
new file mode 100644
index 000000000..e0fd05a82
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt
@@ -0,0 +1,34 @@
+package io.github.wulkanowy.data.repositories.grade
+
+import io.github.wulkanowy.api.toDate
+import org.threeten.bp.LocalDate
+import io.github.wulkanowy.api.grades.Grade as GradeRemote
+import io.github.wulkanowy.data.db.entities.Grade as GradeLocal
+
+fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal {
+ return GradeLocal(
+ semesterId = semesterId,
+ studentId = 1,
+ modifier = .0,
+ teacher = "",
+ subject = "",
+ date = date,
+ color = "",
+ comment = "",
+ description = desc,
+ entry = "",
+ gradeSymbol = "",
+ value = value,
+ weight = "",
+ weightValue = weight
+ )
+}
+
+fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote {
+ return GradeRemote().apply {
+ this.value = value
+ this.weightValue = weight
+ this.date = date.toDate()
+ this.description = desc
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt
new file mode 100644
index 000000000..0057a26e5
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt
@@ -0,0 +1,69 @@
+package io.github.wulkanowy.data.repositories.gradestatistics
+
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import io.github.wulkanowy.data.db.AppDatabase
+import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.Semester
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.threeten.bp.LocalDate
+import kotlin.test.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+class GradeStatisticsLocalTest {
+
+ private lateinit var gradeStatisticsLocal: GradeStatisticsLocal
+
+ private lateinit var testDb: AppDatabase
+
+ @Before
+ fun createDb() {
+ testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
+ .build()
+ gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics)
+ }
+
+ @After
+ fun closeDb() {
+ testDb.close()
+ }
+
+ @Test
+ fun saveAndRead_subject() {
+ gradeStatisticsLocal.saveGradesStatistics(listOf(
+ getGradeStatistics("Matematyka", 2, 1),
+ getGradeStatistics("Fizyka", 1, 2)
+ ))
+
+ val stats = gradeStatisticsLocal.getGradesStatistics(
+ Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
+ "Matematyka"
+ ).blockingGet()
+ assertEquals(1, stats.size)
+ assertEquals(stats[0].subject, "Matematyka")
+ }
+
+ @Test
+ fun saveAndRead_all() {
+ gradeStatisticsLocal.saveGradesStatistics(listOf(
+ getGradeStatistics("Matematyka", 2, 1),
+ getGradeStatistics("Chemia", 2, 1),
+ getGradeStatistics("Fizyka", 1, 2)
+ ))
+
+ val stats = gradeStatisticsLocal.getGradesStatistics(
+ Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
+ "Wszystkie"
+ ).blockingGet()
+ assertEquals(1, stats.size)
+ assertEquals(stats[0].subject, "Wszystkie")
+ }
+
+ private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics {
+ return GradeStatistics(studentId, semesterId, subject, 5, 5, false)
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt
new file mode 100644
index 000000000..77ddafb9c
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt
@@ -0,0 +1,47 @@
+package io.github.wulkanowy.data.repositories.luckynumber
+
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import io.github.wulkanowy.data.db.AppDatabase
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.data.db.entities.Semester
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.threeten.bp.LocalDate
+import kotlin.test.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+class LuckyNumberLocalTest {
+
+ private lateinit var luckyNumberLocal: LuckyNumberLocal
+
+ private lateinit var testDb: AppDatabase
+
+ @Before
+ fun createDb() {
+ testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
+ .build()
+ luckyNumberLocal = LuckyNumberLocal(testDb.luckyNumberDao)
+ }
+
+ @After
+ fun closeDb() {
+ testDb.close()
+ }
+
+ @Test
+ fun saveAndReadTest() {
+ luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14))
+
+ val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
+ LocalDate.of(2019, 1, 20)
+ ).blockingGet()
+
+ assertEquals(1, luckyNumber.studentId)
+ assertEquals(LocalDate.of(2019, 1, 20), luckyNumber.date)
+ assertEquals(14, luckyNumber.luckyNumber)
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt
new file mode 100644
index 000000000..6edaccdb4
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt
@@ -0,0 +1,60 @@
+package io.github.wulkanowy.data.repositories.recipient
+
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import io.github.wulkanowy.data.db.AppDatabase
+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 org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.threeten.bp.LocalDateTime
+import kotlin.test.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+class RecipientLocalTest {
+
+ private lateinit var recipientLocal: RecipientLocal
+
+ private lateinit var testDb: AppDatabase
+
+ @Before
+ fun createDb() {
+ testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
+ .build()
+ recipientLocal = RecipientLocal(testDb.recipientDao)
+ }
+
+ @After
+ fun closeDb() {
+ testDb.close()
+ }
+
+ @Test
+ fun saveAndReadTest() {
+ recipientLocal.saveRecipients(listOf(
+ Recipient(1, "2rPracownik", "Kowalski Jan", "Kowalski Jan [KJ] - Pracownik (Fake123456)", 3, 4, 2, "hash"),
+ Recipient(1, "3rPracownik", "Kowalska Karolina", "Kowalska Karolina [KK] - Pracownik (Fake123456)", 4, 4, 2, "hash"),
+ Recipient(1, "4rPracownik", "Krupa Stanisław", "Krupa Stanisław [KS] - Uczeń (Fake123456)", 5, 4, 1, "hash")
+ ))
+
+ val recipients = recipientLocal.getRecipients(
+ Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", "", 1, true, LocalDateTime.now()),
+ 2,
+ ReportingUnit(1, 4, "", 0, "", emptyList())
+ ).blockingGet()
+
+ assertEquals(2, recipients.size)
+ assertEquals(1, recipients[0].studentId)
+ assertEquals("3rPracownik", recipients[1].realId)
+ assertEquals("Kowalski Jan", recipients[0].name)
+ assertEquals("Kowalska Karolina [KK] - Pracownik (Fake123456)", recipients[1].realName)
+ assertEquals(3, recipients[0].loginId)
+ assertEquals(4, recipients[1].unitId)
+ assertEquals(2, recipients[0].role)
+ assertEquals("hash", recipients[1].hash)
+ }
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt
similarity index 73%
rename from app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/StudentLocalTest.kt
rename to app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt
index 732dc5bc4..cecd80992 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/StudentLocalTest.kt
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.student
import android.content.Context
import androidx.room.Room
@@ -11,6 +11,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.threeten.bp.LocalDateTime.now
import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
@@ -28,7 +29,7 @@ class StudentLocalTest {
testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.build()
sharedHelper = SharedPrefHelper(context.getSharedPreferences("TEST", Context.MODE_PRIVATE))
- studentLocal = StudentLocal(testDb.studentDao, sharedHelper, context)
+ studentLocal = StudentLocal(testDb.studentDao, context)
}
@After
@@ -38,9 +39,8 @@ class StudentLocalTest {
@Test
fun saveAndReadTest() {
- studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true))
+ studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "")))
.blockingGet()
- assert(studentLocal.isStudentSaved)
val student = studentLocal.getCurrentStudent(true).blockingGet()
assertEquals("23", student.schoolSymbol)
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt
new file mode 100644
index 000000000..eea5463d1
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt
@@ -0,0 +1,45 @@
+package io.github.wulkanowy.data.repositories.timetable
+
+import io.github.wulkanowy.api.toDate
+import io.github.wulkanowy.utils.toDate
+import org.threeten.bp.LocalDateTime
+import org.threeten.bp.LocalDateTime.now
+import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote
+import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
+
+fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = "", teacher: String = ""): TimetableLocal {
+ return TimetableLocal(
+ studentId = 1,
+ diaryId = 2,
+ number = number,
+ start = start,
+ end = now(),
+ date = start.toLocalDate(),
+ subject = subject,
+ subjectOld = "",
+ group = "",
+ room = room,
+ roomOld = "",
+ teacher = teacher,
+ teacherOld = "",
+ info = "",
+ changes = false,
+ canceled = false
+ )
+}
+
+fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = "", teacher: String = ""): TimetableRemote {
+ return TimetableRemote(
+ number = number,
+ start = start.toDate(),
+ end = start.plusMinutes(45).toDate(),
+ date = start.toLocalDate().toDate(),
+ subject = subject,
+ group = "",
+ room = room,
+ teacher = teacher,
+ info = "",
+ changes = false,
+ canceled = false
+ )
+}
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt
similarity index 58%
rename from app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt
rename to app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt
index df0a79948..fe25e4e96 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt
@@ -1,17 +1,16 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.timetable
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.db.entities.Timetable
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.threeten.bp.LocalDate
-import org.threeten.bp.LocalDateTime
+import org.threeten.bp.LocalDateTime.of
import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
@@ -23,7 +22,8 @@ class TimetableLocalTest {
@Before
fun createDb() {
- testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build()
+ testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
+ .build()
timetableDb = TimetableLocal(testDb.timetableDao)
}
@@ -35,19 +35,17 @@ class TimetableLocalTest {
@Test
fun saveAndReadTest() {
timetableDb.saveTimetable(listOf(
- Timetable(1, 2, 1, LocalDateTime.now(), LocalDateTime.now(),
- LocalDate.of(2018, 9, 10), "", "", "", "", ""),
- Timetable(1, 2, 1, LocalDateTime.now(), LocalDateTime.now(),
- LocalDate.of(2018, 9, 14), "", "", "", "", ""),
- Timetable(1, 2, 1, LocalDateTime.now(), LocalDateTime.now(),
- LocalDate.of(2018, 9, 17), "", "", "", "", "")
+ createTimetableLocal(1, of(2018, 9, 10, 0, 0, 0)),
+ createTimetableLocal(1, of(2018, 9, 14, 0, 0, 0)),
+ createTimetableLocal(1, of(2018, 9, 17, 0, 0, 0))
))
val exams = timetableDb.getTimetable(
- Semester(0, 1, 2, "3", 1, 1),
- LocalDate.of(2018, 9, 10),
- LocalDate.of(2018, 9, 14)
+ Semester(1, 2, "", 1, 1, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
+ LocalDate.of(2018, 9, 10),
+ LocalDate.of(2018, 9, 14)
).blockingGet()
+
assertEquals(2, exams.size)
assertEquals(exams[0].date, LocalDate.of(2018, 9, 10))
assertEquals(exams[1].date, LocalDate.of(2018, 9, 14))
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt
new file mode 100644
index 000000000..79216d552
--- /dev/null
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt
@@ -0,0 +1,89 @@
+package io.github.wulkanowy.data.repositories.timetable
+
+import android.os.Build.VERSION_CODES.P
+import androidx.room.Room
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.api.Api
+import io.github.wulkanowy.data.db.AppDatabase
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.impl.annotations.SpyK
+import io.reactivex.Single
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.threeten.bp.LocalDate
+import org.threeten.bp.LocalDateTime.of
+import kotlin.test.assertEquals
+
+@SdkSuppress(minSdkVersion = P)
+@RunWith(AndroidJUnit4::class)
+class TimetableRepositoryTest {
+
+ @SpyK
+ private var mockApi = Api()
+
+ private val settings = InternetObservingSettings.builder()
+ .strategy(TestInternetObservingStrategy())
+ .build()
+
+ @MockK
+ private lateinit var semesterMock: Semester
+
+ private lateinit var timetableRemote: TimetableRemote
+
+ private lateinit var timetableLocal: TimetableLocal
+
+ private lateinit var testDb: AppDatabase
+
+ @Before
+ fun initApi() {
+ MockKAnnotations.init(this)
+ testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
+ timetableLocal = TimetableLocal(testDb.timetableDao)
+ timetableRemote = TimetableRemote(mockApi)
+
+ every { semesterMock.studentId } returns 1
+ every { semesterMock.diaryId } returns 2
+ }
+
+ @After
+ fun closeDb() {
+ testDb.close()
+ }
+
+ @Test
+ fun copyDetailsToCompletedFromPrevious() {
+ timetableLocal.saveTimetable(listOf(
+ createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"),
+ createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia"),
+ createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F"),
+ createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "Jan Kowalski")
+ ))
+
+ every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf(
+ createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda"),
+ createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia"),
+ createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F"),
+ createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F")
+ ))
+
+ val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)
+ .getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true)
+ .blockingGet()
+
+ assertEquals(4, lessons.size)
+ assertEquals("123", lessons[0].room)
+ assertEquals("321", lessons[1].room)
+ assertEquals("213", lessons[2].room)
+
+ assertEquals("", lessons[3].teacher)
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2bc7e0a91..2a214b8b2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,7 +4,7 @@
package="io.github.wulkanowy"
android:installLocation="internalOnly">
-
+
@@ -20,14 +20,13 @@
android:supportsRtl="false"
android:theme="@style/WulkanowyTheme"
android:usesCleartextTraffic="true"
- tools:ignore="GoogleAppIndexingWarning">
+ tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
-
@@ -35,26 +34,44 @@
android:name=".ui.modules.login.LoginActivity"
android:configChanges="orientation|screenSize"
android:label="@string/login_title"
+ android:theme="@style/WulkanowyTheme.NoActionBar"
android:windowSoftInputMode="adjustResize" />
-
-
+
+
-
+
-
+
+
+
+
+
+
+
@@ -64,6 +81,23 @@
android:resource="@xml/provider_widget_timetable" />
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
deleted file mode 100644
index de216d36b..000000000
Binary files a/app/src/main/ic_launcher-web.png and /dev/null differ
diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
index 628d6ce6f..a58a9804e 100644
--- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
+++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
@@ -1,8 +1,9 @@
package io.github.wulkanowy
import android.content.Context
-import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDex
+import androidx.work.Configuration
+import androidx.work.WorkManager
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import com.jakewharton.threetenabp.AndroidThreeTen
@@ -11,18 +12,23 @@ import dagger.android.support.DaggerApplication
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.utils.Log
import io.fabric.sdk.android.Fabric
+import io.github.wulkanowy.BuildConfig.CRASHLYTICS_ENABLED
import io.github.wulkanowy.BuildConfig.DEBUG
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.di.DaggerAppComponent
+import io.github.wulkanowy.services.sync.SyncWorkerFactory
+import io.github.wulkanowy.utils.ActivityLifecycleLogger
import io.github.wulkanowy.utils.CrashlyticsTree
import io.github.wulkanowy.utils.DebugLogTree
+import io.reactivex.exceptions.UndeliverableException
+import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber
+import java.io.IOException
import javax.inject.Inject
class WulkanowyApp : DaggerApplication() {
@Inject
- lateinit var prefRepository: PreferencesRepository
+ lateinit var workerFactory: SyncWorkerFactory
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
@@ -32,24 +38,36 @@ class WulkanowyApp : DaggerApplication() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
- initializeFabric()
- if (DEBUG) enableDebugLog()
- AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme)
+ WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build())
+ RxJavaPlugins.setErrorHandler(::onError)
+
+ initCrashlytics()
+ initLogging()
}
- private fun enableDebugLog() {
- Timber.plant(DebugLogTree())
- FlexibleAdapter.enableLogs(Log.Level.DEBUG)
+ private fun initLogging() {
+ if (DEBUG) {
+ Timber.plant(DebugLogTree())
+ FlexibleAdapter.enableLogs(Log.Level.DEBUG)
+ } else {
+ Timber.plant(CrashlyticsTree())
+ }
+ registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
}
- private fun initializeFabric() {
+ private fun initCrashlytics() {
Fabric.with(Fabric.Builder(this).kits(
- Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!BuildConfig.CRASHLYTICS_ENABLED).build()).build()
- ).debuggable(BuildConfig.DEBUG).build())
- Timber.plant(CrashlyticsTree())
+ Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!CRASHLYTICS_ENABLED).build()).build()
+ ).debuggable(DEBUG).build())
+ }
+
+ private fun onError(error: Throwable) {
+ if (error is UndeliverableException && error.cause is IOException || error.cause is InterruptedException) {
+ Timber.e(error.cause, "An undeliverable error occurred")
+ } else throw error
}
override fun applicationInjector(): AndroidInjector {
- return DaggerAppComponent.builder().create(this)
+ return DaggerAppComponent.factory().create(this)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt b/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt
index 5b7b1072c..b6eee316b 100644
--- a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt
@@ -14,14 +14,22 @@ class ApiHelper @Inject constructor(private val api: Api) {
symbol = student.symbol
schoolSymbol = student.schoolSymbol
studentId = student.studentId
- useNewStudent = false
+ classId = student.classId
host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") }
ssl = student.endpoint.startsWith("https")
loginType = Api.LoginType.valueOf(student.loginType)
+ useNewStudent = true
}
}
fun initApi(email: String, password: String, symbol: String, endpoint: String) {
- initApi(Student(email = email, password = password, symbol = symbol, endpoint = endpoint, loginType = "AUTO"))
+ api.apply {
+ this.email = email
+ this.password = password
+ this.symbol = symbol
+ host = URL(endpoint).run { host + ":$port".removeSuffix(":-1") }
+ ssl = endpoint.startsWith("https")
+ useNewStudent = true
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
index f5bb1b2a4..c832368a5 100644
--- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
@@ -5,7 +5,7 @@ import android.content.SharedPreferences
import android.content.res.Resources
import androidx.preference.PreferenceManager
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
-import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.SocketInternetObservingStrategy
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy
import com.readystatesoftware.chuck.api.ChuckCollector
import com.readystatesoftware.chuck.api.ChuckInterceptor
import com.readystatesoftware.chuck.api.RetentionManager
@@ -13,7 +13,7 @@ import dagger.Module
import dagger.Provides
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.AppDatabase
-import io.github.wulkanowy.data.repositories.PreferencesRepository
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC
import okhttp3.logging.HttpLoggingInterceptor.Level.NONE
@@ -27,8 +27,7 @@ internal class RepositoryModule {
@Provides
fun provideInternetObservingSettings(): InternetObservingSettings {
return InternetObservingSettings.builder()
- .strategy(SocketInternetObservingStrategy())
- .host("www.google.com")
+ .strategy(WalledGardenInternetObservingStrategy())
.build()
}
@@ -50,7 +49,7 @@ internal class RepositoryModule {
@Provides
fun provideChuckCollector(context: Context, prefRepository: PreferencesRepository): ChuckCollector {
return ChuckCollector(context)
- .showNotification(prefRepository.isShowChuckerNotification)
+ .showNotification(prefRepository.isDebugNotificationEnable)
.retentionManager(RetentionManager(context, ChuckCollector.Period.ONE_HOUR))
}
@@ -82,6 +81,10 @@ internal class RepositoryModule {
@Provides
fun provideGradeSummaryDao(database: AppDatabase) = database.gradeSummaryDao
+ @Singleton
+ @Provides
+ fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics
+
@Singleton
@Provides
fun provideMessagesDao(database: AppDatabase) = database.messagesDao
@@ -113,4 +116,20 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideSubjectDao(database: AppDatabase) = database.subjectDao
+
+ @Singleton
+ @Provides
+ fun provideLuckyNumberDao(database: AppDatabase) = database.luckyNumberDao
+
+ @Singleton
+ @Provides
+ fun provideCompletedLessonsDao(database: AppDatabase) = database.completedLessonsDao
+
+ @Singleton
+ @Provides
+ fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao
+
+ @Singleton
+ @Provides
+ fun provideRecipientDao(database: AppDatabase) = database.recipientDao
}
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 30c957b43..d867583e4 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
@@ -8,28 +8,50 @@ import androidx.room.RoomDatabase.JournalMode.TRUNCATE
import androidx.room.TypeConverters
import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
+import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.dao.ExamDao
import io.github.wulkanowy.data.db.dao.GradeDao
+import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
-import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.dao.HomeworkDao
+import io.github.wulkanowy.data.db.dao.LuckyNumberDao
+import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.dao.NoteDao
+import io.github.wulkanowy.data.db.dao.RecipientDao
+import io.github.wulkanowy.data.db.dao.ReportingUnitDao
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.dao.SubjectDao
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.AttendanceSummary
+import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Grade
+import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.GradeSummary
-import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Homework
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Note
+import io.github.wulkanowy.data.db.entities.Recipient
+import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.db.entities.Timetable
+import io.github.wulkanowy.data.db.migrations.Migration10
+import io.github.wulkanowy.data.db.migrations.Migration11
+import io.github.wulkanowy.data.db.migrations.Migration12
+import io.github.wulkanowy.data.db.migrations.Migration13
+import io.github.wulkanowy.data.db.migrations.Migration2
+import io.github.wulkanowy.data.db.migrations.Migration3
+import io.github.wulkanowy.data.db.migrations.Migration4
+import io.github.wulkanowy.data.db.migrations.Migration5
+import io.github.wulkanowy.data.db.migrations.Migration6
+import io.github.wulkanowy.data.db.migrations.Migration7
+import io.github.wulkanowy.data.db.migrations.Migration8
+import io.github.wulkanowy.data.db.migrations.Migration9
import javax.inject.Singleton
@Singleton
@@ -43,21 +65,44 @@ import javax.inject.Singleton
AttendanceSummary::class,
Grade::class,
GradeSummary::class,
+ GradeStatistics::class,
Message::class,
Note::class,
Homework::class,
- Subject::class
+ Subject::class,
+ LuckyNumber::class,
+ CompletedLesson::class,
+ ReportingUnit::class,
+ Recipient::class
],
- version = 1,
- exportSchema = false
+ version = AppDatabase.VERSION_SCHEMA,
+ exportSchema = true
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
companion object {
+ const val VERSION_SCHEMA = 13
+
fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
.setJournalMode(TRUNCATE)
+ .fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
+ .fallbackToDestructiveMigrationOnDowngrade()
+ .addMigrations(
+ Migration2(),
+ Migration3(),
+ Migration4(),
+ Migration5(),
+ Migration6(),
+ Migration7(),
+ Migration8(),
+ Migration9(),
+ Migration10(),
+ Migration11(),
+ Migration12(),
+ Migration13()
+ )
.build()
}
}
@@ -78,6 +123,8 @@ abstract class AppDatabase : RoomDatabase() {
abstract val gradeSummaryDao: GradeSummaryDao
+ abstract val gradeStatistics: GradeStatisticsDao
+
abstract val messagesDao: MessagesDao
abstract val noteDao: NoteDao
@@ -85,4 +132,12 @@ abstract class AppDatabase : RoomDatabase() {
abstract val homeworkDao: HomeworkDao
abstract val subjectDao: SubjectDao
+
+ abstract val luckyNumberDao: LuckyNumberDao
+
+ abstract val completedLessonsDao: CompletedLessonsDao
+
+ abstract val reportingUnitDao: ReportingUnitDao
+
+ abstract val recipientDao: RecipientDao
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
index a550df893..73a04d236 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
@@ -1,6 +1,8 @@
package io.github.wulkanowy.data.db
import androidx.room.TypeConverter
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
import org.threeten.bp.DateTimeUtils
import org.threeten.bp.Instant
import org.threeten.bp.LocalDate
@@ -36,4 +38,14 @@ class Converters {
@TypeConverter
fun intToMonth(value: Int?) = value?.let { Month.of(it) }
+
+ @TypeConverter
+ fun intListToGson(list: List): String {
+ return Gson().toJson(list)
+ }
+
+ @TypeConverter
+ fun gsonToIntList(value: String): List {
+ return Gson().fromJson(value, object : TypeToken>() {}.type)
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt
index 656b39d49..74f9fa654 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt
@@ -6,26 +6,16 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
+@SuppressLint("ApplySharedPref")
class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) {
- @SuppressLint("ApplySharedPref")
fun putLong(key: String, value: Long, sync: Boolean = false) {
sharedPref.edit().putLong(key, value).apply {
if (sync) commit() else apply()
}
}
- fun getLong(key: String, defaultValue: Long): Long {
- return sharedPref.getLong(key, defaultValue)
- }
-
- fun putBoolean(key: String, value: Boolean) {
- sharedPref.edit().putBoolean(key, value).apply()
- }
-
- fun getBoolean(key: String, defaultValue: Boolean): Boolean {
- return sharedPref.getBoolean(key, defaultValue)
- }
+ fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue)
fun delete(key: String) {
sharedPref.edit().remove(key).apply()
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt
new file mode 100644
index 000000000..6816ceaaf
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt
@@ -0,0 +1,24 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.CompletedLesson
+import io.reactivex.Maybe
+import org.threeten.bp.LocalDate
+import javax.inject.Singleton
+
+@Singleton
+@Dao
+interface CompletedLessonsDao {
+
+ @Insert
+ fun insertAll(exams: List)
+
+ @Delete
+ fun deleteAll(exams: List)
+
+ @Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
+ fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe>
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt
index 629f201d4..0bd210b02 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt
@@ -16,9 +16,6 @@ interface GradeDao {
@Insert
fun insertAll(grades: List)
- @Update
- fun update(grade: Grade)
-
@Update
fun updateAll(grade: List)
@@ -28,6 +25,4 @@ interface GradeDao {
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
fun loadAll(semesterId: Int, studentId: Int): Maybe>
- @Query("SELECT * FROM Grades WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId")
- fun loadAllNew(semesterId: Int, studentId: Int): Maybe>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt
new file mode 100644
index 000000000..338c369fa
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt
@@ -0,0 +1,26 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.reactivex.Maybe
+import javax.inject.Singleton
+
+@Singleton
+@Dao
+interface GradeStatisticsDao {
+
+ @Insert
+ fun insertAll(gradesStatistics: List)
+
+ @Delete
+ fun deleteAll(gradesStatistics: List)
+
+ @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester")
+ fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Maybe>
+
+ @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester")
+ fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Maybe>
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt
index 3530118c2..3f2e87bd0 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt
@@ -18,6 +18,6 @@ interface GradeSummaryDao {
@Delete
fun deleteAll(gradesSummary: List)
- @Query("SELECT * FROM grades_summary WHERE student_id = :studentId AND semester_id = :semesterId")
+ @Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Maybe>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt
index 4127460f0..253bdb11f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt
@@ -19,6 +19,6 @@ interface HomeworkDao {
@Delete
fun deleteAll(homework: List)
- @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date = :date")
- fun loadAll(semesterId: Int, studentId: Int, date: LocalDate): Maybe>
+ @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end")
+ fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt
new file mode 100644
index 000000000..afd7905c0
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt
@@ -0,0 +1,29 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import androidx.room.Update
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.reactivex.Maybe
+import org.threeten.bp.LocalDate
+import javax.inject.Singleton
+
+@Singleton
+@Dao
+interface LuckyNumberDao {
+
+ @Insert
+ fun insert(luckyNumber: LuckyNumber)
+
+ @Update
+ fun update(luckyNumber: LuckyNumber)
+
+ @Delete
+ fun delete(luckyNumber: LuckyNumber)
+
+ @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
+ fun load(studentId: Int, date: LocalDate): Maybe
+
+}
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 5018b6900..4f72c6c91 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
@@ -12,26 +12,20 @@ import io.reactivex.Maybe
interface MessagesDao {
@Insert
- fun insertAll(messages: List): List
+ fun insertAll(messages: List)
@Delete
fun deleteAll(messages: List)
- @Update
- fun update(message: Message)
-
@Update
fun updateAll(messages: List)
- @Query("SELECT * FROM Messages WHERE student_id = :studentId AND real_id = :id")
- fun loadOne(studentId: Int, id: Int): Maybe
+ @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC")
+ fun loadAll(studentId: Int, folder: Int): Maybe>
- @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC")
- fun load(studentId: Int, folder: Int): Maybe>
+ @Query("SELECT * FROM Messages WHERE id = :id")
+ fun load(id: Long): Maybe
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
fun loadDeleted(studentId: Int): Maybe>
-
- @Query("SELECT * FROM Messages WHERE unread = 1 AND student_id = :studentId")
- fun loadNewMessages(studentId: Int): Maybe>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt
index 2c1828606..867e06a25 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt
@@ -16,18 +16,13 @@ interface NoteDao {
@Insert
fun insertAll(notes: List)
- @Update
- fun update(note: Note)
-
@Update
fun updateAll(notes: List)
@Delete
fun deleteAll(notes: List)
- @Query("SELECT * FROM Notes WHERE semester_id = :semesterId AND student_id = :studentId")
- fun loadAll(semesterId: Int, studentId: Int): Maybe>
+ @Query("SELECT * FROM Notes WHERE student_id = :studentId")
+ fun loadAll(studentId: Int): Maybe>
- @Query("SELECT * FROM Notes WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId")
- fun loadNew(semesterId: Int, studentId: Int): Maybe>
}
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
new file mode 100644
index 000000000..7c5fd6ca6
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt
@@ -0,0 +1,23 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.Recipient
+import io.reactivex.Maybe
+import javax.inject.Singleton
+
+@Singleton
+@Dao
+interface RecipientDao {
+
+ @Insert
+ fun insertAll(messages: List)
+
+ @Delete
+ fun deleteAll(messages: List)
+
+ @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId")
+ fun load(studentId: Int, role: Int, unitId: Int): Maybe>
+}
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
new file mode 100644
index 000000000..1898390a9
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt
@@ -0,0 +1,26 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.ReportingUnit
+import io.reactivex.Maybe
+import javax.inject.Singleton
+
+@Singleton
+@Dao
+interface ReportingUnitDao {
+
+ @Insert
+ fun insertAll(reportingUnits: List)
+
+ @Delete
+ fun deleteAll(reportingUnits: List)
+
+ @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId")
+ fun load(studentId: Int): Maybe>
+
+ @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId")
+ fun loadOne(studentId: Int, unitId: Int): Maybe
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt
index 44de31d85..01841fb67 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt
@@ -1,8 +1,8 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
+import androidx.room.Delete
import androidx.room.Insert
-import androidx.room.OnConflictStrategy.IGNORE
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
@@ -12,15 +12,12 @@ import javax.inject.Singleton
@Dao
interface SemesterDao {
- @Insert(onConflict = IGNORE)
+ @Insert
fun insertAll(semester: List)
- @Query("SELECT * FROM Semesters WHERE student_id = :studentId")
- fun loadAll(studentId: Int): Maybe>
+ @Delete
+ fun deleteAll(semester: List)
- @Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId AND diary_id = :diaryId")
- fun updateCurrent(semesterId: Int, diaryId: Int)
-
- @Query("UPDATE Semesters SET is_current = 0 WHERE student_id = :studentId")
- fun resetCurrent(studentId: Int)
+ @Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId")
+ fun loadAll(studentId: Int, classId: Int): Maybe>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
index 76e295392..57bf25fb8 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
@@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
-import androidx.room.OnConflictStrategy.FAIL
+import androidx.room.OnConflictStrategy.ABORT
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe
@@ -13,8 +13,8 @@ import javax.inject.Singleton
@Dao
interface StudentDao {
- @Insert(onConflict = FAIL)
- fun insert(student: Student): Long
+ @Insert(onConflict = ABORT)
+ fun insertAll(student: List): List
@Delete
fun delete(student: Student)
@@ -25,8 +25,8 @@ interface StudentDao {
@Query("SELECT * FROM Students")
fun loadAll(): Maybe>
- @Query("UPDATE Students SET is_current = 1 WHERE student_id = :studentId")
- fun updateCurrent(studentId: Int)
+ @Query("UPDATE Students SET is_current = 1 WHERE id = :id")
+ fun updateCurrent(id: Long)
@Query("UPDATE Students SET is_current = 0")
fun resetCurrent()
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt
index 7588201b3..3c58971ae 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt
@@ -10,30 +10,30 @@ import java.io.Serializable
data class Attendance(
@ColumnInfo(name = "student_id")
- var studentId: Int,
+ val studentId: Int,
@ColumnInfo(name = "diary_id")
- var diaryId: Int,
+ val diaryId: Int,
- var date: LocalDate,
+ val date: LocalDate,
- var number: Int,
+ val number: Int,
- var subject: String,
+ val subject: String,
- var name: String,
+ val name: String,
- var presence: Boolean = false,
+ val presence: Boolean,
- var absence: Boolean = false,
+ val absence: Boolean,
- var exemption: Boolean = false,
+ val exemption: Boolean,
- var lateness: Boolean = false,
+ val lateness: Boolean,
- var excused: Boolean = false,
+ val excused: Boolean,
- var deleted: Boolean = false
+ val deleted: Boolean
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt
index de2de98f6..d2e1f174e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt
@@ -10,13 +10,13 @@ import java.io.Serializable
data class AttendanceSummary(
@ColumnInfo(name = "student_id")
- var studentId: Int,
+ val studentId: Int,
@ColumnInfo(name = "diary_id")
- var diaryId: Int,
+ val diaryId: Int,
@ColumnInfo(name = "subject_id")
- var subjectId: Int = 0,
+ val subjectId: Int,
val month: Month,
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt
new file mode 100644
index 000000000..775f3f558
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt
@@ -0,0 +1,40 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import org.threeten.bp.LocalDate
+import java.io.Serializable
+
+@Entity(tableName = "CompletedLesson")
+data class CompletedLesson(
+
+ @ColumnInfo(name = "student_id")
+ val studentId: Int,
+
+ @ColumnInfo(name = "diary_id")
+ val diaryId: Int,
+
+ val date: LocalDate,
+
+ val number: Int,
+
+ val subject: String,
+
+ val topic: String,
+
+ val teacher: String,
+
+ @ColumnInfo(name = "teacher_symbol")
+ val teacherSymbol: String,
+
+ val substitution: String,
+
+ val absence: String,
+
+ val resources: String
+) : Serializable {
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt
index daa886d54..9ae795e71 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt
@@ -9,29 +9,29 @@ import java.io.Serializable
@Entity(tableName = "Exams")
data class Exam(
- @ColumnInfo(name = "student_id")
- var studentId: Int,
+ @ColumnInfo(name = "student_id")
+ val studentId: Int,
- @ColumnInfo(name = "diary_id")
- var diaryId: Int,
+ @ColumnInfo(name = "diary_id")
+ val diaryId: Int,
- var date: LocalDate,
+ val date: LocalDate,
- @ColumnInfo(name = "entry_date")
- var entryDate: LocalDate = LocalDate.now(),
+ @ColumnInfo(name = "entry_date")
+ val entryDate: LocalDate,
- var subject: String,
+ val subject: String,
- var group: String,
+ val group: String,
- var type: String,
+ val type: String,
- var description: String,
+ val description: String,
- var teacher: String,
+ val teacher: String,
- @ColumnInfo(name = "teacher_symbol")
- var teacherSymbol: String
+ @ColumnInfo(name = "teacher_symbol")
+ val teacherSymbol: String
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt
index d665f9d20..1221a7aab 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt
@@ -10,35 +10,35 @@ import java.io.Serializable
data class Grade(
@ColumnInfo(name = "semester_id")
- var semesterId: Int,
+ val semesterId: Int,
@ColumnInfo(name = "student_id")
- var studentId: Int,
+ val studentId: Int,
- var subject: String,
+ val subject: String,
- var entry: String,
+ val entry: String,
- var value: Int,
+ val value: Int,
- var modifier: Double,
+ val modifier: Double,
- var comment: String,
+ val comment: String,
- var color: String,
+ val color: String,
@ColumnInfo(name = "grade_symbol")
- var gradeSymbol: String,
+ val gradeSymbol: String,
- var description: String,
+ val description: String,
- var weight: String,
+ val weight: String,
- var weightValue: Int,
+ val weightValue: Double,
- var date: LocalDate,
+ val date: LocalDate,
- var teacher: String
+ val teacher: String
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt
new file mode 100644
index 000000000..8ad8b8b8d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt
@@ -0,0 +1,27 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "GradesStatistics")
+data class GradeStatistics(
+
+ @ColumnInfo(name = "student_id")
+ val studentId: Int,
+
+ @ColumnInfo(name = "semester_id")
+ val semesterId: Int,
+
+ val subject: String,
+
+ val grade: Int,
+
+ val amount: Int,
+
+ @ColumnInfo(name = "is_semester")
+ val semester: Boolean
+) {
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt
index 74d709bb3..e6ac4926d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt
@@ -4,22 +4,21 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
-@Entity(tableName = "Grades_Summary")
+@Entity(tableName = "GradesSummary")
data class GradeSummary(
- @ColumnInfo(name = "semester_id")
- var semesterId: Int,
+ @ColumnInfo(name = "semester_id")
+ val semesterId: Int,
- @ColumnInfo(name = "student_id")
- var studentId: Int,
+ @ColumnInfo(name = "student_id")
+ val studentId: Int,
- var subject: String,
+ val subject: String,
- var predictedGrade: String,
+ val predictedGrade: String,
- var finalGrade: String
+ val finalGrade: String
) {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
-
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt
index 705d1d639..a22df0961 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt
@@ -10,24 +10,24 @@ import java.io.Serializable
data class Homework(
@ColumnInfo(name = "semester_id")
- var semesterId: Int,
+ val semesterId: Int,
@ColumnInfo(name = "student_id")
- var studentId: Int,
+ val studentId: Int,
- var date: LocalDate,
+ val date: LocalDate,
@ColumnInfo(name = "entry_date")
- var entryDate: LocalDate,
+ val entryDate: LocalDate,
- var subject: String,
+ val subject: String,
- var content: String,
+ val content: String,
- var teacher: String,
+ val teacher: String,
@ColumnInfo(name = "teacher_symbol")
- var teacherSymbol: String
+ val teacherSymbol: String
) : Serializable {
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt
new file mode 100644
index 000000000..5b9130f5d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt
@@ -0,0 +1,27 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import org.threeten.bp.LocalDate
+import java.io.Serializable
+
+@Entity(tableName = "LuckyNumbers")
+data class LuckyNumber (
+
+ @ColumnInfo(name = "student_id")
+ val studentId: Int,
+
+ val date: LocalDate,
+
+ @ColumnInfo(name = "lucky_number")
+ val luckyNumber: Int
+
+) : Serializable {
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+
+ @ColumnInfo(name = "is_notified")
+ var isNotified: Boolean = true
+}
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 320e9322e..48b4fd022 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
@@ -10,40 +10,39 @@ import java.io.Serializable
data class Message(
@ColumnInfo(name = "student_id")
- var studentId: Int? = null,
+ val studentId: Int,
@ColumnInfo(name = "real_id")
- val realId: Int? = null,
+ val realId: Int,
@ColumnInfo(name = "message_id")
- val messageId: Int? = null,
+ val messageId: Int,
@ColumnInfo(name = "sender_name")
- val sender: String? = null,
+ val sender: String,
@ColumnInfo(name = "sender_id")
- val senderId: Int? = null,
-
- @ColumnInfo(name = "recipient_id")
- val recipientId: Int? = null,
+ val senderId: Int,
@ColumnInfo(name = "recipient_name")
- val recipient: String? = "",
+ val recipient: String,
- val subject: String = "",
+ val subject: String,
- val date: LocalDateTime? = null,
+ val date: LocalDateTime,
@ColumnInfo(name = "folder_id")
- val folderId: Int = 0,
+ val folderId: Int,
- var unread: Boolean? = false,
+ var unread: Boolean,
- val unreadBy: Int? = 0,
+ @ColumnInfo(name = "unread_by")
+ val unreadBy: Int,
- val readBy: Int? = 0,
+ @ColumnInfo(name = "read_by")
+ val readBy: Int,
- val removed: Boolean = false
+ val removed: Boolean
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt
index 1f61f0870..5f3a92ab6 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt
@@ -9,26 +9,23 @@ import java.io.Serializable
@Entity(tableName = "Notes")
data class Note(
- @ColumnInfo(name = "semester_id")
- var semesterId: Int,
-
@ColumnInfo(name = "student_id")
- var studentId: Int,
+ val studentId: Int,
- var date: LocalDate,
+ val date: LocalDate,
- var teacher: String,
+ val teacher: String,
- var category: String,
+ val category: String,
- var content: String
+ val content: String
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
@ColumnInfo(name = "is_read")
- var isRead: Boolean = false
+ var isRead: Boolean = true
@ColumnInfo(name = "is_notified")
var isNotified: Boolean = true
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
new file mode 100644
index 000000000..3021da72d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt
@@ -0,0 +1,38 @@
+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 = "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
+
+) : Serializable {
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+
+ override fun toString() = name
+}
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
new file mode 100644
index 000000000..601d8aac7
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt
@@ -0,0 +1,32 @@
+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 realId: 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/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
index 509a692e6..6c06be111 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
@@ -4,28 +4,43 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
+import org.threeten.bp.LocalDate
@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)])
data class Semester(
- @PrimaryKey(autoGenerate = true)
- var id: Long = 0,
-
@ColumnInfo(name = "student_id")
- var studentId: Int,
+ val studentId: Int,
@ColumnInfo(name = "diary_id")
- var diaryId: Int,
+ val diaryId: Int,
@ColumnInfo(name = "diary_name")
- var diaryName: String,
+ val diaryName: String,
+
+ @ColumnInfo(name = "school_year")
+ val schoolYear: Int,
@ColumnInfo(name = "semester_id")
- var semesterId: Int,
+ val semesterId: Int,
@ColumnInfo(name = "semester_name")
- var semesterName: Int,
+ val semesterName: Int,
@ColumnInfo(name = "is_current")
- var isCurrent: Boolean = false
-)
+ val isCurrent: Boolean,
+
+ val start: LocalDate,
+
+ val end: LocalDate,
+
+ @ColumnInfo(name = "class_id")
+ val classId: Int,
+
+ @ColumnInfo(name = "unit_id")
+ val unitId: Int
+) {
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
index 1fe4cf8d0..13c5ee084 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
@@ -4,35 +4,47 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
+import org.threeten.bp.LocalDateTime
+import java.io.Serializable
-@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id"], unique = true)])
+@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)])
data class Student(
- @PrimaryKey(autoGenerate = true)
- var id: Long = 0,
+ val endpoint: String,
- var endpoint: String,
+ val loginType: String,
- var loginType: String,
-
- var email: String,
+ val email: String,
var password: String,
- var symbol: String = "",
+ val symbol: String,
@ColumnInfo(name = "student_id")
- var studentId: Int = 0,
+ val studentId: Int,
@ColumnInfo(name = "student_name")
- var studentName: String = "",
+ val studentName: String,
@ColumnInfo(name = "school_id")
- var schoolSymbol: String = "",
+ val schoolSymbol: String,
@ColumnInfo(name = "school_name")
- var schoolName: String = "",
+ val schoolName: String,
+
+ @ColumnInfo(name = "class_name")
+ val className: String,
+
+ @ColumnInfo(name = "class_id")
+ val classId: Int,
@ColumnInfo(name = "is_current")
- var isCurrent: Boolean = false
-)
+ val isCurrent: Boolean,
+
+ @ColumnInfo(name = "registration_date")
+ val registrationDate: LocalDateTime
+) : Serializable {
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt
index 45306be36..dbaa6f4ed 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt
@@ -9,15 +9,15 @@ import java.io.Serializable
data class Subject(
@ColumnInfo(name = "student_id")
- var studentId: Int,
+ val studentId: Int,
@ColumnInfo(name = "diary_id")
- var diaryId: Int,
+ val diaryId: Int,
@ColumnInfo(name = "real_id")
- var realId: Int,
+ val realId: Int,
- var name: String
+ val name: String
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt
index 2aa1eafa7..9bc3d2140 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt
@@ -10,33 +10,39 @@ import java.io.Serializable
@Entity(tableName = "Timetable")
data class Timetable(
- @ColumnInfo(name = "student_id")
- var studentId: Int,
+ @ColumnInfo(name = "student_id")
+ val studentId: Int,
- @ColumnInfo(name = "diary_id")
- var diaryId: Int,
+ @ColumnInfo(name = "diary_id")
+ val diaryId: Int,
- val number: Int = 0,
+ val number: Int,
- val start: LocalDateTime = LocalDateTime.now(),
+ val start: LocalDateTime,
- val end: LocalDateTime = LocalDateTime.now(),
+ val end: LocalDateTime,
- val date: LocalDate,
+ val date: LocalDate,
- val subject: String,
+ val subject: String,
- val group: String,
+ val subjectOld: String,
- val room: String,
+ val group: String,
- val teacher: String,
+ val room: String,
- val info: String,
+ val roomOld: String,
- val changes: Boolean = false,
+ val teacher: String,
- val canceled: Boolean = false
+ val teacherOld: String,
+
+ val info: String,
+
+ val changes: Boolean,
+
+ val canceled: Boolean
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt
new file mode 100644
index 000000000..c26a02d1f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt
@@ -0,0 +1,11 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration10 : Migration(9, 10) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary")
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt
new file mode 100644
index 000000000..6d129bca0
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt
@@ -0,0 +1,34 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration11 : Migration(10, 11) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS Grades_temp (
+ id INTEGER PRIMARY KEY NOT NULL,
+ is_read INTEGER NOT NULL,
+ is_notified INTEGER NOT NULL,
+ semester_id INTEGER NOT NULL,
+ student_id INTEGER NOT NULL,
+ subject TEXT NOT NULL,
+ entry TEXT NOT NULL,
+ value INTEGER 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
+ )
+ """)
+ database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades")
+ database.execSQL("DROP TABLE Grades")
+ database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades")
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt
new file mode 100644
index 000000000..1dc38e14c
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt
@@ -0,0 +1,69 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration12 : Migration(11, 12) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ createTempStudentsTable(database)
+ replaceStudentTable(database)
+ updateStudentsWithClassId(database, getStudentsIds(database))
+ removeStudentsWithNoClassId(database)
+ ensureThereIsOnlyOneCurrentStudent(database)
+ }
+
+ private fun createTempStudentsTable(database: SupportSQLiteDatabase) {
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS Students_tmp (
+ id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ endpoint TEXT NOT NULL,
+ loginType TEXT NOT NULL,
+ email TEXT NOT NULL,
+ password TEXT NOT NULL,
+ symbol TEXT NOT NULL,
+ student_id INTEGER NOT NULL,
+ student_name TEXT NOT NULL,
+ school_id TEXT NOT NULL,
+ school_name TEXT NOT NULL,
+ is_current INTEGER NOT NULL,
+ registration_date INTEGER NOT NULL,
+ class_id INTEGER NOT NULL
+ )
+ """)
+ database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)")
+ }
+
+ private fun replaceStudentTable(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL")
+ database.execSQL("INSERT INTO Students_tmp SELECT * FROM Students")
+ database.execSQL("DROP TABLE Students")
+ database.execSQL("ALTER TABLE Students_tmp RENAME TO Students")
+ }
+
+ private fun getStudentsIds(database: SupportSQLiteDatabase): List {
+ val students = mutableListOf()
+ val studentsCursor = database.query("SELECT student_id FROM Students")
+ if (studentsCursor.moveToFirst()) {
+ do {
+ students.add(studentsCursor.getInt(0))
+ } while (studentsCursor.moveToNext())
+ }
+ return students
+ }
+
+ private fun updateStudentsWithClassId(database: SupportSQLiteDatabase, students: List) {
+ students.forEach {
+ database.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it")
+ }
+ }
+
+ private fun removeStudentsWithNoClassId(database: SupportSQLiteDatabase) {
+ database.execSQL("DELETE FROM Students WHERE class_id = 0")
+ }
+
+ private fun ensureThereIsOnlyOneCurrentStudent(database: SupportSQLiteDatabase) {
+ database.execSQL("UPDATE Students SET is_current = 0")
+ database.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)")
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt
new file mode 100644
index 000000000..0cf8cd9b0
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt
@@ -0,0 +1,64 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration13 : Migration(12, 13) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ addClassNameToStudents(database, getStudentsIds(database))
+ updateSemestersTable(database)
+ markAtLeastAndOnlyOneSemesterAtCurrent(database, getStudentsAndClassIds(database))
+ clearMessagesTable(database)
+ }
+
+ private fun addClassNameToStudents(database: SupportSQLiteDatabase, students: List>) {
+ database.execSQL("ALTER TABLE Students ADD COLUMN class_name TEXT DEFAULT \"\" NOT NULL")
+
+ students.forEach { (id, name) ->
+ val schoolName = name.substringAfter(" - ")
+ val className = name.substringBefore(" - ", "").replace("Klasa ", "")
+ database.execSQL("UPDATE Students SET class_name = '$className' WHERE id = '$id'")
+ database.execSQL("UPDATE Students SET school_name = '$schoolName' WHERE id = '$id'")
+ }
+ }
+
+ private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList> {
+ val students = mutableListOf>()
+ val studentsCursor = database.query("SELECT id, school_name FROM Students")
+ if (studentsCursor.moveToFirst()) {
+ do {
+ students.add(studentsCursor.getInt(0) to studentsCursor.getString(1))
+ } while (studentsCursor.moveToNext())
+ }
+ return students
+ }
+
+ private fun updateSemestersTable(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE Semesters ADD COLUMN school_year INTEGER DEFAULT 1970 NOT NULL")
+ database.execSQL("ALTER TABLE Semesters ADD COLUMN start INTEGER DEFAULT 0 NOT NULL")
+ database.execSQL("ALTER TABLE Semesters ADD COLUMN `end` INTEGER DEFAULT 0 NOT NULL")
+ }
+
+ private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List> {
+ val students = mutableListOf>()
+ val studentsCursor = database.query("SELECT student_id, class_id FROM Students")
+ if (studentsCursor.moveToFirst()) {
+ do {
+ students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1))
+ } while (studentsCursor.moveToNext())
+ }
+ return students
+ }
+
+ private fun markAtLeastAndOnlyOneSemesterAtCurrent(database: SupportSQLiteDatabase, students: List>) {
+ students.forEach { (studentId, classId) ->
+ database.execSQL("UPDATE Semesters SET is_current = 0 WHERE student_id = '$studentId' AND class_id = '$classId'")
+ database.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)")
+ }
+ }
+
+ private fun clearMessagesTable(database: SupportSQLiteDatabase) {
+ database.execSQL("DELETE FROM Messages")
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt
new file mode 100644
index 000000000..c5a30991a
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt
@@ -0,0 +1,18 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration2 : Migration(1, 2) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS LuckyNumbers (
+ id INTEGER PRIMARY KEY NOT NULL,
+ is_notified INTEGER NOT NULL,
+ student_id INTEGER NOT NULL,
+ date INTEGER NOT NULL,
+ lucky_number INTEGER NOT NULL)
+ """)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt
new file mode 100644
index 000000000..d9699c0f4
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt
@@ -0,0 +1,25 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration3 : Migration(2, 3) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS CompletedLesson (
+ id INTEGER PRIMARY KEY NOT NULL,
+ 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)
+ """)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt
new file mode 100644
index 000000000..0ae89bdd6
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt
@@ -0,0 +1,31 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration4 : Migration(3, 4) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS Messages")
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS Messages (
+ id INTEGER PRIMARY KEY NOT NULL,
+ is_notified INTEGER NOT NULL,
+ content TEXT,
+ 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_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,
+ unreadBy INTEGER NOT NULL,
+ readBy INTEGER NOT NULL,
+ removed INTEGER NOT NULL)
+ """)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt
new file mode 100644
index 000000000..fe0dec48f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt
@@ -0,0 +1,26 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import org.threeten.bp.LocalDateTime.now
+import org.threeten.bp.ZoneOffset
+
+class Migration5 : Migration(4, 5) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL")
+ database.execSQL("UPDATE Students SET registration_date = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'")
+ database.execSQL("DROP TABLE IF EXISTS Notes")
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS Notes (
+ id INTEGER PRIMARY KEY NOT NULL,
+ is_read INTEGER NOT NULL,
+ is_notified INTEGER NOT NULL,
+ student_id INTEGER NOT NULL,
+ date INTEGER NOT NULL,
+ teacher TEXT NOT NULL,
+ category TEXT NOT NULL,
+ content TEXT NOT NULL)
+ """)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt
new file mode 100644
index 000000000..fa9436187
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt
@@ -0,0 +1,37 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration6 : Migration(5, 6) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS ReportingUnits (
+ id INTEGER PRIMARY KEY NOT NULL,
+ 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)
+ """)
+
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS Recipients (
+ id INTEGER PRIMARY KEY NOT NULL,
+ 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)
+ """)
+
+ database.execSQL("DELETE FROM Semesters WHERE 1")
+ database.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL")
+ database.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL")
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt
new file mode 100644
index 000000000..120716c81
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt
@@ -0,0 +1,20 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration7 : Migration(6, 7) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS GradesStatistics (
+ id INTEGER PRIMARY KEY NOT NULL,
+ student_id INTEGER NOT NULL,
+ semester_id INTEGER NOT NULL,
+ subject TEXT NOT NULL,
+ grade INTEGER NOT NULL,
+ amount INTEGER NOT NULL,
+ is_semester INTEGER NOT NULL)
+ """)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt
new file mode 100644
index 000000000..7009ee129
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt
@@ -0,0 +1,13 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration8 : Migration(7, 8) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL")
+ database.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL")
+ database.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL")
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt
new file mode 100644
index 000000000..d79a57062
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt
@@ -0,0 +1,30 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration9 : Migration(8, 9) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS Messages")
+ database.execSQL("""
+ CREATE TABLE IF NOT EXISTS Messages (
+ id INTEGER PRIMARY KEY NOT NULL,
+ 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,
+ unread_by INTEGER NOT NULL,
+ read_by INTEGER NOT NULL,
+ removed INTEGER NOT NULL,
+ is_notified INTEGER NOT NULL,
+ content TEXT)
+ """)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt
new file mode 100644
index 000000000..58a2396e2
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt
@@ -0,0 +1,3 @@
+package io.github.wulkanowy.data.exceptions
+
+class NoCurrentStudentException : Exception("There no set current student in database")
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
deleted file mode 100644
index 42266955e..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package io.github.wulkanowy.data.repositories
-
-import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
-import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
-import io.github.wulkanowy.data.db.entities.Grade
-import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.repositories.local.GradeLocal
-import io.github.wulkanowy.data.repositories.remote.GradeRemote
-import io.reactivex.Completable
-import io.reactivex.Single
-import java.net.UnknownHostException
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class GradeRepository @Inject constructor(
- private val settings: InternetObservingSettings,
- private val local: GradeLocal,
- private val remote: GradeRemote
-) {
-
- fun getGrades(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> {
- return local.getGrades(semester).filter { !forceRefresh }
- .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
- .flatMap {
- if (it) remote.getGrades(semester)
- else Single.error(UnknownHostException())
- }.flatMap { newGrades ->
- local.getGrades(semester).toSingle(emptyList())
- .doOnSuccess { oldGrades ->
- local.deleteGrades(oldGrades - newGrades)
- local.saveGrades((newGrades - oldGrades)
- .onEach {
- if (oldGrades.isNotEmpty()) it.isRead = false
- if (notify) it.isNotified = false
- })
- }
- }.flatMap { local.getGrades(semester).toSingle(emptyList()) })
- }
-
- fun getNewGrades(semester: Semester): Single> {
- return local.getNewGrades(semester).toSingle(emptyList())
- }
-
- fun updateGrade(grade: Grade): Completable {
- return local.updateGrade(grade)
- }
-
- fun updateGrades(grades: List): Completable {
- return local.updateGrades(grades)
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
deleted file mode 100644
index a2a477b56..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package io.github.wulkanowy.data.repositories
-
-import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
-import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
-import io.github.wulkanowy.data.db.entities.Homework
-import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.repositories.local.HomeworkLocal
-import io.github.wulkanowy.data.repositories.remote.HomeworkRemote
-import io.reactivex.Single
-import org.threeten.bp.LocalDate
-import java.net.UnknownHostException
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class HomeworkRepository @Inject constructor(
- private val settings: InternetObservingSettings,
- private val local: HomeworkLocal,
- private val remote: HomeworkRemote
-) {
-
- fun getHomework(semester: Semester, date: LocalDate, forceRefresh: Boolean = false): Single> {
- return local.getHomework(semester, date).filter { !forceRefresh }
- .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
- .flatMap {
- if (it) remote.getHomework(semester, date)
- else Single.error(UnknownHostException())
- }.flatMap { newGrades ->
- local.getHomework(semester, date).toSingle(emptyList())
- .doOnSuccess { oldGrades ->
- local.deleteHomework(oldGrades - newGrades)
- local.saveHomework(newGrades - oldGrades)
- }
- }.flatMap { local.getHomework(semester, date).toSingle(emptyList()) })
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessagesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessagesRepository.kt
deleted file mode 100644
index 06a0f6f95..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessagesRepository.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-package io.github.wulkanowy.data.repositories
-
-import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
-import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
-import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.repositories.local.MessagesLocal
-import io.github.wulkanowy.data.repositories.remote.MessagesRemote
-import io.reactivex.Completable
-import io.reactivex.Single
-import java.net.UnknownHostException
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class MessagesRepository @Inject constructor(
- private val settings: InternetObservingSettings,
- private val local: MessagesLocal,
- private val remote: MessagesRemote
-) {
-
- enum class MessageFolder(val id: Int = 1) {
- RECEIVED(1),
- SENT(2),
- TRASHED(3)
- }
-
- fun getMessages(studentId: Int, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single> {
- return local.getMessages(studentId, folder).filter { !forceRefresh }
- .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
- .flatMap {
- if (it) remote.getMessages(studentId, folder)
- else Single.error(UnknownHostException())
- }.flatMap { new ->
- local.getMessages(studentId, folder).toSingle(emptyList())
- .doOnSuccess { old ->
- local.deleteMessages(old - new)
- local.saveMessages((new - old)
- .onEach {
- it.isNotified = !notify
- })
- }
- }.flatMap { local.getMessages(studentId, folder).toSingle(emptyList()) }
- )
- }
-
- fun getMessage(studentId: Int, messageId: Int, markAsRead: Boolean = false): Single {
- return local.getMessage(studentId, messageId)
- .filter { !it.content.isNullOrEmpty() }
- .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
- .flatMap {
- if (it) local.getMessage(studentId, messageId).toSingle()
- else Single.error(UnknownHostException())
- }
- .flatMap { dbMessage ->
- remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
- local.updateMessage(dbMessage.copy(unread = false).apply {
- id = dbMessage.id
- content = it
- })
- }
- }.flatMap {
- local.getMessage(studentId, messageId).toSingle()
- }
- )
- }
-
- fun getNewMessages(student: Student): Single> {
- return local.getNewMessages(student).toSingle(emptyList())
- }
-
- fun updateMessage(message: Message): Completable {
- return Completable.fromCallable { local.updateMessage(message) }
- }
-
- fun updateMessages(messages: List): Completable {
- return Completable.fromCallable { local.updateMessages(messages) }
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
deleted file mode 100644
index 2836894f2..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package io.github.wulkanowy.data.repositories
-
-import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
-import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
-import io.github.wulkanowy.data.db.entities.Note
-import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.repositories.local.NoteLocal
-import io.github.wulkanowy.data.repositories.remote.NoteRemote
-import io.reactivex.Completable
-import io.reactivex.Single
-import java.net.UnknownHostException
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class NoteRepository @Inject constructor(
- private val settings: InternetObservingSettings,
- private val local: NoteLocal,
- private val remote: NoteRemote
-) {
-
- fun getNotes(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> {
- return local.getNotes(semester).filter { !forceRefresh }
- .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
- .flatMap {
- if (it) remote.getNotes(semester)
- else Single.error(UnknownHostException())
- }.flatMap { new ->
- local.getNotes(semester).toSingle(emptyList())
- .doOnSuccess { old ->
- local.deleteNotes(old - new)
- local.saveNotes((new - old)
- .onEach {
- if (notify) it.isNotified = false
- })
- }
- }.flatMap { local.getNotes(semester).toSingle(emptyList()) }
- )
- }
-
- fun getNewNotes(semester: Semester): Single> {
- return local.getNewNotes(semester).toSingle(emptyList())
- }
-
- fun updateNote(note: Note): Completable {
- return local.updateNote(note)
- }
-
- fun updateNotes(notes: List): Completable {
- return local.updateNotes(notes)
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
deleted file mode 100644
index 6cc6a0435..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package io.github.wulkanowy.data.repositories
-
-import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
-import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
-import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.db.entities.Timetable
-import io.github.wulkanowy.data.repositories.local.TimetableLocal
-import io.github.wulkanowy.data.repositories.remote.TimetableRemote
-import io.github.wulkanowy.utils.friday
-import io.github.wulkanowy.utils.monday
-import io.reactivex.Single
-import org.threeten.bp.LocalDate
-import java.net.UnknownHostException
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class TimetableRepository @Inject constructor(
- private val settings: InternetObservingSettings,
- private val local: TimetableLocal,
- private val remote: TimetableRemote
-) {
-
- fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false)
- : Single> {
- return Single.fromCallable { startDate.monday to endDate.friday }
- .flatMap { dates ->
- local.getTimetable(semester, dates.first, dates.second).filter { !forceRefresh }
- .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
- .flatMap {
- if (it) remote.getTimetable(semester, dates.first, dates.second)
- else Single.error(UnknownHostException())
- }.flatMap { newTimetable ->
- local.getTimetable(semester, dates.first, dates.second)
- .toSingle(emptyList())
- .doOnSuccess { oldTimetable ->
- local.deleteTimetable(oldTimetable - newTimetable)
- local.saveTimetable(newTimetable - oldTimetable)
- }
- }.flatMap {
- local.getTimetable(semester, dates.first, dates.second)
- .toSingle(emptyList())
- }).map { list -> list.filter { it.date in startDate..endDate } }
- }
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt
similarity index 82%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt
index 9a318dba2..0f5873766 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.entities.Attendance
@@ -6,14 +6,11 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
import javax.inject.Inject
+import javax.inject.Singleton
+@Singleton
class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) {
- fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> {
- return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
- .filter { !it.isEmpty() }
- }
-
fun saveAttendance(attendance: List) {
attendanceDb.insertAll(attendance)
}
@@ -21,4 +18,8 @@ class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDa
fun deleteAttendance(attendance: List) {
attendanceDb.deleteAll(attendance)
}
+
+ fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> {
+ return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt
similarity index 95%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt
index 79047606f..b3544c3f5 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Attendance
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt
similarity index 89%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt
index eb7230ae5..85102b3c5 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt
@@ -1,13 +1,12 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.data.repositories.attendance
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.repositories.local.AttendanceLocal
-import io.github.wulkanowy.data.repositories.remote.AttendanceRemote
import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import org.threeten.bp.LocalDate
import java.net.UnknownHostException
@@ -33,8 +32,8 @@ class AttendanceRepository @Inject constructor(
local.getAttendance(semester, dates.first, dates.second)
.toSingle(emptyList())
.doOnSuccess { oldAttendance ->
- local.deleteAttendance(oldAttendance - newAttendance)
- local.saveAttendance(newAttendance - oldAttendance)
+ local.deleteAttendance(oldAttendance.uniqueSubtract(newAttendance))
+ local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance))
}
}.flatMap {
local.getAttendance(semester, dates.first, dates.second)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt
similarity index 82%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceSummaryLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt
index 2bb9f1220..2e9a10067 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceSummaryLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt
@@ -1,17 +1,15 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import javax.inject.Inject
+import javax.inject.Singleton
+@Singleton
class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) {
- fun getAttendanceSummary(semester: Semester, subjectId: Int): Maybe> {
- return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId).filter { !it.isEmpty() }
- }
-
fun saveAttendanceSummary(attendance: List) {
attendanceDb.insertAll(attendance)
}
@@ -19,4 +17,8 @@ class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: Atten
fun deleteAttendanceSummary(attendance: List) {
attendanceDb.deleteAll(attendance)
}
+
+ fun getAttendanceSummary(semester: Semester, subjectId: Int): Maybe> {
+ return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId).filter { it.isNotEmpty() }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt
similarity index 95%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceSummaryRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt
index 079eb4b8e..d38dd3a4b 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceSummaryRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.AttendanceSummary
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt
similarity index 80%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt
index 8e21b12c6..c65870508 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt
@@ -1,11 +1,10 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.data.repositories.attendancesummary
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.repositories.local.AttendanceSummaryLocal
-import io.github.wulkanowy.data.repositories.remote.AttendanceSummaryRemote
+import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
@@ -18,7 +17,7 @@ class AttendanceSummaryRepository @Inject constructor(
private val remote: AttendanceSummaryRemote
) {
- fun getAttendanceSummary(semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single>? {
+ fun getAttendanceSummary(semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single> {
return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
@@ -27,8 +26,8 @@ class AttendanceSummaryRepository @Inject constructor(
}.flatMap { new ->
local.getAttendanceSummary(semester, subjectId).toSingle(emptyList())
.doOnSuccess { old ->
- local.deleteAttendanceSummary(old - new)
- local.saveAttendanceSummary(new - old)
+ local.deleteAttendanceSummary(old.uniqueSubtract(new))
+ local.saveAttendanceSummary(new.uniqueSubtract(old))
}
}.flatMap { local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) })
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt
new file mode 100644
index 000000000..9b275908e
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt
@@ -0,0 +1,25 @@
+package io.github.wulkanowy.data.repositories.completedlessons
+
+import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
+import io.github.wulkanowy.data.db.entities.CompletedLesson
+import io.github.wulkanowy.data.db.entities.Semester
+import io.reactivex.Maybe
+import org.threeten.bp.LocalDate
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb: CompletedLessonsDao) {
+
+ fun saveCompletedLessons(completedLessons: List) {
+ completedLessonsDb.insertAll(completedLessons)
+ }
+
+ fun deleteCompleteLessons(completedLessons: List) {
+ completedLessonsDb.deleteAll(completedLessons)
+ }
+
+ fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Maybe> {
+ return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end).filter { it.isNotEmpty() }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt
new file mode 100644
index 000000000..58dd5a9d1
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt
@@ -0,0 +1,37 @@
+package io.github.wulkanowy.data.repositories.completedlessons
+
+import io.github.wulkanowy.api.Api
+import io.github.wulkanowy.api.toLocalDate
+import io.github.wulkanowy.data.db.entities.CompletedLesson
+import io.github.wulkanowy.data.db.entities.Semester
+import io.reactivex.Single
+import org.threeten.bp.LocalDate
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class CompletedLessonsRemote @Inject constructor(private val api: Api) {
+
+ fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> {
+ return Single.just(api.apply { diaryId = semester.diaryId })
+ .flatMap { it.getCompletedLessons(startDate, endDate) }
+ .map { lessons ->
+ lessons.map {
+ it.absence
+ CompletedLesson(
+ studentId = semester.studentId,
+ diaryId = semester.diaryId,
+ date = it.date.toLocalDate(),
+ number = it.number,
+ subject = it.subject,
+ topic = it.topic,
+ teacher = it.teacher,
+ teacherSymbol = it.teacherSymbol,
+ substitution = it.substitution,
+ absence = it.absence,
+ resources = it.resources
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt
new file mode 100644
index 000000000..c22fabc39
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt
@@ -0,0 +1,44 @@
+package io.github.wulkanowy.data.repositories.completedlessons
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.db.entities.CompletedLesson
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.utils.friday
+import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.uniqueSubtract
+import io.reactivex.Single
+import org.threeten.bp.LocalDate
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class CompletedLessonsRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: CompletedLessonsLocal,
+ private val remote: CompletedLessonsRemote
+) {
+
+ fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single> {
+ return Single.fromCallable { startDate.monday to endDate.friday }
+ .flatMap { dates ->
+ local.getCompletedLessons(semester, dates.first, dates.second).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.getCompletedLessons(semester, dates.first, dates.second)
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getCompletedLessons(semester, dates.first, dates.second)
+ .toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteCompleteLessons(old.uniqueSubtract(new))
+ local.saveCompletedLessons(new.uniqueSubtract(old))
+ }
+ }.flatMap {
+ local.getCompletedLessons(semester, dates.first, dates.second)
+ .toSingle(emptyList())
+ }).map { list -> list.filter { it.date in startDate..endDate } }
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt
similarity index 88%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt
index 3e32a6357..0f484d323 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.data.db.dao.ExamDao
import io.github.wulkanowy.data.db.entities.Exam
@@ -6,7 +6,9 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
import javax.inject.Inject
+import javax.inject.Singleton
+@Singleton
class ExamLocal @Inject constructor(private val examDb: ExamDao) {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt
similarity index 92%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt
index 3937b768d..f6d653a61 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Exam
@@ -7,7 +7,9 @@ import io.github.wulkanowy.utils.toLocalDate
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
+import javax.inject.Singleton
+@Singleton
class ExamRemote @Inject constructor(private val api: Api) {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt
similarity index 80%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt
index cb78df53d..be60eaecd 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt
@@ -1,13 +1,12 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.data.repositories.exam
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.repositories.local.ExamLocal
-import io.github.wulkanowy.data.repositories.remote.ExamRemote
import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import org.threeten.bp.LocalDate
import java.net.UnknownHostException
@@ -29,12 +28,12 @@ class ExamRepository @Inject constructor(
.flatMap {
if (it) remote.getExams(semester, dates.first, dates.second)
else Single.error(UnknownHostException())
- }.flatMap { newExams ->
+ }.flatMap { new ->
local.getExams(semester, dates.first, dates.second)
.toSingle(emptyList())
- .doOnSuccess { oldExams ->
- local.deleteExams(oldExams - newExams)
- local.saveExams(newExams - oldExams)
+ .doOnSuccess { old ->
+ local.deleteExams(old.uniqueSubtract(new))
+ local.saveExams(new.uniqueSubtract(old))
}
}.flatMap {
local.getExams(semester, dates.first, dates.second)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt
similarity index 55%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt
index d6d46340f..4983a4740 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt
@@ -1,9 +1,8 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.Semester
-import io.reactivex.Completable
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton
@@ -11,27 +10,19 @@ import javax.inject.Singleton
@Singleton
class GradeLocal @Inject constructor(private val gradeDb: GradeDao) {
- fun getGrades(semester: Semester): Maybe> {
- return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { !it.isEmpty() }
- }
-
- fun getNewGrades(semester: Semester): Maybe> {
- return gradeDb.loadAllNew(semester.semesterId, semester.studentId)
- }
-
fun saveGrades(grades: List) {
gradeDb.insertAll(grades)
}
- fun updateGrade(grade: Grade): Completable {
- return Completable.fromCallable { gradeDb.update(grade) }
- }
-
- fun updateGrades(grades: List): Completable {
- return Completable.fromCallable { gradeDb.updateAll(grades) }
- }
-
fun deleteGrades(grades: List) {
gradeDb.deleteAll(grades)
}
+
+ fun updateGrades(grades: List) {
+ gradeDb.updateAll(grades)
+ }
+
+ fun getGrades(semester: Semester): Maybe> {
+ return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt
similarity index 87%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt
index df3bf4d28..570ab7a77 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Grade
@@ -25,8 +25,8 @@ class GradeRemote @Inject constructor(private val api: Api) {
modifier = it.modifier,
comment = it.comment,
color = it.color,
- gradeSymbol = it.symbol ?: "",
- description = it.description,
+ gradeSymbol = it.symbol.orEmpty(),
+ description = it.description.orEmpty(),
weight = it.weight,
weightValue = it.weightValue,
date = it.date.toLocalDate(),
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt
new file mode 100644
index 000000000..7f8e92a2e
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt
@@ -0,0 +1,59 @@
+package io.github.wulkanowy.data.repositories.grade
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.db.entities.Grade
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.utils.uniqueSubtract
+import io.reactivex.Completable
+import io.reactivex.Single
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class GradeRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: GradeLocal,
+ private val remote: GradeRemote
+) {
+
+ fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> {
+ return local.getGrades(semester).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.getGrades(semester)
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getGrades(semester).toSingle(emptyList())
+ .doOnSuccess { old ->
+ val notifyBreakDate = old.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate()
+ local.deleteGrades(old.uniqueSubtract(new))
+ local.saveGrades(new.uniqueSubtract(old)
+ .onEach {
+ if (it.date >= notifyBreakDate) it.apply {
+ isRead = false
+ if (notify) isNotified = false
+ }
+ })
+ }
+ }.flatMap { local.getGrades(semester).toSingle(emptyList()) })
+ }
+
+ fun getUnreadGrades(semester: Semester): Single> {
+ return local.getGrades(semester).map { it.filter { grade -> !grade.isRead } }.toSingle(emptyList())
+ }
+
+ fun getNotNotifiedGrades(semester: Semester): Single> {
+ return local.getGrades(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList())
+ }
+
+ fun updateGrade(grade: Grade): Completable {
+ return Completable.fromCallable { local.updateGrades(listOf(grade)) }
+ }
+
+ fun updateGrades(grades: List): Completable {
+ return Completable.fromCallable { local.updateGrades(grades) }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt
similarity index 87%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeSummaryLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt
index 6a72416d2..e74641d3a 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeSummaryLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.gradessummary
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.GradeSummary
@@ -10,11 +10,6 @@ import javax.inject.Singleton
@Singleton
class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) {
- fun getGradesSummary(semester: Semester): Maybe> {
- return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
- .filter { !it.isEmpty() }
- }
-
fun saveGradesSummary(gradesSummary: List) {
gradeSummaryDb.insertAll(gradesSummary)
}
@@ -22,4 +17,8 @@ class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSum
fun deleteGradesSummary(gradesSummary: List) {
gradeSummaryDb.deleteAll(gradesSummary)
}
+
+ fun getGradesSummary(semester: Semester): Maybe> {
+ return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt
similarity index 94%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeSummaryRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt
index af45ca737..d395decf8 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeSummaryRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.gradessummary
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.GradeSummary
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt
similarity index 71%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/GradeSummaryRepository.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt
index a59a2cd54..660a032c2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeSummaryRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt
@@ -1,11 +1,10 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.data.repositories.gradessummary
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.repositories.local.GradeSummaryLocal
-import io.github.wulkanowy.data.repositories.remote.GradeSummaryRemote
+import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
@@ -24,11 +23,11 @@ class GradeSummaryRepository @Inject constructor(
.flatMap {
if (it) remote.getGradeSummary(semester)
else Single.error(UnknownHostException())
- }.flatMap { newGradesSummary ->
+ }.flatMap { new ->
local.getGradesSummary(semester).toSingle(emptyList())
- .doOnSuccess { oldGradesSummary ->
- local.deleteGradesSummary(oldGradesSummary - newGradesSummary)
- local.saveGradesSummary(newGradesSummary - oldGradesSummary)
+ .doOnSuccess { old ->
+ local.deleteGradesSummary(old.uniqueSubtract(new))
+ local.saveGradesSummary(new.uniqueSubtract(old))
}
}.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) })
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt
new file mode 100644
index 000000000..581ac2f81
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt
@@ -0,0 +1,34 @@
+package io.github.wulkanowy.data.repositories.gradestatistics
+
+import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
+import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.Semester
+import io.reactivex.Maybe
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class GradeStatisticsLocal @Inject constructor(private val gradeStatisticsDb: GradeStatisticsDao) {
+
+ fun getGradesStatistics(semester: Semester, isSemester: Boolean): Maybe> {
+ return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
+ .filter { !it.isEmpty() }
+ }
+
+ fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): Maybe> {
+ return (if ("Wszystkie" == subjectName) gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list ->
+ list.groupBy { it.grade }.map {
+ GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key, it.value.fold(0) { acc, e -> acc + e.amount }, false)
+ }
+ }
+ else gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)).filter { !it.isEmpty() }
+ }
+
+ fun saveGradesStatistics(gradesStatistics: List) {
+ gradeStatisticsDb.insertAll(gradesStatistics)
+ }
+
+ fun deleteGradesStatistics(gradesStatistics: List) {
+ gradeStatisticsDb.deleteAll(gradesStatistics)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt
new file mode 100644
index 000000000..fa3b951f6
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt
@@ -0,0 +1,29 @@
+package io.github.wulkanowy.data.repositories.gradestatistics
+
+import io.github.wulkanowy.api.Api
+import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.Semester
+import io.reactivex.Single
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class GradeStatisticsRemote @Inject constructor(private val api: Api) {
+
+ fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single> {
+ return Single.just(api.apply { diaryId = semester.diaryId })
+ .flatMap { it.getGradesStatistics(semester.semesterId, isSemester) }
+ .map { gradeStatistics ->
+ gradeStatistics.map {
+ GradeStatistics(
+ semesterId = semester.semesterId,
+ studentId = semester.studentId,
+ subject = it.subject,
+ grade = it.gradeValue,
+ amount = it.amount ?: 0,
+ semester = isSemester
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt
new file mode 100644
index 000000000..9617cdd64
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt
@@ -0,0 +1,34 @@
+package io.github.wulkanowy.data.repositories.gradestatistics
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.utils.uniqueSubtract
+import io.reactivex.Single
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class GradeStatisticsRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: GradeStatisticsLocal,
+ private val remote: GradeStatisticsRemote
+) {
+
+ fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single> {
+ return local.getGradesStatistics(semester, isSemester, subjectName).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.getGradeStatistics(semester, isSemester)
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getGradesStatistics(semester, isSemester).toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteGradesStatistics(old.uniqueSubtract(new))
+ local.saveGradesStatistics(new.uniqueSubtract(old))
+ }
+ }.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/HomeworkLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt
similarity index 73%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/HomeworkLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt
index ea18d8145..671ecafd7 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/HomeworkLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.homework
import io.github.wulkanowy.data.db.dao.HomeworkDao
import io.github.wulkanowy.data.db.entities.Homework
@@ -11,10 +11,6 @@ import javax.inject.Singleton
@Singleton
class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
- fun getHomework(semester: Semester, date: LocalDate): Maybe> {
- return homeworkDb.loadAll(semester.semesterId, semester.studentId, date).filter { !it.isEmpty() }
- }
-
fun saveHomework(homework: List) {
homeworkDb.insertAll(homework)
}
@@ -22,4 +18,9 @@ class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
fun deleteHomework(homework: List) {
homeworkDb.deleteAll(homework)
}
+
+ fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> {
+ return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate)
+ .filter { it.isNotEmpty() }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/HomeworkRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt
similarity index 82%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/HomeworkRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt
index 0fdc02512..681b66469 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/HomeworkRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.homework
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Homework
@@ -12,9 +12,9 @@ import javax.inject.Singleton
@Singleton
class HomeworkRemote @Inject constructor(private val api: Api) {
- fun getHomework(semester: Semester, date: LocalDate): Single> {
+ fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> {
return Single.just(api.apply { diaryId = semester.diaryId })
- .flatMap { it.getHomework(date, date) }
+ .flatMap { it.getHomework(startDate, endDate) }
.map { homework ->
homework.map {
Homework(
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt
new file mode 100644
index 000000000..3f924944d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt
@@ -0,0 +1,39 @@
+package io.github.wulkanowy.data.repositories.homework
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.db.entities.Homework
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.utils.friday
+import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.uniqueSubtract
+import io.reactivex.Single
+import org.threeten.bp.LocalDate
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class HomeworkRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: HomeworkLocal,
+ private val remote: HomeworkRemote
+) {
+
+ fun getHomework(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> {
+ return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) ->
+ local.getHomework(semester, monday, friday).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.getHomework(semester, monday, friday)
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getHomework(semester, monday, friday).toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteHomework(old.uniqueSubtract(new))
+ local.saveHomework(new.uniqueSubtract(old))
+ }
+ }.flatMap { local.getHomework(semester, monday, friday).toSingle(emptyList()) })
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/MessagesLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/MessagesLocal.kt
deleted file mode 100644
index 531cd8b0d..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/MessagesLocal.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package io.github.wulkanowy.data.repositories.local
-
-import io.github.wulkanowy.data.db.dao.MessagesDao
-import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.repositories.MessagesRepository
-import io.reactivex.Maybe
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class MessagesLocal @Inject constructor(private val messagesDb: MessagesDao) {
-
- fun getMessage(studentId: Int, id: Int): Maybe {
- return messagesDb.loadOne(studentId, id)
- }
-
- fun getMessages(studentId: Int, folder: MessagesRepository.MessageFolder): Maybe> {
- return when (folder) {
- MessagesRepository.MessageFolder.TRASHED -> messagesDb.loadDeleted(studentId)
- else -> messagesDb.load(studentId, folder.id)
- }.filter { !it.isEmpty() }
- }
-
- fun getNewMessages(student: Student): Maybe> {
- return messagesDb.loadNewMessages(student.studentId)
- }
-
- fun saveMessages(messages: List): List {
- return messagesDb.insertAll(messages)
- }
-
- fun updateMessage(message: Message) {
- return messagesDb.update(message)
- }
-
- fun updateMessages(messages: List) {
- return messagesDb.updateAll(messages)
- }
-
- fun deleteMessages(messages: List) {
- messagesDb.deleteAll(messages)
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/NoteLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/NoteLocal.kt
deleted file mode 100644
index 543eab9b3..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/NoteLocal.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.github.wulkanowy.data.repositories.local
-
-import io.github.wulkanowy.data.db.dao.NoteDao
-import io.github.wulkanowy.data.db.entities.Note
-import io.github.wulkanowy.data.db.entities.Semester
-import io.reactivex.Completable
-import io.reactivex.Maybe
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class NoteLocal @Inject constructor(private val noteDb: NoteDao) {
-
- fun getNotes(semester: Semester): Maybe> {
- return noteDb.loadAll(semester.semesterId, semester.studentId).filter { !it.isEmpty() }
- }
-
- fun getNewNotes(semester: Semester): Maybe> {
- return noteDb.loadNew(semester.semesterId, semester.studentId)
- }
-
- fun saveNotes(notes: List) {
- noteDb.insertAll(notes)
- }
-
- fun updateNote(note: Note): Completable {
- return Completable.fromCallable { noteDb.update(note) }
- }
-
- fun updateNotes(notes: List): Completable {
- return Completable.fromCallable { noteDb.updateAll(notes) }
- }
-
- fun deleteNotes(notes: List) {
- noteDb.deleteAll(notes)
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt
new file mode 100644
index 000000000..115c89652
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt
@@ -0,0 +1,29 @@
+package io.github.wulkanowy.data.repositories.luckynumber
+
+import io.github.wulkanowy.data.db.dao.LuckyNumberDao
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.data.db.entities.Semester
+import io.reactivex.Maybe
+import org.threeten.bp.LocalDate
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumberDao) {
+
+ fun saveLuckyNumber(luckyNumber: LuckyNumber) {
+ luckyNumberDb.insert(luckyNumber)
+ }
+
+ fun updateLuckyNumber(luckyNumber: LuckyNumber) {
+ luckyNumberDb.update(luckyNumber)
+ }
+
+ fun deleteLuckyNumber(luckyNumber: LuckyNumber) {
+ luckyNumberDb.delete(luckyNumber)
+ }
+
+ fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe {
+ return luckyNumberDb.load(semester.studentId, date)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt
new file mode 100644
index 000000000..1b0f12b3e
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt
@@ -0,0 +1,26 @@
+package io.github.wulkanowy.data.repositories.luckynumber
+
+import io.github.wulkanowy.api.Api
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.data.db.entities.Semester
+import io.reactivex.Maybe
+import io.reactivex.Single
+import org.threeten.bp.LocalDate
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class LuckyNumberRemote @Inject constructor(private val api: Api) {
+
+ fun getLuckyNumber(semester: Semester): Maybe {
+ return Single.just(api.apply { diaryId = semester.diaryId })
+ .flatMapMaybe { it.getLuckyNumber() }
+ .map {
+ LuckyNumber(
+ studentId = semester.studentId,
+ date = LocalDate.now(),
+ luckyNumber = it
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt
new file mode 100644
index 000000000..4036521f7
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt
@@ -0,0 +1,54 @@
+package io.github.wulkanowy.data.repositories.luckynumber
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.data.db.entities.Semester
+import io.reactivex.Completable
+import io.reactivex.Maybe
+import org.threeten.bp.LocalDate
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class LuckyNumberRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: LuckyNumberLocal,
+ private val remote: LuckyNumberRemote
+) {
+
+ fun getLuckyNumber(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Maybe {
+ return local.getLuckyNumber(semester, LocalDate.now()).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMapMaybe {
+ if (it) remote.getLuckyNumber(semester)
+ else Maybe.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getLuckyNumber(semester, LocalDate.now())
+ .doOnSuccess { old ->
+ if (new != old) {
+ local.deleteLuckyNumber(old)
+ local.saveLuckyNumber(new.apply {
+ if (notify) isNotified = false
+ })
+ }
+ }
+ .doOnComplete {
+ local.saveLuckyNumber(new.apply {
+ if (notify) isNotified = false
+ })
+ }
+ }.flatMap({ local.getLuckyNumber(semester, LocalDate.now()) }, { Maybe.error(it) },
+ { local.getLuckyNumber(semester, LocalDate.now()) })
+ )
+ }
+
+ fun getNotNotifiedLuckyNumber(semester: Semester): Maybe {
+ return local.getLuckyNumber(semester, LocalDate.now()).filter { !it.isNotified }
+ }
+
+ fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable {
+ return Completable.fromCallable { local.updateLuckyNumber(luckyNumber) }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt
new file mode 100644
index 000000000..06f5a1e05
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt
@@ -0,0 +1,7 @@
+package io.github.wulkanowy.data.repositories.message
+
+enum class MessageFolder(val id: Int = 1) {
+ RECEIVED(1),
+ SENT(2),
+ TRASHED(3)
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt
new file mode 100644
index 000000000..ce08d13ec
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt
@@ -0,0 +1,36 @@
+package io.github.wulkanowy.data.repositories.message
+
+import io.github.wulkanowy.data.db.dao.MessagesDao
+import io.github.wulkanowy.data.db.entities.Message
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
+import io.reactivex.Maybe
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) {
+
+ fun saveMessages(messages: List) {
+ messagesDb.insertAll(messages)
+ }
+
+ fun updateMessages(messages: List) {
+ messagesDb.updateAll(messages)
+ }
+
+ fun deleteMessages(messages: List) {
+ messagesDb.deleteAll(messages)
+ }
+
+ fun getMessage(id: Long): Maybe {
+ return messagesDb.load(id)
+ }
+
+ fun getMessages(student: Student, folder: MessageFolder): Maybe> {
+ return when (folder) {
+ TRASHED -> messagesDb.loadDeleted(student.id.toInt())
+ else -> messagesDb.loadAll(student.id.toInt(), folder.id)
+ }.filter { it.isNotEmpty() }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt
new file mode 100644
index 000000000..b2bff1e77
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt
@@ -0,0 +1,66 @@
+package io.github.wulkanowy.data.repositories.message
+
+import io.github.wulkanowy.api.Api
+import io.github.wulkanowy.api.messages.Folder
+import io.github.wulkanowy.api.messages.SentMessage
+import io.github.wulkanowy.data.db.entities.Message
+import io.github.wulkanowy.data.db.entities.Recipient
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.utils.toLocalDateTime
+import io.reactivex.Single
+import org.threeten.bp.LocalDateTime.now
+import javax.inject.Inject
+import javax.inject.Singleton
+import io.github.wulkanowy.api.messages.Message as ApiMessage
+import io.github.wulkanowy.api.messages.Recipient as ApiRecipient
+
+@Singleton
+class MessageRemote @Inject constructor(private val api: Api) {
+
+ fun getMessages(student: Student, folder: MessageFolder): Single> {
+ return api.getMessages(Folder.valueOf(folder.name)).map { messages ->
+ messages.map {
+ Message(
+ studentId = student.id.toInt(),
+ realId = it.id ?: 0,
+ messageId = it.messageId ?: 0,
+ sender = it.sender.orEmpty(),
+ senderId = it.senderId ?: 0,
+ recipient = it.recipient.orEmpty(),
+ subject = it.subject.trim(),
+ date = it.date?.toLocalDateTime() ?: now(),
+ folderId = it.folderId,
+ unread = it.unread ?: false,
+ unreadBy = it.unreadBy ?: 0,
+ readBy = it.readBy ?: 0,
+ removed = it.removed
+ )
+ }
+ }
+ }
+
+ fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single {
+ return api.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId)
+ }
+
+ fun sendMessage(subject: String, content: String, recipients: List): Single {
+ return api.sendMessage(
+ subject = subject,
+ content = content,
+ recipients = recipients.map {
+ ApiRecipient(
+ id = it.realId,
+ name = it.realName,
+ loginId = it.loginId,
+ reportingUnitId = it.unitId,
+ role = it.role,
+ hash = it.hash
+ )
+ }
+ )
+ }
+
+ fun deleteMessage(message: Message): Single {
+ return api.deleteMessages(listOf(Pair(message.realId, message.folderId)))
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt
new file mode 100644
index 000000000..c10cd5181
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt
@@ -0,0 +1,110 @@
+package io.github.wulkanowy.data.repositories.message
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.api.messages.SentMessage
+import io.github.wulkanowy.data.ApiHelper
+import io.github.wulkanowy.data.db.entities.Message
+import io.github.wulkanowy.data.db.entities.Recipient
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
+import io.github.wulkanowy.utils.uniqueSubtract
+import io.reactivex.Completable
+import io.reactivex.Maybe
+import io.reactivex.Single
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class MessageRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: MessageLocal,
+ private val remote: MessageRemote,
+ private val apiHelper: ApiHelper
+) {
+
+ fun getMessages(student: Student, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single> {
+ return Single.just(apiHelper.initApi(student))
+ .flatMap { _ ->
+ local.getMessages(student, folder).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.getMessages(student, folder)
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getMessages(student, folder).toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteMessages(old.uniqueSubtract(new))
+ local.saveMessages(new.uniqueSubtract(old)
+ .onEach {
+ it.isNotified = !notify
+ })
+ }
+ }.flatMap { local.getMessages(student, folder).toSingle(emptyList()) }
+ )
+ }
+ }
+
+ fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single {
+ return Single.just(apiHelper.initApi(student))
+ .flatMap { _ ->
+ local.getMessage(messageDbId)
+ .filter { !it.content.isNullOrEmpty() }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) local.getMessage(messageDbId).toSingle()
+ else Single.error(UnknownHostException())
+ }
+ .flatMap { dbMessage ->
+ remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
+ local.updateMessages(listOf(dbMessage.copy(unread = false).apply {
+ id = dbMessage.id
+ content = it
+ }))
+ }
+ }.flatMap {
+ local.getMessage(messageDbId).toSingle()
+ }
+ )
+ }
+ }
+
+ fun getNotNotifiedMessages(student: Student): Single> {
+ return local.getMessages(student, RECEIVED)
+ .map { it.filter { message -> !message.isNotified && message.unread } }
+ .toSingle(emptyList())
+ }
+
+ fun updateMessage(message: Message): Completable {
+ return Completable.fromCallable { local.updateMessages(listOf(message)) }
+ }
+
+ fun updateMessages(messages: List): Completable {
+ return Completable.fromCallable { local.updateMessages(messages) }
+ }
+
+ fun sendMessage(subject: String, content: String, recipients: List): Single {
+ return ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.sendMessage(subject, content, recipients)
+ else Single.error(UnknownHostException())
+ }
+ }
+
+ fun deleteMessage(message: Message): Maybe {
+ return ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.deleteMessage(message)
+ else Single.error(UnknownHostException())
+ }
+ .filter { it }
+ .doOnSuccess {
+ if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply {
+ id = message.id
+ content = message.content
+ }))
+ else local.deleteMessages(listOf(message))
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt
new file mode 100644
index 000000000..784e61f0f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt
@@ -0,0 +1,28 @@
+package io.github.wulkanowy.data.repositories.note
+
+import io.github.wulkanowy.data.db.dao.NoteDao
+import io.github.wulkanowy.data.db.entities.Note
+import io.github.wulkanowy.data.db.entities.Student
+import io.reactivex.Maybe
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class NoteLocal @Inject constructor(private val noteDb: NoteDao) {
+
+ fun saveNotes(notes: List) {
+ noteDb.insertAll(notes)
+ }
+
+ fun updateNotes(notes: List) {
+ noteDb.updateAll(notes)
+ }
+
+ fun deleteNotes(notes: List) {
+ noteDb.deleteAll(notes)
+ }
+
+ fun getNotes(student: Student): Maybe> {
+ return noteDb.loadAll(student.studentId).filter { it.isNotEmpty() }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/NoteRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt
similarity index 89%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/NoteRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt
index 31f28e113..aebc6230e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/NoteRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.note
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Note
@@ -17,7 +17,6 @@ class NoteRemote @Inject constructor(private val api: Api) {
.map { notes ->
notes.map {
Note(
- semesterId = semester.semesterId,
studentId = semester.studentId,
date = it.date.toLocalDate(),
teacher = it.teacher,
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt
new file mode 100644
index 000000000..52cb2d0f5
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt
@@ -0,0 +1,54 @@
+package io.github.wulkanowy.data.repositories.note
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.db.entities.Note
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.utils.uniqueSubtract
+import io.reactivex.Completable
+import io.reactivex.Single
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class NoteRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: NoteLocal,
+ private val remote: NoteRemote
+) {
+
+ fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> {
+ return local.getNotes(student).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.getNotes(semester)
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getNotes(student).toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteNotes(old.uniqueSubtract(new))
+ local.saveNotes(new.uniqueSubtract(old)
+ .onEach {
+ if (it.date >= student.registrationDate.toLocalDate()) it.apply {
+ isRead = false
+ if (notify) isNotified = false
+ }
+ })
+ }
+ }.flatMap { local.getNotes(student).toSingle(emptyList()) })
+ }
+
+ fun getNotNotifiedNotes(student: Student): Single> {
+ return local.getNotes(student).map { it.filter { note -> !note.isNotified } }.toSingle(emptyList())
+ }
+
+ fun updateNote(note: Note): Completable {
+ return Completable.fromCallable { local.updateNotes(listOf(note)) }
+ }
+
+ fun updateNotes(notes: List): Completable {
+ return Completable.fromCallable { local.updateNotes(notes) }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt
similarity index 56%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt
index d83ae667d..91717ed77 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.data.repositories.preferences
import android.content.Context
import android.content.SharedPreferences
@@ -12,31 +12,31 @@ class PreferencesRepository @Inject constructor(
val context: Context
) {
val startMenuIndex: Int
- get() = sharedPref.getString(context.getString(R.string.pref_key_start_menu), "0")?.toInt() ?: 0
+ get() = sharedPref.getString(context.getString(R.string.pref_key_start_menu), "0")?.toIntOrNull() ?: 0
val isShowPresent: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true)
+ val gradeAverageMode: String
+ get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester"
+
val isGradeExpandable: Boolean
get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false)
- val currentThemeKey: String = context.getString(R.string.pref_key_theme)
- val currentTheme: Int
- get() = sharedPref.getString(currentThemeKey, "1")?.toInt() ?: 1
+ val appThemeKey: String = context.getString(R.string.pref_key_app_theme)
+ val appTheme: String
+ get() = sharedPref.getString(appThemeKey, "light") ?: "light"
- val gradePlusModifier: Double
- get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0
+ val gradeColorTheme: String
+ get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan"
- val gradeMinusModifier: Double
- get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0
-
- val serviceEnablesKey: String = context.getString(R.string.pref_key_services_enable)
+ val serviceEnableKey: String = context.getString(R.string.pref_key_services_enable)
val isServiceEnabled: Boolean
- get() = sharedPref.getBoolean(serviceEnablesKey, true)
+ get() = sharedPref.getBoolean(serviceEnableKey, true)
val servicesIntervalKey: String = context.getString(R.string.pref_key_services_interval)
- val servicesInterval: Int
- get() = sharedPref.getString(servicesIntervalKey, "60")?.toInt() ?: 60
+ val servicesInterval: Long
+ get() = sharedPref.getString(servicesIntervalKey, "60")?.toLongOrNull() ?: 60
val servicesOnlyWifiKey: String = context.getString(R.string.pref_key_services_wifi_only)
val isServicesOnlyWifi: Boolean
@@ -45,7 +45,16 @@ class PreferencesRepository @Inject constructor(
val isNotificationsEnable: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true)
- val isShowChuckerNotificationKey: String = context.getString(R.string.pref_key_debug_chucker_notification)
- val isShowChuckerNotification: Boolean
- get() = sharedPref.getBoolean(isShowChuckerNotificationKey, false)
+ val isDebugNotificationEnableKey: String = context.getString(R.string.pref_key_notification_debug)
+ val isDebugNotificationEnable: Boolean
+ get() = sharedPref.getBoolean(isDebugNotificationEnableKey, false)
+
+ val gradePlusModifier: Double
+ get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0
+
+ val gradeMinusModifier: Double
+ get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0
+
+ val fillMessageContent: Boolean
+ get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt
new file mode 100644
index 000000000..6b8328ec2
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt
@@ -0,0 +1,25 @@
+package io.github.wulkanowy.data.repositories.recipient
+
+import io.github.wulkanowy.data.db.dao.RecipientDao
+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.reactivex.Maybe
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) {
+
+ fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Maybe> {
+ return recipientDb.load(student.studentId, role, unit.realId).filter { !it.isEmpty() }
+ }
+
+ fun saveRecipients(recipients: List) {
+ return recipientDb.insertAll(recipients)
+ }
+
+ fun deleteRecipients(recipients: List) {
+ recipientDb.deleteAll(recipients)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt
new file mode 100644
index 000000000..b726edda9
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt
@@ -0,0 +1,41 @@
+package io.github.wulkanowy.data.repositories.recipient
+
+import io.github.wulkanowy.api.Api
+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.reactivex.Single
+import javax.inject.Inject
+import javax.inject.Singleton
+import io.github.wulkanowy.api.messages.Recipient as ApiRecipient
+
+@Singleton
+class RecipientRemote @Inject constructor(private val api: Api) {
+
+ fun getRecipients(role: Int, unit: ReportingUnit): Single> {
+ return api.getRecipients(unit.realId, role)
+ .map { recipients ->
+ recipients.map { it.toRecipient() }
+ }
+ }
+
+ fun getMessageRecipients(message: Message): Single> {
+ return api.getMessageRecipients(message.messageId, message.senderId)
+ .map { recipients ->
+ recipients.map { it.toRecipient() }
+ }
+ }
+
+ private fun ApiRecipient.toRecipient(): Recipient {
+ return Recipient(
+ studentId = api.studentId,
+ realId = id,
+ realName = name,
+ name = shortName,
+ hash = hash,
+ loginId = loginId,
+ role = role,
+ unitId = reportingUnitId ?: 0
+ )
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt
new file mode 100644
index 000000000..cde75ea8b
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt
@@ -0,0 +1,53 @@
+package io.github.wulkanowy.data.repositories.recipient
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.ApiHelper
+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.utils.uniqueSubtract
+import io.reactivex.Single
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class RecipientRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: RecipientLocal,
+ private val remote: RecipientRemote,
+ private val apiHelper: ApiHelper
+) {
+
+ fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single> {
+ return Single.just(apiHelper.initApi(student))
+ .flatMap { _ ->
+ local.getRecipients(student, role, unit).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.getRecipients(role, unit)
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getRecipients(student, role, unit).toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteRecipients(old.uniqueSubtract(new))
+ local.saveRecipients(new.uniqueSubtract(old))
+ }
+ }.flatMap {
+ local.getRecipients(student, role, unit).toSingle(emptyList())
+ }
+ )
+ }
+ }
+
+ fun getMessageRecipients(student: Student, message: Message): Single> {
+ return Single.just(apiHelper.initApi(student))
+ .flatMap { ReactiveNetwork.checkInternetConnectivity(settings) }
+ .flatMap {
+ if (it) remote.getMessageRecipients(message)
+ else Single.error(UnknownHostException())
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/MessagesRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/MessagesRemote.kt
deleted file mode 100644
index bc0f3400a..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/MessagesRemote.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package io.github.wulkanowy.data.repositories.remote
-
-import io.github.wulkanowy.api.Api
-import io.github.wulkanowy.api.messages.Folder
-import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.repositories.MessagesRepository
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.reactivex.Single
-import javax.inject.Inject
-import io.github.wulkanowy.api.messages.Message as ApiMessage
-
-class MessagesRemote @Inject constructor(private val api: Api) {
-
- fun getMessages(studentId: Int, folder: MessagesRepository.MessageFolder): Single> {
- return api.getMessages(Folder.valueOf(folder.name)).map { messages ->
- messages.map {
- Message(
- studentId = studentId,
- realId = it.id,
- messageId = it.messageId,
- sender = it.sender,
- senderId = it.senderId,
- recipient = it.recipient,
- recipientId = it.recipientId,
- subject = it.subject.trim(),
- date = it.date?.toLocalDateTime(),
- folderId = it.folderId,
- unread = it.unread,
- unreadBy = it.unreadBy,
- readBy = it.readBy,
- removed = it.removed
- )
- }
- }
- }
-
- fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single {
- return api.getMessageContent(message.messageId ?: 0, message.folderId, markAsRead, message.realId ?: 0)
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt
deleted file mode 100644
index 6c6e26a9d..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package io.github.wulkanowy.data.repositories.remote
-
-import io.github.wulkanowy.api.Api
-import io.github.wulkanowy.data.db.entities.Student
-import io.reactivex.Single
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class StudentRemote @Inject constructor(private val api: Api) {
-
- fun getStudents(email: String, password: String, endpoint: String): Single> {
- return api.getPupils().map { students ->
- students.map { pupil ->
- Student(
- email = email,
- password = password,
- symbol = pupil.symbol,
- studentId = pupil.studentId,
- studentName = pupil.studentName,
- schoolSymbol = pupil.schoolSymbol,
- schoolName = pupil.description,
- endpoint = endpoint,
- loginType = pupil.loginType.name
- )
- }
- }
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt
new file mode 100644
index 000000000..b4281cbfd
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt
@@ -0,0 +1,28 @@
+package io.github.wulkanowy.data.repositories.reportingunit
+
+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.reactivex.Maybe
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: ReportingUnitDao) {
+
+ fun getReportingUnits(student: Student): Maybe> {
+ return reportingUnitDb.load(student.studentId).filter { !it.isEmpty() }
+ }
+
+ fun getReportingUnit(student: Student, unitId: Int): Maybe {
+ return reportingUnitDb.loadOne(student.studentId, unitId)
+ }
+
+ fun saveReportingUnits(reportingUnits: List) {
+ return reportingUnitDb.insertAll(reportingUnits)
+ }
+
+ fun deleteReportingUnits(reportingUnits: List) {
+ reportingUnitDb.deleteAll(reportingUnits)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt
new file mode 100644
index 000000000..feb4b0134
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt
@@ -0,0 +1,26 @@
+package io.github.wulkanowy.data.repositories.reportingunit
+
+import io.github.wulkanowy.api.Api
+import io.github.wulkanowy.data.db.entities.ReportingUnit
+import io.reactivex.Single
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ReportingUnitRemote @Inject constructor(private val api: Api) {
+
+ fun getReportingUnits(): Single> {
+ return api.getReportingUnits().map {
+ it.map { unit ->
+ ReportingUnit(
+ studentId = api.studentId,
+ realId = unit.id,
+ roles = unit.roles,
+ senderId = unit.senderId,
+ senderName = unit.senderName,
+ shortName = unit.short
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt
new file mode 100644
index 000000000..6758898e2
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt
@@ -0,0 +1,56 @@
+package io.github.wulkanowy.data.repositories.reportingunit
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.ApiHelper
+import io.github.wulkanowy.data.db.entities.ReportingUnit
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.utils.uniqueSubtract
+import io.reactivex.Maybe
+import io.reactivex.Single
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ReportingUnitRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: ReportingUnitLocal,
+ private val remote: ReportingUnitRemote,
+ private val apiHelper: ApiHelper
+) {
+
+ fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single> {
+ return Single.just(apiHelper.initApi(student))
+ .flatMap { _ ->
+ local.getReportingUnits(student).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) remote.getReportingUnits()
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getReportingUnits(student).toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteReportingUnits(old.uniqueSubtract(new))
+ local.saveReportingUnits(new.uniqueSubtract(old))
+ }
+ }.flatMap { local.getReportingUnits(student).toSingle(emptyList()) }
+ )
+ }
+ }
+
+ fun getReportingUnit(student: Student, unitId: Int): Maybe {
+ return Maybe.just(apiHelper.initApi(student))
+ .flatMap { _ ->
+ local.getReportingUnit(student, unitId)
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
+ .flatMap {
+ if (it) getReportingUnits(student, true)
+ else Single.error(UnknownHostException())
+ }.flatMapMaybe {
+ local.getReportingUnit(student, unitId)
+ }
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SemesterLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt
similarity index 55%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/SemesterLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt
index 77000478f..b9750e7d5 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SemesterLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.entities.Semester
@@ -11,17 +11,14 @@ import javax.inject.Singleton
class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
fun saveSemesters(semesters: List) {
- return semesterDb.insertAll(semesters)
+ semesterDb.insertAll(semesters)
+ }
+
+ fun deleteSemesters(semesters: List) {
+ semesterDb.deleteAll(semesters)
}
fun getSemesters(student: Student): Maybe> {
- return semesterDb.loadAll(student.studentId).filter { !it.isEmpty() }
- }
-
- fun setCurrentSemester(semester: Semester) {
- semesterDb.run {
- resetCurrent(semester.studentId)
- updateCurrent(semester.semesterId, semester.diaryId)
- }
+ return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt
similarity index 70%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/SemesterRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt
index 1cf105014..c199c16c0 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SemesterRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
@@ -17,9 +17,14 @@ class SemesterRemote @Inject constructor(private val api: Api) {
studentId = student.studentId,
diaryId = semester.diaryId,
diaryName = semester.diaryName,
+ schoolYear = semester.schoolYear,
semesterId = semester.semesterId,
semesterName = semester.semesterNumber,
- isCurrent = semester.current
+ isCurrent = semester.current,
+ start = semester.start,
+ end = semester.end,
+ classId = semester.classId,
+ unitId = semester.unitId
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt
similarity index 63%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt
index b79021064..593014032 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt
@@ -1,14 +1,14 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.data.repositories.semester
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.repositories.local.SemesterLocal
-import io.github.wulkanowy.data.repositories.remote.SemesterRemote
+import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Maybe
import io.reactivex.Single
+import timber.log.Timber
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@@ -27,10 +27,17 @@ class SemesterRepository @Inject constructor(
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getSemesters(student) else Single.error(UnknownHostException())
- }.map { newSemesters ->
- local.apply {
- saveSemesters(newSemesters)
- setCurrentSemester(newSemesters.single { it.isCurrent })
+ }.flatMap { new ->
+ val currentSemesters = new.filter { it.isCurrent }
+ if (currentSemesters.size == 1) {
+ local.getSemesters(student).toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteSemesters(old.uniqueSubtract(new))
+ local.saveSemesters(new.uniqueSubtract(old))
+ }
+ } else {
+ Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}")
+ throw IllegalArgumentException("Current semester can be only one.")
}
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
}
@@ -39,4 +46,3 @@ class SemesterRepository @Inject constructor(
return getSemesters(student, forceRefresh).map { item -> item.single { it.isCurrent } }
}
}
-
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt
similarity index 57%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt
index 0e4e9cbd0..e6d744213 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt
@@ -1,7 +1,6 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.student
import android.content.Context
-import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.security.decrypt
@@ -15,25 +14,17 @@ import javax.inject.Singleton
@Singleton
class StudentLocal @Inject constructor(
private val studentDb: StudentDao,
- private val sharedPref: SharedPrefHelper,
private val context: Context
) {
- companion object {
- const val STUDENT_SAVED_KEY: String = "is_student_saved"
- }
-
- val isStudentSaved
- get() = sharedPref.getBoolean(STUDENT_SAVED_KEY, false)
-
- fun saveStudent(student: Student): Single {
- return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) }
- .doOnSuccess { sharedPref.putBoolean(STUDENT_SAVED_KEY, true) }
+ fun saveStudents(students: List): Single> {
+ return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) }
}
fun getStudents(decryptPass: Boolean): Maybe> {
return studentDb.loadAll()
.map { list -> list.map { it.apply { if (decryptPass) password = decrypt(password) } } }
+ .filter { !it.isEmpty() }
}
fun getCurrentStudent(decryptPass: Boolean): Maybe {
@@ -44,15 +35,12 @@ class StudentLocal @Inject constructor(
return Completable.fromCallable {
studentDb.run {
resetCurrent()
- updateCurrent(student.studentId)
+ updateCurrent(student.id)
}
- }.doOnComplete { sharedPref.putBoolean(STUDENT_SAVED_KEY, true) }
+ }
}
fun logoutStudent(student: Student): Completable {
- return Completable.fromCallable {
- studentDb.delete(student)
- if (student.isCurrent) sharedPref.putBoolean(STUDENT_SAVED_KEY, false)
- }
+ return Completable.fromCallable { studentDb.delete(student) }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt
new file mode 100644
index 000000000..251d38344
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt
@@ -0,0 +1,34 @@
+package io.github.wulkanowy.data.repositories.student
+
+import io.github.wulkanowy.api.Api
+import io.github.wulkanowy.data.db.entities.Student
+import io.reactivex.Single
+import org.threeten.bp.LocalDateTime.now
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class StudentRemote @Inject constructor(private val api: Api) {
+
+ fun getStudents(email: String, password: String, endpoint: String): Single> {
+ return api.getStudents().map { students ->
+ students.map { student ->
+ Student(
+ email = email,
+ password = password,
+ symbol = student.symbol,
+ studentId = student.studentId,
+ studentName = student.studentName,
+ schoolSymbol = student.schoolSymbol,
+ schoolName = student.schoolName,
+ className = student.className,
+ classId = student.classId,
+ endpoint = endpoint,
+ loginType = student.loginType.name,
+ isCurrent = false,
+ registrationDate = now()
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt
similarity index 63%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt
index 97210da0b..5c4a60558 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt
@@ -1,12 +1,12 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.data.repositories.student
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.repositories.local.StudentLocal
-import io.github.wulkanowy.data.repositories.remote.StudentRemote
+import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.reactivex.Completable
+import io.reactivex.Maybe
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
@@ -20,20 +20,17 @@ class StudentRepository @Inject constructor(
private val apiHelper: ApiHelper
) {
- val isStudentSaved
- get() = local.isStudentSaved
+ fun isStudentSaved(): Single = local.getStudents(false).isEmpty.map { !it }
- lateinit var cachedStudents: Single>
- private set
+ fun isCurrentStudentSet(): Single = local.getCurrentStudent(false).isEmpty.map { !it }
- fun getStudents(email: String, password: String, symbol: String, endpoint: String): Single> {
- cachedStudents = ReactiveNetwork.checkInternetConnectivity(settings)
+ fun getStudents(email: String, password: String, endpoint: String, symbol: String = ""): Single> {
+ return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
apiHelper.initApi(email, password, symbol, endpoint)
if (it) remote.getStudents(email, password, endpoint)
else Single.error(UnknownHostException("No internet connection"))
- }.doOnSuccess { cachedStudents = Single.just(it) }
- return cachedStudents
+ }
}
fun getSavedStudents(decryptPass: Boolean = true): Single> {
@@ -41,11 +38,13 @@ class StudentRepository @Inject constructor(
}
fun getCurrentStudent(decryptPass: Boolean = true): Single {
- return local.getCurrentStudent(decryptPass).toSingle()
+ return local.getCurrentStudent(decryptPass)
+ .switchIfEmpty(Maybe.error(NoCurrentStudentException()))
+ .toSingle()
}
- fun saveStudent(student: Student): Single {
- return local.saveStudent(student)
+ fun saveStudents(students: List): Single> {
+ return local.saveStudents(students)
}
fun switchStudent(student: Student): Completable {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SubjectLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt
similarity index 92%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/SubjectLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt
index 7ca7c1b06..63e334019 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SubjectLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.subject
import io.github.wulkanowy.data.db.dao.SubjectDao
import io.github.wulkanowy.data.db.entities.Semester
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SubjectRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt
similarity index 93%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/SubjectRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt
index ae565d48c..88fbb196b 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SubjectRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.subject
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepostory.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt
similarity index 78%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepostory.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt
index e3e8a2a7b..0c5f386b6 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepostory.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt
@@ -1,18 +1,17 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.data.repositories.subject
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Subject
-import io.github.wulkanowy.data.repositories.local.SubjectLocal
-import io.github.wulkanowy.data.repositories.remote.SubjectRemote
+import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-class SubjectRepostory @Inject constructor(
+class SubjectRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: SubjectLocal,
private val remote: SubjectRemote
@@ -28,8 +27,8 @@ class SubjectRepostory @Inject constructor(
local.getSubjects(semester)
.toSingle(emptyList())
.doOnSuccess { old ->
- local.deleteSubjects(old - new)
- local.saveSubjects(new - old)
+ local.deleteSubjects(old.uniqueSubtract(new))
+ local.saveSubjects(new.uniqueSubtract(old))
}
}.flatMap {
local.getSubjects(semester).toSingle(emptyList())
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/TimetableLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt
similarity index 82%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/local/TimetableLocal.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt
index 63cc7c94b..e074ce2a1 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/TimetableLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.local
+package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.entities.Semester
@@ -6,14 +6,11 @@ import io.github.wulkanowy.data.db.entities.Timetable
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
import javax.inject.Inject
+import javax.inject.Singleton
+@Singleton
class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) {
- fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> {
- return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
- .filter { !it.isEmpty() }
- }
-
fun saveTimetable(timetables: List) {
timetableDb.insertAll(timetables)
}
@@ -21,4 +18,8 @@ class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao)
fun deleteTimetable(timetables: List) {
timetableDb.deleteAll(timetables)
}
+
+ fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> {
+ return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt
similarity index 85%
rename from app/src/main/java/io/github/wulkanowy/data/repositories/remote/TimetableRemote.kt
rename to app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt
index aa8949fd8..77742e7b3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/TimetableRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.data.repositories.remote
+package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
@@ -8,7 +8,9 @@ import io.github.wulkanowy.utils.toLocalDateTime
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
+import javax.inject.Singleton
+@Singleton
class TimetableRemote @Inject constructor(private val api: Api) {
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> {
@@ -24,9 +26,12 @@ class TimetableRemote @Inject constructor(private val api: Api) {
end = it.end.toLocalDateTime(),
date = it.date.toLocalDate(),
subject = it.subject,
+ subjectOld = it.subjectOld,
group = it.group,
room = it.room,
+ roomOld = it.roomOld,
teacher = it.teacher,
+ teacherOld = it.teacherOld,
info = it.info,
changes = it.changes,
canceled = it.canceled
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt
new file mode 100644
index 000000000..0e6331f91
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt
@@ -0,0 +1,49 @@
+package io.github.wulkanowy.data.repositories.timetable
+
+import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Timetable
+import io.github.wulkanowy.utils.friday
+import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.uniqueSubtract
+import io.reactivex.Single
+import org.threeten.bp.LocalDate
+import java.net.UnknownHostException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class TimetableRepository @Inject constructor(
+ private val settings: InternetObservingSettings,
+ private val local: TimetableLocal,
+ private val remote: TimetableRemote
+) {
+
+ fun getTimetable(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> {
+ return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) ->
+ local.getTimetable(semester, monday, friday).filter { !forceRefresh }
+ .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
+ if (it) remote.getTimetable(semester, monday, friday)
+ else Single.error(UnknownHostException())
+ }.flatMap { new ->
+ local.getTimetable(semester, monday, friday)
+ .toSingle(emptyList())
+ .doOnSuccess { old ->
+ local.deleteTimetable(old.uniqueSubtract(new))
+ local.saveTimetable(new.uniqueSubtract(old).map { item ->
+ item.apply {
+ old.singleOrNull { this.start == it.start }?.let {
+ return@map copy(
+ room = if (room.isEmpty()) it.room else room
+ )
+ }
+ }
+ })
+ }
+ }.flatMap {
+ local.getTimetable(semester, monday, friday).toSingle(emptyList())
+ }).map { list -> list.filter { it.date in start..end } }
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt b/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt
index 21c193e5a..3ac3a525b 100644
--- a/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt
+++ b/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt
@@ -5,6 +5,7 @@ import dagger.android.AndroidInjector
import dagger.android.support.AndroidSupportInjectionModule
import io.github.wulkanowy.WulkanowyApp
import io.github.wulkanowy.data.RepositoryModule
+import io.github.wulkanowy.services.ServicesModule
import javax.inject.Singleton
@Singleton
@@ -12,9 +13,10 @@ import javax.inject.Singleton
AndroidSupportInjectionModule::class,
AppModule::class,
RepositoryModule::class,
+ ServicesModule::class,
BuilderModule::class])
interface AppComponent : AndroidInjector {
- @Component.Builder
- abstract class Builder : AndroidInjector.Builder()
+ @Component.Factory
+ interface Factory : AndroidInjector.Factory
}
diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt
index fc2e92972..da1d8f7a8 100644
--- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt
@@ -1,16 +1,16 @@
package io.github.wulkanowy.di
+import android.appwidget.AppWidgetManager
import android.content.Context
-import com.firebase.jobdispatcher.FirebaseJobDispatcher
-import com.firebase.jobdispatcher.GooglePlayDriver
import com.google.firebase.analytics.FirebaseAnalytics
import dagger.Module
import dagger.Provides
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
+import io.github.wulkanowy.BuildConfig.DEBUG
import io.github.wulkanowy.WulkanowyApp
-import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
+import javax.inject.Named
import javax.inject.Singleton
@Module
@@ -29,9 +29,14 @@ internal class AppModule {
@Singleton
@Provides
- fun provideJobDispatcher(context: Context) = FirebaseJobDispatcher(GooglePlayDriver(context))
+ fun provideFirebaseAnalytics(context: Context) = FirebaseAnalytics.getInstance(context)
@Singleton
@Provides
- fun provideFirebaseAnalyticsHelper(context: Context) = FirebaseAnalyticsHelper(FirebaseAnalytics.getInstance(context))
+ fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context)
+
+ @Singleton
+ @Named("isDebug")
+ @Provides
+ fun provideIsDebug() = DEBUG
}
diff --git a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt
index 9f4ecb80f..a963e9a96 100644
--- a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt
@@ -3,20 +3,22 @@ package io.github.wulkanowy.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.di.scopes.PerActivity
-import io.github.wulkanowy.services.job.SyncWorker
-import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule
+import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainModule
+import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.modules.splash.SplashActivity
-import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider
+import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
+import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
+import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
@Module
internal abstract class BuilderModule {
@PerActivity
- @ContributesAndroidInjector()
+ @ContributesAndroidInjector
abstract fun bindSplashActivity(): SplashActivity
@PerActivity
@@ -28,11 +30,17 @@ internal abstract class BuilderModule {
abstract fun bindMainActivity(): MainActivity
@ContributesAndroidInjector
- abstract fun bindTimetableWidgetService(): TimetableWidgetService
+ abstract fun bindMessageSendActivity(): SendMessageActivity
+
+ @ContributesAndroidInjector
+ abstract fun bindTimetableWidgetAccountActivity(): TimetableWidgetConfigureActivity
@ContributesAndroidInjector
abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider
@ContributesAndroidInjector
- abstract fun bindSyncJob(): SyncWorker
+ abstract fun bindLuckyNumberWidgetAccountActivity(): LuckyNumberWidgetConfigureActivity
+
+ @ContributesAndroidInjector
+ abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider
}
diff --git a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt
new file mode 100644
index 000000000..84409a845
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt
@@ -0,0 +1,107 @@
+package io.github.wulkanowy.services
+
+import android.app.NotificationManager
+import android.content.Context
+import android.content.Context.NOTIFICATION_SERVICE
+import androidx.core.app.NotificationManagerCompat
+import androidx.work.WorkManager
+import com.squareup.inject.assisted.dagger2.AssistedModule
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.android.ContributesAndroidInjector
+import dagger.multibindings.IntoSet
+import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
+import io.github.wulkanowy.services.sync.works.AttendanceWork
+import io.github.wulkanowy.services.sync.works.CompletedLessonWork
+import io.github.wulkanowy.services.sync.works.ExamWork
+import io.github.wulkanowy.services.sync.works.GradeStatisticsWork
+import io.github.wulkanowy.services.sync.works.GradeSummaryWork
+import io.github.wulkanowy.services.sync.works.GradeWork
+import io.github.wulkanowy.services.sync.works.HomeworkWork
+import io.github.wulkanowy.services.sync.works.LuckyNumberWork
+import io.github.wulkanowy.services.sync.works.MessageWork
+import io.github.wulkanowy.services.sync.works.NoteWork
+import io.github.wulkanowy.services.sync.works.RecipientWork
+import io.github.wulkanowy.services.sync.works.TimetableWork
+import io.github.wulkanowy.services.sync.works.Work
+import io.github.wulkanowy.services.widgets.TimetableWidgetService
+import javax.inject.Singleton
+
+@AssistedModule
+@Module(includes = [AssistedInject_ServicesModule::class])
+abstract class ServicesModule {
+
+ @Module
+ companion object {
+
+ @JvmStatic
+ @Provides
+ fun provideWorkManager() = WorkManager.getInstance()
+
+ @JvmStatic
+ @Singleton
+ @Provides
+ fun provideNotificationManagerCompat(context: Context) = NotificationManagerCompat.from(context)
+
+ @JvmStatic
+ @Singleton
+ @Provides
+ fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+ }
+
+ @ContributesAndroidInjector
+ abstract fun bindTimetableWidgetService(): TimetableWidgetService
+
+ @Binds
+ @IntoSet
+ abstract fun provideGradeWork(work: GradeWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideNoteWork(work: NoteWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideAttendanceWork(work: AttendanceWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideGradeSummaryWork(work: GradeSummaryWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideExamWork(work: ExamWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideAttendanceSummaryWork(work: AttendanceSummaryWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideTimetableWork(work: TimetableWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideLuckyNumberWork(work: LuckyNumberWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideCompletedLessonWork(work: CompletedLessonWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideHomeworkWork(work: HomeworkWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideMessageWork(work: MessageWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideRecipientWork(work: RecipientWork): Work
+
+ @Binds
+ @IntoSet
+ abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/job/ServiceHelper.kt b/app/src/main/java/io/github/wulkanowy/services/job/ServiceHelper.kt
deleted file mode 100644
index d81b4b6d4..000000000
--- a/app/src/main/java/io/github/wulkanowy/services/job/ServiceHelper.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package io.github.wulkanowy.services.job
-
-import com.firebase.jobdispatcher.Constraint.ON_ANY_NETWORK
-import com.firebase.jobdispatcher.Constraint.ON_UNMETERED_NETWORK
-import com.firebase.jobdispatcher.FirebaseJobDispatcher
-import com.firebase.jobdispatcher.Lifetime.FOREVER
-import com.firebase.jobdispatcher.RetryStrategy.DEFAULT_EXPONENTIAL
-import com.firebase.jobdispatcher.Trigger.executionWindow
-import io.github.wulkanowy.data.repositories.PreferencesRepository
-import io.github.wulkanowy.utils.isHolidays
-import org.threeten.bp.LocalDate
-import timber.log.Timber
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class ServiceHelper @Inject constructor(
- private val prefRepository: PreferencesRepository,
- private val dispatcher: FirebaseJobDispatcher
-) {
-
- fun reloadFullSyncService() {
- startFullSyncService(true)
- }
-
- fun startFullSyncService(replaceCurrent: Boolean = false) {
- if (LocalDate.now().isHolidays || !prefRepository.isServiceEnabled) {
- Timber.d("Services disabled or it's holidays")
- return
- }
-
- dispatcher.mustSchedule(
- dispatcher.newJobBuilder()
- .setLifetime(FOREVER)
- .setRecurring(true)
- .setService(SyncWorker::class.java)
- .setTag(SyncWorker.WORK_TAG)
- .setTrigger(
- executionWindow(
- prefRepository.servicesInterval * 60,
- (prefRepository.servicesInterval + 10) * 60
- )
- )
- .setConstraints(if (prefRepository.isServicesOnlyWifi) ON_UNMETERED_NETWORK else ON_ANY_NETWORK)
- .setReplaceCurrent(replaceCurrent)
- .setRetryStrategy(DEFAULT_EXPONENTIAL)
- .build()
- )
-
- Timber.d("Services started")
- }
-
- fun stopFullSyncService() {
- dispatcher.cancel(SyncWorker.WORK_TAG)
- Timber.d("Services stopped")
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt
deleted file mode 100644
index 65fb3abcf..000000000
--- a/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt
+++ /dev/null
@@ -1,174 +0,0 @@
-package io.github.wulkanowy.services.job
-
-import com.firebase.jobdispatcher.JobParameters
-import com.firebase.jobdispatcher.SimpleJobService
-import dagger.android.AndroidInjection
-import io.github.wulkanowy.data.repositories.AttendanceRepository
-import io.github.wulkanowy.data.repositories.ExamRepository
-import io.github.wulkanowy.data.repositories.GradeRepository
-import io.github.wulkanowy.data.repositories.GradeSummaryRepository
-import io.github.wulkanowy.data.repositories.HomeworkRepository
-import io.github.wulkanowy.data.repositories.MessagesRepository
-import io.github.wulkanowy.data.repositories.MessagesRepository.MessageFolder.RECEIVED
-import io.github.wulkanowy.data.repositories.NoteRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
-import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.data.repositories.TimetableRepository
-import io.github.wulkanowy.services.notification.GradeNotification
-import io.github.wulkanowy.services.notification.MessageNotification
-import io.github.wulkanowy.services.notification.NoteNotification
-import io.github.wulkanowy.utils.friday
-import io.github.wulkanowy.utils.isHolidays
-import io.github.wulkanowy.utils.monday
-import io.reactivex.Single
-import io.reactivex.disposables.CompositeDisposable
-import org.threeten.bp.LocalDate
-import timber.log.Timber
-import javax.inject.Inject
-
-class SyncWorker : SimpleJobService() {
-
- @Inject
- lateinit var student: StudentRepository
-
- @Inject
- lateinit var semester: SemesterRepository
-
- @Inject
- lateinit var gradesDetails: GradeRepository
-
- @Inject
- lateinit var gradesSummary: GradeSummaryRepository
-
- @Inject
- lateinit var attendance: AttendanceRepository
-
- @Inject
- lateinit var exam: ExamRepository
-
- @Inject
- lateinit var timetable: TimetableRepository
-
- @Inject
- lateinit var message: MessagesRepository
-
- @Inject
- lateinit var note: NoteRepository
-
- @Inject
- lateinit var homework: HomeworkRepository
-
- @Inject
- lateinit var prefRepository: PreferencesRepository
-
- private val disposable = CompositeDisposable()
-
- companion object {
- const val WORK_TAG = "FULL_SYNC"
- }
-
- override fun onCreate() {
- super.onCreate()
- AndroidInjection.inject(this)
- }
-
- override fun onRunJob(job: JobParameters?): Int {
- Timber.d("Synchronization started")
-
- val start = LocalDate.now().monday
- val end = LocalDate.now().friday
-
- if (start.isHolidays) return RESULT_FAIL_NORETRY
-
- var error: Throwable? = null
-
- disposable.add(student.getCurrentStudent()
- .flatMap { semester.getCurrentSemester(it, true) }
- .flatMapPublisher {
- Single.merge(
- listOf(
- gradesDetails.getGrades(it, true, true),
- gradesSummary.getGradesSummary(it, true),
- attendance.getAttendance(it, start, end, true),
- exam.getExams(it, start, end, true),
- timetable.getTimetable(it, start, end, true),
- message.getMessages(it.studentId, RECEIVED, true, true),
- note.getNotes(it, true, true),
- homework.getHomework(it, LocalDate.now(), true),
- homework.getHomework(it, LocalDate.now().plusDays(1), true)
- )
- )
- }
- .subscribe({}, { error = it }))
-
- return if (null === error) {
- if (prefRepository.isNotificationsEnable) sendNotifications()
- Timber.d("Synchronization successful")
- RESULT_SUCCESS
- } else {
- Timber.e(error, "Synchronization failed")
- RESULT_FAIL_RETRY
- }
- }
-
- private fun sendNotifications() {
- sendGradeNotifications()
- sendMessageNotification()
- sendNoteNotification()
- }
-
- private fun sendGradeNotifications() {
- disposable.add(student.getCurrentStudent()
- .flatMap { semester.getCurrentSemester(it) }
- .flatMap { gradesDetails.getNewGrades(it) }
- .map { it.filter { grade -> !grade.isNotified } }
- .doOnSuccess {
- if (it.isNotEmpty()) {
- Timber.d("Found ${it.size} unread grades")
- GradeNotification(applicationContext).sendNotification(it)
- }
- }
- .map { it.map { grade -> grade.apply { isNotified = true } } }
- .flatMapCompletable { gradesDetails.updateGrades(it) }
- .subscribe({}, { Timber.e(it, "Grade notifications sending failed") }))
- }
-
- private fun sendMessageNotification() {
- disposable.add(student.getCurrentStudent()
- .flatMap { message.getNewMessages(it) }
- .map { it.filter { message -> !message.isNotified } }
- .doOnSuccess{
- if (it.isNotEmpty()) {
- Timber.d("Found ${it.size} unread messages")
- MessageNotification(applicationContext).sendNotification(it)
- }
- }
- .map { it.map { message -> message.apply { isNotified = true } } }
- .flatMapCompletable { message.updateMessages(it) }
- .subscribe({}, { Timber.e(it, "Message notifications sending failed") })
- )
- }
-
- private fun sendNoteNotification() {
- disposable.add(student.getCurrentStudent()
- .flatMap { semester.getCurrentSemester(it) }
- .flatMap { note.getNewNotes(it) }
- .map { it.filter { note -> !note.isNotified } }
- .doOnSuccess {
- if (it.isNotEmpty()) {
- Timber.d("Found ${it.size} unread notes")
- NoteNotification(applicationContext).sendNotification(it)
- }
- }
- .map { it.map { note -> note.apply { isNotified = true } } }
- .flatMapCompletable { note.updateNotes(it) }
- .subscribe({}, { Timber.e("Notifications sending failed") })
- )
- }
-
- override fun onDestroy() {
- super.onDestroy()
- disposable.clear()
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/services/notification/BaseNotification.kt b/app/src/main/java/io/github/wulkanowy/services/notification/BaseNotification.kt
deleted file mode 100644
index 945d0b153..000000000
--- a/app/src/main/java/io/github/wulkanowy/services/notification/BaseNotification.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package io.github.wulkanowy.services.notification
-
-import android.app.Notification
-import android.app.NotificationManager
-import android.content.Context
-import android.content.Context.NOTIFICATION_SERVICE
-import android.os.Build.VERSION.SDK_INT
-import android.os.Build.VERSION_CODES.O
-import androidx.core.app.NotificationCompat
-import timber.log.Timber
-import kotlin.random.Random
-
-abstract class BaseNotification(protected val context: Context) {
-
- protected val notificationManager: NotificationManager by lazy {
- context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
- }
-
- fun notify(notification: Notification) {
- notificationManager.notify(Random.nextInt(1000), notification)
- }
-
- fun notificationBuilder(channelId: String): NotificationCompat.Builder {
- if (SDK_INT >= O) createChannel(channelId)
- return NotificationCompat.Builder(context, channelId)
- }
-
- fun cancelAll() {
- notificationManager.cancelAll()
- Timber.d("Notifications canceled")
- }
-
- abstract fun createChannel(channelId: String)
-}
diff --git a/app/src/main/java/io/github/wulkanowy/services/notification/GradeNotification.kt b/app/src/main/java/io/github/wulkanowy/services/notification/GradeNotification.kt
deleted file mode 100644
index df57ec28c..000000000
--- a/app/src/main/java/io/github/wulkanowy/services/notification/GradeNotification.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package io.github.wulkanowy.services.notification
-
-import android.annotation.TargetApi
-import android.app.Notification.VISIBILITY_PUBLIC
-import android.app.NotificationChannel
-import android.app.NotificationManager.IMPORTANCE_HIGH
-import android.app.PendingIntent
-import android.app.PendingIntent.FLAG_UPDATE_CURRENT
-import android.content.Context
-import androidx.core.app.NotificationCompat
-import androidx.core.content.ContextCompat
-import io.github.wulkanowy.R
-import io.github.wulkanowy.data.db.entities.Grade
-import io.github.wulkanowy.ui.modules.main.MainActivity
-import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
-import timber.log.Timber
-
-class GradeNotification(context: Context) : BaseNotification(context) {
-
- private val channelId = "Grade_Notify"
-
- @TargetApi(26)
- override fun createChannel(channelId: String) {
- notificationManager.createNotificationChannel(NotificationChannel(
- channelId, context.getString(R.string.notify_grade_channel), IMPORTANCE_HIGH
- ).apply {
- enableLights(true)
- enableVibration(true)
- lockscreenVisibility = VISIBILITY_PUBLIC
- })
- }
-
- fun sendNotification(items: List) {
- notify(notificationBuilder(channelId)
- .setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, items.size, items.size))
- .setContentText(context.resources.getQuantityString(R.plurals.notify_grade_new_items, items.size, items.size))
- .setSmallIcon(R.drawable.ic_stat_notify_grade)
- .setAutoCancel(true)
- .setDefaults(NotificationCompat.DEFAULT_ALL)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
- .setContentIntent(
- PendingIntent.getActivity(context, 0,
- MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 0),
- FLAG_UPDATE_CURRENT
- )
- )
- .setStyle(NotificationCompat.InboxStyle().run {
- setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, items.size, items.size))
- items.forEach {
- addLine("${it.subject}: ${it.entry}")
- }
- this
- })
- .build()
- )
-
- Timber.d("Notification sent")
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/services/notification/MessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/notification/MessageNotification.kt
deleted file mode 100644
index 8733cadab..000000000
--- a/app/src/main/java/io/github/wulkanowy/services/notification/MessageNotification.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package io.github.wulkanowy.services.notification
-
-import android.annotation.TargetApi
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Context
-import androidx.core.app.NotificationCompat
-import androidx.core.content.ContextCompat
-import io.github.wulkanowy.R
-import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.ui.modules.main.MainActivity
-import timber.log.Timber
-
-class MessageNotification(context: Context) : BaseNotification(context) {
-
- private val channelId = "Message_Notify"
-
- @TargetApi(26)
- override fun createChannel(channelId: String) {
- notificationManager.createNotificationChannel(NotificationChannel(
- channelId, context.getString(R.string.notify_message_channel), NotificationManager.IMPORTANCE_HIGH
- ).apply {
- enableLights(true)
- enableVibration(true)
- lockscreenVisibility = Notification.VISIBILITY_PUBLIC
- })
- }
-
- fun sendNotification(items: List) {
- notify(notificationBuilder(channelId)
- .setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, items.size, items.size))
- .setContentText(context.resources.getQuantityString(R.plurals.notify_message_new_items, items.size, items.size))
- .setSmallIcon(R.drawable.ic_stat_notify_message)
- .setAutoCancel(true)
- .setDefaults(NotificationCompat.DEFAULT_ALL)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
- .setContentIntent(
- PendingIntent.getActivity(context, 0,
- MainActivity.getStartIntent(context).putExtra(MainActivity.EXTRA_START_MENU_INDEX, 4),
- PendingIntent.FLAG_UPDATE_CURRENT
- )
- )
- .setStyle(NotificationCompat.InboxStyle().run {
- setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, items.size, items.size))
- items.forEach {
- addLine("${it.sender}: ${it.subject}")
- }
- this
- })
- .build()
- )
-
- Timber.d("Notification sent")
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/services/notification/NoteNotification.kt b/app/src/main/java/io/github/wulkanowy/services/notification/NoteNotification.kt
deleted file mode 100644
index 33c2fdc71..000000000
--- a/app/src/main/java/io/github/wulkanowy/services/notification/NoteNotification.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package io.github.wulkanowy.services.notification
-
-import android.annotation.TargetApi
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Context
-import androidx.core.app.NotificationCompat
-import androidx.core.content.ContextCompat
-import io.github.wulkanowy.R
-import io.github.wulkanowy.data.db.entities.Note
-import io.github.wulkanowy.ui.modules.main.MainActivity
-import timber.log.Timber
-
-class NoteNotification(context: Context) : BaseNotification(context) {
-
- private val channelId = "Note_Notify"
-
- @TargetApi(26)
- override fun createChannel(channelId: String) {
- notificationManager.createNotificationChannel(NotificationChannel(
- channelId, context.getString(R.string.notify_note_channel), NotificationManager.IMPORTANCE_HIGH
- ).apply {
- enableLights(true)
- enableVibration(true)
- lockscreenVisibility = Notification.VISIBILITY_PUBLIC
- })
- }
-
- fun sendNotification(items: List) {
- notify(notificationBuilder(channelId)
- .setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, items.size, items.size))
- .setContentText(context.resources.getQuantityString(R.plurals.notify_note_new_items, items.size, items.size))
- .setSmallIcon(R.drawable.ic_stat_notify_note)
- .setAutoCancel(true)
- .setDefaults(NotificationCompat.DEFAULT_ALL)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
- .setContentIntent(
- PendingIntent.getActivity(context, 0,
- MainActivity.getStartIntent(context).putExtra(MainActivity.EXTRA_START_MENU_INDEX, 4),
- PendingIntent.FLAG_UPDATE_CURRENT
- )
- )
- .setStyle(NotificationCompat.InboxStyle().run {
- setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, items.size, items.size))
- items.forEach {
- addLine("${it.teacher}: ${it.category}")
- }
- this
- })
- .build()
- )
-
- Timber.d("Notification sent")
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt
new file mode 100644
index 000000000..c1820afb9
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt
@@ -0,0 +1,55 @@
+package io.github.wulkanowy.services.sync
+
+import android.os.Build.VERSION.SDK_INT
+import android.os.Build.VERSION_CODES.O
+import androidx.work.BackoffPolicy.EXPONENTIAL
+import androidx.work.Constraints
+import androidx.work.ExistingPeriodicWorkPolicy.KEEP
+import androidx.work.ExistingPeriodicWorkPolicy.REPLACE
+import androidx.work.NetworkType.METERED
+import androidx.work.NetworkType.UNMETERED
+import androidx.work.PeriodicWorkRequest
+import androidx.work.WorkManager
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.services.sync.channels.DebugChannel
+import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
+import io.github.wulkanowy.utils.isHolidays
+import org.threeten.bp.LocalDate.now
+import timber.log.Timber
+import java.util.concurrent.TimeUnit.MINUTES
+import javax.inject.Inject
+import javax.inject.Named
+import javax.inject.Singleton
+
+@Singleton
+class SyncManager @Inject constructor(
+ private val workManager: WorkManager,
+ private val preferencesRepository: PreferencesRepository,
+ newEntriesChannel: NewEntriesChannel,
+ debugChannel: DebugChannel,
+ @Named("isDebug") isDebug: Boolean
+) {
+
+ init {
+ if (SDK_INT >= O) newEntriesChannel.create()
+ if (SDK_INT >= O && isDebug) debugChannel.create()
+ if (now().isHolidays) stopSyncWorker()
+ Timber.i("SyncManager was initialized")
+ }
+
+ fun startSyncWorker(restart: Boolean = false) {
+ if (preferencesRepository.isServiceEnabled && !now().isHolidays) {
+ workManager.enqueueUniquePeriodicWork(SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP,
+ PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES, 10, MINUTES)
+ .setBackoffCriteria(EXPONENTIAL, 30, MINUTES)
+ .setConstraints(Constraints.Builder()
+ .setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) METERED else UNMETERED)
+ .build())
+ .build())
+ }
+ }
+
+ fun stopSyncWorker() {
+ workManager.cancelUniqueWork(SyncWorker::class.java.simpleName)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt
new file mode 100644
index 000000000..6a4c4ca6d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt
@@ -0,0 +1,81 @@
+package io.github.wulkanowy.services.sync
+
+import android.content.Context
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationCompat.BigTextStyle
+import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
+import androidx.core.app.NotificationManagerCompat
+import androidx.work.ListenableWorker
+import androidx.work.RxWorker
+import androidx.work.WorkerParameters
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
+import io.github.wulkanowy.R
+import io.github.wulkanowy.api.interceptor.FeatureDisabledException
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.data.repositories.semester.SemesterRepository
+import io.github.wulkanowy.data.repositories.student.StudentRepository
+import io.github.wulkanowy.services.sync.channels.DebugChannel
+import io.github.wulkanowy.services.sync.works.Work
+import io.github.wulkanowy.utils.getCompatColor
+import io.reactivex.Completable
+import io.reactivex.Single
+import timber.log.Timber
+import kotlin.random.Random
+
+class SyncWorker @AssistedInject constructor(
+ @Assisted appContext: Context,
+ @Assisted workerParameters: WorkerParameters,
+ private val studentRepository: StudentRepository,
+ private val semesterRepository: SemesterRepository,
+ private val works: Set<@JvmSuppressWildcards Work>,
+ private val preferencesRepository: PreferencesRepository,
+ private val notificationManager: NotificationManagerCompat
+) : RxWorker(appContext, workerParameters) {
+
+ override fun createWork(): Single {
+ Timber.i("SyncWorker is starting")
+ return studentRepository.isCurrentStudentSet()
+ .filter { true }
+ .flatMap { studentRepository.getCurrentStudent().toMaybe() }
+ .flatMapCompletable { student ->
+ semesterRepository.getCurrentSemester(student, true)
+ .flatMapCompletable { semester ->
+ Completable.mergeDelayError(works.map { work ->
+ work.create(student, semester)
+ .doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") }
+ .doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") }
+ .doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") }
+ })
+ }
+ }
+ .toSingleDefault(Result.success())
+ .onErrorReturn {
+ Timber.e(it, "There was an error during synchronization")
+ if (it is FeatureDisabledException) Result.success()
+ else Result.retry()
+ }
+ .doOnSuccess {
+ if (preferencesRepository.isDebugNotificationEnable) notify(it)
+ Timber.i("SyncWorker result: $it")
+ }
+ }
+
+ private fun notify(result: Result) {
+ notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID)
+ .setContentTitle("Debug notification")
+ .setSmallIcon(R.drawable.ic_more_settings_24dp)
+ .setAutoCancel(true)
+ .setColor(applicationContext.getCompatColor(R.color.colorPrimary))
+ .setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result"))
+ .setPriority(PRIORITY_DEFAULT)
+ .build())
+ }
+
+ @AssistedInject.Factory
+ interface Factory {
+
+ fun create(appContext: Context, workerParameters: WorkerParameters): ListenableWorker
+ }
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt
new file mode 100644
index 000000000..aadfc27f4
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt
@@ -0,0 +1,20 @@
+package io.github.wulkanowy.services.sync
+
+import android.content.Context
+import androidx.work.ListenableWorker
+import androidx.work.WorkerFactory
+import androidx.work.WorkerParameters
+import timber.log.Timber
+import javax.inject.Inject
+
+class SyncWorkerFactory @Inject constructor(private val syncWorkerFactory: SyncWorker.Factory) : WorkerFactory() {
+
+ override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): ListenableWorker? {
+ return if (workerClassName == SyncWorker::class.java.name) {
+ syncWorkerFactory.create(appContext, workerParameters)
+ } else {
+ Timber.e(IllegalArgumentException("Unknown worker class name: $workerClassName"))
+ null
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt
new file mode 100644
index 000000000..02fff5690
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt
@@ -0,0 +1,28 @@
+package io.github.wulkanowy.services.sync.channels
+
+import android.annotation.TargetApi
+import android.app.Notification.VISIBILITY_PUBLIC
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.content.Context
+import io.github.wulkanowy.R
+import javax.inject.Inject
+
+@TargetApi(26)
+class DebugChannel @Inject constructor(
+ private val notificationManager: NotificationManager,
+ private val context: Context
+) {
+
+ companion object {
+ const val CHANNEL_ID = "debug_channel"
+ }
+
+ fun create() {
+ notificationManager.createNotificationChannel(
+ NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT).apply {
+ lockscreenVisibility = VISIBILITY_PUBLIC
+ })
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt
new file mode 100644
index 000000000..8e24a2a67
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt
@@ -0,0 +1,30 @@
+package io.github.wulkanowy.services.sync.channels
+
+import android.annotation.TargetApi
+import android.app.Notification.VISIBILITY_PUBLIC
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.content.Context
+import io.github.wulkanowy.R
+import javax.inject.Inject
+
+@TargetApi(26)
+class NewEntriesChannel @Inject constructor(
+ private val notificationManager: NotificationManager,
+ private val context: Context
+) {
+
+ companion object {
+ const val CHANNEL_ID = "new_entries_channel"
+ }
+
+ fun create() {
+ notificationManager.createNotificationChannel(
+ NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_entries), IMPORTANCE_HIGH).apply {
+ enableLights(true)
+ enableVibration(true)
+ lockscreenVisibility = VISIBILITY_PUBLIC
+ })
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt
new file mode 100644
index 000000000..01978c5b6
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt
@@ -0,0 +1,17 @@
+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.repositories.attendancesummary.AttendanceSummaryRepository
+import io.reactivex.Completable
+import javax.inject.Inject
+
+class AttendanceSummaryWork @Inject constructor(
+ private val attendanceSummaryRepository: AttendanceSummaryRepository
+) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return attendanceSummaryRepository.getAttendanceSummary(semester, -1, true).ignoreElement()
+ }
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt
new file mode 100644
index 000000000..e4b55b0ea
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt
@@ -0,0 +1,18 @@
+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.repositories.attendance.AttendanceRepository
+import io.github.wulkanowy.utils.friday
+import io.github.wulkanowy.utils.monday
+import io.reactivex.Completable
+import org.threeten.bp.LocalDate.now
+import javax.inject.Inject
+
+class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return attendanceRepository.getAttendance(semester, now().monday, now().friday, true)
+ .ignoreElement()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt
new file mode 100644
index 000000000..29642ad6f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt
@@ -0,0 +1,21 @@
+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.repositories.completedlessons.CompletedLessonsRepository
+import io.github.wulkanowy.utils.friday
+import io.github.wulkanowy.utils.monday
+import io.reactivex.Completable
+import org.threeten.bp.LocalDate.now
+import javax.inject.Inject
+
+class CompletedLessonWork @Inject constructor(
+ private val completedLessonsRepository: CompletedLessonsRepository
+) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return completedLessonsRepository.getCompletedLessons(semester, now().monday, now().friday, true)
+ .ignoreElement()
+ }
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt
new file mode 100644
index 000000000..8744fcc79
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt
@@ -0,0 +1,17 @@
+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.repositories.exam.ExamRepository
+import io.github.wulkanowy.utils.friday
+import io.github.wulkanowy.utils.monday
+import io.reactivex.Completable
+import org.threeten.bp.LocalDate.now
+import javax.inject.Inject
+
+class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return examRepository.getExams(semester, now().monday, now().friday, true).ignoreElement()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt
new file mode 100644
index 000000000..1de39a95f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt
@@ -0,0 +1,16 @@
+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.repositories.gradestatistics.GradeStatisticsRepository
+import io.reactivex.Completable
+import javax.inject.Inject
+
+class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return gradeStatisticsRepository.getGradesStatistics(semester, "Wszystkie", false, true)
+ .ignoreElement()
+ }
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt
new file mode 100644
index 000000000..6de0bc5b0
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt
@@ -0,0 +1,14 @@
+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.repositories.gradessummary.GradeSummaryRepository
+import io.reactivex.Completable
+import javax.inject.Inject
+
+class GradeSummaryWork @Inject constructor(private val gradeSummaryRepository: GradeSummaryRepository) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return gradeSummaryRepository.getGradesSummary(semester, true).ignoreElement()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
new file mode 100644
index 000000000..0c70b476f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
@@ -0,0 +1,61 @@
+package io.github.wulkanowy.services.sync.works
+
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.content.Context
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationCompat.DEFAULT_ALL
+import androidx.core.app.NotificationCompat.PRIORITY_HIGH
+import androidx.core.app.NotificationManagerCompat
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Grade
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.grade.GradeRepository
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainView.MenuView
+import io.github.wulkanowy.utils.getCompatColor
+import io.reactivex.Completable
+import javax.inject.Inject
+import kotlin.random.Random
+
+class GradeWork @Inject constructor(
+ private val context: Context,
+ private val notificationManager: NotificationManagerCompat,
+ private val gradeRepository: GradeRepository,
+ private val preferencesRepository: PreferencesRepository
+) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable)
+ .flatMap { gradeRepository.getNotNotifiedGrades(semester) }
+ .flatMapCompletable {
+ if (it.isNotEmpty()) notify(it)
+ gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true })
+ }
+ }
+
+ private fun notify(grades: List) {
+ notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
+ .setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size))
+ .setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
+ .setSmallIcon(R.drawable.ic_stat_notify_grade)
+ .setAutoCancel(true)
+ .setPriority(PRIORITY_HIGH)
+ .setDefaults(DEFAULT_ALL)
+ .setColor(context.getCompatColor(R.color.colorPrimary))
+ .setContentIntent(
+ PendingIntent.getActivity(context, MenuView.GRADE.id,
+ MainActivity.getStartIntent(context, MenuView.GRADE, true), FLAG_UPDATE_CURRENT))
+ .setStyle(NotificationCompat.InboxStyle().run {
+ setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size))
+ grades.forEach { addLine("${it.subject}: ${it.entry}") }
+ this
+ })
+ .build()
+ )
+ }
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt
new file mode 100644
index 000000000..32b356c68
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt
@@ -0,0 +1,17 @@
+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.repositories.homework.HomeworkRepository
+import io.github.wulkanowy.utils.friday
+import io.github.wulkanowy.utils.monday
+import io.reactivex.Completable
+import org.threeten.bp.LocalDate.now
+import javax.inject.Inject
+
+class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return homeworkRepository.getHomework(semester, now().monday, now().friday, true).ignoreElement()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt
new file mode 100644
index 000000000..ebd536b75
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt
@@ -0,0 +1,54 @@
+package io.github.wulkanowy.services.sync.works
+
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.content.Context
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationCompat.DEFAULT_ALL
+import androidx.core.app.NotificationCompat.PRIORITY_HIGH
+import androidx.core.app.NotificationManagerCompat
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainView.MenuView
+import io.github.wulkanowy.utils.getCompatColor
+import io.reactivex.Completable
+import javax.inject.Inject
+import kotlin.random.Random
+
+class LuckyNumberWork @Inject constructor(
+ private val context: Context,
+ private val notificationManager: NotificationManagerCompat,
+ private val luckyNumberRepository: LuckyNumberRepository,
+ private val preferencesRepository: PreferencesRepository
+) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return luckyNumberRepository.getLuckyNumber(semester, true, preferencesRepository.isNotificationsEnable)
+ .flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(semester) }
+ .flatMapCompletable {
+ notify(it)
+ luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
+ }
+ }
+
+ private fun notify(luckyNumber: LuckyNumber) {
+ notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
+ .setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title))
+ .setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber))
+ .setSmallIcon(R.drawable.ic_stat_notify_lucky_number)
+ .setAutoCancel(true)
+ .setDefaults(DEFAULT_ALL)
+ .setPriority(PRIORITY_HIGH)
+ .setColor(context.getCompatColor(R.color.colorPrimary))
+ .setContentIntent(
+ PendingIntent.getActivity(context, MenuView.MESSAGE.id,
+ MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
+ .build())
+ }
+}
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
new file mode 100644
index 000000000..d5016aeed
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt
@@ -0,0 +1,61 @@
+package io.github.wulkanowy.services.sync.works
+
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.content.Context
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationCompat.DEFAULT_ALL
+import androidx.core.app.NotificationCompat.PRIORITY_HIGH
+import androidx.core.app.NotificationManagerCompat
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Message
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
+import io.github.wulkanowy.data.repositories.message.MessageRepository
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainView.MenuView
+import io.github.wulkanowy.utils.getCompatColor
+import io.reactivex.Completable
+import javax.inject.Inject
+import kotlin.random.Random
+
+class MessageWork @Inject constructor(
+ private val context: Context,
+ private val notificationManager: NotificationManagerCompat,
+ private val messageRepository: MessageRepository,
+ private val preferencesRepository: PreferencesRepository
+) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return messageRepository.getMessages(student, RECEIVED, true, preferencesRepository.isNotificationsEnable)
+ .flatMap { messageRepository.getNotNotifiedMessages(student) }
+ .flatMapCompletable {
+ if (it.isNotEmpty()) notify(it)
+ messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })
+ }
+ }
+
+ private fun notify(messages: List) {
+ notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
+ .setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size))
+ .setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size))
+ .setSmallIcon(R.drawable.ic_stat_notify_message)
+ .setAutoCancel(true)
+ .setDefaults(DEFAULT_ALL)
+ .setPriority(PRIORITY_HIGH)
+ .setColor(context.getCompatColor(R.color.colorPrimary))
+ .setContentIntent(
+ PendingIntent.getActivity(context, MenuView.MESSAGE.id,
+ MainActivity.getStartIntent(context, MenuView.MESSAGE, true), FLAG_UPDATE_CURRENT)
+ )
+ .setStyle(NotificationCompat.InboxStyle().run {
+ setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size))
+ messages.forEach { addLine("${it.sender}: ${it.subject}") }
+ this
+ })
+ .build())
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt
new file mode 100644
index 000000000..86e540c43
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt
@@ -0,0 +1,60 @@
+package io.github.wulkanowy.services.sync.works
+
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.content.Context
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationCompat.DEFAULT_ALL
+import androidx.core.app.NotificationCompat.PRIORITY_HIGH
+import androidx.core.app.NotificationManagerCompat
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Note
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.note.NoteRepository
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainView.MenuView
+import io.github.wulkanowy.utils.getCompatColor
+import io.reactivex.Completable
+import javax.inject.Inject
+import kotlin.random.Random
+
+class NoteWork @Inject constructor(
+ private val context: Context,
+ private val notificationManager: NotificationManagerCompat,
+ private val noteRepository: NoteRepository,
+ private val preferencesRepository: PreferencesRepository
+) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable)
+ .flatMap { noteRepository.getNotNotifiedNotes(student) }
+ .flatMapCompletable {
+ if (it.isNotEmpty()) notify(it)
+ noteRepository.updateNotes(it.onEach { note -> note.isNotified = true })
+ }
+ }
+
+ private fun notify(notes: List) {
+ notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
+ .setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size))
+ .setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
+ .setSmallIcon(R.drawable.ic_stat_notify_note)
+ .setAutoCancel(true)
+ .setDefaults(DEFAULT_ALL)
+ .setPriority(PRIORITY_HIGH)
+ .setColor(context.getCompatColor(R.color.colorPrimary))
+ .setContentIntent(
+ PendingIntent.getActivity(context, MenuView.NOTE.id,
+ MainActivity.getStartIntent(context, MenuView.NOTE, true), FLAG_UPDATE_CURRENT))
+ .setStyle(NotificationCompat.InboxStyle().run {
+ setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size))
+ notes.forEach { addLine("${it.teacher}: ${it.category}") }
+ this
+ })
+ .build())
+ }
+}
+
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
new file mode 100644
index 000000000..fa610dee4
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
@@ -0,0 +1,24 @@
+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.repositories.recipient.RecipientRepository
+import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository
+import io.reactivex.Completable
+import javax.inject.Inject
+
+class RecipientWork @Inject constructor(
+ private val reportingUnitRepository: ReportingUnitRepository,
+ private val recipientRepository: RecipientRepository
+) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return reportingUnitRepository.getReportingUnits(student, true)
+ .flatMapCompletable { units ->
+ Completable.mergeDelayError(units.map {
+ recipientRepository.getRecipients(student, 2, it, true).ignoreElement()
+ })
+ }
+ }
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
new file mode 100644
index 000000000..743ae0e83
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
@@ -0,0 +1,18 @@
+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.repositories.timetable.TimetableRepository
+import io.github.wulkanowy.utils.friday
+import io.github.wulkanowy.utils.monday
+import io.reactivex.Completable
+import org.threeten.bp.LocalDate.now
+import javax.inject.Inject
+
+class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work {
+
+ override fun create(student: Student, semester: Semester): Completable {
+ return timetableRepository.getTimetable(semester, now().monday, now().friday, true)
+ .ignoreElement()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt
new file mode 100644
index 000000000..1601a103b
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt
@@ -0,0 +1,11 @@
+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.reactivex.Completable
+
+interface Work {
+
+ fun create(student: Student, semester: Semester): Completable
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt
index d1e275b71..f3429457b 100644
--- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt
@@ -4,10 +4,11 @@ import android.content.Intent
import android.widget.RemoteViewsService
import dagger.android.AndroidInjection
import io.github.wulkanowy.data.db.SharedPrefHelper
-import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.data.repositories.TimetableRepository
-import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory
+import io.github.wulkanowy.data.repositories.semester.SemesterRepository
+import io.github.wulkanowy.data.repositories.student.StudentRepository
+import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
+import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory
+import io.github.wulkanowy.utils.SchedulersProvider
import javax.inject.Inject
class TimetableWidgetService : RemoteViewsService() {
@@ -24,8 +25,11 @@ class TimetableWidgetService : RemoteViewsService() {
@Inject
lateinit var sharedPref: SharedPrefHelper
+ @Inject
+ lateinit var schedulers: SchedulersProvider
+
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
AndroidInjection.inject(this)
- return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, applicationContext, intent)
+ return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, schedulers, applicationContext, intent)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
index f227dc19a..d8b53607b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
@@ -2,33 +2,59 @@ package io.github.wulkanowy.ui.base
import android.os.Bundle
import android.view.View
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
+import androidx.fragment.app.Fragment
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
-import dagger.android.support.DaggerAppCompatActivity
+import dagger.android.AndroidInjection
+import dagger.android.DispatchingAndroidInjector
+import dagger.android.support.HasSupportFragmentInjector
import io.github.wulkanowy.R
+import io.github.wulkanowy.utils.FragmentLifecycleLogger
+import javax.inject.Inject
-abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
+abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentInjector {
- protected lateinit var messageContainer: View
+ @Inject
+ lateinit var supportFragmentInjector: DispatchingAndroidInjector
+
+ @Inject
+ lateinit var fragmentLifecycleLogger: FragmentLifecycleLogger
+
+ @Inject
+ lateinit var themeManager: ThemeManager
+
+ protected var messageContainer: View? = null
public override fun onCreate(savedInstanceState: Bundle?) {
+ AndroidInjection.inject(this)
+ themeManager.applyTheme(this)
super.onCreate(savedInstanceState)
+ supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
}
override fun showError(text: String, error: Throwable) {
- Snackbar.make(messageContainer, text, LENGTH_LONG).setAction(R.string.all_details) {
- ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString())
- }.show()
+ if (messageContainer != null) {
+ Snackbar.make(messageContainer!!, text, LENGTH_LONG)
+ .setAction(R.string.all_details) {
+ ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString())
+ }
+ .show()
+ } else showMessage(text)
}
override fun showMessage(text: String) {
- Snackbar.make(messageContainer, text, LENGTH_LONG).show()
+ if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
+ else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
}
override fun onDestroy() {
super.onDestroy()
invalidateOptionsMenu()
}
+
+ override fun supportFragmentInjector() = supportFragmentInjector
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt
index 6e863c511..4beb0ac4f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt
@@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.base
import android.view.View
import com.google.android.material.snackbar.Snackbar
+import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
import dagger.android.support.DaggerFragment
import io.github.wulkanowy.R
@@ -10,16 +11,22 @@ abstract class BaseFragment : DaggerFragment(), BaseView {
protected var messageContainer: View? = null
override fun showError(text: String, error: Throwable) {
- if (messageContainer == null) (activity as? BaseActivity)?.showError(text, error)
- else messageContainer?.also {
- Snackbar.make(it, text, Snackbar.LENGTH_LONG).setAction(R.string.all_details) {
- ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
- }.show()
+ if (messageContainer != null) {
+ Snackbar.make(messageContainer!!, text, LENGTH_LONG)
+ .setAction(R.string.all_details) {
+ if (isAdded) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
+ }
+ .show()
+ } else {
+ (activity as? BaseActivity)?.showError(text, error)
}
}
override fun showMessage(text: String) {
- if (messageContainer == null) (activity as? BaseActivity)?.showMessage(text)
- else messageContainer?.also { Snackbar.make(it, text, Snackbar.LENGTH_LONG).show() }
+ if (messageContainer != null) {
+ Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
+ } else {
+ (activity as? BaseActivity)?.showMessage(text)
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt
new file mode 100644
index 000000000..fcf7e9809
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt
@@ -0,0 +1,31 @@
+package io.github.wulkanowy.ui.base
+
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentPagerAdapter
+
+class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager) {
+
+ private val pages = mutableMapOf()
+
+ var containerId = 0
+
+ fun getFragmentInstance(position: Int): Fragment? {
+ if (containerId == 0) throw IllegalArgumentException("Container id is 0")
+ return fragmentManager.findFragmentByTag("android:switcher:$containerId:$position")
+ }
+
+ fun addFragments(fragments: List) {
+ fragments.forEach { pages[it] = null }
+ }
+
+ fun addFragmentsWithTitle(pages: Map) {
+ this.pages.putAll(pages)
+ }
+
+ override fun getItem(position: Int) = pages.keys.elementAt(position)
+
+ override fun getCount() = pages.size
+
+ override fun getPageTitle(position: Int) = pages.values.elementAt(position)
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.kt
deleted file mode 100644
index 000efdcd3..000000000
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package io.github.wulkanowy.ui.base
-
-import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
-import androidx.fragment.app.FragmentStatePagerAdapter
-
-class BasePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) {
-
- val fragments = mutableMapOf()
-
- val registeredFragments = mutableMapOf()
-
- override fun getItem(position: Int) = fragments.values.elementAt(position)
-
- override fun getCount() = fragments.size
-
- override fun getPageTitle(position: Int): CharSequence? {
- return fragments.keys.elementAtOrNull(position)
- }
-
- override fun instantiateItem(container: ViewGroup, position: Int): Any {
- return super.instantiateItem(container, position).also {
- registeredFragments[position] = it as Fragment
- }
- }
-
- override fun destroyItem(container: ViewGroup, position: Int, fragment: Any) {
- registeredFragments.remove(position)
- super.destroyItem(container, position, fragment)
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt
index a8f210e67..83cbf0ef0 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.base
import android.content.res.Resources
import com.readystatesoftware.chuck.api.ChuckCollector
import io.github.wulkanowy.R
+import io.github.wulkanowy.api.interceptor.FeatureDisabledException
import io.github.wulkanowy.api.interceptor.ServiceUnavailableException
import io.github.wulkanowy.api.login.NotLoggedInException
import timber.log.Timber
@@ -26,6 +27,7 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
is SocketTimeoutException -> resources.getString(R.string.error_timeout)
is NotLoggedInException -> resources.getString(R.string.error_login_failed)
is ServiceUnavailableException -> resources.getString(R.string.error_service_unavailable)
+ is FeatureDisabledException -> resources.getString(R.string.error_feature_disabled)
else -> resources.getString(R.string.error_unknown)
}), error)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt
new file mode 100644
index 000000000..449f451be
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt
@@ -0,0 +1,33 @@
+package io.github.wulkanowy.ui.base
+
+import android.content.pm.PackageManager.GET_ACTIVITIES
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
+import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import javax.inject.Inject
+
+class ThemeManager @Inject constructor(private val preferencesRepository: PreferencesRepository) {
+
+ fun applyTheme(activity: AppCompatActivity) {
+ if (isThemeApplicable(activity)) {
+ activity.delegate.apply {
+ when (preferencesRepository.appTheme) {
+ "light" -> setLocalNightMode(MODE_NIGHT_NO)
+ "dark" -> setLocalNightMode(MODE_NIGHT_YES)
+ "black" -> {
+ setLocalNightMode(MODE_NIGHT_YES)
+ activity.setTheme(R.style.WulkanowyTheme_Black)
+ }
+ }
+ }
+ }
+ }
+
+ private fun isThemeApplicable(activity: AppCompatActivity): Boolean {
+ return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
+ .activities.singleOrNull { it.name == activity::class.java.canonicalName }?.theme
+ .let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt
index 42622b500..110729d4f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt
@@ -1,6 +1,9 @@
package io.github.wulkanowy.ui.base.session
+import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.main.MainActivity
open class BaseSessionFragment : BaseFragment(), BaseSessionView {
@@ -8,4 +11,11 @@ open class BaseSessionFragment : BaseFragment(), BaseSessionView {
override fun showExpiredDialog() {
(activity as? MainActivity)?.showExpiredDialog()
}
+
+ override fun openLoginView() {
+ activity?.also {
+ startActivity(LoginActivity.getStartIntent(it)
+ .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
+ }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt
index 17ed65371..2c9f50ea9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt
@@ -7,6 +7,9 @@ open class BaseSessionPresenter(private val errorHandler: S
override fun onAttachView(view: T) {
super.onAttachView(view)
- errorHandler.onDecryptionFail = { view.showExpiredDialog() }
+ errorHandler.apply {
+ onDecryptionFail = { view.showExpiredDialog() }
+ onNoCurrentStudent = { view.openLoginView() }
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt
index 81ce4d8f9..619c2863a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt
@@ -5,4 +5,6 @@ import io.github.wulkanowy.ui.base.BaseView
interface BaseSessionView : BaseView {
fun showExpiredDialog()
+
+ fun openLoginView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt
index f8ea6e3db..017eabbab 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt
@@ -2,17 +2,24 @@ package io.github.wulkanowy.ui.base.session
import android.content.res.Resources
import com.readystatesoftware.chuck.api.ChuckCollector
+import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.security.ScramblerException
import javax.inject.Inject
-class SessionErrorHandler @Inject constructor(resources: Resources, chuckCollector: ChuckCollector) : ErrorHandler(resources, chuckCollector) {
+open class SessionErrorHandler @Inject constructor(
+ resources: Resources,
+ chuckCollector: ChuckCollector
+) : ErrorHandler(resources, chuckCollector) {
var onDecryptionFail: () -> Unit = {}
+ var onNoCurrentStudent: () -> Unit = {}
+
override fun proceed(error: Throwable) {
when (error) {
is ScramblerException -> onDecryptionFail()
+ is NoCurrentStudentException -> onNoCurrentStudent()
else -> super.proceed(error)
}
}
@@ -20,5 +27,6 @@ class SessionErrorHandler @Inject constructor(resources: Resources, chuckCollect
override fun clear() {
super.clear()
onDecryptionFail = {}
+ onNoCurrentStudent = {}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt
index ddc4e11fa..8d07566f7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt
@@ -1,15 +1,18 @@
package io.github.wulkanowy.ui.modules.about
import android.content.Intent
+import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.aboutlibraries.LibsFragmentCompat
+import io.github.wulkanowy.BuildConfig
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.withOnExtraListener
import javax.inject.Inject
@@ -36,12 +39,13 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
.withAboutVersionShown(true)
.withAboutIconShown(true)
.withLicenseShown(true)
- .withAboutSpecial1(getString(R.string.about_source_code))
- .withAboutSpecial2(getString(R.string.about_feedback))
+ .withAboutSpecial1(getString(R.string.about_discord_invite))
+ .withAboutSpecial2(getString(R.string.about_homepage))
+ .withAboutSpecial3(getString(R.string.about_feedback))
.withFields(R.string::class.java.fields)
.withCheckCachedDetection(false)
- .withExcludedLibraries("fastadapter", "AndroidIconics", "gson",
- "Jsoup", "Retrofit", "okio", "OkHttp")
+ .withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio",
+ "Butterknife", "CircleImageView")
.withOnExtraListener { presenter.onExtraSelect(it) })
}.let {
fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it)
@@ -53,12 +57,33 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
fragmentCompat.onViewCreated(view, savedInstanceState)
}
- override fun openSourceWebView() {
- startActivity(Intent.parseUri("https://github.com/wulkanowy/wulkanowy", 0))
+ override fun openDiscordInviteView() {
+ context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
}
- override fun openIssuesWebView() {
- startActivity(Intent.parseUri("https://github.com/wulkanowy/wulkanowy/issues", 0))
+ override fun openHomepageWebView() {
+ context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage)
+ }
+
+ override fun openEmailClientView() {
+ val intent = Intent(Intent.ACTION_SENDTO).apply {
+ data = Uri.parse("mailto:")
+ putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" })
+ putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu")
+ putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """
+ Build: ${BuildConfig.VERSION_CODE}
+ SDK: ${android.os.Build.VERSION.SDK_INT}
+ Device: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL}
+ """.trimIndent())
+ }
+
+ context?.let {
+ if (intent.resolveActivity(it.packageManager) != null) {
+ startActivity(Intent.createChooser(intent, getString(R.string.about_feedback)))
+ } else {
+ it.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage)
+ }
+ }
}
override fun onDestroyView() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
index 9a512f70f..0b6db107c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
@@ -15,21 +15,28 @@ class AboutPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter(errorHandler) {
+ override fun onAttachView(view: AboutView) {
+ super.onAttachView(view)
+ Timber.i("About view was initialized")
+ }
+
fun onExtraSelect(type: Libs.SpecialButton?) {
view?.run {
when (type) {
SPECIAL1 -> {
- Timber.i("Opening github page")
- analytics.logEvent("open_page", mapOf("name" to "github"))
- openSourceWebView()
+ Timber.i("Opening discord invide page")
+ analytics.logEvent("open_page", "name" to "discord")
+ openDiscordInviteView()
}
SPECIAL2 -> {
- Timber.i("Opening issues page")
- analytics.logEvent("open_page", mapOf("name" to "issues"))
- openIssuesWebView()
+ Timber.i("Opening home page")
+ analytics.logEvent("open_page", "name" to "home")
+ openHomepageWebView()
}
SPECIAL3 -> {
- //empty for now
+ Timber.i("Opening email client")
+ analytics.logEvent("open_page", "name" to "email")
+ openEmailClientView()
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt
index a3ae2ada8..5b206ad82 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt
@@ -4,7 +4,9 @@ import io.github.wulkanowy.ui.base.BaseView
interface AboutView : BaseView {
- fun openSourceWebView()
+ fun openDiscordInviteView()
- fun openIssuesWebView()
+ fun openEmailClientView()
+
+ fun openHomepageWebView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt
index 32458f276..1eaa07c16 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt
@@ -15,7 +15,6 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.login.LoginActivity
-import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.dialog_account.*
import javax.inject.Inject
@@ -97,11 +96,8 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
}
}
- override fun recreateView() {
- activity?.also {
- startActivity(MainActivity.getStartIntent(it))
- it.finish()
- }
+ override fun recreateMainView() {
+ activity?.recreate()
}
override fun onDestroy() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt
index a564306d5..972d10a26 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt
@@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.account
+import android.annotation.SuppressLint
import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@@ -14,13 +15,14 @@ class AccountItem(val student: Student) : AbstractFlexibleItem>?): ViewHolder {
+ override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder {
return ViewHolder(view, adapter)
}
- override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, position: Int, payloads: MutableList?) {
- holder?.apply {
- accountItemName.text = student.studentName
+ @SuppressLint("SetTextI18n")
+ override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) {
+ holder.apply {
+ accountItemName.text = "${student.studentName} ${student.className}"
accountItemSchool.text = student.schoolName
accountItemImage.setBackgroundResource(if (student.isCurrent) R.drawable.ic_account_circular_border else 0)
}
@@ -38,14 +40,13 @@ class AccountItem(val student: Student) : AbstractFlexibleItem>?) : FlexibleViewHolder(view, adapter),
- LayoutContainer {
-
- override val containerView: View?
+ class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
+ override val containerView: View
get() = contentView
}
}
-
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
index e1459fe7d..a16c544a8 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
@@ -1,34 +1,41 @@
package io.github.wulkanowy.ui.modules.account
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
-import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.repositories.student.StudentRepository
+import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Single
+import timber.log.Timber
import javax.inject.Inject
class AccountPresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val studentRepository: StudentRepository,
+ private val syncManager: SyncManager,
private val schedulers: SchedulersProvider
) : BasePresenter(errorHandler) {
override fun onAttachView(view: AccountView) {
super.onAttachView(view)
view.initView()
+ Timber.i("Account dialog view was initialized")
loadData()
}
fun onAddSelected() {
+ Timber.i("Select add account")
view?.openLoginView()
}
fun onRemoveSelected() {
+ Timber.i("Select remove account")
view?.showConfirmDialog()
}
fun onLogoutConfirm() {
+ Timber.i("Attempt to logout current user ")
disposable.add(studentRepository.getCurrentStudent()
.flatMapCompletable { studentRepository.logoutStudent(it) }
.andThen(studentRepository.getSavedStudents(false))
@@ -41,31 +48,56 @@ class AccountPresenter @Inject constructor(
.doFinally { view?.dismissView() }
.subscribe({
view?.apply {
- if (it.isEmpty()) openClearLoginView()
- else recreateView()
+ if (it.isEmpty()) {
+ Timber.i("Logout result: Open login view")
+ syncManager.stopSyncWorker()
+ openClearLoginView()
+ } else {
+ Timber.i("Logout result: Switch to another student")
+ recreateMainView()
+ }
}
- }, { errorHandler.dispatch(it) }))
+ }, {
+ Timber.i("Logout result: An exception occurred")
+ errorHandler.dispatch(it)
+ }))
}
fun onItemSelected(item: AbstractFlexibleItem<*>) {
if (item is AccountItem) {
+ Timber.i("Select student item ${item.student.id}")
if (item.student.isCurrent) {
view?.dismissView()
} else {
+ Timber.i("Attempt to change a student")
disposable.add(studentRepository.switchStudent(item.student)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
- .subscribe({ view?.recreateView() }, { errorHandler.dispatch(it) }))
+ .doFinally { view?.dismissView() }
+ .subscribe({
+ Timber.i("Change a student result: Success")
+ view?.recreateMainView()
+ }, {
+ Timber.i("Change a student result: An exception occurred")
+ errorHandler.dispatch(it)
+ }))
}
}
}
private fun loadData() {
+ Timber.i("Loading account data started")
disposable.add(studentRepository.getSavedStudents(false)
.map { it.map { item -> AccountItem(item) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
- .subscribe({ view?.updateData(it) }, { errorHandler.dispatch(it) }))
+ .subscribe({
+ Timber.i("Loading account result: Success")
+ view?.updateData(it)
+ }, {
+ Timber.i("Loading account result: An exception occurred")
+ errorHandler.dispatch(it)
+ }))
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
index ca74998a1..74662cf4e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
@@ -16,6 +16,6 @@ interface AccountView : BaseView {
fun openClearLoginView()
- fun recreateView()
+ fun recreateMainView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt
index 3b60d8cb7..e5d8150d3 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt
@@ -8,6 +8,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter
+import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
@@ -66,18 +67,22 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
attendanceRecycler.run {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = attendanceAdapter
+ addItemDecoration(FlexibleItemDecoration(context)
+ .withDefaultDivider()
+ .withDrawDividerOnLastItem(false)
+ )
}
attendanceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() }
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
}
- override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
- inflater?.inflate(R.menu.action_menu_attendance, menu)
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ inflater.inflate(R.menu.action_menu_attendance, menu)
}
- override fun onOptionsItemSelected(item: MenuItem?): Boolean {
- return if (item?.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
else false
}
@@ -98,7 +103,7 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
}
override fun onFragmentReselected() {
- presenter.onViewReselected()
+ if (::presenter.isInitialized) presenter.onViewReselected()
}
override fun popView() {
@@ -113,6 +118,10 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
attendanceProgress.visibility = if (show) View.VISIBLE else View.GONE
}
+ override fun enableSwipe(enable: Boolean) {
+ attendanceSwipe.isEnabled = enable
+ }
+
override fun showContent(show: Boolean) {
attendanceRecycler.visibility = if (show) View.VISIBLE else View.GONE
}
@@ -130,7 +139,7 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
}
override fun showAttendanceDialog(lesson: Attendance) {
- AttendanceDialog.newInstance(lesson).show(fragmentManager, lesson.toString())
+ (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson))
}
override fun openSummaryView() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt
index c2698ebba..16a140cbf 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt
@@ -14,14 +14,13 @@ import kotlinx.android.synthetic.main.item_attendance.*
class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem() {
+ override fun getLayoutRes() = R.layout.item_attendance
+
override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder {
return ViewHolder(view, adapter)
}
- override fun getLayoutRes(): Int = R.layout.item_attendance
-
- override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder,
- position: Int, payloads: MutableList?) {
+ override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) {
holder.apply {
attendanceItemNumber.text = attendance.number.toString()
attendanceItemSubject.text = attendance.subject
@@ -37,16 +36,17 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem) : FlexibleViewHolder(view, adapter),
- LayoutContainer {
-
+ class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
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 9be87b441..ec3d57e34 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
@@ -2,10 +2,10 @@ package io.github.wulkanowy.ui.modules.attendance
import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
-import io.github.wulkanowy.data.repositories.AttendanceRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
-import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.data.repositories.semester.SemesterRepository
+import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
@@ -18,6 +18,7 @@ import io.github.wulkanowy.utils.toFormattedString
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
+import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
@@ -37,6 +38,7 @@ class AttendancePresenter @Inject constructor(
fun onAttachView(view: AttendanceView, date: Long?) {
super.onAttachView(view)
view.initView()
+ Timber.i("Attendance view was initialized")
loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay()))
reloadView()
}
@@ -52,10 +54,12 @@ class AttendancePresenter @Inject constructor(
}
fun onSwipeRefresh() {
+ Timber.i("Force refreshing the attendance")
loadData(currentDate, true)
}
fun onViewReselected() {
+ Timber.i("Attendance view is reselected")
view?.also { view ->
if (view.currentStackSize == 1) {
now().previousOrSameSchoolDay.also {
@@ -69,7 +73,10 @@ class AttendancePresenter @Inject constructor(
}
fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) {
- if (item is AttendanceItem) view?.showAttendanceDialog(item.attendance)
+ if (item is AttendanceItem) {
+ Timber.i("Select attendance item ${item.attendance.id}")
+ view?.showAttendanceDialog(item.attendance)
+ }
}
fun onSummarySwitchSelected(): Boolean {
@@ -78,6 +85,7 @@ class AttendancePresenter @Inject constructor(
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
+ Timber.i("Loading attendance data started")
currentDate = date
disposable.apply {
clear()
@@ -97,16 +105,19 @@ class AttendancePresenter @Inject constructor(
view?.run {
hideRefresh()
showProgress(false)
+ enableSwipe(true)
}
}
.subscribe({
+ Timber.i("Loading attendance result: Success")
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
- analytics.logEvent("load_attendance", mapOf("items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")))
+ analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))
}) {
+ Timber.i("Loading attendance result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
}
@@ -115,14 +126,16 @@ class AttendancePresenter @Inject constructor(
}
private fun reloadView() {
+ Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}")
view?.apply {
showProgress(true)
+ enableSwipe(false)
showContent(false)
showEmpty(false)
clearData()
showNextButton(!currentDate.plusDays(1).isHolidays)
showPreButton(!currentDate.minusDays(1).isHolidays)
- updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
+ updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize())
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt
index f839e89a9..ef3b874b6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt
@@ -25,6 +25,8 @@ interface AttendanceView : BaseSessionView {
fun showProgress(show: Boolean)
+ fun enableSwipe(enable: Boolean)
+
fun showContent(show: Boolean)
fun showPreButton(show: Boolean)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt
index 8f36d7468..b8d2c9528 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt
@@ -97,6 +97,10 @@ class AttendanceSummaryFragment : BaseSessionFragment(), AttendanceSummaryView,
attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE
}
+ override fun enableSwipe(enable: Boolean) {
+ attendanceSummarySwipe.isEnabled = enable
+ }
+
override fun showContent(show: Boolean) {
attendanceSummaryRecycler.visibility = if (show) VISIBLE else GONE
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt
index 3102ce116..265d6ce44 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt
@@ -23,12 +23,12 @@ class AttendanceSummaryItem(
override fun getLayoutRes() = R.layout.item_attendance_summary
- override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder {
+ override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder {
return ViewHolder(view, adapter)
}
- override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, position: Int, payloads: MutableList?) {
- holder?.apply {
+ override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) {
+ holder.apply {
attendanceSummaryMonth.text = month
attendanceSummaryPercentage.text = percentage
attendanceSummaryPresent.text = present
@@ -73,10 +73,8 @@ class AttendanceSummaryItem(
return result
}
- class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter),
- LayoutContainer {
-
- override val containerView: View?
+ class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
+ override val containerView: View
get() = contentView
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt
index 5086e80cc..09ad8980f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt
@@ -2,16 +2,17 @@ package io.github.wulkanowy.ui.modules.attendance.summary
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Subject
-import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
-import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.data.repositories.SubjectRepostory
+import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository
+import io.github.wulkanowy.data.repositories.semester.SemesterRepository
+import io.github.wulkanowy.data.repositories.student.StudentRepository
+import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.calculatePercentage
import io.github.wulkanowy.utils.getFormattedName
+import timber.log.Timber
import java.lang.String.format
import java.util.Locale.FRANCE
import java.util.concurrent.TimeUnit.MILLISECONDS
@@ -20,7 +21,7 @@ import javax.inject.Inject
class AttendanceSummaryPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val attendanceSummaryRepository: AttendanceSummaryRepository,
- private val subjectRepository: SubjectRepostory,
+ private val subjectRepository: SubjectRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val schedulers: SchedulersProvider,
@@ -35,24 +36,31 @@ class AttendanceSummaryPresenter @Inject constructor(
fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) {
super.onAttachView(view)
view.initView()
+ Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}")
loadData(subjectId ?: -1)
loadSubjects()
}
fun onSwipeRefresh() {
+ Timber.i("Force refreshing the attendance summary")
loadData(currentSubjectId, true)
}
fun onSubjectSelected(name: String) {
+ Timber.i("Select attendance summary subject $name")
view?.run {
showContent(false)
showProgress(true)
+ enableSwipe(false)
clearView()
}
- loadData(subjects.singleOrNull { it.name == name }?.realId ?: -1)
+ (subjects.singleOrNull { it.name == name }?.realId ?: -1).let {
+ if (it != currentSubjectId) loadData(it)
+ }
}
private fun loadData(subjectId: Int, forceRefresh: Boolean = false) {
+ Timber.i("Loading attendance summary data started")
currentSubjectId = subjectId
disposable.apply {
clear()
@@ -67,16 +75,19 @@ class AttendanceSummaryPresenter @Inject constructor(
view?.run {
hideRefresh()
showProgress(false)
+ enableSwipe(true)
}
}
.subscribe({
+ Timber.i("Loading attendance summary result: Success")
view?.apply {
showEmpty(it.first.isEmpty())
showContent(it.first.isNotEmpty())
updateDataSet(it.first, it.second)
}
- analytics.logEvent("load_attendance_summary", mapOf("items" to it.first.size, "force_refresh" to forceRefresh, "item_id" to subjectId))
+ analytics.logEvent("load_attendance_summary", "items" to it.first.size, "force_refresh" to forceRefresh, "item_id" to subjectId)
}) {
+ Timber.i("Loading attendance summary result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
}
@@ -85,6 +96,7 @@ class AttendanceSummaryPresenter @Inject constructor(
}
private fun loadSubjects() {
+ Timber.i("Loading attendance summary subjects started")
disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { subjectRepository.getSubjects(it) }
@@ -93,11 +105,15 @@ class AttendanceSummaryPresenter @Inject constructor(
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
+ Timber.i("Loading attendance summary subjects result: Success")
view?.run {
view?.updateSubjects(it)
showSubjects(true)
}
- }, { errorHandler.dispatch(it) })
+ }, {
+ Timber.i("Loading attendance summary subjects result: An exception occurred")
+ errorHandler.dispatch(it)
+ })
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt
index af62a2ded..e4c36db7b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt
@@ -14,6 +14,8 @@ interface AttendanceSummaryView : BaseSessionView {
fun showProgress(show: Boolean)
+ fun enableSwipe(enable: Boolean)
+
fun showEmpty(show: Boolean)
fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt
index d39688d4f..8f9576f2a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt
@@ -8,11 +8,13 @@ import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter
+import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
+import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_exam.*
@@ -55,6 +57,10 @@ class ExamFragment : BaseSessionFragment(), ExamView, MainView.MainChildView, Ma
examRecycler.run {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = examAdapter
+ addItemDecoration(FlexibleItemDecoration(context)
+ .withDefaultDivider(R.layout.item_exam)
+ .withDrawDividerOnLastItem(false)
+ )
}
examSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
examPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
@@ -82,7 +88,7 @@ class ExamFragment : BaseSessionFragment(), ExamView, MainView.MainChildView, Ma
}
override fun onFragmentReselected() {
- presenter.onViewReselected()
+ if (::presenter.isInitialized) presenter.onViewReselected()
}
override fun showEmpty(show: Boolean) {
@@ -93,6 +99,10 @@ class ExamFragment : BaseSessionFragment(), ExamView, MainView.MainChildView, Ma
examProgress.visibility = if (show) VISIBLE else GONE
}
+ override fun enableSwipe(enable: Boolean) {
+ examSwipe.isEnabled = enable
+ }
+
override fun showContent(show: Boolean) {
examRecycler.visibility = if (show) VISIBLE else GONE
}
@@ -106,7 +116,7 @@ class ExamFragment : BaseSessionFragment(), ExamView, MainView.MainChildView, Ma
}
override fun showExamDialog(exam: Exam) {
- ExamDialog.newInstance(exam).show(fragmentManager, exam.toString())
+ (activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam))
}
override fun onSaveInstanceState(outState: Bundle) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt
index 318685e93..8971b4df3 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt
@@ -18,8 +18,7 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem>, holder: ViewHolder,
- position: Int, payloads: MutableList?) {
+ override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) {
holder.run {
examItemSubject.text = exam.subject
examItemTeacher.text = exam.teacher
@@ -39,12 +38,12 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem) : FlexibleViewHolder(view, adapter),
- LayoutContainer {
-
+ class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
index 88a0eaa53..a18f7b2a4 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
@@ -3,9 +3,9 @@ package io.github.wulkanowy.ui.modules.exam
import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.db.entities.Exam
-import io.github.wulkanowy.data.repositories.ExamRepository
-import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.repositories.exam.ExamRepository
+import io.github.wulkanowy.data.repositories.semester.SemesterRepository
+import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
@@ -18,6 +18,7 @@ import io.github.wulkanowy.utils.toFormattedString
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
+import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
@@ -36,6 +37,7 @@ class ExamPresenter @Inject constructor(
fun onAttachView(view: ExamView, date: Long?) {
super.onAttachView(view)
view.initView()
+ Timber.i("Exam view was initialized")
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
reloadView()
}
@@ -51,14 +53,19 @@ class ExamPresenter @Inject constructor(
}
fun onSwipeRefresh() {
+ Timber.i("Force refreshing the exam")
loadData(currentDate, true)
}
fun onExamItemSelected(item: AbstractFlexibleItem<*>?) {
- if (item is ExamItem) view?.showExamDialog(item.exam)
+ if (item is ExamItem) {
+ Timber.i("Select exam item ${item.exam.id}")
+ view?.showExamDialog(item.exam)
+ }
}
fun onViewReselected() {
+ Timber.i("Exam view is reselected")
now().nextOrSameSchoolDay.also {
if (currentDate != it) {
loadData(it)
@@ -68,6 +75,7 @@ class ExamPresenter @Inject constructor(
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
+ Timber.i("Loading exam data started")
currentDate = date
disposable.apply {
clear()
@@ -84,16 +92,19 @@ class ExamPresenter @Inject constructor(
view?.run {
hideRefresh()
showProgress(false)
+ enableSwipe(true)
}
}
.subscribe({
+ Timber.i("Loading exam result: Success")
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
- analytics.logEvent("load_exam", mapOf("items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")))
+ analytics.logEvent("load_exam", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))
}) {
+ Timber.i("Loading exam result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
@@ -109,8 +120,10 @@ class ExamPresenter @Inject constructor(
}
private fun reloadView() {
+ Timber.i("Reload exam view with the date ${currentDate.toFormattedString()}")
view?.apply {
showProgress(true)
+ enableSwipe(false)
showContent(false)
showEmpty(false)
clearData()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt
index b6a926f3a..2ced3f2d4 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt
@@ -23,6 +23,8 @@ interface ExamView : BaseSessionView {
fun showProgress(show: Boolean)
+ fun enableSwipe(enable: Boolean)
+
fun showContent(show: Boolean)
fun showNextButton(show: Boolean)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt
new file mode 100644
index 000000000..e6f01497c
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt
@@ -0,0 +1,34 @@
+package io.github.wulkanowy.ui.modules.grade
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.ViewGroup
+import com.google.android.material.tabs.TabLayout
+
+/**
+ * @see Tabs don't fit to screen with tabmode=scrollable, Even with a Custom Tab Layout
+ */
+class CustomTabLayout : TabLayout {
+
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
+
+ constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ setMeasuredDimension(widthMeasureSpec, heightMeasureSpec)
+ val tabLayout = getChildAt(0) as ViewGroup
+ val childCount = tabLayout.childCount
+
+ if (childCount == 0) return
+
+ val tabMinWidth = context.resources.displayMetrics.widthPixels / childCount
+
+ for (i in 0 until childCount) {
+ tabLayout.getChildAt(i).minimumWidth = tabMinWidth
+ }
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
new file mode 100644
index 000000000..a76f3d5ac
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
@@ -0,0 +1,54 @@
+package io.github.wulkanowy.ui.modules.grade
+
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.grade.GradeRepository
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.utils.calcAverage
+import io.github.wulkanowy.utils.changeModifier
+import io.reactivex.Single
+import javax.inject.Inject
+
+class GradeAverageProvider @Inject constructor(
+ private val preferencesRepository: PreferencesRepository,
+ private val gradeRepository: GradeRepository
+) {
+ fun getGradeAverage(student: Student, semesters: List, selectedSemesterId: Int, forceRefresh: Boolean): Single