Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
df0a1e59cc | |||
dbbc8069b1 | |||
f84040109c | |||
baf1420193 | |||
4a36d78709 | |||
4464812651 | |||
72a35481e5 | |||
017c200115 | |||
2bf7755157 | |||
269af4b7ba | |||
7431738366 | |||
034b99c7ab | |||
74e98e4430 | |||
cbf3215dd1 | |||
c18877466f | |||
6cd6cae1e0 | |||
333f7bfa16 | |||
aa6dcaff94 | |||
f2fa04105d | |||
7d97d71066 | |||
8daea5c900 | |||
2bff468e56 | |||
f2855d598d | |||
297a2909ba | |||
935bec3f5b | |||
e71dd55066 | |||
4e3864f26f | |||
8601093725 | |||
b97b94ae29 | |||
c6c7357623 | |||
1ebc296bfe | |||
c2ab53cfeb | |||
87268b3ef6 | |||
b3cd7e8ac1 | |||
a2a18e5652 | |||
5a997dacb7 | |||
ed9458d9a5 | |||
3656d3161f | |||
d178c15d2f | |||
1f65b8465e | |||
6bb03b3be8 | |||
68b9847927 | |||
e1a83927c4 | |||
fc9981aa5d | |||
2d6610e05c | |||
316cd2f7f9 | |||
36785f019a | |||
4b78862486 | |||
cd1ceea860 | |||
20d0abba29 |
@ -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:
|
||||
|
25
.idea/codeStyles/Project.xml
generated
25
.idea/codeStyles/Project.xml
generated
@ -21,31 +21,6 @@
|
||||
<option name="CONTINUATION_INDENT_IN_ELVIS" value="false" />
|
||||
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
||||
</JetCodeStyleSettings>
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<XML>
|
||||
<option name="XML_KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
|
||||
|
@ -16,15 +16,24 @@ android {
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 28
|
||||
versionCode 27
|
||||
versionName "0.7.1"
|
||||
versionCode 33
|
||||
versionName "0.8.0"
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
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 {
|
||||
@ -76,48 +85,49 @@ play {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation('io.github.wulkanowy:api:0.7.1') { exclude module: "threetenbp" }
|
||||
implementation 'io.github.wulkanowy:api:0.8.0'
|
||||
|
||||
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.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
|
||||
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 "android.arch.work:work-rxjava2:1.0.0"
|
||||
implementation "android.arch.work:work-runtime:1.0.0"
|
||||
implementation "androidx.work:work-runtime:2.0.1"
|
||||
implementation "androidx.work:work-rxjava2:2.0.1"
|
||||
|
||||
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.3'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.3'
|
||||
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.21"
|
||||
kapt "com.google.dagger:dagger-compiler:2.21"
|
||||
kapt "com.google.dagger:dagger-android-processor:2.21"
|
||||
|
||||
implementation "androidx.room:room-runtime:2.1.0-alpha05"
|
||||
implementation "androidx.room:room-rxjava2:2.1.0-alpha05"
|
||||
kapt "androidx.room:room-compiler:2.1.0-alpha05"
|
||||
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.1.0'
|
||||
|
||||
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
|
||||
implementation 'com.github.PhilJay:MPAndroidChart:971640b29d'
|
||||
implementation 'com.ncapdevi:frag-nav:3.2.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.7"
|
||||
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.mikepenz:aboutlibraries:6.2.3"
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
|
||||
|
||||
implementation 'com.google.firebase:firebase-core:16.0.8'
|
||||
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'
|
||||
@ -128,15 +138,16 @@ dependencies {
|
||||
debugImplementation "com.amitshekhar.android:debug-db:1.0.6"
|
||||
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation "io.mockk:mockk:1.9.1"
|
||||
testImplementation "org.mockito:mockito-inline:2.25.0"
|
||||
testImplementation "io.mockk:mockk:1.9.2"
|
||||
testImplementation "org.mockito:mockito-inline:2.27.0"
|
||||
testImplementation 'org.threeten:threetenbp:1.3.8'
|
||||
|
||||
androidTestImplementation "io.mockk:mockk-android:1.9.1"
|
||||
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.25.0'
|
||||
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"
|
||||
}
|
||||
|
||||
|
@ -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"]
|
||||
)
|
||||
))
|
||||
}
|
||||
|
1325
app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json
Normal file
1325
app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json
Normal file
File diff suppressed because it is too large
Load Diff
1332
app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json
Normal file
1332
app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json
Normal file
File diff suppressed because it is too large
Load Diff
1356
app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json
Normal file
1356
app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
}
|
@ -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")
|
||||
})
|
||||
}
|
||||
}
|
@ -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")
|
||||
})
|
||||
}
|
||||
}
|
@ -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)
|
||||
@ -40,7 +41,7 @@ class AttendanceLocalTest {
|
||||
))
|
||||
|
||||
val attendance = attendanceLocal
|
||||
.getAttendance(Semester(1, 2, "", 1, 3, true, 1, 1),
|
||||
.getAttendance(Semester(1, 2, "", 1, 3, 2019, true, now(), now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
)
|
||||
|
@ -41,7 +41,7 @@ class CompletedLessonsLocalTest {
|
||||
))
|
||||
|
||||
val completed = completedLessonsLocal
|
||||
.getCompletedLessons(Semester(1, 2, "", 1, 3, true, 1, 1),
|
||||
.getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
)
|
||||
|
@ -40,7 +40,7 @@ class ExamLocalTest {
|
||||
))
|
||||
|
||||
val exams = examLocal
|
||||
.getExams(Semester(1, 2, "", 1, 3, true, 1, 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)
|
||||
)
|
||||
|
@ -10,6 +10,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)
|
||||
@ -39,8 +40,10 @@ class GradeLocalTest {
|
||||
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(1, 2, "", 2, 3, true, 1, 1))
|
||||
.getGrades(semester)
|
||||
.blockingGet()
|
||||
|
||||
assertEquals(2, grades.size)
|
||||
|
@ -10,6 +10,7 @@ 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)
|
||||
@ -39,7 +40,7 @@ class GradeStatisticsLocalTest {
|
||||
))
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(
|
||||
Semester(2, 2, "", 1, 2, true, 1, 1), false,
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
|
||||
"Matematyka"
|
||||
).blockingGet()
|
||||
assertEquals(1, stats.size)
|
||||
@ -55,7 +56,7 @@ class GradeStatisticsLocalTest {
|
||||
))
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(
|
||||
Semester(2, 2, "", 1, 2, true, 1, 1), false,
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
|
||||
"Wszystkie"
|
||||
).blockingGet()
|
||||
assertEquals(1, stats.size)
|
||||
|
@ -36,7 +36,7 @@ class LuckyNumberLocalTest {
|
||||
fun saveAndReadTest() {
|
||||
luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14))
|
||||
|
||||
val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, true, 1, 1),
|
||||
val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
LocalDate.of(2019, 1, 20)
|
||||
).blockingGet()
|
||||
|
||||
|
@ -42,7 +42,7 @@ class RecipientLocalTest {
|
||||
))
|
||||
|
||||
val recipients = recipientLocal.getRecipients(
|
||||
Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", true, LocalDateTime.now()),
|
||||
Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", "", 1, true, LocalDateTime.now()),
|
||||
2,
|
||||
ReportingUnit(1, 4, "", 0, "", emptyList())
|
||||
).blockingGet()
|
||||
|
@ -39,7 +39,7 @@ class StudentLocalTest {
|
||||
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, symbol = "", registrationDate = now()))
|
||||
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()
|
||||
|
||||
val student = studentLocal.getCurrentStudent(true).blockingGet()
|
||||
|
@ -41,7 +41,7 @@ class TimetableLocalTest {
|
||||
))
|
||||
|
||||
val exams = timetableDb.getTimetable(
|
||||
Semester(1, 2, "", 1, 1, true, 1, 1),
|
||||
Semester(1, 2, "", 1, 1, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
).blockingGet()
|
||||
|
@ -34,6 +34,7 @@
|
||||
android:name=".ui.modules.login.LoginActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/login_title"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.main.MainActivity"
|
||||
@ -45,13 +46,32 @@
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/send_message_title"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
android:noHistory="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity"
|
||||
android:noHistory="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".services.widgets.TimetableWidgetService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
|
||||
<receiver
|
||||
android:name=".ui.widgets.timetable.TimetableWidgetProvider"
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
||||
android:exported="true"
|
||||
android:label="@string/timetable_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
@ -61,6 +81,17 @@
|
||||
android:resource="@xml/provider_widget_timetable" />
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
|
||||
android:label="@string/lucky_number_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/provider_widget_lucky_number" />
|
||||
</receiver>
|
||||
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
|
@ -1,7 +1,6 @@
|
||||
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
|
||||
@ -13,20 +12,21 @@ 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.preferences.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
|
||||
|
||||
@Inject
|
||||
lateinit var workerFactory: SyncWorkerFactory
|
||||
|
||||
@ -38,25 +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<out DaggerApplication> {
|
||||
return DaggerAppComponent.builder().create(this)
|
||||
return DaggerAppComponent.factory().create(this)
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ class ApiHelper @Inject constructor(private val api: Api) {
|
||||
symbol = student.symbol
|
||||
schoolSymbol = student.schoolSymbol
|
||||
studentId = student.studentId
|
||||
classId = student.classId
|
||||
host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") }
|
||||
ssl = student.endpoint.startsWith("https")
|
||||
loginType = Api.LoginType.valueOf(student.loginType)
|
||||
@ -28,6 +29,7 @@ class ApiHelper @Inject constructor(private val api: Api) {
|
||||
this.symbol = symbol
|
||||
host = URL(endpoint).run { host + ":$port".removeSuffix(":-1") }
|
||||
ssl = endpoint.startsWith("https")
|
||||
useNewStudent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ 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
|
||||
@ -74,13 +76,13 @@ import javax.inject.Singleton
|
||||
Recipient::class
|
||||
],
|
||||
version = AppDatabase.VERSION_SCHEMA,
|
||||
exportSchema = false
|
||||
exportSchema = true
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 11
|
||||
const val VERSION_SCHEMA = 13
|
||||
|
||||
fun newInstance(context: Context): AppDatabase {
|
||||
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
|
||||
@ -97,7 +99,9 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
Migration8(),
|
||||
Migration9(),
|
||||
Migration10(),
|
||||
Migration11()
|
||||
Migration11(),
|
||||
Migration12(),
|
||||
Migration13()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
@ -6,18 +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 getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue)
|
||||
|
||||
fun delete(key: String) {
|
||||
sharedPref.edit().remove(key).apply()
|
||||
|
@ -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<Semester>)
|
||||
|
||||
@Query("SELECT * FROM Semesters WHERE student_id = :studentId")
|
||||
fun loadAll(studentId: Int): Maybe<List<Semester>>
|
||||
@Delete
|
||||
fun deleteAll(semester: List<Semester>)
|
||||
|
||||
@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<List<Semester>>
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import javax.inject.Singleton
|
||||
interface StudentDao {
|
||||
|
||||
@Insert(onConflict = ABORT)
|
||||
fun insert(student: Student): Long
|
||||
fun insertAll(student: List<Student>): List<Long>
|
||||
|
||||
@Delete
|
||||
fun delete(student: Student)
|
||||
@ -25,8 +25,8 @@ interface StudentDao {
|
||||
@Query("SELECT * FROM Students")
|
||||
fun loadAll(): Maybe<List<Student>>
|
||||
|
||||
@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()
|
||||
|
@ -4,6 +4,7 @@ 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(
|
||||
@ -17,6 +18,9 @@ data class Semester(
|
||||
@ColumnInfo(name = "diary_name")
|
||||
val diaryName: String,
|
||||
|
||||
@ColumnInfo(name = "school_year")
|
||||
val schoolYear: Int,
|
||||
|
||||
@ColumnInfo(name = "semester_id")
|
||||
val semesterId: Int,
|
||||
|
||||
@ -26,6 +30,10 @@ data class Semester(
|
||||
@ColumnInfo(name = "is_current")
|
||||
val isCurrent: Boolean,
|
||||
|
||||
val start: LocalDate,
|
||||
|
||||
val end: LocalDate,
|
||||
|
||||
@ColumnInfo(name = "class_id")
|
||||
val classId: Int,
|
||||
|
||||
|
@ -7,7 +7,7 @@ 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(
|
||||
|
||||
val endpoint: String,
|
||||
@ -32,6 +32,12 @@ data class Student(
|
||||
@ColumnInfo(name = "school_name")
|
||||
val schoolName: String,
|
||||
|
||||
@ColumnInfo(name = "class_name")
|
||||
val className: String,
|
||||
|
||||
@ColumnInfo(name = "class_id")
|
||||
val classId: Int,
|
||||
|
||||
@ColumnInfo(name = "is_current")
|
||||
val isCurrent: Boolean,
|
||||
|
||||
|
@ -8,7 +8,7 @@ class Migration11 : Migration(10, 11) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("""
|
||||
CREATE TABLE IF NOT EXISTS Grades_temp (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
is_read INTEGER NOT NULL,
|
||||
is_notified INTEGER NOT NULL,
|
||||
semester_id INTEGER NOT NULL,
|
||||
|
@ -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<Int> {
|
||||
val students = mutableListOf<Int>()
|
||||
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<Int>) {
|
||||
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)")
|
||||
}
|
||||
}
|
@ -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<Pair<Int, String>>) {
|
||||
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<Pair<Int, String>> {
|
||||
val students = mutableListOf<Pair<Int, String>>()
|
||||
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<Pair<Int, Int>> {
|
||||
val students = mutableListOf<Pair<Int, Int>>()
|
||||
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<Pair<Int, Int>>) {
|
||||
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")
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package io.github.wulkanowy.data.exceptions
|
||||
|
||||
class NoCurrentStudentException : Exception("There no set current student in database")
|
@ -24,13 +24,13 @@ class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) {
|
||||
}
|
||||
|
||||
fun getMessage(student: Student, id: Int): Maybe<Message> {
|
||||
return messagesDb.load(student.studentId, id)
|
||||
return messagesDb.load(student.id.toInt(), id)
|
||||
}
|
||||
|
||||
fun getMessages(student: Student, folder: MessageFolder): Maybe<List<Message>> {
|
||||
return when (folder) {
|
||||
TRASHED -> messagesDb.loadDeleted(student.studentId)
|
||||
else -> messagesDb.loadAll(student.studentId, folder.id)
|
||||
TRASHED -> messagesDb.loadDeleted(student.id.toInt())
|
||||
else -> messagesDb.loadAll(student.id.toInt(), folder.id)
|
||||
}.filter { it.isNotEmpty() }
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ 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
|
||||
@ -16,11 +17,11 @@ import io.github.wulkanowy.api.messages.Recipient as ApiRecipient
|
||||
@Singleton
|
||||
class MessageRemote @Inject constructor(private val api: Api) {
|
||||
|
||||
fun getMessages(studentId: Int, folder: MessageFolder): Single<List<Message>> {
|
||||
fun getMessages(student: Student, folder: MessageFolder): Single<List<Message>> {
|
||||
return api.getMessages(Folder.valueOf(folder.name)).map { messages ->
|
||||
messages.map {
|
||||
Message(
|
||||
studentId = studentId,
|
||||
studentId = student.id.toInt(),
|
||||
realId = it.id ?: 0,
|
||||
messageId = it.messageId ?: 0,
|
||||
sender = it.sender.orEmpty(),
|
||||
@ -58,4 +59,8 @@ class MessageRemote @Inject constructor(private val api: Api) {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteMessage(message: Message): Single<Boolean> {
|
||||
return api.deleteMessages(listOf(Pair(message.realId, message.folderId)))
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ 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.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -28,7 +29,7 @@ class MessageRepository @Inject constructor(
|
||||
local.getMessages(student, folder).filter { !forceRefresh }
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMap {
|
||||
if (it) remote.getMessages(student.studentId, folder)
|
||||
if (it) remote.getMessages(student, folder)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { new ->
|
||||
local.getMessages(student, folder).toSingle(emptyList())
|
||||
@ -89,4 +90,20 @@ class MessageRepository @Inject constructor(
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteMessage(message: Message): Maybe<Boolean> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,15 @@ class PreferencesRepository @Inject constructor(
|
||||
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")?.toIntOrNull() ?: 1
|
||||
val appThemeKey: String = context.getString(R.string.pref_key_app_theme)
|
||||
val appTheme: String
|
||||
get() = sharedPref.getString(appThemeKey, "light") ?: "light"
|
||||
|
||||
val gradeColorTheme: String
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan"
|
||||
@ -50,8 +53,7 @@ class PreferencesRepository @Inject constructor(
|
||||
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
|
||||
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)
|
||||
|
@ -11,17 +11,14 @@ import javax.inject.Singleton
|
||||
class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
|
||||
|
||||
fun saveSemesters(semesters: List<Semester>) {
|
||||
return semesterDb.insertAll(semesters)
|
||||
semesterDb.insertAll(semesters)
|
||||
}
|
||||
|
||||
fun deleteSemesters(semesters: List<Semester>) {
|
||||
semesterDb.deleteAll(semesters)
|
||||
}
|
||||
|
||||
fun getSemesters(student: Student): Maybe<List<Semester>> {
|
||||
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() }
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,12 @@ 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,
|
||||
start = semester.start,
|
||||
end = semester.end,
|
||||
classId = semester.classId,
|
||||
unitId = semester.unitId
|
||||
)
|
||||
|
@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -25,10 +26,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 - new)
|
||||
local.saveSemesters(new - 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()) })
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ class StudentLocal @Inject constructor(
|
||||
private val context: Context
|
||||
) {
|
||||
|
||||
fun saveStudent(student: Student): Single<Long> {
|
||||
return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) }
|
||||
fun saveStudents(students: List<Student>): Single<List<Long>> {
|
||||
return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) }
|
||||
}
|
||||
|
||||
fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {
|
||||
@ -35,7 +35,7 @@ class StudentLocal @Inject constructor(
|
||||
return Completable.fromCallable {
|
||||
studentDb.run {
|
||||
resetCurrent()
|
||||
updateCurrent(student.studentId)
|
||||
updateCurrent(student.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ class StudentRemote @Inject constructor(private val api: Api) {
|
||||
studentName = student.studentName,
|
||||
schoolSymbol = student.schoolSymbol,
|
||||
schoolName = student.schoolName,
|
||||
className = student.className,
|
||||
classId = student.classId,
|
||||
endpoint = endpoint,
|
||||
loginType = student.loginType.name,
|
||||
isCurrent = false,
|
||||
|
@ -4,6 +4,7 @@ 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.exceptions.NoCurrentStudentException
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
@ -36,12 +37,12 @@ class StudentRepository @Inject constructor(
|
||||
|
||||
fun getCurrentStudent(decryptPass: Boolean = true): Single<Student> {
|
||||
return local.getCurrentStudent(decryptPass)
|
||||
.switchIfEmpty(Maybe.error(NoSuchElementException("No current student")))
|
||||
.switchIfEmpty(Maybe.error(NoCurrentStudentException()))
|
||||
.toSingle()
|
||||
}
|
||||
|
||||
fun saveStudent(student: Student): Single<Long> {
|
||||
return local.saveStudent(student)
|
||||
fun saveStudents(students: List<Student>): Single<List<Long>> {
|
||||
return local.saveStudents(students)
|
||||
}
|
||||
|
||||
fun switchStudent(student: Student): Completable {
|
||||
|
@ -17,6 +17,6 @@ import javax.inject.Singleton
|
||||
BuilderModule::class])
|
||||
interface AppComponent : AndroidInjector<WulkanowyApp> {
|
||||
|
||||
@Component.Builder
|
||||
abstract class Builder : AndroidInjector.Builder<WulkanowyApp>()
|
||||
@Component.Factory
|
||||
interface Factory : AndroidInjector.Factory<WulkanowyApp>
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ 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
|
||||
@ -30,11 +29,11 @@ internal class AppModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideFirebaseAnalyticsHelper(context: Context) = FirebaseAnalyticsHelper(FirebaseAnalytics.getInstance(context))
|
||||
fun provideFirebaseAnalytics(context: Context) = FirebaseAnalytics.getInstance(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAppWidgetManager(context: Context) = AppWidgetManager.getInstance(context)
|
||||
fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context)
|
||||
|
||||
@Singleton
|
||||
@Named("isDebug")
|
||||
|
@ -3,14 +3,16 @@ package io.github.wulkanowy.di
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import io.github.wulkanowy.di.scopes.PerActivity
|
||||
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 {
|
||||
@ -31,8 +33,14 @@ internal abstract class BuilderModule {
|
||||
abstract fun bindMessageSendActivity(): SendMessageActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindTimetableWidgetService(): TimetableWidgetService
|
||||
abstract fun bindTimetableWidgetAccountActivity(): TimetableWidgetConfigureActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLuckyNumberWidgetAccountActivity(): LuckyNumberWidgetConfigureActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ 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
|
||||
@ -24,6 +25,7 @@ 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
|
||||
@ -48,6 +50,9 @@ abstract class ServicesModule {
|
||||
fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
}
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindTimetableWidgetService(): TimetableWidgetService
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideGradeWork(work: GradeWork): Work
|
||||
|
@ -40,7 +40,7 @@ class SyncManager @Inject constructor(
|
||||
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)
|
||||
PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES, 10, MINUTES)
|
||||
.setBackoffCriteria(EXPONENTIAL, 30, MINUTES)
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) METERED else UNMETERED)
|
||||
|
@ -11,6 +11,7 @@ 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
|
||||
@ -33,19 +34,31 @@ class SyncWorker @AssistedInject constructor(
|
||||
) : RxWorker(appContext, workerParameters) {
|
||||
|
||||
override fun createWork(): Single<Result> {
|
||||
return studentRepository.getCurrentStudent()
|
||||
Timber.i("SyncWorker is starting")
|
||||
return studentRepository.isStudentSaved()
|
||||
.filter { true }
|
||||
.flatMap { studentRepository.getCurrentStudent().toMaybe() }
|
||||
.flatMapCompletable { student ->
|
||||
semesterRepository.getCurrentSemester(student, true)
|
||||
.flatMapCompletable { semester ->
|
||||
Completable.mergeDelayError(works.map { it.create(student, 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")
|
||||
Result.retry()
|
||||
if (it is FeatureDisabledException) Result.success()
|
||||
else Result.retry()
|
||||
}
|
||||
.doOnSuccess {
|
||||
if (preferencesRepository.isDebugNotificationEnable) notify(it)
|
||||
Timber.i("SyncWorker result: $it")
|
||||
}
|
||||
.doOnSuccess { if (preferencesRepository.isDebugNotificationEnable) notify(it) }
|
||||
}
|
||||
|
||||
private fun notify(result: Result) {
|
||||
|
@ -15,7 +15,8 @@ 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.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -48,7 +49,7 @@ class GradeWork @Inject constructor(
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 0), FLAG_UPDATE_CURRENT))
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU, MainView.MenuView.GRADE), 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}") }
|
||||
|
@ -15,7 +15,8 @@ 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.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -47,9 +48,9 @@ class LuckyNumberWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
PendingIntent.getActivity(context, MainView.MenuView.MESSAGE.id,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU, MainView.MenuView.LUCKY_NUMBER)
|
||||
, FLAG_UPDATE_CURRENT))
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ 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.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -48,8 +49,8 @@ class MessageWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getActivity(context, MainView.MenuView.MESSAGE.id, MainActivity.getStartIntent(context)
|
||||
.putExtra(EXTRA_START_MENU, MainView.MenuView.MESSAGE), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
.setStyle(NotificationCompat.InboxStyle().run {
|
||||
setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size))
|
||||
|
@ -15,7 +15,8 @@ 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.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -47,9 +48,9 @@ class NoteWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
PendingIntent.getActivity(context, MainView.MenuView.NOTE.id,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU, MainView.MenuView.NOTE)
|
||||
, 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}") }
|
||||
|
@ -13,10 +13,10 @@ class RecipientWork @Inject constructor(
|
||||
) : Work {
|
||||
|
||||
override fun create(student: Student, semester: Semester): Completable {
|
||||
return reportingUnitRepository.getReportingUnits(student)
|
||||
return reportingUnitRepository.getReportingUnits(student, true)
|
||||
.flatMapCompletable { units ->
|
||||
Completable.mergeDelayError(units.map {
|
||||
recipientRepository.getRecipients(student, 2, it).ignoreElement()
|
||||
recipientRepository.getRecipients(student, 2, it, true).ignoreElement()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
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.widgets.timetable.TimetableWidgetFactory
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -2,18 +2,36 @@ package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
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 {
|
||||
|
||||
@Inject
|
||||
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
|
||||
|
||||
@Inject
|
||||
lateinit var fragmentLifecycleLogger: FragmentLifecycleLogger
|
||||
|
||||
@Inject
|
||||
lateinit var themeManager: ThemeManager
|
||||
|
||||
protected lateinit var messageContainer: View
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
themeManager.applyTheme(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
}
|
||||
|
||||
@ -31,4 +49,6 @@ abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
|
||||
super.onDestroy()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun supportFragmentInjector() = supportFragmentInjector
|
||||
}
|
||||
|
@ -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.localClassName }?.theme
|
||||
.let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar }
|
||||
}
|
||||
}
|
@ -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) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ open class BaseSessionPresenter<T : BaseSessionView>(private val errorHandler: S
|
||||
|
||||
override fun onAttachView(view: T) {
|
||||
super.onAttachView(view)
|
||||
errorHandler.onDecryptionFail = { view.showExpiredDialog() }
|
||||
errorHandler.apply {
|
||||
onDecryptionFail = { view.showExpiredDialog() }
|
||||
onNoCurrentStudent = { view.openLoginView() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,6 @@ import io.github.wulkanowy.ui.base.BaseView
|
||||
interface BaseSessionView : BaseView {
|
||||
|
||||
fun showExpiredDialog()
|
||||
|
||||
fun openLoginView()
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ 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
|
||||
@ -13,9 +14,12 @@ open class SessionErrorHandler @Inject constructor(
|
||||
|
||||
var onDecryptionFail: () -> Unit = {}
|
||||
|
||||
var onNoCurrentStudent: () -> Unit = {}
|
||||
|
||||
override fun proceed(error: Throwable) {
|
||||
when (error) {
|
||||
is ScramblerException -> onDecryptionFail()
|
||||
is NoCurrentStudentException -> onNoCurrentStudent()
|
||||
else -> super.proceed(error)
|
||||
}
|
||||
}
|
||||
@ -23,5 +27,6 @@ open class SessionErrorHandler @Inject constructor(
|
||||
override fun clear() {
|
||||
super.clear()
|
||||
onDecryptionFail = {}
|
||||
onNoCurrentStudent = {}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,8 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
.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)
|
||||
@ -68,7 +69,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
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" + """
|
||||
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}
|
||||
|
@ -17,7 +17,7 @@ class AboutPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: AboutView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("About view is attached")
|
||||
Timber.i("About view was initialized")
|
||||
}
|
||||
|
||||
fun onExtraSelect(type: Libs.SpecialButton?) {
|
||||
|
@ -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<AccountItem.ViewH
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_account
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
|
||||
holder?.apply {
|
||||
accountItemName.text = student.studentName
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
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<AccountItem.ViewH
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return student.hashCode()
|
||||
var result = student.hashCode()
|
||||
result = 31 * result + student.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,8 @@ class AccountPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: AccountView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Account dialog is attached")
|
||||
view.initView()
|
||||
Timber.i("Account dialog view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -77,12 +77,12 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -14,14 +14,13 @@ import kotlinx.android.synthetic.main.item_attendance.*
|
||||
|
||||
class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_attendance
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun getLayoutRes(): Int = R.layout.item_attendance
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
|
||||
position: Int, payloads: MutableList<Any>?) {
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
attendanceItemNumber.text = attendance.number.toString()
|
||||
attendanceItemSubject.text = attendance.subject
|
||||
@ -37,16 +36,17 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
|
||||
other as AttendanceItem
|
||||
|
||||
if (attendance != other.attendance) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return attendance.hashCode()
|
||||
var result = attendance.hashCode()
|
||||
result = 31 * result + attendance.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
|
@ -37,8 +37,8 @@ class AttendancePresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: AttendanceView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Attendance view is attached")
|
||||
view.initView()
|
||||
Timber.i("Attendance view was initialized")
|
||||
loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
@ -135,7 +135,7 @@ class AttendancePresenter @Inject constructor(
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,12 +23,12 @@ class AttendanceSummaryItem(
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_attendance_summary
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
|
||||
holder?.apply {
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
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<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ class AttendanceSummaryPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Attendance summary view is attached with subject id ${subjectId ?: -1}")
|
||||
view.initView()
|
||||
Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}")
|
||||
loadData(subjectId ?: -1)
|
||||
loadSubjects()
|
||||
}
|
||||
|
@ -18,8 +18,7 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<Exa
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
|
||||
position: Int, payloads: MutableList<Any>?) {
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
examItemSubject.text = exam.subject
|
||||
examItemTeacher.text = exam.teacher
|
||||
@ -39,12 +38,12 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<Exa
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return exam.hashCode()
|
||||
var result = exam.hashCode()
|
||||
result = 31 * result + exam.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ class ExamPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: ExamView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Exam view is attached")
|
||||
view.initView()
|
||||
Timber.i("Exam view was initialized")
|
||||
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -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<Semester>, selectedSemesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
return when (preferencesRepository.gradeAverageMode) {
|
||||
"all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh)
|
||||
"only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh)
|
||||
else -> throw IllegalArgumentException("Incorrect grade average mode: ${preferencesRepository.gradeAverageMode} ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
val plusModifier = preferencesRepository.gradePlusModifier
|
||||
val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
return gradeRepository.getGrades(student, selectedSemester, forceRefresh)
|
||||
.flatMap { firstGrades ->
|
||||
if (selectedSemester == firstSemester) Single.just(firstGrades)
|
||||
else gradeRepository.getGrades(student, firstSemester)
|
||||
.map { secondGrades -> secondGrades + firstGrades }
|
||||
}.map { grades ->
|
||||
grades.map { it.changeModifier(plusModifier, minusModifier) }
|
||||
.groupBy { it.subject }
|
||||
.mapValues { it.value.calcAverage() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOnlyOneSemesterAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val plusModifier = preferencesRepository.gradePlusModifier
|
||||
val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
return gradeRepository.getGrades(student, selectedSemester, forceRefresh)
|
||||
.map { grades ->
|
||||
grades.map { it.changeModifier(plusModifier, minusModifier) }
|
||||
.groupBy { it.subject }
|
||||
.mapValues { it.value.calcAverage() }
|
||||
}
|
||||
}
|
||||
}
|
@ -57,9 +57,9 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
|
||||
presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_grade, menu)
|
||||
semesterSwitchMenu = menu?.findItem(R.id.gradeMenuSemester)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_grade, menu)
|
||||
semesterSwitchMenu = menu.findItem(R.id.gradeMenuSemester)
|
||||
presenter.onCreateMenu()
|
||||
}
|
||||
|
||||
@ -82,8 +82,8 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
|
||||
gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,7 @@ 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.reactivex.Completable
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import javax.inject.Inject
|
||||
|
||||
class GradePresenter @Inject constructor(
|
||||
@ -29,16 +27,13 @@ class GradePresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: GradeView, savedIndex: Int?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Grade view is attached")
|
||||
disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread)
|
||||
.subscribe {
|
||||
selectedIndex = savedIndex ?: 0
|
||||
view.run {
|
||||
initView()
|
||||
enableSwipe(false)
|
||||
}
|
||||
loadData()
|
||||
})
|
||||
selectedIndex = savedIndex ?: 0
|
||||
view.run {
|
||||
initView()
|
||||
enableSwipe(false)
|
||||
}
|
||||
Timber.i("Grade view was initialized with $selectedIndex index")
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onCreateMenu() {
|
||||
@ -82,7 +77,7 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onPageSelected(index: Int) {
|
||||
loadChild(index)
|
||||
if (semesters.isNotEmpty()) loadChild(index)
|
||||
}
|
||||
|
||||
fun onSwipeRefresh() {
|
||||
|
@ -67,8 +67,8 @@ class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_grade_details, menu)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_grade_details, menu)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
@ -88,8 +88,8 @@ class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.
|
||||
gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,7 @@ class GradeDetailsItem(
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
|
||||
position: Int, payloads: MutableList<Any>?
|
||||
) {
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
gradeItemValue.run {
|
||||
text = grade.entry
|
||||
@ -70,9 +67,7 @@ class GradeDetailsItem(
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
|
@ -8,10 +8,9 @@ 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.ui.modules.grade.GradeAverageProvider
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import io.github.wulkanowy.utils.getBackgroundColor
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
@ -23,6 +22,7 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
private val studentRepository: StudentRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val averageProvider: GradeAverageProvider,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BaseSessionPresenter<GradeDetailsView>(errorHandler) {
|
||||
|
||||
@ -109,11 +109,16 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
|
||||
Timber.i("Loading grade details data started")
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } }
|
||||
.flatMap { gradeRepository.getGrades(it.second, it.first.first { item -> item.semesterId == semesterId }, forceRefresh) }
|
||||
.map { it.sortedByDescending { grade -> grade.date } }
|
||||
.map { it.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } }
|
||||
.map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } }
|
||||
.flatMap { (student, semesters) ->
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
|
||||
.flatMap { averages ->
|
||||
gradeRepository.getGrades(student, semesters.first { semester -> semester.semesterId == semesterId })
|
||||
.map { it.sortedByDescending { grade -> grade.date } }
|
||||
.map { it.groupBy { grade -> grade.subject }.toSortedMap() }
|
||||
.map { createGradeItems(it, averages) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
@ -139,32 +144,36 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
})
|
||||
}
|
||||
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>): List<GradeDetailsHeader> {
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>, averages: Map<String, Double>): List<GradeDetailsHeader> {
|
||||
val isGradeExpandable = preferencesRepository.isGradeExpandable
|
||||
val gradeColorTheme = preferencesRepository.gradeColorTheme
|
||||
|
||||
val noDescriptionString = view?.noDescriptionString.orEmpty()
|
||||
val weightString = view?.weightString.orEmpty()
|
||||
|
||||
return items.map {
|
||||
it.value.calcAverage().let { average ->
|
||||
GradeDetailsHeader(
|
||||
subject = it.key,
|
||||
average = formatAverage(average),
|
||||
number = view?.getGradeNumberString(it.value.size).orEmpty(),
|
||||
newGrades = it.value.filter { grade -> !grade.isRead }.size,
|
||||
isExpandable = preferencesRepository.isGradeExpandable
|
||||
).apply {
|
||||
subItems = it.value.map { item ->
|
||||
GradeDetailsItem(
|
||||
grade = item,
|
||||
valueBgColor = item.getBackgroundColor(preferencesRepository.gradeColorTheme),
|
||||
weightString = view?.weightString.orEmpty(),
|
||||
noDescriptionString = view?.noDescriptionString.orEmpty()
|
||||
)
|
||||
}
|
||||
GradeDetailsHeader(
|
||||
subject = it.key,
|
||||
average = formatAverage(averages[it.key]),
|
||||
number = view?.getGradeNumberString(it.value.size).orEmpty(),
|
||||
newGrades = it.value.filter { grade -> !grade.isRead }.size,
|
||||
isExpandable = isGradeExpandable
|
||||
).apply {
|
||||
subItems = it.value.map { item ->
|
||||
GradeDetailsItem(
|
||||
grade = item,
|
||||
valueBgColor = item.getBackgroundColor(gradeColorTheme),
|
||||
weightString = weightString,
|
||||
noDescriptionString = noDescriptionString
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatAverage(average: Double): String {
|
||||
private fun formatAverage(average: Double?): String {
|
||||
return view?.run {
|
||||
if (average == 0.0) emptyAverageString
|
||||
if (average == null || average == .0) emptyAverageString
|
||||
else averageString.format(average)
|
||||
}.orEmpty()
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onSubjectSelected(name: String) {
|
||||
Timber.i("Select attendance stats subject $name")
|
||||
Timber.i("Select grade stats subject $name")
|
||||
view?.run {
|
||||
showContent(false)
|
||||
showProgress(true)
|
||||
@ -77,7 +77,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onTypeChange(isSemester: Boolean) {
|
||||
Timber.i("Select attendance stats semester: $isSemester")
|
||||
Timber.i("Select grade stats semester: $isSemester")
|
||||
disposable.clear()
|
||||
view?.run {
|
||||
showContent(false)
|
||||
|
@ -18,15 +18,12 @@ class GradeSummaryItem(
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_grade_summary
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?,
|
||||
position: Int, payloads: MutableList<Any>?
|
||||
) {
|
||||
holder?.run {
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
gradeSummaryItemTitle.text = title
|
||||
gradeSummaryItemAverage.text = average
|
||||
gradeSummaryItemPredicted.text = predictedGrade
|
||||
@ -56,10 +53,8 @@ class GradeSummaryItem(
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.summary
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
|
||||
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.ui.modules.grade.GradeAverageProvider
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import timber.log.Timber
|
||||
import java.lang.String.format
|
||||
import java.util.Locale.FRANCE
|
||||
@ -20,10 +18,9 @@ import javax.inject.Inject
|
||||
class GradeSummaryPresenter @Inject constructor(
|
||||
private val errorHandler: SessionErrorHandler,
|
||||
private val gradeSummaryRepository: GradeSummaryRepository,
|
||||
private val gradeRepository: GradeRepository,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val averageProvider: GradeAverageProvider,
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BaseSessionPresenter<GradeSummaryView>(errorHandler) {
|
||||
@ -36,25 +33,13 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
|
||||
Timber.i("Loading grade summary data started")
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } }
|
||||
.map { pair -> pair.first.first { it.semesterId == semesterId } to pair.second }
|
||||
.flatMap {
|
||||
gradeSummaryRepository.getGradesSummary(it.first, forceRefresh)
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } }
|
||||
.flatMap { (student, semesters) ->
|
||||
gradeSummaryRepository.getGradesSummary(semesters.first { it.semesterId == semesterId }, forceRefresh)
|
||||
.map { it.sortedBy { subject -> subject.subject } }
|
||||
.flatMap { gradesSummary ->
|
||||
gradeRepository.getGrades(it.second, it.first, forceRefresh)
|
||||
.map { grades ->
|
||||
grades.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) }
|
||||
.groupBy { grade -> grade.subject }
|
||||
.mapValues { entry -> entry.value.calcAverage() }
|
||||
.filterValues { value -> value != 0.0 }
|
||||
.let { averages ->
|
||||
createGradeSummaryItems(gradesSummary, averages) to
|
||||
GradeSummaryScrollableHeader(
|
||||
formatAverage(gradesSummary.calcAverage()),
|
||||
formatAverage(averages.values.average())
|
||||
)
|
||||
}
|
||||
}
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
|
||||
.map { averages -> createGradeSummaryItemsAndHeader(gradesSummary, averages) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
@ -66,14 +51,14 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
enableSwipe(true)
|
||||
notifyParentDataLoaded(semesterId)
|
||||
}
|
||||
}.subscribe({
|
||||
}.subscribe({ (gradeSummaryItems, gradeSummaryHeader) ->
|
||||
Timber.i("Loading grade summary result: Success")
|
||||
view?.run {
|
||||
showEmpty(it.first.isEmpty())
|
||||
showContent(it.first.isNotEmpty())
|
||||
updateData(it.first, it.second)
|
||||
showEmpty(gradeSummaryItems.isEmpty())
|
||||
showContent(gradeSummaryItems.isNotEmpty())
|
||||
updateData(gradeSummaryItems, gradeSummaryHeader)
|
||||
}
|
||||
analytics.logEvent("load_grade_summary", "items" to it.first.size, "force_refresh" to forceRefresh)
|
||||
analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh)
|
||||
}) {
|
||||
Timber.i("Loading grade summary result: An exception occurred")
|
||||
view?.run { showEmpty(isViewEmpty) }
|
||||
@ -104,16 +89,24 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
disposable.clear()
|
||||
}
|
||||
|
||||
private fun createGradeSummaryItems(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
|
||||
: List<GradeSummaryItem> {
|
||||
return gradesSummary.filter { !checkEmpty(it, averages) }.map { it ->
|
||||
GradeSummaryItem(
|
||||
title = it.subject,
|
||||
average = formatAverage(averages.getOrElse(it.subject) { 0.0 }, ""),
|
||||
predictedGrade = it.predictedGrade,
|
||||
finalGrade = it.finalGrade
|
||||
)
|
||||
}
|
||||
private fun createGradeSummaryItemsAndHeader(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
|
||||
: Pair<List<GradeSummaryItem>, GradeSummaryScrollableHeader> {
|
||||
return averages.filterValues { value -> value != 0.0 }
|
||||
.let { filteredAverages ->
|
||||
gradesSummary.filter { !checkEmpty(it, filteredAverages) }
|
||||
.map {
|
||||
GradeSummaryItem(
|
||||
title = it.subject,
|
||||
average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""),
|
||||
predictedGrade = it.predictedGrade,
|
||||
finalGrade = it.finalGrade
|
||||
)
|
||||
}.let {
|
||||
it to GradeSummaryScrollableHeader(
|
||||
formatAverage(gradesSummary.calcAverage()),
|
||||
formatAverage(filteredAverages.values.average()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkEmpty(gradeSummary: GradeSummary, averages: Map<String, Double>): Boolean {
|
||||
|
@ -10,9 +10,8 @@ import io.github.wulkanowy.data.db.entities.Homework
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_homework.*
|
||||
|
||||
class HomeworkItem(
|
||||
header: HomeworkHeader, val homework: Homework
|
||||
) : AbstractSectionableItem<HomeworkItem.ViewHolder, HomeworkHeader>(header) {
|
||||
class HomeworkItem(header: HomeworkHeader, val homework: Homework) :
|
||||
AbstractSectionableItem<HomeworkItem.ViewHolder, HomeworkHeader>(header) {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_homework
|
||||
|
||||
@ -20,10 +19,7 @@ class HomeworkItem(
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
|
||||
position: Int, payloads: MutableList<Any>?
|
||||
) {
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.apply {
|
||||
homeworkItemSubject.text = homework.subject
|
||||
homeworkItemTeacher.text = homework.teacher
|
||||
@ -42,12 +38,12 @@ class HomeworkItem(
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return homework.hashCode()
|
||||
var result = homework.hashCode()
|
||||
result = 31 * result + homework.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ class HomeworkPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: HomeworkView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Homework view is attached")
|
||||
view.initView()
|
||||
Timber.i("Homework view was initialized")
|
||||
loadData(LocalDate.ofEpochDay(date ?: LocalDate.now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePrese
|
||||
initAdapter()
|
||||
showActionBar(false)
|
||||
}
|
||||
Timber.i("Login view is attached")
|
||||
Timber.i("Login view was initialized")
|
||||
}
|
||||
|
||||
fun onFormViewAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy.ui.modules.login.form
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -53,7 +54,8 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() }
|
||||
loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() }
|
||||
loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() }
|
||||
loginFormSignIn.setOnClickListener { presenter.attemptLogin() }
|
||||
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
|
||||
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
|
||||
|
||||
loginFormPass.setOnEditorActionListener { _, id, _ ->
|
||||
if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false
|
||||
@ -130,6 +132,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
}
|
||||
}
|
||||
|
||||
override fun showPrivacyPolicy() {
|
||||
loginFormPrivacyLink.visibility = VISIBLE
|
||||
}
|
||||
|
||||
override fun notifyParentAccountLogged(students: List<Student>) {
|
||||
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
|
||||
loginFormName.text.toString(),
|
||||
@ -138,6 +144,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
))
|
||||
}
|
||||
|
||||
override fun openPrivacyPolicyPage() {
|
||||
startActivity(Intent.parseUri("https://wulkanowy.github.io/polityka-prywatnosci.html", 0))
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
presenter.onDetachView()
|
||||
|
@ -22,7 +22,8 @@ class LoginFormPresenter @Inject constructor(
|
||||
super.onAttachView(view)
|
||||
view.run {
|
||||
initView()
|
||||
if (isDebug) showVersion()
|
||||
if (isDebug) showVersion() else showPrivacyPolicy()
|
||||
|
||||
errorHandler.onBadCredentials = {
|
||||
setErrorPassIncorrect()
|
||||
showSoftKeyboard()
|
||||
@ -31,6 +32,10 @@ class LoginFormPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onPrivacyLinkClick() {
|
||||
view?.openPrivacyPolicyPage()
|
||||
}
|
||||
|
||||
fun onHostSelected() {
|
||||
view?.apply {
|
||||
clearPassError()
|
||||
@ -47,7 +52,7 @@ class LoginFormPresenter @Inject constructor(
|
||||
view?.clearNameError()
|
||||
}
|
||||
|
||||
fun attemptLogin() {
|
||||
fun onSignInClick() {
|
||||
val email = view?.formNameValue.orEmpty()
|
||||
val password = view?.formPassValue.orEmpty()
|
||||
val endpoint = view?.formHostValue.orEmpty()
|
||||
|
@ -37,5 +37,9 @@ interface LoginFormView : BaseView {
|
||||
|
||||
fun showVersion()
|
||||
|
||||
fun showPrivacyPolicy()
|
||||
|
||||
fun notifyParentAccountLogged(students: List<Student>)
|
||||
|
||||
fun openPrivacyPolicyPage()
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
@ -45,6 +44,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() }
|
||||
loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } }
|
||||
|
||||
loginStudentSelectRecycler.apply {
|
||||
@ -54,7 +54,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LoginStudentSelectItem>) {
|
||||
loginAdapter.updateDataSet(data, true)
|
||||
loginAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun openMainView() {
|
||||
@ -69,11 +69,11 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
loginStudentSelectRecycler.visibility = if (show) VISIBLE else GONE
|
||||
loginStudentSelectContent.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showActionBar(show: Boolean) {
|
||||
(activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() }
|
||||
override fun enableSignIn(enable: Boolean) {
|
||||
loginStudentSelectSignIn.isEnabled = enable
|
||||
}
|
||||
|
||||
fun onParentInitStudentSelectFragment(students: List<Student>) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
@ -8,19 +9,22 @@ import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_login_student_select.view.*
|
||||
import kotlinx.android.synthetic.main.item_login_student_select.*
|
||||
|
||||
class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
|
||||
|
||||
override fun getLayoutRes(): Int = R.layout.item_login_student_select
|
||||
override fun getLayoutRes() = R.layout.item_login_student_select
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ItemViewHolder {
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ItemViewHolder {
|
||||
return ItemViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ItemViewHolder?,
|
||||
position: Int, payloads: MutableList<Any>?) {
|
||||
holder?.bind(student)
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
loginItemName.text = "${student.studentName} ${student.className}"
|
||||
loginItemSchool.text = student.schoolName
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@ -38,17 +42,18 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
||||
return student.hashCode()
|
||||
}
|
||||
|
||||
class ItemViewHolder(view: View?, adapter: FlexibleAdapter<*>?)
|
||||
: FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
override val containerView: View
|
||||
get() = itemView
|
||||
|
||||
fun bind(item: Student) {
|
||||
itemView.run {
|
||||
loginItemName.text = item.studentName
|
||||
loginItemSchool.text = item.schoolName
|
||||
}
|
||||
init {
|
||||
loginItemCheck.setOnClickListener { super.onClick(loginItemContainer) }
|
||||
}
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
super.onClick(view)
|
||||
loginItemCheck.apply { isChecked = !isChecked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.io.Serializable
|
||||
import javax.inject.Inject
|
||||
@ -22,10 +21,13 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
|
||||
var students = emptyList<Student>()
|
||||
|
||||
var selectedStudents = mutableListOf<Student>()
|
||||
|
||||
fun onAttachView(view: LoginStudentSelectView, students: Serializable?) {
|
||||
super.onAttachView(view)
|
||||
view.run {
|
||||
initView()
|
||||
enableSignIn(false)
|
||||
errorHandler.onStudentDuplicate = {
|
||||
showMessage(it)
|
||||
Timber.i("The student already registered in the app was selected")
|
||||
@ -37,13 +39,21 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onSignIn() {
|
||||
registerStudents(selectedStudents)
|
||||
}
|
||||
|
||||
fun onParentInitStudentSelectView(students: List<Student>) {
|
||||
loadData(students)
|
||||
if (students.size == 1) registerStudents(students)
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is LoginStudentSelectItem) {
|
||||
registerStudent(item.student)
|
||||
selectedStudents.removeAll { it == item.student }
|
||||
.let { if (!it) selectedStudents.add(item.student) }
|
||||
|
||||
view?.enableSignIn(selectedStudents.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,33 +64,30 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerStudent(student: Student) {
|
||||
disposable.add(studentRepository.saveStudent(student)
|
||||
.map { student.apply { id = it } }
|
||||
.onErrorResumeNext { studentRepository.logoutStudent(student).andThen(Single.error(it)) }
|
||||
.flatMapCompletable { studentRepository.switchStudent(student) }
|
||||
private fun registerStudents(students: List<Student>) {
|
||||
disposable.add(studentRepository.saveStudents(students)
|
||||
.map { students.first().apply { id = it.first() } }
|
||||
.flatMapCompletable { studentRepository.switchStudent(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
view?.apply {
|
||||
showProgress(true)
|
||||
showContent(false)
|
||||
showActionBar(false)
|
||||
}
|
||||
Timber.i("Registration started")
|
||||
}
|
||||
.subscribe({
|
||||
analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to "No error")
|
||||
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") }
|
||||
Timber.i("Registration result: Success")
|
||||
view?.openMainView()
|
||||
}, {
|
||||
analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to it.localizedMessage)
|
||||
}, { error ->
|
||||
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.localizedMessage) }
|
||||
Timber.i("Registration result: An exception occurred ")
|
||||
errorHandler.dispatch(it)
|
||||
errorHandler.dispatch(error)
|
||||
view?.apply {
|
||||
showProgress(false)
|
||||
showContent(true)
|
||||
showActionBar(true)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -14,5 +14,5 @@ interface LoginStudentSelectView : BaseView {
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showActionBar(show: Boolean)
|
||||
fun enableSignIn(enable: Boolean)
|
||||
}
|
||||
|
@ -21,8 +21,12 @@ class LuckyNumberPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: LuckyNumberView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Lucky number view is attached")
|
||||
view.initView()
|
||||
view.run {
|
||||
initView()
|
||||
showContent(false)
|
||||
enableSwipe(false)
|
||||
}
|
||||
Timber.i("Lucky number view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
@ -35,12 +39,6 @@ class LuckyNumberPresenter @Inject constructor(
|
||||
.flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
view?.run {
|
||||
showContent(false)
|
||||
enableSwipe(false)
|
||||
}
|
||||
}
|
||||
.doFinally {
|
||||
view?.run {
|
||||
hideRefresh()
|
||||
|
@ -0,0 +1,77 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.activity_widget_configure.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetConfigureActivity : BaseActivity(), LuckyNumberWidgetConfigureView {
|
||||
|
||||
@Inject
|
||||
lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: LuckyNumberWidgetConfigurePresenter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setResult(RESULT_CANCELED)
|
||||
setContentView(R.layout.activity_widget_configure)
|
||||
|
||||
intent.extras.let {
|
||||
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID))
|
||||
}
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
widgetConfigureRecycler.apply {
|
||||
adapter = configureAdapter
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
}
|
||||
configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LuckyNumberWidgetConfigureItem>) {
|
||||
configureAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun updateLuckyNumberWidget(widgetId: Int) {
|
||||
sendBroadcast(Intent(this, LuckyNumberWidgetProvider::class.java)
|
||||
.apply {
|
||||
action = ACTION_APPWIDGET_UPDATE
|
||||
putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
|
||||
})
|
||||
}
|
||||
|
||||
override fun setSuccessResult(widgetId: Int) {
|
||||
setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) })
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
Toast.makeText(this, text, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun finishView() {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun openLoginView() {
|
||||
startActivity(LoginActivity.getStartIntent(this))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
presenter.onDetachView()
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_account.*
|
||||
|
||||
class LuckyNumberWidgetConfigureItem(var student: Student, val isCurrent: Boolean) :
|
||||
AbstractFlexibleItem<LuckyNumberWidgetConfigureItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_account
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
accountItemImage.setBackgroundResource(if (isCurrent) R.drawable.ic_account_circular_border else 0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TimetableWidgetConfigureItem
|
||||
|
||||
if (student != other.student) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = student.hashCode()
|
||||
result = 31 * result + student.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetConfigurePresenter @Inject constructor(
|
||||
private val errorHandler: ErrorHandler,
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val sharedPref: SharedPrefHelper
|
||||
) : BasePresenter<LuckyNumberWidgetConfigureView>(errorHandler) {
|
||||
|
||||
private var appWidgetId: Int? = null
|
||||
|
||||
fun onAttachView(view: LuckyNumberWidgetConfigureView, appWidgetId: Int?) {
|
||||
super.onAttachView(view)
|
||||
this.appWidgetId = appWidgetId
|
||||
view.initView()
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelect(item: AbstractFlexibleItem<*>) {
|
||||
if (item is LuckyNumberWidgetConfigureItem) {
|
||||
registerStudent(item.student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(studentRepository.getSavedStudents(false)
|
||||
.map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } }
|
||||
.map { (students, currentStudentId) ->
|
||||
students.map { student -> LuckyNumberWidgetConfigureItem(student, student.id == currentStudentId) }
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({
|
||||
when {
|
||||
it.isEmpty() -> view?.openLoginView()
|
||||
it.size == 1 -> registerStudent(it.single().student)
|
||||
else -> view?.updateData(it)
|
||||
}
|
||||
}, { errorHandler.dispatch(it) }))
|
||||
}
|
||||
|
||||
private fun registerStudent(student: Student) {
|
||||
appWidgetId?.also {
|
||||
sharedPref.putLong(getStudentWidgetKey(it), student.id)
|
||||
view?.apply {
|
||||
updateLuckyNumberWidget(it)
|
||||
setSuccessResult(it)
|
||||
}
|
||||
}
|
||||
view?.finishView()
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
|
||||
|
||||
interface LuckyNumberWidgetConfigureView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<LuckyNumberWidgetConfigureItem>)
|
||||
|
||||
fun updateLuckyNumberWidget(widgetId: Int)
|
||||
|
||||
fun setSuccessResult(widgetId: Int)
|
||||
|
||||
fun finishView()
|
||||
|
||||
fun openLoginView()
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OPTIONS
|
||||
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
|
||||
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.widget.RemoteViews
|
||||
import dagger.android.AndroidInjection
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.reactivex.Maybe
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
||||
|
||||
@Inject
|
||||
lateinit var studentRepository: StudentRepository
|
||||
|
||||
@Inject
|
||||
lateinit var semesterRepository: SemesterRepository
|
||||
|
||||
@Inject
|
||||
lateinit var luckyNumberRepository: LuckyNumberRepository
|
||||
|
||||
@Inject
|
||||
lateinit var schedulers: SchedulersProvider
|
||||
|
||||
@Inject
|
||||
lateinit var appWidgetManager: AppWidgetManager
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPref: SharedPrefHelper
|
||||
|
||||
companion object {
|
||||
fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId"
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
AndroidInjection.inject(this, context)
|
||||
when (intent.action) {
|
||||
ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent)
|
||||
ACTION_APPWIDGET_DELETED -> onDelete(intent)
|
||||
ACTION_APPWIDGET_OPTIONS_CHANGED -> onOptionsChange(context, intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onUpdate(context: Context, intent: Intent) {
|
||||
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS).forEach { appWidgetId ->
|
||||
RemoteViews(context.packageName, R.layout.widget_luckynumber).apply {
|
||||
setTextViewText(R.id.luckyNumberWidgetNumber,
|
||||
getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)?.luckyNumber?.toString() ?: "#"
|
||||
)
|
||||
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer,
|
||||
PendingIntent.getActivity(context, MainView.MenuView.LUCKY_NUMBER.id, MainActivity.getStartIntent(context).apply {
|
||||
putExtra(EXTRA_START_MENU, MainView.MenuView.LUCKY_NUMBER)
|
||||
}, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
}.also {
|
||||
setStyles(it, intent)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDelete(intent: Intent) {
|
||||
intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let {
|
||||
if (it != 0) sharedPref.delete(getStudentWidgetKey(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLuckyNumber(studentId: Long, appWidgetId: Int): LuckyNumber? {
|
||||
return try {
|
||||
studentRepository.isStudentSaved()
|
||||
.filter { true }
|
||||
.flatMap { studentRepository.getSavedStudents().toMaybe() }
|
||||
.flatMap { students ->
|
||||
students.singleOrNull { student -> student.id == studentId }
|
||||
.let { student ->
|
||||
when {
|
||||
student != null -> Maybe.just(student)
|
||||
studentId != 0L -> {
|
||||
studentRepository.getCurrentStudent(false)
|
||||
.toMaybe()
|
||||
.doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) }
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
|
||||
.flatMap { luckyNumberRepository.getLuckyNumber(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred in lucky number provider")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun onOptionsChange(context: Context, intent: Intent) {
|
||||
intent.extras?.let { extras ->
|
||||
RemoteViews(context.packageName, R.layout.widget_luckynumber).apply {
|
||||
setStyles(this, intent)
|
||||
appWidgetManager.updateAppWidget(extras.getInt(EXTRA_APPWIDGET_ID), this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
private fun setStyles(views: RemoteViews, intent: Intent) {
|
||||
val options = intent.extras?.getBundle(EXTRA_APPWIDGET_OPTIONS)
|
||||
|
||||
val maxWidth = options?.getInt(OPTION_APPWIDGET_MAX_WIDTH) ?: 150
|
||||
val maxHeight = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: 40
|
||||
|
||||
Timber.d("New lucky number widget measurement: %dx%d", maxWidth, maxHeight)
|
||||
|
||||
when {
|
||||
// 1x1
|
||||
maxWidth < 150 && maxHeight < 110 -> {
|
||||
Timber.d("Lucky number widget size: 1x1")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 1x2
|
||||
maxWidth < 150 && maxHeight > 110 -> {
|
||||
Timber.d("Lucky number widget size: 1x2")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 2x1
|
||||
maxWidth >= 150 && maxHeight <= 110 -> {
|
||||
Timber.d("Lucky number widget size: 2x1")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 2x2 and bigger
|
||||
else -> {
|
||||
Timber.d("Lucky number widget size: 2x2 and bigger")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,10 @@ package io.github.wulkanowy.ui.modules.main
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
@ -19,8 +22,12 @@ import io.github.wulkanowy.ui.modules.account.AccountDialog
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.more.MoreFragment
|
||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.safelyPopFragment
|
||||
@ -37,7 +44,7 @@ class MainActivity : BaseActivity(), MainView {
|
||||
lateinit var navController: FragNavController
|
||||
|
||||
companion object {
|
||||
const val EXTRA_START_MENU_INDEX = "extraStartMenuIndex"
|
||||
const val EXTRA_START_MENU = "extraStartMenu"
|
||||
|
||||
fun getStartIntent(context: Context) = Intent(context, MainActivity::class.java)
|
||||
}
|
||||
@ -53,14 +60,27 @@ class MainActivity : BaseActivity(), MainView {
|
||||
|
||||
override var startMenuIndex = 0
|
||||
|
||||
override var startMenuMoreIndex = -1
|
||||
|
||||
private val moreMenuFragments = listOf<Fragment>(
|
||||
MessageFragment.newInstance(),
|
||||
HomeworkFragment.newInstance(),
|
||||
NoteFragment.newInstance(),
|
||||
LuckyNumberFragment.newInstance()
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(mainToolbar)
|
||||
messageContainer = mainFragmentContainer
|
||||
|
||||
presenter.onAttachView(this, intent.getIntExtra(EXTRA_START_MENU_INDEX, -1))
|
||||
navController.initialize(startMenuIndex, savedInstanceState)
|
||||
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_START_MENU) as? MainView.MenuView)
|
||||
|
||||
navController.run {
|
||||
initialize(startMenuIndex, savedInstanceState)
|
||||
pushFragment(moreMenuFragments.getOrNull(startMenuMoreIndex))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
@ -68,11 +88,6 @@ class MainActivity : BaseActivity(), MainView {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
presenter.onViewChange()
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
mainBottomNav.run {
|
||||
addItems(
|
||||
@ -144,7 +159,9 @@ class MainActivity : BaseActivity(), MainView {
|
||||
}
|
||||
|
||||
override fun notifyMenuViewReselected() {
|
||||
(navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected()
|
||||
Handler().postDelayed({
|
||||
(navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected()
|
||||
}, 250)
|
||||
}
|
||||
|
||||
fun showDialogFragment(dialog: DialogFragment) {
|
||||
@ -165,10 +182,10 @@ class MainActivity : BaseActivity(), MainView {
|
||||
|
||||
override fun openLoginView() {
|
||||
startActivity(LoginActivity.getStartIntent(this)
|
||||
.apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) })
|
||||
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle?) {
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
navController.onSaveInstanceState(outState)
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.di.scopes.PerActivity
|
||||
import io.github.wulkanowy.di.scopes.PerFragment
|
||||
import io.github.wulkanowy.ui.modules.about.AboutFragment
|
||||
import io.github.wulkanowy.ui.modules.about.AboutModule
|
||||
@ -33,7 +32,6 @@ abstract class MainModule {
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
@PerActivity
|
||||
@Provides
|
||||
fun provideFragNavController(activity: MainActivity): FragNavController {
|
||||
return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer)
|
||||
|
@ -22,22 +22,19 @@ class MainPresenter @Inject constructor(
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BasePresenter<MainView>(errorHandler) {
|
||||
|
||||
fun onAttachView(view: MainView, initMenuIndex: Int) {
|
||||
fun onAttachView(view: MainView, initMenu: MainView.MenuView?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Main view is attached with $initMenuIndex menu index")
|
||||
view.run {
|
||||
startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex
|
||||
view.apply {
|
||||
getProperViewIndexes(initMenu).let { (main, more) ->
|
||||
startMenuIndex = main
|
||||
startMenuMoreIndex = more
|
||||
}
|
||||
initView()
|
||||
Timber.i("Main view was initialized with $startMenuIndex menu index and $startMenuMoreIndex more index")
|
||||
}
|
||||
|
||||
syncManager.startSyncWorker()
|
||||
|
||||
analytics.logEvent(APP_OPEN, DESTINATION to when (initMenuIndex) {
|
||||
1 -> "Grades"
|
||||
3 -> "Timetable"
|
||||
4 -> "More"
|
||||
else -> "User action"
|
||||
})
|
||||
analytics.logEvent(APP_OPEN, DESTINATION to initMenu?.name)
|
||||
}
|
||||
|
||||
fun onViewChange() {
|
||||
@ -104,4 +101,12 @@ class MainPresenter @Inject constructor(
|
||||
errorHandler.dispatch(it)
|
||||
}))
|
||||
}
|
||||
|
||||
private fun getProperViewIndexes(initMenu: MainView.MenuView?): Pair<Int, Int> {
|
||||
return when {
|
||||
initMenu?.id in 0..3 -> initMenu!!.id to -1
|
||||
(initMenu?.id ?: 0) > 3 -> 4 to initMenu!!.id - 4
|
||||
else -> prefRepository.startMenuIndex to -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ interface MainView : BaseView {
|
||||
|
||||
var startMenuIndex: Int
|
||||
|
||||
var startMenuMoreIndex: Int
|
||||
|
||||
val isRootView: Boolean
|
||||
|
||||
val currentViewTitle: String?
|
||||
@ -37,4 +39,15 @@ interface MainView : BaseView {
|
||||
|
||||
val titleStringId: Int
|
||||
}
|
||||
|
||||
enum class MenuView(val id: Int) {
|
||||
GRADE(0),
|
||||
ATTENDANCE(1),
|
||||
EXAM(2),
|
||||
TIMETABLE(3),
|
||||
MESSAGE(4),
|
||||
HOMEWORK(5),
|
||||
NOTE(6),
|
||||
LUCKY_NUMBER(7),
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user