forked from github/wulkanowy-mirror
Merge branch 'release/0.16.0'
This commit is contained in:
commit
5de2e9afbd
1
.gitignore
vendored
1
.gitignore
vendored
@ -63,6 +63,7 @@ captures/
|
|||||||
.idea/dynamic.xml
|
.idea/dynamic.xml
|
||||||
.idea/uiDesigner.xml
|
.idea/uiDesigner.xml
|
||||||
.idea/runConfigurations.xml
|
.idea/runConfigurations.xml
|
||||||
|
.idea/discord.xml
|
||||||
|
|
||||||
# Keystore files
|
# Keystore files
|
||||||
*.jks
|
*.jks
|
||||||
|
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
@ -1,9 +1,6 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<option name="LINE_SEPARATOR" value=" " />
|
<option name="LINE_SEPARATOR" value=" " />
|
||||||
<AndroidXmlCodeStyleSettings>
|
|
||||||
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
|
|
||||||
</AndroidXmlCodeStyleSettings>
|
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
<value>
|
<value>
|
||||||
|
10
.travis.yml
10
.travis.yml
@ -4,7 +4,7 @@ jdk: oraclejdk8
|
|||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- ANDROID_API_LEVEL=29
|
- ANDROID_API_LEVEL=29
|
||||||
- ANDROID_BUILD_TOOLS_VERSION=29.0.2
|
- ANDROID_BUILD_TOOLS_VERSION=29.0.3
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
@ -14,7 +14,7 @@ cache:
|
|||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- develop
|
- develop
|
||||||
- 0.15.0
|
- 0.16.0
|
||||||
|
|
||||||
android:
|
android:
|
||||||
licenses:
|
licenses:
|
||||||
@ -49,9 +49,9 @@ script:
|
|||||||
- ./gradlew dependencies --stacktrace --daemon
|
- ./gradlew dependencies --stacktrace --daemon
|
||||||
- fossa --no-ansi || true
|
- fossa --no-ansi || true
|
||||||
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
|
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
|
||||||
- ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
|
- ./gradlew -Pcoverage testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
|
||||||
- ./gradlew createFdroidDebugCoverageReport --stacktrace --daemon
|
- ./gradlew -Pcoverage createFdroidDebugCoverageReport --stacktrace --daemon
|
||||||
- ./gradlew jacocoTestReport --stacktrace --daemon
|
- ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon
|
||||||
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
|
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
|
||||||
git fetch --unshallow;
|
git fetch --unshallow;
|
||||||
./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesPlayRelease -x fabricGenerateResourcesFdroidRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} --stacktrace --daemon;
|
./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesPlayRelease -x fabricGenerateResourcesFdroidRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} --stacktrace --daemon;
|
||||||
|
@ -10,15 +10,15 @@ apply from: 'hooks.gradle'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 29
|
||||||
buildToolsVersion '29.0.2'
|
buildToolsVersion '29.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "io.github.wulkanowy"
|
applicationId "io.github.wulkanowy"
|
||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 16
|
minSdkVersion 17
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 52
|
versionCode 53
|
||||||
versionName "0.15.0"
|
versionName "0.16.0"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@ -61,7 +61,7 @@ android {
|
|||||||
buildConfigField "boolean", "CRASHLYTICS_ENABLED", project.hasProperty("enableCrashlytics") ? "true" : "false"
|
buildConfigField "boolean", "CRASHLYTICS_ENABLED", project.hasProperty("enableCrashlytics") ? "true" : "false"
|
||||||
applicationIdSuffix ".dev"
|
applicationIdSuffix ".dev"
|
||||||
versionNameSuffix "-dev"
|
versionNameSuffix "-dev"
|
||||||
testCoverageEnabled = true
|
testCoverageEnabled = project.hasProperty('coverage')
|
||||||
ext.enableCrashlytics = project.hasProperty("enableCrashlytics")
|
ext.enableCrashlytics = project.hasProperty("enableCrashlytics")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,9 +110,9 @@ play {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
work_manager = "2.3.0"
|
work_manager = "2.3.2"
|
||||||
room = "2.2.3"
|
room = "2.2.4"
|
||||||
dagger = "2.25.4"
|
dagger = "2.26"
|
||||||
chucker = "2.0.4"
|
chucker = "2.0.4"
|
||||||
mockk = "1.9.2"
|
mockk = "1.9.2"
|
||||||
}
|
}
|
||||||
@ -122,14 +122,14 @@ configurations.all {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "io.github.wulkanowy:sdk:0.15.0"
|
implementation "io.github.wulkanowy:sdk:0.16.0"
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "androidx.core:core-ktx:1.2.0-rc01"
|
implementation "androidx.core:core-ktx:1.2.0"
|
||||||
implementation "androidx.activity:activity-ktx:1.1.0"
|
implementation "androidx.activity:activity-ktx:1.1.0"
|
||||||
implementation "androidx.appcompat:appcompat:1.1.0"
|
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||||
implementation "androidx.appcompat:appcompat-resources:1.1.0"
|
implementation "androidx.appcompat:appcompat-resources:1.1.0"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.2.0"
|
implementation "androidx.fragment:fragment-ktx:1.2.2"
|
||||||
implementation "androidx.annotation:annotation:1.1.0"
|
implementation "androidx.annotation:annotation:1.1.0"
|
||||||
implementation "androidx.multidex:multidex:2.0.1"
|
implementation "androidx.multidex:multidex:2.0.1"
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ dependencies {
|
|||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
||||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
||||||
implementation "com.google.android.material:material:1.1.0-rc02"
|
implementation "com.google.android.material:material:1.1.0"
|
||||||
implementation "com.github.wulkanowy:material-chips-input:2.0.1"
|
implementation "com.github.wulkanowy:material-chips-input:2.0.1"
|
||||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||||
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
||||||
@ -167,18 +167,19 @@ dependencies {
|
|||||||
|
|
||||||
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
|
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
|
||||||
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
|
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
|
||||||
implementation "io.reactivex.rxjava2:rxjava:2.2.17"
|
implementation "io.reactivex.rxjava2:rxjava:2.2.18"
|
||||||
|
|
||||||
implementation "com.google.code.gson:gson:2.8.6"
|
implementation "com.google.code.gson:gson:2.8.6"
|
||||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.2"
|
implementation "com.jakewharton.threetenabp:threetenabp:1.2.2"
|
||||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||||
|
implementation "fr.bipi.treessence:treessence:0.3.2"
|
||||||
implementation "com.mikepenz:aboutlibraries-core:7.1.0"
|
implementation "com.mikepenz:aboutlibraries-core:7.1.0"
|
||||||
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
|
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
|
||||||
|
|
||||||
implementation("io.coil-kt:coil:0.9.2")
|
implementation("io.coil-kt:coil:0.9.5")
|
||||||
|
|
||||||
playImplementation "com.google.firebase:firebase-core:17.2.2"
|
playImplementation "com.google.firebase:firebase-core:17.2.3"
|
||||||
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
|
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
|
||||||
|
|
||||||
releaseImplementation "fr.o80.chucker:library-no-op:$chucker"
|
releaseImplementation "fr.o80.chucker:library-no-op:$chucker"
|
||||||
@ -189,7 +190,7 @@ dependencies {
|
|||||||
testImplementation "junit:junit:4.13"
|
testImplementation "junit:junit:4.13"
|
||||||
testImplementation "io.mockk:mockk:$mockk"
|
testImplementation "io.mockk:mockk:$mockk"
|
||||||
testImplementation "org.threeten:threetenbp:1.4.1"
|
testImplementation "org.threeten:threetenbp:1.4.1"
|
||||||
testImplementation "org.mockito:mockito-inline:3.2.4"
|
testImplementation "org.mockito:mockito-inline:3.3.1"
|
||||||
|
|
||||||
androidTestImplementation "androidx.test:core:1.2.0"
|
androidTestImplementation "androidx.test:core:1.2.0"
|
||||||
androidTestImplementation "androidx.test:runner:1.2.0"
|
androidTestImplementation "androidx.test:runner:1.2.0"
|
||||||
@ -197,7 +198,7 @@ dependencies {
|
|||||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||||
androidTestImplementation "androidx.room:room-testing:$room"
|
androidTestImplementation "androidx.room:room-testing:$room"
|
||||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
androidTestImplementation "org.mockito:mockito-android:3.2.4"
|
androidTestImplementation "org.mockito:mockito-android:3.3.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.google.gms.google-services'
|
apply plugin: 'com.google.gms.google-services'
|
||||||
|
1658
app/schemas/io.github.wulkanowy.data.db.AppDatabase/22.json
Normal file
1658
app/schemas/io.github.wulkanowy.data.db.AppDatabase/22.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.threeten.bp.LocalDate.now
|
|
||||||
import org.threeten.bp.LocalDate.of
|
import org.threeten.bp.LocalDate.of
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
@ -104,45 +103,45 @@ class Migration13Test : AbstractMigrationTest() {
|
|||||||
val db = helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
|
val db = helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
|
||||||
|
|
||||||
val semesters1 = getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 1 AND class_id = 5")
|
val semesters1 = getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 1 AND class_id = 5")
|
||||||
assertTrue { semesters1.single { it.isCurrent }.isCurrent }
|
assertTrue { semesters1.single { it.second }.second }
|
||||||
semesters1[0].run {
|
semesters1[0].run {
|
||||||
assertFalse(isCurrent)
|
assertFalse(second)
|
||||||
assertEquals(1, semesterId)
|
assertEquals(1, first.semesterId)
|
||||||
assertEquals(1, diaryId)
|
assertEquals(1, first.diaryId)
|
||||||
}
|
}
|
||||||
semesters1[2].run {
|
semesters1[2].run {
|
||||||
assertFalse(isCurrent)
|
assertFalse(second)
|
||||||
assertEquals(3, semesterId)
|
assertEquals(3, first.semesterId)
|
||||||
assertEquals(2, diaryId)
|
assertEquals(2, first.diaryId)
|
||||||
}
|
}
|
||||||
semesters1[3].run {
|
semesters1[3].run {
|
||||||
assertTrue(isCurrent)
|
assertTrue(second)
|
||||||
assertEquals(4, semesterId)
|
assertEquals(4, first.semesterId)
|
||||||
assertEquals(2, diaryId)
|
assertEquals(2, first.diaryId)
|
||||||
}
|
}
|
||||||
|
|
||||||
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
|
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
|
||||||
assertTrue { it.single { it.isCurrent }.isCurrent }
|
assertTrue { it.single { it.second }.second }
|
||||||
assertEquals(1970, it[0].schoolYear)
|
assertEquals(1970, it[0].first.schoolYear)
|
||||||
assertEquals(of(1970, 1, 1), it[0].end)
|
assertEquals(of(1970, 1, 1), it[0].first.end)
|
||||||
assertEquals(of(1970, 1, 1), it[0].start)
|
assertEquals(of(1970, 1, 1), it[0].first.start)
|
||||||
assertFalse(it[0].isCurrent)
|
assertFalse(it[0].second)
|
||||||
assertFalse(it[1].isCurrent)
|
assertFalse(it[1].second)
|
||||||
assertFalse(it[2].isCurrent)
|
assertFalse(it[2].second)
|
||||||
assertTrue(it[3].isCurrent)
|
assertTrue(it[3].second)
|
||||||
}
|
}
|
||||||
|
|
||||||
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
|
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
|
||||||
assertTrue { it.single { it.isCurrent }.isCurrent }
|
assertTrue { it.single { it.second }.second }
|
||||||
assertFalse(it[0].isCurrent)
|
assertFalse(it[0].second)
|
||||||
assertFalse(it[1].isCurrent)
|
assertFalse(it[1].second)
|
||||||
assertFalse(it[2].isCurrent)
|
assertFalse(it[2].second)
|
||||||
assertTrue(it[3].isCurrent)
|
assertTrue(it[3].second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List<Semester> {
|
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List<Pair<Semester, Boolean>> {
|
||||||
val semesters = mutableListOf<Semester>()
|
val semesters = mutableListOf<Pair<Semester, Boolean>>()
|
||||||
|
|
||||||
val cursor = db.query(query)
|
val cursor = db.query(query)
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
@ -153,13 +152,12 @@ class Migration13Test : AbstractMigrationTest() {
|
|||||||
diaryName = cursor.getString(3),
|
diaryName = cursor.getString(3),
|
||||||
semesterId = cursor.getInt(4),
|
semesterId = cursor.getInt(4),
|
||||||
semesterName = cursor.getInt(5),
|
semesterName = cursor.getInt(5),
|
||||||
isCurrent = cursor.getInt(6) == 1,
|
|
||||||
classId = cursor.getInt(7),
|
classId = cursor.getInt(7),
|
||||||
unitId = cursor.getInt(8),
|
unitId = cursor.getInt(8),
|
||||||
schoolYear = cursor.getInt(9),
|
schoolYear = cursor.getInt(9),
|
||||||
start = Converters().timestampToDate(cursor.getLong(10))!!,
|
start = Converters().timestampToDate(cursor.getLong(10))!!,
|
||||||
end = Converters().timestampToDate(cursor.getLong(11))!!
|
end = Converters().timestampToDate(cursor.getLong(11))!!
|
||||||
))
|
) to (cursor.getInt(6) == 1))
|
||||||
} while (cursor.moveToNext())
|
} while (cursor.moveToNext())
|
||||||
}
|
}
|
||||||
return semesters.toList()
|
return semesters.toList()
|
||||||
|
@ -10,8 +10,8 @@ import org.junit.After
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.threeten.bp.LocalDate
|
|
||||||
import org.threeten.bp.LocalDate.now
|
import org.threeten.bp.LocalDate.now
|
||||||
|
import org.threeten.bp.LocalDate.of
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@ -35,19 +35,19 @@ class AttendanceLocalTest {
|
|||||||
@Test
|
@Test
|
||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
attendanceLocal.saveAttendance(listOf(
|
attendanceLocal.saveAttendance(listOf(
|
||||||
Attendance(1, 2, 3, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name),
|
Attendance(1, 2, 3, of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name),
|
||||||
Attendance(1, 2, 3, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name),
|
Attendance(1, 2, 3, of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name),
|
||||||
Attendance(1, 2, 3, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name)
|
Attendance(1, 2, 3, of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name)
|
||||||
))
|
))
|
||||||
|
|
||||||
val attendance = attendanceLocal
|
val attendance = attendanceLocal
|
||||||
.getAttendance(Semester(1, 2, "", 1, 3, 2019, true, now(), now(), 1, 1),
|
.getAttendance(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||||
LocalDate.of(2018, 9, 10),
|
of(2018, 9, 10),
|
||||||
LocalDate.of(2018, 9, 14)
|
of(2018, 9, 14)
|
||||||
)
|
)
|
||||||
.blockingGet()
|
.blockingGet()
|
||||||
assertEquals(2, attendance.size)
|
assertEquals(2, attendance.size)
|
||||||
assertEquals(attendance[0].date, LocalDate.of(2018, 9, 10))
|
assertEquals(attendance[0].date, of(2018, 9, 10))
|
||||||
assertEquals(attendance[1].date, LocalDate.of(2018, 9, 14))
|
assertEquals(attendance[1].date, of(2018, 9, 14))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
|
import org.threeten.bp.LocalDate.now
|
||||||
|
import org.threeten.bp.LocalDate.of
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@ -35,20 +37,20 @@ class CompletedLessonsLocalTest {
|
|||||||
@Test
|
@Test
|
||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
completedLessonsLocal.saveCompletedLessons(listOf(
|
completedLessonsLocal.saveCompletedLessons(listOf(
|
||||||
getCompletedLesson(LocalDate.of(2018, 9, 10), 1),
|
getCompletedLesson(of(2018, 9, 10), 1),
|
||||||
getCompletedLesson(LocalDate.of(2018, 9, 14), 2),
|
getCompletedLesson(of(2018, 9, 14), 2),
|
||||||
getCompletedLesson(LocalDate.of(2018, 9, 17), 3)
|
getCompletedLesson(of(2018, 9, 17), 3)
|
||||||
))
|
))
|
||||||
|
|
||||||
val completed = completedLessonsLocal
|
val completed = completedLessonsLocal
|
||||||
.getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
.getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||||
LocalDate.of(2018, 9, 10),
|
of(2018, 9, 10),
|
||||||
LocalDate.of(2018, 9, 14)
|
of(2018, 9, 14)
|
||||||
)
|
)
|
||||||
.blockingGet()
|
.blockingGet()
|
||||||
assertEquals(2, completed.size)
|
assertEquals(2, completed.size)
|
||||||
assertEquals(completed[0].date, LocalDate.of(2018, 9, 10))
|
assertEquals(completed[0].date, of(2018, 9, 10))
|
||||||
assertEquals(completed[1].date, LocalDate.of(2018, 9, 14))
|
assertEquals(completed[1].date, of(2018, 9, 14))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson {
|
private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson {
|
||||||
|
@ -10,7 +10,8 @@ import org.junit.After
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate.now
|
||||||
|
import org.threeten.bp.LocalDate.of
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@ -34,19 +35,19 @@ class ExamLocalTest {
|
|||||||
@Test
|
@Test
|
||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
examLocal.saveExams(listOf(
|
examLocal.saveExams(listOf(
|
||||||
Exam(1, 2, LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""),
|
Exam(1, 2, of(2018, 9, 10), now(), "", "", "", "", "", ""),
|
||||||
Exam(1, 2, LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""),
|
Exam(1, 2, of(2018, 9, 14), now(), "", "", "", "", "", ""),
|
||||||
Exam(1, 2, LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "")
|
Exam(1, 2, of(2018, 9, 17), now(), "", "", "", "", "", "")
|
||||||
))
|
))
|
||||||
|
|
||||||
val exams = examLocal
|
val exams = examLocal
|
||||||
.getExams(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
.getExams(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||||
LocalDate.of(2018, 9, 10),
|
of(2018, 9, 10),
|
||||||
LocalDate.of(2018, 9, 14)
|
of(2018, 9, 14)
|
||||||
)
|
)
|
||||||
.blockingGet()
|
.blockingGet()
|
||||||
assertEquals(2, exams.size)
|
assertEquals(2, exams.size)
|
||||||
assertEquals(exams[0].date, LocalDate.of(2018, 9, 10))
|
assertEquals(exams[0].date, of(2018, 9, 10))
|
||||||
assertEquals(exams[1].date, LocalDate.of(2018, 9, 14))
|
assertEquals(exams[1].date, of(2018, 9, 14))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class GradeLocalTest {
|
|||||||
createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2)
|
createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2)
|
||||||
))
|
))
|
||||||
|
|
||||||
val semester = Semester(1, 2, "", 2019, 2, 1, true, now(), now(), 1, 1)
|
val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1)
|
||||||
|
|
||||||
val grades = gradeLocal
|
val grades = gradeLocal
|
||||||
.getGrades(semester)
|
.getGrades(semester)
|
||||||
|
@ -6,7 +6,6 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.filters.SdkSuppress
|
import androidx.test.filters.SdkSuppress
|
||||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||||
import io.github.wulkanowy.data.SdkHelper
|
|
||||||
import io.github.wulkanowy.data.db.AppDatabase
|
import io.github.wulkanowy.data.db.AppDatabase
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
@ -15,9 +14,6 @@ import io.github.wulkanowy.sdk.Sdk
|
|||||||
import io.mockk.MockKAnnotations
|
import io.mockk.MockKAnnotations
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.impl.annotations.MockK
|
import io.mockk.impl.annotations.MockK
|
||||||
import io.mockk.impl.annotations.SpyK
|
|
||||||
import io.mockk.just
|
|
||||||
import io.mockk.runs
|
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
@ -11,7 +11,7 @@ import org.junit.After
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate.now
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@ -40,10 +40,7 @@ class GradeStatisticsLocalTest {
|
|||||||
getGradeStatistics("Fizyka", 1, 2)
|
getGradeStatistics("Fizyka", 1, 2)
|
||||||
))
|
))
|
||||||
|
|
||||||
val stats = gradeStatisticsLocal.getGradesStatistics(
|
val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Matematyka").blockingGet()
|
||||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
|
|
||||||
"Matematyka"
|
|
||||||
).blockingGet()
|
|
||||||
assertEquals(1, stats.size)
|
assertEquals(1, stats.size)
|
||||||
assertEquals(stats[0].subject, "Matematyka")
|
assertEquals(stats[0].subject, "Matematyka")
|
||||||
}
|
}
|
||||||
@ -56,12 +53,11 @@ class GradeStatisticsLocalTest {
|
|||||||
getGradeStatistics("Fizyka", 1, 2)
|
getGradeStatistics("Fizyka", 1, 2)
|
||||||
))
|
))
|
||||||
|
|
||||||
val stats = gradeStatisticsLocal.getGradesStatistics(
|
val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Wszystkie").blockingGet()
|
||||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
|
assertEquals(3, stats.size)
|
||||||
"Wszystkie"
|
|
||||||
).blockingGet()
|
|
||||||
assertEquals(1, stats.size)
|
|
||||||
assertEquals(stats[0].subject, "Wszystkie")
|
assertEquals(stats[0].subject, "Wszystkie")
|
||||||
|
assertEquals(stats[1].subject, "Matematyka")
|
||||||
|
assertEquals(stats[2].subject, "Chemia")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -72,11 +68,8 @@ class GradeStatisticsLocalTest {
|
|||||||
getGradePointsStatistics("Fizyka", 1, 2)
|
getGradePointsStatistics("Fizyka", 1, 2)
|
||||||
))
|
))
|
||||||
|
|
||||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet()
|
||||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
with(stats[0]) {
|
||||||
"Matematyka"
|
|
||||||
).blockingGet()
|
|
||||||
with(stats) {
|
|
||||||
assertEquals(subject, "Matematyka")
|
assertEquals(subject, "Matematyka")
|
||||||
assertEquals(others, 5.0)
|
assertEquals(others, 5.0)
|
||||||
assertEquals(student, 5.0)
|
assertEquals(student, 5.0)
|
||||||
@ -87,10 +80,7 @@ class GradeStatisticsLocalTest {
|
|||||||
fun saveAndRead_subjectEmpty() {
|
fun saveAndRead_subjectEmpty() {
|
||||||
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
||||||
|
|
||||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet()
|
||||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
|
||||||
"Matematyka"
|
|
||||||
).blockingGet()
|
|
||||||
assertEquals(null, stats)
|
assertEquals(null, stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,13 +88,14 @@ class GradeStatisticsLocalTest {
|
|||||||
fun saveAndRead_allEmpty() {
|
fun saveAndRead_allEmpty() {
|
||||||
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
||||||
|
|
||||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Wszystkie").blockingGet()
|
||||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
|
||||||
"Wszystkie"
|
|
||||||
).blockingGet()
|
|
||||||
assertEquals(null, stats)
|
assertEquals(null, stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getSemester(): Semester {
|
||||||
|
return Semester(2, 2, "", 2019, 1, 2, now(), now(), 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics {
|
private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics {
|
||||||
return GradeStatistics(studentId, semesterId, subject, 5, 5, false)
|
return GradeStatistics(studentId, semesterId, subject, 5, 5, false)
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||||||
import io.github.wulkanowy.data.db.AppDatabase
|
import io.github.wulkanowy.data.db.AppDatabase
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
|
import org.threeten.bp.LocalDateTime.now
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@ -36,7 +38,7 @@ class LuckyNumberLocalTest {
|
|||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14))
|
luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14))
|
||||||
|
|
||||||
val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
val luckyNumber = luckyNumberLocal.getLuckyNumber(Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", 1, false, now()),
|
||||||
LocalDate.of(2019, 1, 20)
|
LocalDate.of(2019, 1, 20)
|
||||||
).blockingGet()
|
).blockingGet()
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class RecipientLocalTest {
|
|||||||
))
|
))
|
||||||
|
|
||||||
val recipients = recipientLocal.getRecipients(
|
val recipients = recipientLocal.getRecipients(
|
||||||
Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", 1, true, LocalDateTime.now()),
|
Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", "", 1, true, LocalDateTime.now()),
|
||||||
2,
|
2,
|
||||||
ReportingUnit(1, 4, "", 0, "", emptyList())
|
ReportingUnit(1, 4, "", 0, "", emptyList())
|
||||||
).blockingGet()
|
).blockingGet()
|
||||||
|
@ -39,7 +39,7 @@ class StudentLocalTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", scrapperBaseUrl = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "", loginMode = "API", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 0, isParent = false)))
|
studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", scrapperBaseUrl = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolShortName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "", loginMode = "API", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 0, isParent = false)))
|
||||||
.blockingGet()
|
.blockingGet()
|
||||||
|
|
||||||
val student = studentLocal.getCurrentStudent(true).blockingGet()
|
val student = studentLocal.getCurrentStudent(true).blockingGet()
|
||||||
|
@ -21,7 +21,7 @@ fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", s
|
|||||||
teacher = teacher,
|
teacher = teacher,
|
||||||
teacherOld = "",
|
teacherOld = "",
|
||||||
info = "",
|
info = "",
|
||||||
studentPlan = true,
|
isStudentPlan = true,
|
||||||
changes = changes,
|
changes = changes,
|
||||||
canceled = false
|
canceled = false
|
||||||
)
|
)
|
||||||
|
@ -41,7 +41,7 @@ class TimetableLocalTest {
|
|||||||
))
|
))
|
||||||
|
|
||||||
val exams = timetableDb.getTimetable(
|
val exams = timetableDb.getTimetable(
|
||||||
Semester(1, 2, "", 1, 1, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
Semester(1, 2, "", 1, 1, 2019, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||||
LocalDate.of(2018, 9, 10),
|
LocalDate.of(2018, 9, 10),
|
||||||
LocalDate.of(2018, 9, 14)
|
LocalDate.of(2018, 9, 14)
|
||||||
).blockingGet()
|
).blockingGet()
|
||||||
|
@ -5,13 +5,12 @@ package io.github.wulkanowy.utils
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
fun initCrashlytics(context: Context, appInfo: AppInfo) {}
|
||||||
// do nothing
|
|
||||||
|
open class TimberTreeNoOp : Timber.Tree() {
|
||||||
|
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CrashlyticsTree : Timber.Tree() {
|
class CrashlyticsTree : TimberTreeNoOp()
|
||||||
|
|
||||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
class CrashlyticsExceptionTree : TimberTreeNoOp()
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -43,8 +43,8 @@
|
|||||||
android:name=".ui.modules.message.send.SendMessageActivity"
|
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:label="@string/send_message_title"
|
android:label="@string/send_message_title"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||||
android:theme="@style/WulkanowyTheme.NoActionBar" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
@ -96,6 +96,16 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
tools:node="remove" />
|
tools:node="remove" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.fabric.ApiKey"
|
android:name="io.fabric.ApiKey"
|
||||||
android:value="${fabric_api_key}" />
|
android:value="${fabric_api_key}" />
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"githubUsername": "doteq"
|
"githubUsername": "doteq"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"displayName": "Pavuloff",
|
"displayName": "Paweł Krzyś",
|
||||||
"githubUsername": "pavuloff"
|
"githubUsername": "pavuloff"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -30,5 +30,9 @@
|
|||||||
{
|
{
|
||||||
"displayName": "Dinolek",
|
"displayName": "Dinolek",
|
||||||
"githubUsername": "Dinolek"
|
"githubUsername": "Dinolek"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "Mateusz Idziejczak",
|
||||||
|
"githubUsername": "PanTajemnic"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy
|
package io.github.wulkanowy
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log.DEBUG
|
||||||
import android.util.Log.INFO
|
import android.util.Log.INFO
|
||||||
import android.util.Log.VERBOSE
|
import android.util.Log.VERBOSE
|
||||||
import androidx.multidex.MultiDex
|
import androidx.multidex.MultiDex
|
||||||
@ -11,11 +12,13 @@ import dagger.android.AndroidInjector
|
|||||||
import dagger.android.support.DaggerApplication
|
import dagger.android.support.DaggerApplication
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.utils.Log
|
import eu.davidea.flexibleadapter.utils.Log
|
||||||
|
import fr.bipi.tressence.file.FileLoggerTree
|
||||||
import io.github.wulkanowy.di.DaggerAppComponent
|
import io.github.wulkanowy.di.DaggerAppComponent
|
||||||
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
||||||
import io.github.wulkanowy.ui.base.ThemeManager
|
import io.github.wulkanowy.ui.base.ThemeManager
|
||||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
|
import io.github.wulkanowy.utils.CrashlyticsExceptionTree
|
||||||
import io.github.wulkanowy.utils.CrashlyticsTree
|
import io.github.wulkanowy.utils.CrashlyticsTree
|
||||||
import io.github.wulkanowy.utils.DebugLogTree
|
import io.github.wulkanowy.utils.DebugLogTree
|
||||||
import io.github.wulkanowy.utils.initCrashlytics
|
import io.github.wulkanowy.utils.initCrashlytics
|
||||||
@ -54,9 +57,17 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
|
|||||||
|
|
||||||
private fun initLogging() {
|
private fun initLogging() {
|
||||||
if (appInfo.isDebug) {
|
if (appInfo.isDebug) {
|
||||||
Timber.plant(DebugLogTree())
|
|
||||||
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
||||||
|
Timber.plant(DebugLogTree())
|
||||||
|
Timber.plant(FileLoggerTree.Builder()
|
||||||
|
.withFileName("wulkanowy.%g.log")
|
||||||
|
.withDirName(applicationContext.filesDir.absolutePath)
|
||||||
|
.withFileLimit(10)
|
||||||
|
.withMinPriority(DEBUG)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
|
Timber.plant(CrashlyticsExceptionTree())
|
||||||
Timber.plant(CrashlyticsTree())
|
Timber.plant(CrashlyticsTree())
|
||||||
}
|
}
|
||||||
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
||||||
|
@ -62,6 +62,7 @@ import io.github.wulkanowy.data.db.migrations.Migration19
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration2
|
import io.github.wulkanowy.data.db.migrations.Migration2
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration20
|
import io.github.wulkanowy.data.db.migrations.Migration20
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration21
|
import io.github.wulkanowy.data.db.migrations.Migration21
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration22
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||||
@ -103,7 +104,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 21
|
const val VERSION_SCHEMA = 22
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
||||||
return arrayOf(
|
return arrayOf(
|
||||||
@ -126,7 +127,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration18(),
|
Migration18(),
|
||||||
Migration19(sharedPrefProvider),
|
Migration19(sharedPrefProvider),
|
||||||
Migration20(),
|
Migration20(),
|
||||||
Migration21()
|
Migration21(),
|
||||||
|
Migration22()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import javax.inject.Singleton
|
|||||||
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
|
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
|
||||||
|
|
||||||
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
|
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
|
||||||
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe<GradePointsStatistics>
|
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe<List<GradePointsStatistics>>
|
||||||
|
|
||||||
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
|
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
|
||||||
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradePointsStatistics>>
|
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradePointsStatistics>>
|
||||||
|
@ -4,6 +4,7 @@ import androidx.room.Dao
|
|||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
|
import io.reactivex.Single
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface MessagesDao : BaseDao<Message> {
|
interface MessagesDao : BaseDao<Message> {
|
||||||
@ -12,7 +13,7 @@ interface MessagesDao : BaseDao<Message> {
|
|||||||
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>
|
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE id = :id")
|
@Query("SELECT * FROM Messages WHERE id = :id")
|
||||||
fun load(id: Long): Maybe<Message>
|
fun load(id: Long): Single<Message>
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
|
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
|
||||||
fun loadDeleted(studentId: Int): Maybe<List<Message>>
|
fun loadDeleted(studentId: Int): Maybe<List<Message>>
|
||||||
|
@ -27,9 +27,6 @@ data class Semester(
|
|||||||
@ColumnInfo(name = "semester_name")
|
@ColumnInfo(name = "semester_name")
|
||||||
val semesterName: Int,
|
val semesterName: Int,
|
||||||
|
|
||||||
@ColumnInfo(name = "is_current")
|
|
||||||
val isCurrent: Boolean,
|
|
||||||
|
|
||||||
val start: LocalDate,
|
val start: LocalDate,
|
||||||
|
|
||||||
val end: LocalDate,
|
val end: LocalDate,
|
||||||
@ -43,4 +40,8 @@ data class Semester(
|
|||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
var id: Long = 0
|
var id: Long = 0
|
||||||
|
|
||||||
|
|
||||||
|
@ColumnInfo(name = "is_current")
|
||||||
|
var current: Boolean = false
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,9 @@ data class Student(
|
|||||||
@ColumnInfo(name = "school_id")
|
@ColumnInfo(name = "school_id")
|
||||||
val schoolSymbol: String,
|
val schoolSymbol: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name ="school_short")
|
||||||
|
val schoolShortName: String,
|
||||||
|
|
||||||
@ColumnInfo(name = "school_name")
|
@ColumnInfo(name = "school_name")
|
||||||
val schoolName: String,
|
val schoolName: String,
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ data class Timetable(
|
|||||||
val info: String,
|
val info: String,
|
||||||
|
|
||||||
@ColumnInfo(name = "student_plan")
|
@ColumnInfo(name = "student_plan")
|
||||||
val studentPlan: Boolean,
|
val isStudentPlan: Boolean,
|
||||||
|
|
||||||
val changes: Boolean,
|
val changes: Boolean,
|
||||||
|
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration22 : Migration(21, 22) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE Students ADD COLUMN school_short TEXT NOT NULL DEFAULT ''")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package io.github.wulkanowy.data.pojos
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
|
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
|
||||||
|
|
||||||
|
data class GradeStatisticsItem(
|
||||||
|
|
||||||
|
val type: ViewType,
|
||||||
|
|
||||||
|
val partial: List<GradeStatistics>,
|
||||||
|
|
||||||
|
val points: GradePointsStatistics?
|
||||||
|
)
|
@ -12,7 +12,7 @@ class AppCreatorRepository @Inject constructor(private val assets: AssetManager)
|
|||||||
fun getAppCreators(): Single<List<AppCreator>> {
|
fun getAppCreators(): Single<List<AppCreator>> {
|
||||||
return Single.fromCallable<List<AppCreator>> {
|
return Single.fromCallable<List<AppCreator>> {
|
||||||
Gson().fromJson(
|
Gson().fromJson(
|
||||||
assets.open("creators.json").bufferedReader().use { it.readText() },
|
assets.open("contributors.json").bufferedReader().use { it.readText() },
|
||||||
Array<AppCreator>::class.java
|
Array<AppCreator>::class.java
|
||||||
).toList()
|
).toList()
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
|
|||||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.utils.roundToDecimalPlaces
|
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -30,23 +29,17 @@ class GradeStatisticsLocal @Inject constructor(
|
|||||||
list.groupBy { it.grade }.map {
|
list.groupBy { it.grade }.map {
|
||||||
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
|
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
|
||||||
it.value.fold(0) { acc, e -> acc + e.amount }, false)
|
it.value.fold(0) { acc, e -> acc + e.amount }, false)
|
||||||
}
|
} + list
|
||||||
}
|
}
|
||||||
else -> gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)
|
else -> gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)
|
||||||
}.filter { it.isNotEmpty() }
|
}.filter { it.isNotEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String): Maybe<GradePointsStatistics> {
|
fun getGradesPointsStatistics(semester: Semester, subjectName: String): Maybe<List<GradePointsStatistics>> {
|
||||||
return when (subjectName) {
|
return when (subjectName) {
|
||||||
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).flatMap { list ->
|
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
|
||||||
if (list.isEmpty()) return@flatMap Maybe.empty<GradePointsStatistics>()
|
|
||||||
Maybe.just(GradePointsStatistics(semester.studentId, semester.semesterId, subjectName,
|
|
||||||
(list.fold(.0) { acc, e -> acc + e.others } / list.size).roundToDecimalPlaces(2),
|
|
||||||
(list.fold(.0) { acc, e -> acc + e.student } / list.size).roundToDecimalPlaces(2)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
|
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
|
||||||
}
|
}.filter { it.isNotEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
|
fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
|
||||||
|
@ -5,8 +5,9 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
|||||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
|
||||||
|
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import io.reactivex.Maybe
|
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -19,8 +20,8 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
private val remote: GradeStatisticsRemote
|
private val remote: GradeStatisticsRemote
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatistics>> {
|
fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatisticsItem>> {
|
||||||
return local.getGradesStatistics(semester, isSemester, subjectName).filter { !forceRefresh }
|
return local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.filter { !forceRefresh }
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
if (it) remote.getGradeStatistics(semester, isSemester)
|
if (it) remote.getGradeStatistics(semester, isSemester)
|
||||||
@ -31,21 +32,43 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
local.deleteGradesStatistics(old.uniqueSubtract(new))
|
local.deleteGradesStatistics(old.uniqueSubtract(new))
|
||||||
local.saveGradesStatistics(new.uniqueSubtract(old))
|
local.saveGradesStatistics(new.uniqueSubtract(old))
|
||||||
}
|
}
|
||||||
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
|
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.toSingle(emptyList()) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Maybe<GradePointsStatistics> {
|
fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Single<List<GradeStatisticsItem>> {
|
||||||
return local.getGradesPointsStatistics(semester, subjectName).filter { !forceRefresh }
|
return local.getGradesPointsStatistics(semester, subjectName).map { it.mapToStatisticsItem() }.filter { !forceRefresh }
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.flatMapMaybe {
|
.flatMap {
|
||||||
if (it) remote.getGradePointsStatistics(semester).toMaybe()
|
if (it) remote.getGradePointsStatistics(semester)
|
||||||
else Maybe.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}.flatMap { new ->
|
}.flatMap { new ->
|
||||||
local.getGradesPointsStatistics(semester).defaultIfEmpty(emptyList())
|
local.getGradesPointsStatistics(semester).toSingle(emptyList())
|
||||||
.doOnSuccess { old ->
|
.doOnSuccess { old ->
|
||||||
local.deleteGradesPointsStatistics(old.uniqueSubtract(new))
|
local.deleteGradesPointsStatistics(old.uniqueSubtract(new))
|
||||||
local.saveGradesPointsStatistics(new.uniqueSubtract(old))
|
local.saveGradesPointsStatistics(new.uniqueSubtract(old))
|
||||||
}
|
}
|
||||||
}.flatMap { local.getGradesPointsStatistics(semester, subjectName) })
|
}.flatMap { local.getGradesPointsStatistics(semester, subjectName).map { it.mapToStatisticsItem() }.toSingle(emptyList()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<GradeStatistics>.mapToStatisticItems(): List<GradeStatisticsItem> {
|
||||||
|
return groupBy { it.subject }.map {
|
||||||
|
GradeStatisticsItem(
|
||||||
|
type = ViewType.PARTIAL,
|
||||||
|
partial = it.value
|
||||||
|
.sortedByDescending { item -> item.grade }
|
||||||
|
.filter { item -> item.amount != 0 },
|
||||||
|
points = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<GradePointsStatistics>.mapToStatisticsItem(): List<GradeStatisticsItem> {
|
||||||
|
return map {
|
||||||
|
GradeStatisticsItem(
|
||||||
|
type = ViewType.POINTS,
|
||||||
|
partial = emptyList(),
|
||||||
|
points = it
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.logger
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import io.reactivex.Single
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LoggerRepository @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
|
fun getLastLogLines(): Single<List<String>> {
|
||||||
|
return getLastModified()
|
||||||
|
.map { it.readText() }
|
||||||
|
.map { it.split("\n") }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLogFiles(): Single<List<File>> {
|
||||||
|
return Single.fromCallable {
|
||||||
|
File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter {
|
||||||
|
it.name.endsWith(".log")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLastModified(): Single<File> {
|
||||||
|
return Single.fromCallable {
|
||||||
|
var lastModifiedTime = Long.MIN_VALUE
|
||||||
|
var chosenFile: File? = null
|
||||||
|
File(context.filesDir.absolutePath).listFiles(File::isFile)?.forEach { file ->
|
||||||
|
if (file.lastModified() > lastModifiedTime) {
|
||||||
|
lastModifiedTime = file.lastModified()
|
||||||
|
chosenFile = file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chosenFile == null) throw FileNotFoundException("Log file not found")
|
||||||
|
chosenFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.luckynumber
|
|||||||
|
|
||||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -23,7 +23,7 @@ class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumbe
|
|||||||
luckyNumberDb.deleteAll(listOf(luckyNumber))
|
luckyNumberDb.deleteAll(listOf(luckyNumber))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe<LuckyNumber> {
|
fun getLuckyNumber(student: Student, date: LocalDate): Maybe<LuckyNumber> {
|
||||||
return luckyNumberDb.load(semester.studentId, date)
|
return luckyNumberDb.load(student.studentId, date)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package io.github.wulkanowy.data.repositories.luckynumber
|
package io.github.wulkanowy.data.repositories.luckynumber
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
@ -11,14 +11,13 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) {
|
class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) {
|
||||||
|
|
||||||
fun getLuckyNumber(semester: Semester): Maybe<LuckyNumber> {
|
fun getLuckyNumber(student: Student): Maybe<LuckyNumber> {
|
||||||
return sdk.getLuckyNumber()
|
return sdk.getLuckyNumber(student.schoolShortName).map {
|
||||||
.map {
|
LuckyNumber(
|
||||||
LuckyNumber(
|
studentId = student.studentId,
|
||||||
studentId = semester.studentId,
|
date = LocalDate.now(),
|
||||||
date = LocalDate.now(),
|
luckyNumber = it
|
||||||
luckyNumber = it
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.data.repositories.luckynumber
|
|||||||
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
@ -18,14 +18,14 @@ class LuckyNumberRepository @Inject constructor(
|
|||||||
private val remote: LuckyNumberRemote
|
private val remote: LuckyNumberRemote
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getLuckyNumber(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> {
|
fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> {
|
||||||
return local.getLuckyNumber(semester, LocalDate.now()).filter { !forceRefresh }
|
return local.getLuckyNumber(student, LocalDate.now()).filter { !forceRefresh }
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.flatMapMaybe {
|
.flatMapMaybe {
|
||||||
if (it) remote.getLuckyNumber(semester)
|
if (it) remote.getLuckyNumber(student)
|
||||||
else Maybe.error(UnknownHostException())
|
else Maybe.error(UnknownHostException())
|
||||||
}.flatMap { new ->
|
}.flatMap { new ->
|
||||||
local.getLuckyNumber(semester, LocalDate.now())
|
local.getLuckyNumber(student, LocalDate.now())
|
||||||
.doOnSuccess { old ->
|
.doOnSuccess { old ->
|
||||||
if (new != old) {
|
if (new != old) {
|
||||||
local.deleteLuckyNumber(old)
|
local.deleteLuckyNumber(old)
|
||||||
@ -39,13 +39,13 @@ class LuckyNumberRepository @Inject constructor(
|
|||||||
if (notify) isNotified = false
|
if (notify) isNotified = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}.flatMap({ local.getLuckyNumber(semester, LocalDate.now()) }, { Maybe.error(it) },
|
}.flatMap({ local.getLuckyNumber(student, LocalDate.now()) }, { Maybe.error(it) },
|
||||||
{ local.getLuckyNumber(semester, LocalDate.now()) })
|
{ local.getLuckyNumber(student, LocalDate.now()) })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNotNotifiedLuckyNumber(semester: Semester): Maybe<LuckyNumber> {
|
fun getNotNotifiedLuckyNumber(student: Student): Maybe<LuckyNumber> {
|
||||||
return local.getLuckyNumber(semester, LocalDate.now()).filter { !it.isNotified }
|
return local.getLuckyNumber(student, LocalDate.now()).filter { !it.isNotified }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable {
|
fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable {
|
||||||
|
@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.Message
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
|
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
|
import io.reactivex.Single
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) {
|
|||||||
messagesDb.deleteAll(messages)
|
messagesDb.deleteAll(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessage(id: Long): Maybe<Message> {
|
fun getMessage(id: Long): Single<Message> {
|
||||||
return messagesDb.load(id)
|
return messagesDb.load(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.utils.uniqueSubtract
|
|||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
import timber.log.Timber
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -51,21 +52,26 @@ class MessageRepository @Inject constructor(
|
|||||||
return Single.just(sdkHelper.init(student))
|
return Single.just(sdkHelper.init(student))
|
||||||
.flatMap { _ ->
|
.flatMap { _ ->
|
||||||
local.getMessage(messageDbId)
|
local.getMessage(messageDbId)
|
||||||
.filter { it.content.isNotEmpty() }
|
.filter {
|
||||||
|
it.content.isNotEmpty().also { status ->
|
||||||
|
Timber.d("Message content in db empty: ${!status}")
|
||||||
|
}
|
||||||
|
}
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
if (it) local.getMessage(messageDbId).toSingle()
|
if (it) local.getMessage(messageDbId)
|
||||||
else Single.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}
|
}
|
||||||
.flatMap { dbMessage ->
|
.flatMap { dbMessage ->
|
||||||
remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
|
remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
|
||||||
local.updateMessages(listOf(dbMessage.copy(unread = false).apply {
|
local.updateMessages(listOf(dbMessage.copy(unread = !markAsRead).apply {
|
||||||
id = dbMessage.id
|
id = dbMessage.id
|
||||||
content = content.ifBlank { it }
|
content = content.ifBlank { it }
|
||||||
}))
|
}))
|
||||||
|
Timber.d("Message $messageDbId with blank content: ${dbMessage.content.isBlank()}, marked as read")
|
||||||
}
|
}
|
||||||
}.flatMap {
|
}.flatMap {
|
||||||
local.getMessage(messageDbId).toSingle()
|
local.getMessage(messageDbId)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.recover
|
||||||
|
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.reactivex.Single
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class RecoverRemote @Inject constructor(private val sdk: Sdk) {
|
||||||
|
|
||||||
|
fun getReCaptchaSiteKey(host: String, symbol: String): Single<Pair<String, String>> {
|
||||||
|
return sdk.getPasswordResetCaptchaCode(host, symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendRecoverRequest(url: String, symbol: String, email: String, reCaptchaResponse: String): Single<String> {
|
||||||
|
return sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.recover
|
||||||
|
|
||||||
|
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||||
|
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||||
|
import io.reactivex.Single
|
||||||
|
import java.net.UnknownHostException
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class RecoverRepository @Inject constructor(private val settings: InternetObservingSettings, private val remote: RecoverRemote) {
|
||||||
|
|
||||||
|
fun getReCaptchaSiteKey(host: String, symbol: String): Single<Pair<String, String>> {
|
||||||
|
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
||||||
|
if (it) remote.getReCaptchaSiteKey(host, symbol)
|
||||||
|
else Single.error(UnknownHostException())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendRecoverRequest(url: String, symbol: String, email: String, reCaptchaResponse: String): Single<String> {
|
||||||
|
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
||||||
|
if (it) remote.sendRecoverRequest(url, symbol, email, reCaptchaResponse)
|
||||||
|
else Single.error(UnknownHostException())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,6 @@ class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSemesters(student: Student): Maybe<List<Semester>> {
|
fun getSemesters(student: Student): Maybe<List<Semester>> {
|
||||||
return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() }
|
return semesterDb.loadAll(student.studentId, student.classId).filter { it.isNotEmpty() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ class SemesterRemote @Inject constructor(private val sdk: Sdk) {
|
|||||||
schoolYear = it.schoolYear,
|
schoolYear = it.schoolYear,
|
||||||
semesterId = it.semesterId,
|
semesterId = it.semesterId,
|
||||||
semesterName = it.semesterNumber,
|
semesterName = it.semesterNumber,
|
||||||
isCurrent = it.current,
|
|
||||||
start = it.start,
|
start = it.start,
|
||||||
end = it.end,
|
end = it.end,
|
||||||
classId = it.classId,
|
classId = it.classId,
|
||||||
|
@ -5,10 +5,11 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
|||||||
import io.github.wulkanowy.data.SdkHelper
|
import io.github.wulkanowy.data.SdkHelper
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||||
|
import io.github.wulkanowy.utils.isCurrent
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import timber.log.Timber
|
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -21,28 +22,30 @@ class SemesterRepository @Inject constructor(
|
|||||||
private val sdkHelper: SdkHelper
|
private val sdkHelper: SdkHelper
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getSemesters(student: Student, forceRefresh: Boolean = false): Single<List<Semester>> {
|
fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): Single<List<Semester>> {
|
||||||
return Maybe.just(sdkHelper.init(student))
|
return Maybe.just(sdkHelper.init(student))
|
||||||
.flatMap { local.getSemesters(student).filter { !forceRefresh } }
|
.flatMap {
|
||||||
|
local.getSemesters(student).filter { !forceRefresh }.filter {
|
||||||
|
if (refreshOnNoCurrent) {
|
||||||
|
it.any { semester -> semester.isCurrent }
|
||||||
|
} else true
|
||||||
|
}
|
||||||
|
}
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
if (it) remote.getSemesters(student) else Single.error(UnknownHostException())
|
if (it) remote.getSemesters(student)
|
||||||
|
else Single.error(UnknownHostException())
|
||||||
}.flatMap { new ->
|
}.flatMap { new ->
|
||||||
val currentSemesters = new.filter { it.isCurrent }
|
if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!")
|
||||||
if (currentSemesters.size == 1) {
|
|
||||||
local.getSemesters(student).toSingle(emptyList())
|
local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old ->
|
||||||
.doOnSuccess { old ->
|
local.deleteSemesters(old.uniqueSubtract(new))
|
||||||
local.deleteSemesters(old.uniqueSubtract(new))
|
local.saveSemesters(new.uniqueSubtract(old))
|
||||||
local.saveSemesters(new.uniqueSubtract(old))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Timber.i("Current semesters list:\n${new.joinToString(separator = "\n")}")
|
|
||||||
throw IllegalArgumentException("Current semester can be only one.")
|
|
||||||
}
|
}
|
||||||
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
|
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single<Semester> {
|
fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single<Semester> {
|
||||||
return getSemesters(student, forceRefresh).map { item -> item.single { it.isCurrent } }
|
return getSemesters(student, forceRefresh).map { it.getCurrentOrLast() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ class StudentRemote @Inject constructor(private val sdk: Sdk) {
|
|||||||
userLoginId = student.userLoginId,
|
userLoginId = student.userLoginId,
|
||||||
studentName = student.studentName,
|
studentName = student.studentName,
|
||||||
schoolSymbol = student.schoolSymbol,
|
schoolSymbol = student.schoolSymbol,
|
||||||
|
schoolShortName = student.schoolShortName,
|
||||||
schoolName = student.schoolName,
|
schoolName = student.schoolName,
|
||||||
className = student.className,
|
className = student.className,
|
||||||
classId = student.classId,
|
classId = student.classId,
|
||||||
|
@ -29,7 +29,7 @@ class StudentRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String = ""): Single<List<Student>> {
|
fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String): Single<List<Student>> {
|
||||||
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
||||||
if (it) remote.getStudentsScrapper(email, password, endpoint, symbol)
|
if (it) remote.getStudentsScrapper(email, password, endpoint, symbol)
|
||||||
else Single.error(UnknownHostException("No internet connection"))
|
else Single.error(UnknownHostException("No internet connection"))
|
||||||
|
@ -30,7 +30,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) {
|
|||||||
teacher = it.teacher,
|
teacher = it.teacher,
|
||||||
teacherOld = it.teacherOld,
|
teacherOld = it.teacherOld,
|
||||||
info = it.info,
|
info = it.info,
|
||||||
studentPlan = it.studentPlan,
|
isStudentPlan = it.studentPlan,
|
||||||
changes = it.changes,
|
changes = it.changes,
|
||||||
canceled = it.canceled
|
canceled = it.canceled
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,12 @@ import dagger.Module
|
|||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.android.ContributesAndroidInjector
|
import dagger.android.ContributesAndroidInjector
|
||||||
import dagger.multibindings.IntoSet
|
import dagger.multibindings.IntoSet
|
||||||
|
import io.github.wulkanowy.services.sync.channels.Channel
|
||||||
|
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||||
|
import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
|
||||||
|
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
|
||||||
|
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||||
|
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||||
import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
|
import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
|
||||||
import io.github.wulkanowy.services.sync.works.AttendanceWork
|
import io.github.wulkanowy.services.sync.works.AttendanceWork
|
||||||
import io.github.wulkanowy.services.sync.works.CompletedLessonWork
|
import io.github.wulkanowy.services.sync.works.CompletedLessonWork
|
||||||
@ -29,11 +35,10 @@ import javax.inject.Singleton
|
|||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@AssistedModule
|
@AssistedModule
|
||||||
@Module(includes = [AssistedInject_ServicesModule::class, ServicesModule.Static::class])
|
@Module(includes = [AssistedInject_ServicesModule::class])
|
||||||
abstract class ServicesModule {
|
abstract class ServicesModule {
|
||||||
|
|
||||||
@Module
|
companion object {
|
||||||
object Static {
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideWorkManager(context: Context) = WorkManager.getInstance(context)
|
fun provideWorkManager(context: Context) = WorkManager.getInstance(context)
|
||||||
@ -101,4 +106,24 @@ abstract class ServicesModule {
|
|||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work
|
abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun provideDebugChannel(channel: DebugChannel): Channel
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun provideLuckyNumberChannel(channel: LuckyNumberChannel): Channel
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun provideNewGradesChannel(channel: NewGradesChannel): Channel
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun provideNewMessageChannel(channel: NewMessagesChannel): Channel
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun provideNewNotesChannel(channel: NewNotesChannel): Channel
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.services.sync
|
|||||||
|
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.os.Build.VERSION_CODES.O
|
import android.os.Build.VERSION_CODES.O
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.work.BackoffPolicy.EXPONENTIAL
|
import androidx.work.BackoffPolicy.EXPONENTIAL
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
|
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
|
||||||
@ -13,8 +14,7 @@ import androidx.work.WorkManager
|
|||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
|
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
|
||||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
import io.github.wulkanowy.services.sync.channels.Channel
|
||||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import io.github.wulkanowy.utils.isHolidays
|
import io.github.wulkanowy.utils.isHolidays
|
||||||
import org.threeten.bp.LocalDate.now
|
import org.threeten.bp.LocalDate.now
|
||||||
@ -27,9 +27,9 @@ import javax.inject.Singleton
|
|||||||
class SyncManager @Inject constructor(
|
class SyncManager @Inject constructor(
|
||||||
private val workManager: WorkManager,
|
private val workManager: WorkManager,
|
||||||
private val preferencesRepository: PreferencesRepository,
|
private val preferencesRepository: PreferencesRepository,
|
||||||
|
channels: Set<@JvmSuppressWildcards Channel>,
|
||||||
|
notificationManager: NotificationManagerCompat,
|
||||||
sharedPrefProvider: SharedPrefProvider,
|
sharedPrefProvider: SharedPrefProvider,
|
||||||
newEntriesChannel: NewEntriesChannel,
|
|
||||||
debugChannel: DebugChannel,
|
|
||||||
appInfo: AppInfo
|
appInfo: AppInfo
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -37,8 +37,8 @@ class SyncManager @Inject constructor(
|
|||||||
if (now().isHolidays) stopSyncWorker()
|
if (now().isHolidays) stopSyncWorker()
|
||||||
|
|
||||||
if (SDK_INT > O) {
|
if (SDK_INT > O) {
|
||||||
newEntriesChannel.create()
|
channels.forEach { it.create() }
|
||||||
if (appInfo.isDebug) debugChannel.create()
|
notificationManager.deleteNotificationChannel("new_entries_channel")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1L) != appInfo.versionCode.toLong()) {
|
if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1L) != appInfo.versionCode.toLong()) {
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package io.github.wulkanowy.services.sync.channels
|
||||||
|
|
||||||
|
interface Channel {
|
||||||
|
|
||||||
|
fun create()
|
||||||
|
}
|
@ -7,19 +7,22 @@ import android.app.NotificationManager.IMPORTANCE_DEFAULT
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@TargetApi(26)
|
@TargetApi(26)
|
||||||
class DebugChannel @Inject constructor(
|
class DebugChannel @Inject constructor(
|
||||||
private val notificationManager: NotificationManagerCompat,
|
private val notificationManager: NotificationManagerCompat,
|
||||||
private val context: Context
|
private val context: Context,
|
||||||
) {
|
private val appInfo: AppInfo
|
||||||
|
) : Channel {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL_ID = "debug_channel"
|
const val CHANNEL_ID = "debug_channel"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create() {
|
override fun create() {
|
||||||
|
if (appInfo.isDebug) return
|
||||||
notificationManager.createNotificationChannel(
|
notificationManager.createNotificationChannel(
|
||||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT)
|
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT)
|
||||||
.apply {
|
.apply {
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package io.github.wulkanowy.services.sync.channels
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@TargetApi(26)
|
||||||
|
class LuckyNumberChannel @Inject constructor(
|
||||||
|
private val notificationManager: NotificationManagerCompat,
|
||||||
|
private val context: Context
|
||||||
|
) : Channel {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CHANNEL_ID = "lucky_number_channel"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create() {
|
||||||
|
notificationManager.createNotificationChannel(
|
||||||
|
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_lucky_number), NotificationManager.IMPORTANCE_HIGH)
|
||||||
|
.apply {
|
||||||
|
enableLights(true)
|
||||||
|
enableVibration(true)
|
||||||
|
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package io.github.wulkanowy.services.sync.channels
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@TargetApi(26)
|
||||||
|
class NewGradesChannel @Inject constructor(
|
||||||
|
private val notificationManager: NotificationManagerCompat,
|
||||||
|
private val context: Context
|
||||||
|
) : Channel {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CHANNEL_ID = "new_grade_channel"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create() {
|
||||||
|
notificationManager.createNotificationChannel(
|
||||||
|
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_grades), NotificationManager.IMPORTANCE_HIGH)
|
||||||
|
.apply {
|
||||||
|
enableLights(true)
|
||||||
|
enableVibration(true)
|
||||||
|
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package io.github.wulkanowy.services.sync.channels
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@TargetApi(26)
|
||||||
|
class NewMessagesChannel @Inject constructor(
|
||||||
|
private val notificationManager: NotificationManagerCompat,
|
||||||
|
private val context: Context
|
||||||
|
) : Channel {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CHANNEL_ID = "new_message_channel"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create() {
|
||||||
|
notificationManager.createNotificationChannel(
|
||||||
|
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_message), NotificationManager.IMPORTANCE_HIGH)
|
||||||
|
.apply {
|
||||||
|
enableLights(true)
|
||||||
|
enableVibration(true)
|
||||||
|
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +1,31 @@
|
|||||||
package io.github.wulkanowy.services.sync.channels
|
package io.github.wulkanowy.services.sync.channels
|
||||||
|
|
||||||
import android.annotation.TargetApi
|
import android.annotation.TargetApi
|
||||||
import android.app.Notification.VISIBILITY_PUBLIC
|
import android.app.Notification
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager.IMPORTANCE_HIGH
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@TargetApi(26)
|
@TargetApi(26)
|
||||||
class NewEntriesChannel @Inject constructor(
|
class NewNotesChannel @Inject constructor(
|
||||||
private val notificationManager: NotificationManagerCompat,
|
private val notificationManager: NotificationManagerCompat,
|
||||||
private val context: Context
|
private val context: Context
|
||||||
) {
|
) : Channel {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL_ID = "new_entries_channel"
|
const val CHANNEL_ID = "new_notes_channel"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create() {
|
override fun create() {
|
||||||
notificationManager.createNotificationChannel(
|
notificationManager.createNotificationChannel(
|
||||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_entries), IMPORTANCE_HIGH)
|
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_notes), NotificationManager.IMPORTANCE_HIGH)
|
||||||
.apply {
|
.apply {
|
||||||
enableLights(true)
|
enableLights(true)
|
||||||
enableVibration(true)
|
enableVibration(true)
|
||||||
lockscreenVisibility = VISIBILITY_PUBLIC
|
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
@ -38,7 +38,7 @@ class GradeWork @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun notify(grades: List<Grade>) {
|
private fun notify(grades: List<Grade>) {
|
||||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
|
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewGradesChannel.CHANNEL_ID)
|
||||||
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size))
|
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size))
|
||||||
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
|
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
|
||||||
.setSmallIcon(R.drawable.ic_stat_grade)
|
.setSmallIcon(R.drawable.ic_stat_grade)
|
||||||
|
@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
||||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
@ -29,8 +29,8 @@ class LuckyNumberWork @Inject constructor(
|
|||||||
) : Work {
|
) : Work {
|
||||||
|
|
||||||
override fun create(student: Student, semester: Semester): Completable {
|
override fun create(student: Student, semester: Semester): Completable {
|
||||||
return luckyNumberRepository.getLuckyNumber(semester, true, preferencesRepository.isNotificationsEnable)
|
return luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable)
|
||||||
.flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(semester) }
|
.flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(student) }
|
||||||
.flatMapCompletable {
|
.flatMapCompletable {
|
||||||
notify(it)
|
notify(it)
|
||||||
luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
|
luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
|
||||||
@ -38,7 +38,7 @@ class LuckyNumberWork @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun notify(luckyNumber: LuckyNumber) {
|
private fun notify(luckyNumber: LuckyNumber) {
|
||||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
|
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, LuckyNumberChannel.CHANNEL_ID)
|
||||||
.setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title))
|
.setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title))
|
||||||
.setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber))
|
.setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber))
|
||||||
.setSmallIcon(R.drawable.ic_stat_luckynumber)
|
.setSmallIcon(R.drawable.ic_stat_luckynumber)
|
||||||
|
@ -14,7 +14,7 @@ import io.github.wulkanowy.data.db.entities.Student
|
|||||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
|
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
|
||||||
import io.github.wulkanowy.data.repositories.message.MessageRepository
|
import io.github.wulkanowy.data.repositories.message.MessageRepository
|
||||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
@ -39,7 +39,7 @@ class MessageWork @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun notify(messages: List<Message>) {
|
private fun notify(messages: List<Message>) {
|
||||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
|
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewMessagesChannel.CHANNEL_ID)
|
||||||
.setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size))
|
.setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size))
|
||||||
.setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size))
|
.setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size))
|
||||||
.setSmallIcon(R.drawable.ic_stat_message)
|
.setSmallIcon(R.drawable.ic_stat_message)
|
||||||
|
@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.note.NoteRepository
|
import io.github.wulkanowy.data.repositories.note.NoteRepository
|
||||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
@ -38,7 +38,7 @@ class NoteWork @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun notify(notes: List<Note>) {
|
private fun notify(notes: List<Note>) {
|
||||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
|
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewNotesChannel.CHANNEL_ID)
|
||||||
.setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size))
|
.setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size))
|
||||||
.setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
|
.setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
|
||||||
.setSmallIcon(R.drawable.ic_stat_note)
|
.setSmallIcon(R.drawable.ic_stat_note)
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Intent
|
|||||||
import android.widget.RemoteViewsService
|
import android.widget.RemoteViewsService
|
||||||
import dagger.android.AndroidInjection
|
import dagger.android.AndroidInjection
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
|
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||||
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
||||||
@ -22,6 +23,9 @@ class TimetableWidgetService : RemoteViewsService() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var semesterRepo: SemesterRepository
|
lateinit var semesterRepo: SemesterRepository
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var prefRepository: PreferencesRepository
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var sharedPref: SharedPrefProvider
|
lateinit var sharedPref: SharedPrefProvider
|
||||||
|
|
||||||
@ -30,6 +34,6 @@ class TimetableWidgetService : RemoteViewsService() {
|
|||||||
|
|
||||||
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
|
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
|
||||||
AndroidInjection.inject(this)
|
AndroidInjection.inject(this)
|
||||||
return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, schedulers, applicationContext, intent)
|
return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, schedulers, applicationContext, intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,9 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
|||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment
|
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
|
||||||
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.about.logviewer.LogViewerFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
@ -45,7 +46,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
|||||||
|
|
||||||
override val creatorsRes: Triple<String, String, Drawable?>?
|
override val creatorsRes: Triple<String, String, Drawable?>?
|
||||||
get() = context?.run {
|
get() = context?.run {
|
||||||
Triple(getString(R.string.about_creator), getString(R.string.about_creator_summary), getCompatDrawable(R.drawable.ic_about_creator))
|
Triple(getString(R.string.about_contributor), getString(R.string.about_contributor_summary), getCompatDrawable(R.drawable.ic_about_creator))
|
||||||
}
|
}
|
||||||
|
|
||||||
override val feedbackRes: Triple<String, String, Drawable?>?
|
override val feedbackRes: Triple<String, String, Drawable?>?
|
||||||
@ -110,6 +111,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openLogViewer() {
|
||||||
|
if (appInfo.isDebug) (activity as? MainActivity)?.pushView(LogViewerFragment.newInstance())
|
||||||
|
}
|
||||||
|
|
||||||
override fun openDiscordInvite() {
|
override fun openDiscordInvite() {
|
||||||
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
|
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
|
||||||
}
|
}
|
||||||
@ -150,7 +155,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun openCreators() {
|
override fun openCreators() {
|
||||||
(activity as? MainActivity)?.pushView(CreatorFragment.newInstance())
|
(activity as? MainActivity)?.pushView(ContributorFragment.newInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openPrivacyPolicy() {
|
override fun openPrivacyPolicy() {
|
||||||
|
@ -27,6 +27,11 @@ class AboutPresenter @Inject constructor(
|
|||||||
if (item !is AboutItem) return
|
if (item !is AboutItem) return
|
||||||
view?.run {
|
view?.run {
|
||||||
when (item.title) {
|
when (item.title) {
|
||||||
|
versionRes?.first -> {
|
||||||
|
Timber.i("Opening log viewer")
|
||||||
|
openLogViewer()
|
||||||
|
analytics.logEvent("about_open", "name" to "log_viewer")
|
||||||
|
}
|
||||||
feedbackRes?.first -> {
|
feedbackRes?.first -> {
|
||||||
Timber.i("Opening email client")
|
Timber.i("Opening email client")
|
||||||
openEmailClient()
|
openEmailClient()
|
||||||
|
@ -25,6 +25,8 @@ interface AboutView : BaseView {
|
|||||||
|
|
||||||
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
|
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
|
||||||
|
|
||||||
|
fun openLogViewer()
|
||||||
|
|
||||||
fun openDiscordInvite()
|
fun openDiscordInvite()
|
||||||
|
|
||||||
fun openEmailClient()
|
fun openEmailClient()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package io.github.wulkanowy.ui.modules.about.creator
|
package io.github.wulkanowy.ui.modules.about.contributor
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -18,18 +18,18 @@ import io.github.wulkanowy.utils.setOnItemClickListener
|
|||||||
import kotlinx.android.synthetic.main.fragment_creator.*
|
import kotlinx.android.synthetic.main.fragment_creator.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView {
|
class ContributorFragment : BaseFragment(), ContributorView, MainView.TitledView {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: CreatorPresenter
|
lateinit var presenter: ContributorPresenter
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var creatorsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
lateinit var creatorsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||||
|
|
||||||
override val titleStringId get() = R.string.creators_title
|
override val titleStringId get() = R.string.contributors_title
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance() = CreatorFragment()
|
fun newInstance() = ContributorFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -53,7 +53,7 @@ class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView {
|
|||||||
creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() }
|
creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateData(data: List<CreatorItem>) {
|
override fun updateData(data: List<ContributorItem>) {
|
||||||
creatorsAdapter.updateDataSet(data)
|
creatorsAdapter.updateDataSet(data)
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package io.github.wulkanowy.ui.modules.about.creator
|
package io.github.wulkanowy.ui.modules.about.contributor
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import coil.api.load
|
import coil.api.load
|
||||||
@ -10,11 +10,12 @@ import eu.davidea.viewholders.FlexibleViewHolder
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.pojos.AppCreator
|
import io.github.wulkanowy.data.pojos.AppCreator
|
||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.item_creator.*
|
import kotlinx.android.synthetic.main.item_contributor.*
|
||||||
|
|
||||||
class CreatorItem(val creator: AppCreator) : AbstractFlexibleItem<CreatorItem.ViewHolder>() {
|
class ContributorItem(val creator: AppCreator) :
|
||||||
|
AbstractFlexibleItem<ContributorItem.ViewHolder>() {
|
||||||
|
|
||||||
override fun getLayoutRes() = R.layout.item_creator
|
override fun getLayoutRes() = R.layout.item_contributor
|
||||||
|
|
||||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ class CreatorItem(val creator: AppCreator) : AbstractFlexibleItem<CreatorItem.Vi
|
|||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
other as CreatorItem
|
other as ContributorItem
|
||||||
|
|
||||||
if (creator != other.creator) return false
|
if (creator != other.creator) return false
|
||||||
|
|
@ -1,30 +1,28 @@
|
|||||||
package io.github.wulkanowy.ui.modules.about.creator
|
package io.github.wulkanowy.ui.modules.about.contributor
|
||||||
|
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import io.github.wulkanowy.data.pojos.AppCreator
|
|
||||||
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
|
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
|
||||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.utils.SchedulersProvider
|
import io.github.wulkanowy.utils.SchedulersProvider
|
||||||
import io.reactivex.Single
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CreatorPresenter @Inject constructor(
|
class ContributorPresenter @Inject constructor(
|
||||||
schedulers: SchedulersProvider,
|
schedulers: SchedulersProvider,
|
||||||
errorHandler: ErrorHandler,
|
errorHandler: ErrorHandler,
|
||||||
studentRepository: StudentRepository,
|
studentRepository: StudentRepository,
|
||||||
private val appCreatorRepository: AppCreatorRepository
|
private val appCreatorRepository: AppCreatorRepository
|
||||||
) : BasePresenter<CreatorView>(errorHandler, studentRepository, schedulers) {
|
) : BasePresenter<ContributorView>(errorHandler, studentRepository, schedulers) {
|
||||||
|
|
||||||
override fun onAttachView(view: CreatorView) {
|
override fun onAttachView(view: ContributorView) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
||||||
if (item !is CreatorItem) return
|
if (item !is ContributorItem) return
|
||||||
view?.openUserGithubPage(item.creator.githubUsername)
|
view?.openUserGithubPage(item.creator.githubUsername)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +32,7 @@ class CreatorPresenter @Inject constructor(
|
|||||||
|
|
||||||
private fun loadData() {
|
private fun loadData() {
|
||||||
disposable.add(appCreatorRepository.getAppCreators()
|
disposable.add(appCreatorRepository.getAppCreators()
|
||||||
.map { it.map { creator -> CreatorItem(creator) } }
|
.map { it.map { creator -> ContributorItem(creator) } }
|
||||||
.subscribeOn(schedulers.backgroundThread)
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
.observeOn(schedulers.mainThread)
|
.observeOn(schedulers.mainThread)
|
||||||
.doFinally { view?.showProgress(false) }
|
.doFinally { view?.showProgress(false) }
|
@ -1,12 +1,12 @@
|
|||||||
package io.github.wulkanowy.ui.modules.about.creator
|
package io.github.wulkanowy.ui.modules.about.contributor
|
||||||
|
|
||||||
import io.github.wulkanowy.ui.base.BaseView
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
interface CreatorView : BaseView {
|
interface ContributorView : BaseView {
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun updateData(data: List<CreatorItem>)
|
fun updateData(data: List<ContributorItem>)
|
||||||
|
|
||||||
fun openUserGithubPage(username: String)
|
fun openUserGithubPage(username: String)
|
||||||
|
|
@ -0,0 +1,22 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
class LogViewerAdapter : RecyclerView.Adapter<LogViewerAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
var lines = emptyList<String>()
|
||||||
|
|
||||||
|
class ViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
return ViewHolder(TextView(parent.context))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = lines.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
holder.textView.text = lines[position]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.Intent.EXTRA_EMAIL
|
||||||
|
import android.content.Intent.EXTRA_STREAM
|
||||||
|
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build.VERSION.SDK_INT
|
||||||
|
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import io.github.wulkanowy.BuildConfig.APPLICATION_ID
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
import kotlinx.android.synthetic.main.fragment_logviewer.*
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LogViewerFragment : BaseFragment(), LogViewerView, MainView.TitledView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: LogViewerPresenter
|
||||||
|
|
||||||
|
private val logAdapter = LogViewerAdapter()
|
||||||
|
|
||||||
|
override val titleStringId: Int
|
||||||
|
get() = R.string.logviewer_title
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = LogViewerFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_logviewer, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
messageContainer = logViewerRecycler
|
||||||
|
presenter.onAttachView(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
inflater.inflate(R.menu.action_menu_logviewer, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return if (item.itemId == R.id.logViewerMenuShare) presenter.onShareLogsSelected()
|
||||||
|
else false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
with(logViewerRecycler) {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = logAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
logViewRefreshButton.setOnClickListener { presenter.onRefreshClick() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setLines(lines: List<String>) {
|
||||||
|
logAdapter.lines = lines
|
||||||
|
logAdapter.notifyDataSetChanged()
|
||||||
|
logViewerRecycler.scrollToPosition(lines.size - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shareLogs(files: List<File>) {
|
||||||
|
val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
|
||||||
|
type = "text/plain"
|
||||||
|
putExtra(EXTRA_EMAIL, arrayOf("wulkanowyinc@gmail.com"))
|
||||||
|
addFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
putParcelableArrayListExtra(EXTRA_STREAM, ArrayList(files.map {
|
||||||
|
if (SDK_INT < LOLLIPOP) Uri.fromFile(it)
|
||||||
|
else FileProvider.getUriForFile(requireContext(), "$APPLICATION_ID.fileprovider", it)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivity(Intent.createChooser(intent, getString(R.string.logviewer_share)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.repositories.logger.LoggerRepository
|
||||||
|
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.utils.SchedulersProvider
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LogViewerPresenter @Inject constructor(
|
||||||
|
schedulers: SchedulersProvider,
|
||||||
|
errorHandler: ErrorHandler,
|
||||||
|
studentRepository: StudentRepository,
|
||||||
|
private val loggerRepository: LoggerRepository
|
||||||
|
) : BasePresenter<LogViewerView>(errorHandler, studentRepository, schedulers) {
|
||||||
|
|
||||||
|
override fun onAttachView(view: LogViewerView) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
view.initView()
|
||||||
|
loadLogFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onShareLogsSelected(): Boolean {
|
||||||
|
disposable.add(loggerRepository.getLogFiles()
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.subscribe({
|
||||||
|
Timber.i("Loading logs files result: ${it.joinToString { it.name }}")
|
||||||
|
view?.shareLogs(it)
|
||||||
|
}, {
|
||||||
|
Timber.i("Loading logs files result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
}))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRefreshClick() {
|
||||||
|
loadLogFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadLogFile() {
|
||||||
|
disposable.add(loggerRepository.getLastLogLines()
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.subscribe({
|
||||||
|
Timber.i("Loading last log file result: load ${it.size} lines")
|
||||||
|
view?.setLines(it)
|
||||||
|
}, {
|
||||||
|
Timber.i("Loading last log file result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||||
|
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
interface LogViewerView : BaseView {
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun setLines(lines: List<String>)
|
||||||
|
|
||||||
|
fun shareLogs(files: List<File>)
|
||||||
|
}
|
@ -36,6 +36,8 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
|
|||||||
fun newInstance() = AttendanceSummaryFragment()
|
fun newInstance() = AttendanceSummaryFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val totalString get() = getString(R.string.attendance_summary_total)
|
||||||
|
|
||||||
override val titleStringId get() = R.string.attendance_title
|
override val titleStringId get() = R.string.attendance_title
|
||||||
|
|
||||||
override val isViewEmpty get() = attendanceSummaryAdapter.isEmpty
|
override val isViewEmpty get() = attendanceSummaryAdapter.isEmpty
|
||||||
|
@ -12,6 +12,7 @@ import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
|||||||
import io.github.wulkanowy.utils.SchedulersProvider
|
import io.github.wulkanowy.utils.SchedulersProvider
|
||||||
import io.github.wulkanowy.utils.calculatePercentage
|
import io.github.wulkanowy.utils.calculatePercentage
|
||||||
import io.github.wulkanowy.utils.getFormattedName
|
import io.github.wulkanowy.utils.getFormattedName
|
||||||
|
import org.threeten.bp.Month
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.lang.String.format
|
import java.lang.String.format
|
||||||
import java.util.Locale.FRANCE
|
import java.util.Locale.FRANCE
|
||||||
@ -144,8 +145,25 @@ class AttendanceSummaryPresenter @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createAttendanceSummaryTotalItem(attendanceSummary: List<AttendanceSummary>): AttendanceSummaryItem {
|
||||||
|
return AttendanceSummaryItem(
|
||||||
|
month = view?.totalString.orEmpty(),
|
||||||
|
percentage = formatPercentage(attendanceSummary.calculatePercentage()),
|
||||||
|
present = attendanceSummary.sumBy { it.presence }.toString(),
|
||||||
|
absence = attendanceSummary.sumBy { it.absence }.toString(),
|
||||||
|
excusedAbsence = attendanceSummary.sumBy { it.absenceExcused }.toString(),
|
||||||
|
schoolAbsence = attendanceSummary.sumBy { it.absenceForSchoolReasons }.toString(),
|
||||||
|
exemption = attendanceSummary.sumBy { it.exemption }.toString(),
|
||||||
|
lateness = attendanceSummary.sumBy { it.lateness }.toString(),
|
||||||
|
excusedLateness = attendanceSummary.sumBy { it.latenessExcused }.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createAttendanceSummaryItems(attendanceSummary: List<AttendanceSummary>): List<AttendanceSummaryItem> {
|
private fun createAttendanceSummaryItems(attendanceSummary: List<AttendanceSummary>): List<AttendanceSummaryItem> {
|
||||||
return attendanceSummary.sortedByDescending { it.id }.map {
|
if (attendanceSummary.isEmpty()) return emptyList()
|
||||||
|
return listOf(createAttendanceSummaryTotalItem(attendanceSummary)) + attendanceSummary.sortedByDescending {
|
||||||
|
if (it.month.value <= Month.JUNE.value) it.month.value + 12 else it.month.value
|
||||||
|
}.map {
|
||||||
AttendanceSummaryItem(
|
AttendanceSummaryItem(
|
||||||
month = it.month.getFormattedName(),
|
month = it.month.getFormattedName(),
|
||||||
percentage = formatPercentage(it.calculatePercentage()),
|
percentage = formatPercentage(it.calculatePercentage()),
|
||||||
|
@ -4,6 +4,8 @@ import io.github.wulkanowy.ui.base.BaseView
|
|||||||
|
|
||||||
interface AttendanceSummaryView : BaseView {
|
interface AttendanceSummaryView : BaseView {
|
||||||
|
|
||||||
|
val totalString: String
|
||||||
|
|
||||||
val isViewEmpty: Boolean
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
@ -40,6 +40,8 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie
|
|||||||
|
|
||||||
override val titleStringId get() = R.string.grade_title
|
override val titleStringId get() = R.string.grade_title
|
||||||
|
|
||||||
|
override var subtitleString = ""
|
||||||
|
|
||||||
override val currentPageIndex get() = gradeViewPager.currentItem
|
override val currentPageIndex get() = gradeViewPager.currentItem
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -133,6 +135,11 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setCurrentSemesterName(semester: Int, schoolYear: Int) {
|
||||||
|
subtitleString = getString(R.string.grade_subtitle, semester, schoolYear, schoolYear + 1)
|
||||||
|
(activity as MainView).setViewSubTitle(subtitleString)
|
||||||
|
}
|
||||||
|
|
||||||
fun onChildRefresh() {
|
fun onChildRefresh() {
|
||||||
presenter.onChildViewRefresh()
|
presenter.onChildViewRefresh()
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,10 @@ import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
|
|||||||
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
|
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@Module(includes = [GradeModule.Static::class])
|
@Module
|
||||||
abstract class GradeModule {
|
abstract class GradeModule {
|
||||||
|
|
||||||
@Module
|
companion object {
|
||||||
object Static {
|
|
||||||
|
|
||||||
@PerFragment
|
@PerFragment
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -7,7 +7,9 @@ import io.github.wulkanowy.ui.base.BasePresenter
|
|||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.SchedulersProvider
|
import io.github.wulkanowy.utils.SchedulersProvider
|
||||||
|
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GradePresenter @Inject constructor(
|
class GradePresenter @Inject constructor(
|
||||||
@ -21,6 +23,8 @@ class GradePresenter @Inject constructor(
|
|||||||
var selectedIndex = 0
|
var selectedIndex = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
private var schoolYear = 0
|
||||||
|
|
||||||
private var semesters = emptyList<Semester>()
|
private var semesters = emptyList<Semester>()
|
||||||
|
|
||||||
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
||||||
@ -56,6 +60,7 @@ class GradePresenter @Inject constructor(
|
|||||||
selectedIndex = index + 1
|
selectedIndex = index + 1
|
||||||
loadedSemesterId.clear()
|
loadedSemesterId.clear()
|
||||||
view?.let {
|
view?.let {
|
||||||
|
it.setCurrentSemesterName(index + 1, schoolYear)
|
||||||
notifyChildrenSemesterChange()
|
notifyChildrenSemesterChange()
|
||||||
loadChild(it.currentPageIndex)
|
loadChild(it.currentPageIndex)
|
||||||
}
|
}
|
||||||
@ -95,17 +100,17 @@ class GradePresenter @Inject constructor(
|
|||||||
private fun loadData() {
|
private fun loadData() {
|
||||||
Timber.i("Loading grade data started")
|
Timber.i("Loading grade data started")
|
||||||
disposable.add(studentRepository.getCurrentStudent()
|
disposable.add(studentRepository.getCurrentStudent()
|
||||||
.flatMap { semesterRepository.getSemesters(it) }
|
.flatMap { semesterRepository.getSemesters(it, refreshOnNoCurrent = true) }
|
||||||
.doOnSuccess {
|
.delay(200, MILLISECONDS)
|
||||||
it.first { item -> item.isCurrent }.also { current ->
|
|
||||||
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
|
|
||||||
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.subscribeOn(schedulers.backgroundThread)
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
.observeOn(schedulers.mainThread)
|
.observeOn(schedulers.mainThread)
|
||||||
.doFinally { view?.showProgress(false) }
|
|
||||||
.subscribe({
|
.subscribe({
|
||||||
|
val current = it.getCurrentOrLast()
|
||||||
|
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
|
||||||
|
schoolYear = current.schoolYear
|
||||||
|
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
||||||
|
view?.setCurrentSemesterName(current.semesterName, schoolYear)
|
||||||
|
|
||||||
view?.run {
|
view?.run {
|
||||||
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
|
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
|
||||||
loadChild(currentPageIndex)
|
loadChild(currentPageIndex)
|
||||||
@ -121,6 +126,7 @@ class GradePresenter @Inject constructor(
|
|||||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
lastError = error
|
lastError = error
|
||||||
view?.run {
|
view?.run {
|
||||||
|
showProgress(false)
|
||||||
showErrorView(true)
|
showErrorView(true)
|
||||||
setErrorDetails(message)
|
setErrorDetails(message)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ interface GradeView : BaseView {
|
|||||||
|
|
||||||
fun showSemesterDialog(selectedIndex: Int)
|
fun showSemesterDialog(selectedIndex: Int)
|
||||||
|
|
||||||
|
fun setCurrentSemesterName(semester: Int, schoolYear: Int)
|
||||||
|
|
||||||
fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean)
|
fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean)
|
||||||
|
|
||||||
fun notifyChildParentReselected(index: Int)
|
fun notifyChildParentReselected(index: Int)
|
||||||
|
@ -0,0 +1,209 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.grade.statistics
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.github.mikephil.charting.components.Legend
|
||||||
|
import com.github.mikephil.charting.components.LegendEntry
|
||||||
|
import com.github.mikephil.charting.data.BarData
|
||||||
|
import com.github.mikephil.charting.data.BarDataSet
|
||||||
|
import com.github.mikephil.charting.data.BarEntry
|
||||||
|
import com.github.mikephil.charting.data.PieData
|
||||||
|
import com.github.mikephil.charting.data.PieDataSet
|
||||||
|
import com.github.mikephil.charting.data.PieEntry
|
||||||
|
import com.github.mikephil.charting.formatter.ValueFormatter
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
|
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
|
import kotlinx.android.synthetic.main.item_grade_statistics_bar.view.*
|
||||||
|
import kotlinx.android.synthetic.main.item_grade_statistics_pie.view.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GradeStatisticsAdapter @Inject constructor() :
|
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
var items = emptyList<GradeStatisticsItem>()
|
||||||
|
|
||||||
|
var theme: String = "vulcan"
|
||||||
|
|
||||||
|
private val vulcanGradeColors = listOf(
|
||||||
|
6 to R.color.grade_vulcan_six,
|
||||||
|
5 to R.color.grade_vulcan_five,
|
||||||
|
4 to R.color.grade_vulcan_four,
|
||||||
|
3 to R.color.grade_vulcan_three,
|
||||||
|
2 to R.color.grade_vulcan_two,
|
||||||
|
1 to R.color.grade_vulcan_one
|
||||||
|
)
|
||||||
|
|
||||||
|
private val materialGradeColors = listOf(
|
||||||
|
6 to R.color.grade_material_six,
|
||||||
|
5 to R.color.grade_material_five,
|
||||||
|
4 to R.color.grade_material_four,
|
||||||
|
3 to R.color.grade_material_three,
|
||||||
|
2 to R.color.grade_material_two,
|
||||||
|
1 to R.color.grade_material_one
|
||||||
|
)
|
||||||
|
|
||||||
|
private val gradePointsColors = listOf(
|
||||||
|
Color.parseColor("#37c69c"),
|
||||||
|
Color.parseColor("#d8b12a")
|
||||||
|
)
|
||||||
|
|
||||||
|
private val gradeLabels = listOf(
|
||||||
|
"6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+"
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return when (items[position].type) {
|
||||||
|
ViewType.SEMESTER, ViewType.PARTIAL -> R.layout.item_grade_statistics_pie
|
||||||
|
ViewType.POINTS -> R.layout.item_grade_statistics_bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
val viewHolder = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||||
|
return when (viewType) {
|
||||||
|
R.layout.item_grade_statistics_bar -> GradeStatisticsBar(viewHolder)
|
||||||
|
else -> GradeStatisticsPie(viewHolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
when (holder) {
|
||||||
|
is GradeStatisticsPie -> bindPieChart(holder, items[position].partial)
|
||||||
|
is GradeStatisticsBar -> bindBarChart(holder, items[position].points!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindPieChart(holder: GradeStatisticsPie, partials: List<GradeStatistics>) {
|
||||||
|
with(holder.view.gradeStatisticsPieTitle) {
|
||||||
|
text = partials.firstOrNull()?.subject
|
||||||
|
visibility = if (items.size == 1) GONE else VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
val gradeColors = when (theme) {
|
||||||
|
"vulcan" -> vulcanGradeColors
|
||||||
|
else -> materialGradeColors
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataset = PieDataSet(partials.map {
|
||||||
|
PieEntry(it.amount.toFloat(), it.grade.toString())
|
||||||
|
}, "Legenda")
|
||||||
|
|
||||||
|
with(dataset) {
|
||||||
|
valueTextSize = 12f
|
||||||
|
sliceSpace = 1f
|
||||||
|
valueTextColor = Color.WHITE
|
||||||
|
setColors(partials.map {
|
||||||
|
gradeColors.single { color -> color.first == it.grade }.second
|
||||||
|
}.toIntArray(), holder.view.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
with(holder.view.gradeStatisticsPie) {
|
||||||
|
setTouchEnabled(false)
|
||||||
|
if (partials.size == 1) animateXY(1000, 1000)
|
||||||
|
data = PieData(dataset).apply {
|
||||||
|
setValueFormatter(object : ValueFormatter() {
|
||||||
|
override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
|
||||||
|
return resources.getQuantityString(R.plurals.grade_number_item, value.toInt(), value.toInt())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
with(legend) {
|
||||||
|
textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||||
|
setCustom(gradeLabels.mapIndexed { i, it ->
|
||||||
|
LegendEntry().apply {
|
||||||
|
label = it
|
||||||
|
formColor = ContextCompat.getColor(context, gradeColors[i].second)
|
||||||
|
form = Legend.LegendForm.SQUARE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
minAngleForSlices = 25f
|
||||||
|
description.isEnabled = false
|
||||||
|
centerText = partials.fold(0) { acc, it -> acc + it.amount }
|
||||||
|
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
|
||||||
|
|
||||||
|
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
|
||||||
|
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindBarChart(holder: GradeStatisticsBar, points: GradePointsStatistics) {
|
||||||
|
with(holder.view.gradeStatisticsBarTitle) {
|
||||||
|
text = points.subject
|
||||||
|
visibility = if (items.size == 1) GONE else VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataset = BarDataSet(listOf(
|
||||||
|
BarEntry(1f, points.others.toFloat()),
|
||||||
|
BarEntry(2f, points.student.toFloat())
|
||||||
|
), "Legenda")
|
||||||
|
|
||||||
|
with(dataset) {
|
||||||
|
valueTextSize = 12f
|
||||||
|
valueTextColor = holder.view.context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||||
|
valueFormatter = object : ValueFormatter() {
|
||||||
|
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%"
|
||||||
|
}
|
||||||
|
colors = gradePointsColors
|
||||||
|
}
|
||||||
|
|
||||||
|
with(holder.view.gradeStatisticsBar) {
|
||||||
|
setTouchEnabled(false)
|
||||||
|
if (items.size == 1) animateXY(1000, 1000)
|
||||||
|
data = BarData(dataset).apply {
|
||||||
|
barWidth = 0.5f
|
||||||
|
setFitBars(true)
|
||||||
|
}
|
||||||
|
legend.setCustom(listOf(
|
||||||
|
LegendEntry().apply {
|
||||||
|
label = "Średnia klasy"
|
||||||
|
formColor = gradePointsColors[0]
|
||||||
|
form = Legend.LegendForm.SQUARE
|
||||||
|
},
|
||||||
|
LegendEntry().apply {
|
||||||
|
label = "Uczeń"
|
||||||
|
formColor = gradePointsColors[1]
|
||||||
|
form = Legend.LegendForm.SQUARE
|
||||||
|
}
|
||||||
|
))
|
||||||
|
legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||||
|
|
||||||
|
description.isEnabled = false
|
||||||
|
|
||||||
|
holder.view.context.getThemeAttrColor(android.R.attr.textColorPrimary).let {
|
||||||
|
axisLeft.textColor = it
|
||||||
|
axisRight.textColor = it
|
||||||
|
}
|
||||||
|
xAxis.setDrawLabels(false)
|
||||||
|
xAxis.setDrawGridLines(false)
|
||||||
|
with(axisLeft) {
|
||||||
|
axisMinimum = 0f
|
||||||
|
axisMaximum = 100f
|
||||||
|
labelCount = 11
|
||||||
|
}
|
||||||
|
with(axisRight) {
|
||||||
|
axisMinimum = 0f
|
||||||
|
axisMaximum = 100f
|
||||||
|
labelCount = 11
|
||||||
|
}
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GradeStatisticsPie(val view: View) : RecyclerView.ViewHolder(view)
|
||||||
|
|
||||||
|
class GradeStatisticsBar(val view: View) : RecyclerView.ViewHolder(view)
|
||||||
|
}
|
@ -1,31 +1,18 @@
|
|||||||
package io.github.wulkanowy.ui.modules.grade.statistics
|
package io.github.wulkanowy.ui.modules.grade.statistics
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.Color.WHITE
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.github.mikephil.charting.components.Legend
|
|
||||||
import com.github.mikephil.charting.components.LegendEntry
|
|
||||||
import com.github.mikephil.charting.data.BarData
|
|
||||||
import com.github.mikephil.charting.data.BarDataSet
|
|
||||||
import com.github.mikephil.charting.data.BarEntry
|
|
||||||
import com.github.mikephil.charting.data.PieData
|
|
||||||
import com.github.mikephil.charting.data.PieDataSet
|
|
||||||
import com.github.mikephil.charting.data.PieEntry
|
|
||||||
import com.github.mikephil.charting.formatter.ValueFormatter
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
|
||||||
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
||||||
import kotlinx.android.synthetic.main.fragment_grade_statistics.*
|
import kotlinx.android.synthetic.main.fragment_grade_statistics.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -35,6 +22,9 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: GradeStatisticsPresenter
|
lateinit var presenter: GradeStatisticsPresenter
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var statisticsAdapter: GradeStatisticsAdapter
|
||||||
|
|
||||||
private lateinit var subjectsAdapter: ArrayAdapter<String>
|
private lateinit var subjectsAdapter: ArrayAdapter<String>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -43,9 +33,7 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
fun newInstance() = GradeStatisticsFragment()
|
fun newInstance() = GradeStatisticsFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val isPieViewEmpty get() = gradeStatisticsChart.isEmpty
|
override val isViewEmpty get() = statisticsAdapter.items.isEmpty()
|
||||||
|
|
||||||
override val isBarViewEmpty get() = gradeStatisticsChartPoints.isEmpty
|
|
||||||
|
|
||||||
override val currentType
|
override val currentType
|
||||||
get() = when (gradeStatisticsTypeSwitch.checkedRadioButtonId) {
|
get() = when (gradeStatisticsTypeSwitch.checkedRadioButtonId) {
|
||||||
@ -54,35 +42,6 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
else -> ViewType.POINTS
|
else -> ViewType.POINTS
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var gradeColors: List<Pair<Int, Int>>
|
|
||||||
|
|
||||||
private val vulcanGradeColors = listOf(
|
|
||||||
6 to R.color.grade_vulcan_six,
|
|
||||||
5 to R.color.grade_vulcan_five,
|
|
||||||
4 to R.color.grade_vulcan_four,
|
|
||||||
3 to R.color.grade_vulcan_three,
|
|
||||||
2 to R.color.grade_vulcan_two,
|
|
||||||
1 to R.color.grade_vulcan_one
|
|
||||||
)
|
|
||||||
|
|
||||||
private val materialGradeColors = listOf(
|
|
||||||
6 to R.color.grade_material_six,
|
|
||||||
5 to R.color.grade_material_five,
|
|
||||||
4 to R.color.grade_material_four,
|
|
||||||
3 to R.color.grade_material_three,
|
|
||||||
2 to R.color.grade_material_two,
|
|
||||||
1 to R.color.grade_material_one
|
|
||||||
)
|
|
||||||
|
|
||||||
private val gradePointsColors = listOf(
|
|
||||||
Color.parseColor("#37c69c"),
|
|
||||||
Color.parseColor("#d8b12a")
|
|
||||||
)
|
|
||||||
|
|
||||||
private val gradeLabels = listOf(
|
|
||||||
"6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+"
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_grade_statistics, container, false)
|
return inflater.inflate(R.layout.fragment_grade_statistics, container, false)
|
||||||
}
|
}
|
||||||
@ -94,31 +53,9 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
with(gradeStatisticsChart) {
|
with(gradeStatisticsRecycler) {
|
||||||
description.isEnabled = false
|
layoutManager = LinearLayoutManager(requireContext())
|
||||||
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
|
adapter = statisticsAdapter
|
||||||
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
|
|
||||||
animateXY(1000, 1000)
|
|
||||||
minAngleForSlices = 25f
|
|
||||||
legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
|
||||||
}
|
|
||||||
|
|
||||||
with(gradeStatisticsChartPoints) {
|
|
||||||
description.isEnabled = false
|
|
||||||
|
|
||||||
animateXY(1000, 1000)
|
|
||||||
legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
|
||||||
|
|
||||||
with(axisLeft) {
|
|
||||||
axisMinimum = 0f
|
|
||||||
axisMaximum = 100f
|
|
||||||
labelCount = 11
|
|
||||||
}
|
|
||||||
with(axisRight) {
|
|
||||||
axisMinimum = 0f
|
|
||||||
axisMaximum = 100f
|
|
||||||
labelCount = 11
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
|
subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
|
||||||
@ -144,86 +81,10 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updatePieData(items: List<GradeStatistics>, theme: String) {
|
override fun updateData(items: List<GradeStatisticsItem>, theme: String) {
|
||||||
gradeColors = when (theme) {
|
statisticsAdapter.theme = theme
|
||||||
"vulcan" -> vulcanGradeColors
|
statisticsAdapter.items = items
|
||||||
else -> materialGradeColors
|
statisticsAdapter.notifyDataSetChanged()
|
||||||
}
|
|
||||||
|
|
||||||
val dataset = PieDataSet(items.map {
|
|
||||||
PieEntry(it.amount.toFloat(), it.grade.toString())
|
|
||||||
}, "Legenda").apply {
|
|
||||||
valueTextSize = 12f
|
|
||||||
sliceSpace = 1f
|
|
||||||
valueTextColor = WHITE
|
|
||||||
setColors(items.map {
|
|
||||||
gradeColors.single { color -> color.first == it.grade }.second
|
|
||||||
}.toIntArray(), context)
|
|
||||||
}
|
|
||||||
|
|
||||||
with(gradeStatisticsChart) {
|
|
||||||
data = PieData(dataset).apply {
|
|
||||||
setTouchEnabled(false)
|
|
||||||
setValueFormatter(object : ValueFormatter() {
|
|
||||||
override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
|
|
||||||
return resources.getQuantityString(R.plurals.grade_number_item, value.toInt(), value.toInt())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
centerText = items.fold(0) { acc, it -> acc + it.amount }
|
|
||||||
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
|
|
||||||
}
|
|
||||||
legend.apply {
|
|
||||||
setCustom(gradeLabels.mapIndexed { i, it ->
|
|
||||||
LegendEntry().apply {
|
|
||||||
label = it
|
|
||||||
formColor = ContextCompat.getColor(context, gradeColors[i].second)
|
|
||||||
form = Legend.LegendForm.SQUARE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateBarData(item: GradePointsStatistics) {
|
|
||||||
val dataset = BarDataSet(listOf(
|
|
||||||
BarEntry(1f, item.others.toFloat()),
|
|
||||||
BarEntry(2f, item.student.toFloat())
|
|
||||||
), "Legenda").apply {
|
|
||||||
valueTextSize = 12f
|
|
||||||
valueTextColor = requireContext().getThemeAttrColor(android.R.attr.textColorPrimary)
|
|
||||||
valueFormatter = object : ValueFormatter() {
|
|
||||||
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%"
|
|
||||||
}
|
|
||||||
colors = gradePointsColors
|
|
||||||
}
|
|
||||||
|
|
||||||
with(gradeStatisticsChartPoints) {
|
|
||||||
data = BarData(dataset).apply {
|
|
||||||
barWidth = 0.5f
|
|
||||||
setFitBars(true)
|
|
||||||
}
|
|
||||||
setTouchEnabled(false)
|
|
||||||
xAxis.setDrawLabels(false)
|
|
||||||
xAxis.setDrawGridLines(false)
|
|
||||||
requireContext().getThemeAttrColor(android.R.attr.textColorPrimary).let {
|
|
||||||
axisLeft.textColor = it
|
|
||||||
axisRight.textColor = it
|
|
||||||
}
|
|
||||||
legend.setCustom(listOf(
|
|
||||||
LegendEntry().apply {
|
|
||||||
label = "Średnia klasy"
|
|
||||||
formColor = gradePointsColors[0]
|
|
||||||
form = Legend.LegendForm.SQUARE
|
|
||||||
},
|
|
||||||
LegendEntry().apply {
|
|
||||||
label = "Uczeń"
|
|
||||||
formColor = gradePointsColors[1]
|
|
||||||
form = Legend.LegendForm.SQUARE
|
|
||||||
}
|
|
||||||
))
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showSubjects(show: Boolean) {
|
override fun showSubjects(show: Boolean) {
|
||||||
@ -232,16 +93,15 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun clearView() {
|
override fun clearView() {
|
||||||
gradeStatisticsChart.clear()
|
statisticsAdapter.items = emptyList()
|
||||||
gradeStatisticsChartPoints.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showPieContent(show: Boolean) {
|
override fun resetView() {
|
||||||
gradeStatisticsChart.visibility = if (show) View.VISIBLE else View.GONE
|
gradeStatisticsScroll.scrollTo(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showBarContent(show: Boolean) {
|
override fun showContent(show: Boolean) {
|
||||||
gradeStatisticsChartPoints.visibility = if (show) View.VISIBLE else View.GONE
|
gradeStatisticsRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showEmpty(show: Boolean) {
|
override fun showEmpty(show: Boolean) {
|
||||||
@ -273,7 +133,7 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onParentReselected() {
|
override fun onParentReselected() {
|
||||||
//
|
presenter.onParentViewReselected()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onParentChangeSemester() {
|
override fun onParentChangeSemester() {
|
||||||
|
@ -48,12 +48,19 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
loadDataByType(semesterId, currentSubjectName, currentType, forceRefresh)
|
loadDataByType(semesterId, currentSubjectName, currentType, forceRefresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun onParentViewReselected() {
|
||||||
|
view?.run {
|
||||||
|
if (!isViewEmpty) resetView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onParentViewChangeSemester() {
|
fun onParentViewChangeSemester() {
|
||||||
view?.run {
|
view?.run {
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
enableSwipe(false)
|
enableSwipe(false)
|
||||||
showRefresh(false)
|
showRefresh(false)
|
||||||
showBarContent(false)
|
showContent(false)
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
showEmpty(false)
|
showEmpty(false)
|
||||||
clearView()
|
clearView()
|
||||||
@ -81,8 +88,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
fun onSubjectSelected(name: String?) {
|
fun onSubjectSelected(name: String?) {
|
||||||
Timber.i("Select grade stats subject $name")
|
Timber.i("Select grade stats subject $name")
|
||||||
view?.run {
|
view?.run {
|
||||||
showBarContent(false)
|
showContent(false)
|
||||||
showPieContent(false)
|
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
enableSwipe(false)
|
enableSwipe(false)
|
||||||
showEmpty(false)
|
showEmpty(false)
|
||||||
@ -99,8 +105,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
Timber.i("Select grade stats semester: $type")
|
Timber.i("Select grade stats semester: $type")
|
||||||
disposable.clear()
|
disposable.clear()
|
||||||
view?.run {
|
view?.run {
|
||||||
showBarContent(false)
|
showContent(false)
|
||||||
showPieContent(false)
|
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
enableSwipe(false)
|
enableSwipe(false)
|
||||||
showEmpty(false)
|
showEmpty(false)
|
||||||
@ -135,20 +140,22 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) {
|
private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) {
|
||||||
currentSubjectName = subjectName
|
currentSubjectName = subjectName
|
||||||
currentType = type
|
currentType = type
|
||||||
when (type) {
|
loadData(semesterId, subjectName, type, forceRefresh)
|
||||||
ViewType.SEMESTER -> loadData(semesterId, subjectName, true, forceRefresh)
|
|
||||||
ViewType.PARTIAL -> loadData(semesterId, subjectName, false, forceRefresh)
|
|
||||||
ViewType.POINTS -> loadPointsData(semesterId, subjectName, forceRefresh)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadData(semesterId: Int, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false) {
|
private fun loadData(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean) {
|
||||||
Timber.i("Loading grade stats data started")
|
Timber.i("Loading grade stats data started")
|
||||||
disposable.add(studentRepository.getCurrentStudent()
|
disposable.add(studentRepository.getCurrentStudent()
|
||||||
.flatMap { semesterRepository.getSemesters(it) }
|
.flatMap { semesterRepository.getSemesters(it) }
|
||||||
.flatMap { gradeStatisticsRepository.getGradesStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, isSemester, forceRefresh) }
|
.flatMap {
|
||||||
.map { list -> list.sortedByDescending { it.grade } }
|
val semester = it.first { item -> item.semesterId == semesterId }
|
||||||
.map { list -> list.filter { it.amount != 0 } }
|
|
||||||
|
when (type) {
|
||||||
|
ViewType.SEMESTER -> gradeStatisticsRepository.getGradesStatistics(semester, subjectName, true, forceRefresh)
|
||||||
|
ViewType.PARTIAL -> gradeStatisticsRepository.getGradesStatistics(semester, subjectName, false, forceRefresh)
|
||||||
|
ViewType.POINTS -> gradeStatisticsRepository.getGradesPointsStatistics(semester, subjectName, forceRefresh)
|
||||||
|
}
|
||||||
|
}
|
||||||
.subscribeOn(schedulers.backgroundThread)
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
.observeOn(schedulers.mainThread)
|
.observeOn(schedulers.mainThread)
|
||||||
.doFinally {
|
.doFinally {
|
||||||
@ -163,10 +170,9 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
Timber.i("Loading grade stats result: Success")
|
Timber.i("Loading grade stats result: Success")
|
||||||
view?.run {
|
view?.run {
|
||||||
showEmpty(it.isEmpty())
|
showEmpty(it.isEmpty())
|
||||||
showBarContent(false)
|
showContent(it.isNotEmpty())
|
||||||
showPieContent(it.isNotEmpty())
|
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
updatePieData(it, preferencesRepository.gradeColorTheme)
|
updateData(it, preferencesRepository.gradeColorTheme)
|
||||||
}
|
}
|
||||||
analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh)
|
analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh)
|
||||||
}) {
|
}) {
|
||||||
@ -175,47 +181,9 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadPointsData(semesterId: Int, subjectName: String, forceRefresh: Boolean = false) {
|
|
||||||
Timber.i("Loading grade points stats data started")
|
|
||||||
disposable.add(studentRepository.getCurrentStudent()
|
|
||||||
.flatMap { semesterRepository.getSemesters(it) }
|
|
||||||
.flatMapMaybe { gradeStatisticsRepository.getGradesPointsStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, forceRefresh) }
|
|
||||||
.subscribeOn(schedulers.backgroundThread)
|
|
||||||
.observeOn(schedulers.mainThread)
|
|
||||||
.doFinally {
|
|
||||||
view?.run {
|
|
||||||
showRefresh(false)
|
|
||||||
showProgress(false)
|
|
||||||
enableSwipe(true)
|
|
||||||
notifyParentDataLoaded(semesterId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.subscribe({
|
|
||||||
Timber.i("Loading grade points stats result: Success")
|
|
||||||
view?.run {
|
|
||||||
showEmpty(false)
|
|
||||||
showPieContent(false)
|
|
||||||
showBarContent(true)
|
|
||||||
showErrorView(false)
|
|
||||||
updateBarData(it)
|
|
||||||
}
|
|
||||||
analytics.logEvent("load_grade_points_statistics", "force_refresh" to forceRefresh)
|
|
||||||
}, {
|
|
||||||
Timber.e("Loading grade points stats result: An exception occurred")
|
|
||||||
errorHandler.dispatch(it)
|
|
||||||
}, {
|
|
||||||
Timber.d("Loading grade points stats result: No point stats found")
|
|
||||||
view?.run {
|
|
||||||
showBarContent(false)
|
|
||||||
showEmpty(true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
view?.run {
|
view?.run {
|
||||||
if ((isBarViewEmpty && currentType == ViewType.POINTS) || (isPieViewEmpty) && currentType != ViewType.POINTS) {
|
if (isViewEmpty) {
|
||||||
lastError = error
|
lastError = error
|
||||||
setErrorDetails(message)
|
setErrorDetails(message)
|
||||||
showErrorView(true)
|
showErrorView(true)
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
package io.github.wulkanowy.ui.modules.grade.statistics
|
package io.github.wulkanowy.ui.modules.grade.statistics
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
|
||||||
import io.github.wulkanowy.ui.base.BaseView
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
interface GradeStatisticsView : BaseView {
|
interface GradeStatisticsView : BaseView {
|
||||||
|
|
||||||
val isPieViewEmpty: Boolean
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
val isBarViewEmpty: Boolean
|
|
||||||
|
|
||||||
val currentType: ViewType
|
val currentType: ViewType
|
||||||
|
|
||||||
@ -16,9 +13,7 @@ interface GradeStatisticsView : BaseView {
|
|||||||
|
|
||||||
fun updateSubjects(data: ArrayList<String>)
|
fun updateSubjects(data: ArrayList<String>)
|
||||||
|
|
||||||
fun updatePieData(items: List<GradeStatistics>, theme: String)
|
fun updateData(items: List<GradeStatisticsItem>, theme: String)
|
||||||
|
|
||||||
fun updateBarData(item: GradePointsStatistics)
|
|
||||||
|
|
||||||
fun showSubjects(show: Boolean)
|
fun showSubjects(show: Boolean)
|
||||||
|
|
||||||
@ -28,9 +23,9 @@ interface GradeStatisticsView : BaseView {
|
|||||||
|
|
||||||
fun clearView()
|
fun clearView()
|
||||||
|
|
||||||
fun showPieContent(show: Boolean)
|
fun resetView()
|
||||||
|
|
||||||
fun showBarContent(show: Boolean)
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
fun showEmpty(show: Boolean)
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import io.github.wulkanowy.ui.base.BaseActivity
|
|||||||
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
|
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
|
||||||
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
|
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
|
||||||
import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
|
import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment
|
||||||
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
|
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
|
||||||
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
|
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
|
||||||
import io.github.wulkanowy.utils.setOnSelectPageListener
|
import io.github.wulkanowy.utils.setOnSelectPageListener
|
||||||
@ -52,7 +53,8 @@ class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
|
|||||||
LoginFormFragment.newInstance(),
|
LoginFormFragment.newInstance(),
|
||||||
LoginSymbolFragment.newInstance(),
|
LoginSymbolFragment.newInstance(),
|
||||||
LoginStudentSelectFragment.newInstance(),
|
LoginStudentSelectFragment.newInstance(),
|
||||||
LoginAdvancedFragment.newInstance()
|
LoginAdvancedFragment.newInstance(),
|
||||||
|
LoginRecoverFragment.newInstance()
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,4 +101,8 @@ class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
|
|||||||
fun onAdvancedLoginClick() {
|
fun onAdvancedLoginClick() {
|
||||||
presenter.onAdvancedLoginClick()
|
presenter.onAdvancedLoginClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onRecoverClick() {
|
||||||
|
presenter.onRecoverClick()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,5 +43,8 @@ class LoginErrorHandler @Inject constructor(
|
|||||||
super.clear()
|
super.clear()
|
||||||
onBadCredentials = {}
|
onBadCredentials = {}
|
||||||
onStudentDuplicate = {}
|
onStudentDuplicate = {}
|
||||||
|
onInvalidToken = {}
|
||||||
|
onInvalidPin = {}
|
||||||
|
onInvalidSymbol = {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,15 @@ import io.github.wulkanowy.di.scopes.PerFragment
|
|||||||
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
|
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
|
||||||
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
|
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
|
||||||
import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
|
import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment
|
||||||
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
|
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
|
||||||
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
|
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@Module(includes = [LoginModule.Static::class])
|
@Module
|
||||||
internal abstract class LoginModule {
|
internal abstract class LoginModule {
|
||||||
|
|
||||||
@Module
|
companion object {
|
||||||
object Static {
|
|
||||||
|
|
||||||
@PerActivity
|
@PerActivity
|
||||||
@Provides
|
@Provides
|
||||||
@ -38,4 +38,8 @@ internal abstract class LoginModule {
|
|||||||
@PerFragment
|
@PerFragment
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract fun bindLoginSelectStudentFragment(): LoginStudentSelectFragment
|
abstract fun bindLoginSelectStudentFragment(): LoginStudentSelectFragment
|
||||||
|
|
||||||
|
@PerFragment
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun bindLoginRecoverFragment(): LoginRecoverFragment
|
||||||
}
|
}
|
||||||
|
@ -49,11 +49,15 @@ class LoginPresenter @Inject constructor(
|
|||||||
view?.switchView(3)
|
view?.switchView(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onRecoverClick() {
|
||||||
|
view?.switchView(4)
|
||||||
|
}
|
||||||
|
|
||||||
fun onViewSelected(index: Int) {
|
fun onViewSelected(index: Int) {
|
||||||
view?.apply {
|
view?.apply {
|
||||||
when (index) {
|
when (index) {
|
||||||
0 -> showActionBar(false)
|
0 -> showActionBar(false)
|
||||||
1, 2 -> showActionBar(true)
|
1, 2, 3, 4 -> showActionBar(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +66,7 @@ class LoginPresenter @Inject constructor(
|
|||||||
Timber.i("Back pressed in login view")
|
Timber.i("Back pressed in login view")
|
||||||
view?.apply {
|
view?.apply {
|
||||||
when (currentViewIndex) {
|
when (currentViewIndex) {
|
||||||
1, 2, 3 -> switchView(0)
|
1, 2, 3, 4 -> switchView(0)
|
||||||
else -> default()
|
else -> default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ package io.github.wulkanowy.ui.modules.login.advanced
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import androidx.appcompat.widget.AppCompatEditText
|
|
||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
@ -15,6 +15,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
|
|||||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||||
import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter
|
import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter
|
||||||
import io.github.wulkanowy.utils.hideSoftInput
|
import io.github.wulkanowy.utils.hideSoftInput
|
||||||
|
import io.github.wulkanowy.utils.setOnEditorDoneSignIn
|
||||||
import io.github.wulkanowy.utils.showSoftInput
|
import io.github.wulkanowy.utils.showSoftInput
|
||||||
import kotlinx.android.synthetic.main.fragment_login_advanced.*
|
import kotlinx.android.synthetic.main.fragment_login_advanced.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -35,8 +36,8 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
|||||||
else -> "HYBRID"
|
else -> "HYBRID"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val formNameValue: String
|
override val formUsernameValue: String
|
||||||
get() = loginFormName.text.toString().trim()
|
get() = loginFormUsername.text.toString().trim()
|
||||||
|
|
||||||
override val formPassValue: String
|
override val formPassValue: String
|
||||||
get() = loginFormPass.text.toString().trim()
|
get() = loginFormPass.text.toString().trim()
|
||||||
@ -45,8 +46,13 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
|||||||
|
|
||||||
private lateinit var hostValues: Array<String>
|
private lateinit var hostValues: Array<String>
|
||||||
|
|
||||||
override val formHostValue: String?
|
private lateinit var hostSymbols: Array<String>
|
||||||
get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString()))
|
|
||||||
|
override val formHostValue: String
|
||||||
|
get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())).orEmpty()
|
||||||
|
|
||||||
|
override val formHostSymbol: String
|
||||||
|
get() = hostSymbols.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())).orEmpty()
|
||||||
|
|
||||||
override val formPinValue: String
|
override val formPinValue: String
|
||||||
get() = loginFormPin.text.toString().trim()
|
get() = loginFormPin.text.toString().trim()
|
||||||
@ -57,6 +63,12 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
|||||||
override val formTokenValue: String
|
override val formTokenValue: String
|
||||||
get() = loginFormToken.text.toString().trim()
|
get() = loginFormToken.text.toString().trim()
|
||||||
|
|
||||||
|
override val nicknameLabel: String
|
||||||
|
get() = getString(R.string.login_nickname_hint)
|
||||||
|
|
||||||
|
override val emailLabel: String
|
||||||
|
get() = getString(R.string.login_email_hint)
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_login_advanced, container, false)
|
return inflater.inflate(R.layout.fragment_login_advanced, container, false)
|
||||||
}
|
}
|
||||||
@ -69,8 +81,9 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
|||||||
override fun initView() {
|
override fun initView() {
|
||||||
hostKeys = resources.getStringArray(R.array.hosts_keys)
|
hostKeys = resources.getStringArray(R.array.hosts_keys)
|
||||||
hostValues = resources.getStringArray(R.array.hosts_values)
|
hostValues = resources.getStringArray(R.array.hosts_values)
|
||||||
|
hostSymbols = resources.getStringArray(R.array.hosts_symbols)
|
||||||
|
|
||||||
loginFormName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() }
|
loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() }
|
||||||
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
|
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
|
||||||
loginFormPin.doOnTextChanged { _, _, _, _ -> presenter.onPinTextChanged() }
|
loginFormPin.doOnTextChanged { _, _, _, _ -> presenter.onPinTextChanged() }
|
||||||
loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() }
|
loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() }
|
||||||
@ -86,33 +99,48 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
loginFormPin.setOnEditorDoneSignIn()
|
loginFormPin.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() }
|
||||||
loginFormPass.setOnEditorDoneSignIn()
|
loginFormPass.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() }
|
||||||
|
|
||||||
loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values)))
|
loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values)))
|
||||||
|
|
||||||
with(loginFormHost) {
|
with(loginFormHost) {
|
||||||
setText(hostKeys.getOrElse(0) { "" })
|
setText(hostKeys.getOrNull(0).orEmpty())
|
||||||
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
|
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
|
||||||
|
setOnClickListener { if (loginFormContainer.visibility == GONE) dismissDropDown() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AppCompatEditText.setOnEditorDoneSignIn() {
|
override fun showMobileApiWarningMessage() {
|
||||||
setOnEditorActionListener { _, id, _ ->
|
loginFormAdvancedWarningInfo.text = getString(R.string.login_advanced_warning_mobile_api)
|
||||||
if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) loginFormSignIn.callOnClick() else false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String) {
|
override fun showScraperWarningMessage() {
|
||||||
loginFormName.setText(name)
|
loginFormAdvancedWarningInfo.text = getString(R.string.login_advanced_warning_scraper)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showHybridWarningMessage() {
|
||||||
|
loginFormAdvancedWarningInfo.text = getString(R.string.login_advanced_warning_hybrid)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setDefaultCredentials(username: String, pass: String, symbol: String, token: String, pin: String) {
|
||||||
|
loginFormUsername.setText(username)
|
||||||
loginFormPass.setText(pass)
|
loginFormPass.setText(pass)
|
||||||
loginFormToken.setText(token)
|
loginFormToken.setText(token)
|
||||||
loginFormSymbol.setText(symbol)
|
loginFormSymbol.setText(symbol)
|
||||||
loginFormPin.setText(pin)
|
loginFormPin.setText(pin)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setErrorNameRequired() {
|
override fun setUsernameLabel(label: String) {
|
||||||
with(loginFormNameLayout) {
|
loginFormUsernameLayout.hint = label
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSymbol(symbol: String) {
|
||||||
|
loginFormSymbol.setText(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorUsernameRequired() {
|
||||||
|
with(loginFormUsernameLayout) {
|
||||||
requestFocus()
|
requestFocus()
|
||||||
error = getString(R.string.login_field_required)
|
error = getString(R.string.login_field_required)
|
||||||
}
|
}
|
||||||
@ -181,8 +209,8 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearNameError() {
|
override fun clearUsernameError() {
|
||||||
loginFormNameLayout.error = null
|
loginFormUsernameLayout.error = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearPassError() {
|
override fun clearPassError() {
|
||||||
@ -202,30 +230,30 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showOnlyHybridModeInputs() {
|
override fun showOnlyHybridModeInputs() {
|
||||||
loginFormNameLayout.visibility = View.VISIBLE
|
loginFormUsernameLayout.visibility = VISIBLE
|
||||||
loginFormPassLayout.visibility = View.VISIBLE
|
loginFormPassLayout.visibility = VISIBLE
|
||||||
loginFormHostLayout.visibility = View.VISIBLE
|
loginFormHostLayout.visibility = VISIBLE
|
||||||
loginFormPinLayout.visibility = View.GONE
|
loginFormPinLayout.visibility = GONE
|
||||||
loginFormSymbolLayout.visibility = View.VISIBLE
|
loginFormSymbolLayout.visibility = VISIBLE
|
||||||
loginFormTokenLayout.visibility = View.GONE
|
loginFormTokenLayout.visibility = GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showOnlyScrapperModeInputs() {
|
override fun showOnlyScrapperModeInputs() {
|
||||||
loginFormNameLayout.visibility = View.VISIBLE
|
loginFormUsernameLayout.visibility = VISIBLE
|
||||||
loginFormPassLayout.visibility = View.VISIBLE
|
loginFormPassLayout.visibility = VISIBLE
|
||||||
loginFormHostLayout.visibility = View.VISIBLE
|
loginFormHostLayout.visibility = VISIBLE
|
||||||
loginFormPinLayout.visibility = View.GONE
|
loginFormPinLayout.visibility = GONE
|
||||||
loginFormSymbolLayout.visibility = View.VISIBLE
|
loginFormSymbolLayout.visibility = VISIBLE
|
||||||
loginFormTokenLayout.visibility = View.GONE
|
loginFormTokenLayout.visibility = GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showOnlyMobileApiModeInputs() {
|
override fun showOnlyMobileApiModeInputs() {
|
||||||
loginFormNameLayout.visibility = View.GONE
|
loginFormUsernameLayout.visibility = GONE
|
||||||
loginFormPassLayout.visibility = View.GONE
|
loginFormPassLayout.visibility = GONE
|
||||||
loginFormHostLayout.visibility = View.GONE
|
loginFormHostLayout.visibility = GONE
|
||||||
loginFormPinLayout.visibility = View.VISIBLE
|
loginFormPinLayout.visibility = VISIBLE
|
||||||
loginFormSymbolLayout.visibility = View.VISIBLE
|
loginFormSymbolLayout.visibility = VISIBLE
|
||||||
loginFormTokenLayout.visibility = View.VISIBLE
|
loginFormTokenLayout.visibility = VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showSoftKeyboard() {
|
override fun showSoftKeyboard() {
|
||||||
@ -237,21 +265,26 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showProgress(show: Boolean) {
|
override fun showProgress(show: Boolean) {
|
||||||
loginFormProgress.visibility = if (show) View.VISIBLE else View.GONE
|
loginFormProgress.visibility = if (show) VISIBLE else GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showContent(show: Boolean) {
|
override fun showContent(show: Boolean) {
|
||||||
loginFormContainer.visibility = if (show) View.VISIBLE else View.GONE
|
loginFormContainer.visibility = if (show) VISIBLE else GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyParentAccountLogged(students: List<Student>) {
|
override fun notifyParentAccountLogged(students: List<Student>) {
|
||||||
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
|
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
|
||||||
loginFormName.text.toString(),
|
loginFormUsername.text.toString(),
|
||||||
loginFormPass.text.toString(),
|
loginFormPass.text.toString(),
|
||||||
resources.getStringArray(R.array.hosts_values)[1]
|
resources.getStringArray(R.array.hosts_values)[1]
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
presenter.updateUsernameLabel()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
|
@ -65,22 +65,39 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateUsernameLabel() {
|
||||||
|
view?.apply {
|
||||||
|
setUsernameLabel(if ("vulcan" in formHostValue || "fakelog" in formHostValue) emailLabel else nicknameLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onHostSelected() {
|
fun onHostSelected() {
|
||||||
view?.apply {
|
view?.apply {
|
||||||
clearPassError()
|
clearPassError()
|
||||||
clearNameError()
|
clearUsernameError()
|
||||||
if (formHostValue?.contains("fakelog") == true) {
|
if (formHostValue.contains("fakelog")) {
|
||||||
setDefaultCredentials("jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999")
|
setDefaultCredentials("jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999")
|
||||||
}
|
}
|
||||||
|
setSymbol(formHostSymbol)
|
||||||
|
updateUsernameLabel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onLoginModeSelected(type: Sdk.Mode) {
|
fun onLoginModeSelected(type: Sdk.Mode) {
|
||||||
view?.run {
|
view?.run {
|
||||||
when (type) {
|
when (type) {
|
||||||
Sdk.Mode.API -> showOnlyMobileApiModeInputs()
|
Sdk.Mode.API -> {
|
||||||
Sdk.Mode.SCRAPPER -> showOnlyScrapperModeInputs()
|
showOnlyMobileApiModeInputs()
|
||||||
Sdk.Mode.HYBRID -> showOnlyHybridModeInputs()
|
showMobileApiWarningMessage()
|
||||||
|
}
|
||||||
|
Sdk.Mode.SCRAPPER -> {
|
||||||
|
showOnlyScrapperModeInputs()
|
||||||
|
showScraperWarningMessage()
|
||||||
|
}
|
||||||
|
Sdk.Mode.HYBRID -> {
|
||||||
|
showOnlyHybridModeInputs()
|
||||||
|
showHybridWarningMessage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,8 +106,8 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||||||
view?.clearPassError()
|
view?.clearPassError()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNameTextChanged() {
|
fun onUsernameTextChanged() {
|
||||||
view?.clearNameError()
|
view?.clearUsernameError()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onPinTextChanged() {
|
fun onPinTextChanged() {
|
||||||
@ -137,7 +154,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getStudentsAppropriatesToLoginType(): Single<List<Student>> {
|
private fun getStudentsAppropriatesToLoginType(): Single<List<Student>> {
|
||||||
val email = view?.formNameValue.orEmpty()
|
val email = view?.formUsernameValue.orEmpty()
|
||||||
val password = view?.formPassValue.orEmpty()
|
val password = view?.formPassValue.orEmpty()
|
||||||
val endpoint = view?.formHostValue.orEmpty()
|
val endpoint = view?.formHostValue.orEmpty()
|
||||||
|
|
||||||
@ -153,7 +170,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun validateCredentials(): Boolean {
|
private fun validateCredentials(): Boolean {
|
||||||
val login = view?.formNameValue.orEmpty()
|
val login = view?.formUsernameValue.orEmpty()
|
||||||
val password = view?.formPassValue.orEmpty()
|
val password = view?.formPassValue.orEmpty()
|
||||||
|
|
||||||
val pin = view?.formPinValue.orEmpty()
|
val pin = view?.formPinValue.orEmpty()
|
||||||
@ -181,7 +198,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
Sdk.Mode.SCRAPPER -> {
|
Sdk.Mode.SCRAPPER -> {
|
||||||
if (login.isEmpty()) {
|
if (login.isEmpty()) {
|
||||||
view?.setErrorNameRequired()
|
view?.setErrorUsernameRequired()
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +214,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
Sdk.Mode.HYBRID -> {
|
Sdk.Mode.HYBRID -> {
|
||||||
if (login.isEmpty()) {
|
if (login.isEmpty()) {
|
||||||
view?.setErrorNameRequired()
|
view?.setErrorUsernameRequired()
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,13 @@ import io.github.wulkanowy.ui.base.BaseView
|
|||||||
|
|
||||||
interface LoginAdvancedView : BaseView {
|
interface LoginAdvancedView : BaseView {
|
||||||
|
|
||||||
val formNameValue: String
|
val formUsernameValue: String
|
||||||
|
|
||||||
val formPassValue: String
|
val formPassValue: String
|
||||||
|
|
||||||
val formHostValue: String?
|
val formHostValue: String
|
||||||
|
|
||||||
|
val formHostSymbol: String
|
||||||
|
|
||||||
val formLoginType: String
|
val formLoginType: String
|
||||||
|
|
||||||
@ -19,11 +21,25 @@ interface LoginAdvancedView : BaseView {
|
|||||||
|
|
||||||
val formTokenValue: String
|
val formTokenValue: String
|
||||||
|
|
||||||
|
val nicknameLabel: String
|
||||||
|
|
||||||
|
val emailLabel: String
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String)
|
fun showMobileApiWarningMessage()
|
||||||
|
|
||||||
fun setErrorNameRequired()
|
fun showScraperWarningMessage()
|
||||||
|
|
||||||
|
fun showHybridWarningMessage()
|
||||||
|
|
||||||
|
fun setDefaultCredentials(username: String, pass: String, symbol: String, token: String, pin: String)
|
||||||
|
|
||||||
|
fun setUsernameLabel(label: String)
|
||||||
|
|
||||||
|
fun setSymbol(symbol: String)
|
||||||
|
|
||||||
|
fun setErrorUsernameRequired()
|
||||||
|
|
||||||
fun setErrorPassRequired(focus: Boolean)
|
fun setErrorPassRequired(focus: Boolean)
|
||||||
|
|
||||||
@ -31,7 +47,7 @@ interface LoginAdvancedView : BaseView {
|
|||||||
|
|
||||||
fun setErrorPassIncorrect()
|
fun setErrorPassIncorrect()
|
||||||
|
|
||||||
fun clearNameError()
|
fun clearUsernameError()
|
||||||
|
|
||||||
fun clearPassError()
|
fun clearPassError()
|
||||||
|
|
||||||
|
@ -7,8 +7,7 @@ import android.view.View
|
|||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
|
import android.widget.ArrayAdapter
|
||||||
import android.view.inputmethod.EditorInfo.IME_NULL
|
|
||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
@ -18,6 +17,7 @@ import io.github.wulkanowy.utils.AppInfo
|
|||||||
import io.github.wulkanowy.utils.hideSoftInput
|
import io.github.wulkanowy.utils.hideSoftInput
|
||||||
import io.github.wulkanowy.utils.openEmailClient
|
import io.github.wulkanowy.utils.openEmailClient
|
||||||
import io.github.wulkanowy.utils.openInternetBrowser
|
import io.github.wulkanowy.utils.openInternetBrowser
|
||||||
|
import io.github.wulkanowy.utils.setOnEditorDoneSignIn
|
||||||
import io.github.wulkanowy.utils.showSoftInput
|
import io.github.wulkanowy.utils.showSoftInput
|
||||||
import kotlinx.android.synthetic.main.fragment_login_form.*
|
import kotlinx.android.synthetic.main.fragment_login_form.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -34,16 +34,33 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
|||||||
fun newInstance() = LoginFormFragment()
|
fun newInstance() = LoginFormFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val formNameValue get() = loginFormName.text.toString()
|
override val formUsernameValue: String
|
||||||
|
get() = loginFormUsername.text.toString()
|
||||||
|
|
||||||
override val formPassValue get() = loginFormPass.text.toString()
|
override val formPassValue: String
|
||||||
|
get() = loginFormPass.text.toString()
|
||||||
|
|
||||||
override val formHostValue get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString()))
|
override val formHostValue: String
|
||||||
|
get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())).orEmpty()
|
||||||
|
|
||||||
|
override val formHostSymbol: String
|
||||||
|
get() = hostSymbols.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())).orEmpty()
|
||||||
|
|
||||||
|
override val formSymbolValue: String
|
||||||
|
get() = loginFormSymbol.text.toString()
|
||||||
|
|
||||||
|
override val nicknameLabel: String
|
||||||
|
get() = getString(R.string.login_nickname_hint)
|
||||||
|
|
||||||
|
override val emailLabel: String
|
||||||
|
get() = getString(R.string.login_email_hint)
|
||||||
|
|
||||||
private lateinit var hostKeys: Array<String>
|
private lateinit var hostKeys: Array<String>
|
||||||
|
|
||||||
private lateinit var hostValues: Array<String>
|
private lateinit var hostValues: Array<String>
|
||||||
|
|
||||||
|
private lateinit var hostSymbols: Array<String>
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_login_form, container, false)
|
return inflater.inflate(R.layout.fragment_login_form, container, false)
|
||||||
}
|
}
|
||||||
@ -56,38 +73,61 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
|||||||
override fun initView() {
|
override fun initView() {
|
||||||
hostKeys = resources.getStringArray(R.array.hosts_keys)
|
hostKeys = resources.getStringArray(R.array.hosts_keys)
|
||||||
hostValues = resources.getStringArray(R.array.hosts_values)
|
hostValues = resources.getStringArray(R.array.hosts_values)
|
||||||
|
hostSymbols = resources.getStringArray(R.array.hosts_symbols)
|
||||||
|
|
||||||
loginFormName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() }
|
loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() }
|
||||||
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
|
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
|
||||||
|
loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() }
|
||||||
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
|
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
|
||||||
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
|
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
|
||||||
loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() }
|
loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() }
|
||||||
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
|
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
|
||||||
loginFormFaq.setOnClickListener { presenter.onFaqClick() }
|
loginFormFaq.setOnClickListener { presenter.onFaqClick() }
|
||||||
loginFormContactEmail.setOnClickListener { presenter.onEmailClick() }
|
loginFormContactEmail.setOnClickListener { presenter.onEmailClick() }
|
||||||
|
loginFormRecoverLink.setOnClickListener { presenter.onRecoverClick() }
|
||||||
|
loginFormPass.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() }
|
||||||
|
loginFormSymbol.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() }
|
||||||
|
|
||||||
loginFormPass.setOnEditorActionListener { _, id, _ ->
|
loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values)))
|
||||||
if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false
|
|
||||||
}
|
|
||||||
|
|
||||||
with(loginFormHost) {
|
with(loginFormHost) {
|
||||||
setText(hostKeys.getOrElse(0) { "" })
|
setText(hostKeys.getOrNull(0).orEmpty())
|
||||||
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
|
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
|
||||||
|
setOnClickListener { if (loginFormContainer.visibility == GONE) dismissDropDown() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setCredentials(name: String, pass: String) {
|
override fun setCredentials(username: String, pass: String) {
|
||||||
loginFormName.setText(name)
|
loginFormUsername.setText(username)
|
||||||
loginFormPass.setText(pass)
|
loginFormPass.setText(pass)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setErrorNameRequired() {
|
override fun setSymbol(symbol: String) {
|
||||||
with(loginFormNameLayout) {
|
loginFormSymbol.setText(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUsernameLabel(label: String) {
|
||||||
|
loginFormUsernameLayout.hint = label
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showSymbol(show: Boolean) {
|
||||||
|
loginFormSymbolLayout.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorUsernameRequired() {
|
||||||
|
with(loginFormUsernameLayout) {
|
||||||
requestFocus()
|
requestFocus()
|
||||||
error = getString(R.string.login_field_required)
|
error = getString(R.string.login_field_required)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setErrorSymbolRequired(focus: Boolean) {
|
||||||
|
with(loginFormSymbolLayout) {
|
||||||
|
if (focus) requestFocus()
|
||||||
|
error = getString(R.string.login_symbol_helper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun setErrorPassRequired(focus: Boolean) {
|
override fun setErrorPassRequired(focus: Boolean) {
|
||||||
with(loginFormPassLayout) {
|
with(loginFormPassLayout) {
|
||||||
if (focus) requestFocus()
|
if (focus) requestFocus()
|
||||||
@ -109,14 +149,18 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearNameError() {
|
override fun clearUsernameError() {
|
||||||
loginFormNameLayout.error = null
|
loginFormUsernameLayout.error = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearPassError() {
|
override fun clearPassError() {
|
||||||
loginFormPassLayout.error = null
|
loginFormPassLayout.error = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clearSymbolError() {
|
||||||
|
loginFormSymbolLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
override fun showSoftKeyboard() {
|
override fun showSoftKeyboard() {
|
||||||
activity?.showSoftInput()
|
activity?.showSoftInput()
|
||||||
}
|
}
|
||||||
@ -154,6 +198,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
|||||||
(activity as? LoginActivity)?.onAdvancedLoginClick()
|
(activity as? LoginActivity)?.onAdvancedLoginClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRecoverClick() {
|
||||||
|
(activity as? LoginActivity)?.onRecoverClick()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
@ -163,6 +211,14 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
|||||||
context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania/dlaczego-nie-moge-sie-zalogowac", ::showMessage)
|
context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania/dlaczego-nie-moge-sie-zalogowac", ::showMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
with(presenter) {
|
||||||
|
updateUsernameLabel()
|
||||||
|
updateSymbolInputVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun openEmail() {
|
override fun openEmail() {
|
||||||
context?.openEmailClient(
|
context?.openEmailClient(
|
||||||
requireContext().getString(R.string.login_email_intent_title),
|
requireContext().getString(R.string.login_email_intent_title),
|
||||||
|
@ -42,10 +42,25 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
fun onHostSelected() {
|
fun onHostSelected() {
|
||||||
view?.apply {
|
view?.apply {
|
||||||
clearPassError()
|
clearPassError()
|
||||||
clearNameError()
|
clearUsernameError()
|
||||||
if (formHostValue?.contains("fakelog") == true) {
|
if (formHostValue.contains("fakelog")) {
|
||||||
setCredentials("jan@fakelog.cf", "jan123")
|
setCredentials("jan@fakelog.cf", "jan123")
|
||||||
}
|
}
|
||||||
|
setSymbol(formHostSymbol)
|
||||||
|
updateUsernameLabel()
|
||||||
|
updateSymbolInputVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateUsernameLabel() {
|
||||||
|
view?.run {
|
||||||
|
setUsernameLabel(if ("standard" in formHostValue) emailLabel else nicknameLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSymbolInputVisibility() {
|
||||||
|
view?.run {
|
||||||
|
showSymbol("adfs" in formHostValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,18 +68,23 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
view?.clearPassError()
|
view?.clearPassError()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNameTextChanged() {
|
fun onUsernameTextChanged() {
|
||||||
view?.clearNameError()
|
view?.clearUsernameError()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSymbolTextChanged() {
|
||||||
|
view?.clearSymbolError()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSignInClick() {
|
fun onSignInClick() {
|
||||||
val email = view?.formNameValue.orEmpty().trim()
|
val email = view?.formUsernameValue.orEmpty().trim()
|
||||||
val password = view?.formPassValue.orEmpty().trim()
|
val password = view?.formPassValue.orEmpty().trim()
|
||||||
val endpoint = view?.formHostValue.orEmpty().trim()
|
val host = view?.formHostValue.orEmpty().trim()
|
||||||
|
val symbol = view?.formSymbolValue.orEmpty().trim()
|
||||||
|
|
||||||
if (!validateCredentials(email, password)) return
|
if (!validateCredentials(email, password, host, symbol)) return
|
||||||
|
|
||||||
disposable.add(studentRepository.getStudentsScrapper(email, password, endpoint)
|
disposable.add(studentRepository.getStudentsScrapper(email, password, host, symbol)
|
||||||
.subscribeOn(schedulers.backgroundThread)
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
.observeOn(schedulers.mainThread)
|
.observeOn(schedulers.mainThread)
|
||||||
.doOnSubscribe {
|
.doOnSubscribe {
|
||||||
@ -83,11 +103,11 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
.subscribe({
|
.subscribe({
|
||||||
Timber.i("Login result: Success")
|
Timber.i("Login result: Success")
|
||||||
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "scrapperBaseUrl" to endpoint, "error" to "No error")
|
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "scrapperBaseUrl" to host, "error" to "No error")
|
||||||
view?.notifyParentAccountLogged(it, Triple(email, password, endpoint))
|
view?.notifyParentAccountLogged(it, Triple(email, password, host))
|
||||||
}, {
|
}, {
|
||||||
Timber.i("Login result: An exception occurred")
|
Timber.i("Login result: An exception occurred")
|
||||||
analytics.logEvent("registration_form", "success" to false, "students" to -1, "scrapperBaseUrl" to endpoint, "error" to it.message.ifNullOrBlank { "No message" })
|
analytics.logEvent("registration_form", "success" to false, "students" to -1, "scrapperBaseUrl" to host, "error" to it.message.ifNullOrBlank { "No message" })
|
||||||
loginErrorHandler.dispatch(it)
|
loginErrorHandler.dispatch(it)
|
||||||
view?.showContact(true)
|
view?.showContact(true)
|
||||||
}))
|
}))
|
||||||
@ -101,11 +121,15 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
view?.openEmail()
|
view?.openEmail()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateCredentials(login: String, password: String): Boolean {
|
fun onRecoverClick() {
|
||||||
|
view?.onRecoverClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateCredentials(login: String, password: String, host: String, symbol: String): Boolean {
|
||||||
var isCorrect = true
|
var isCorrect = true
|
||||||
|
|
||||||
if (login.isEmpty()) {
|
if (login.isEmpty()) {
|
||||||
view?.setErrorNameRequired()
|
view?.setErrorUsernameRequired()
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +142,12 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
view?.setErrorPassInvalid(focus = isCorrect)
|
view?.setErrorPassInvalid(focus = isCorrect)
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("standard" !in host && symbol.isBlank()) {
|
||||||
|
view?.setErrorSymbolRequired(focus = isCorrect)
|
||||||
|
isCorrect = false
|
||||||
|
}
|
||||||
|
|
||||||
return isCorrect
|
return isCorrect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,31 @@ interface LoginFormView : BaseView {
|
|||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
val formNameValue: String
|
val formUsernameValue: String
|
||||||
|
|
||||||
val formPassValue: String
|
val formPassValue: String
|
||||||
|
|
||||||
val formHostValue: String?
|
val formHostValue: String
|
||||||
|
|
||||||
fun setCredentials(name: String, pass: String)
|
val formHostSymbol: String
|
||||||
|
|
||||||
fun setErrorNameRequired()
|
val formSymbolValue: String
|
||||||
|
|
||||||
|
val nicknameLabel: String
|
||||||
|
|
||||||
|
val emailLabel: String
|
||||||
|
|
||||||
|
fun setCredentials(username: String, pass: String)
|
||||||
|
|
||||||
|
fun setSymbol(symbol: String)
|
||||||
|
|
||||||
|
fun setUsernameLabel(label: String)
|
||||||
|
|
||||||
|
fun showSymbol(show: Boolean)
|
||||||
|
|
||||||
|
fun setErrorUsernameRequired()
|
||||||
|
|
||||||
|
fun setErrorSymbolRequired(focus: Boolean)
|
||||||
|
|
||||||
fun setErrorPassRequired(focus: Boolean)
|
fun setErrorPassRequired(focus: Boolean)
|
||||||
|
|
||||||
@ -23,10 +39,12 @@ interface LoginFormView : BaseView {
|
|||||||
|
|
||||||
fun setErrorPassIncorrect()
|
fun setErrorPassIncorrect()
|
||||||
|
|
||||||
fun clearNameError()
|
fun clearUsernameError()
|
||||||
|
|
||||||
fun clearPassError()
|
fun clearPassError()
|
||||||
|
|
||||||
|
fun clearSymbolError()
|
||||||
|
|
||||||
fun showSoftKeyboard()
|
fun showSoftKeyboard()
|
||||||
|
|
||||||
fun hideSoftKeyboard()
|
fun hideSoftKeyboard()
|
||||||
@ -48,4 +66,6 @@ interface LoginFormView : BaseView {
|
|||||||
fun openEmail()
|
fun openEmail()
|
||||||
|
|
||||||
fun openAdvancedLogin()
|
fun openAdvancedLogin()
|
||||||
|
|
||||||
|
fun onRecoverClick()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,220 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.login.recover
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.webkit.JavascriptInterface
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import androidx.core.widget.doOnTextChanged
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||||
|
import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter
|
||||||
|
import io.github.wulkanowy.utils.hideSoftInput
|
||||||
|
import io.github.wulkanowy.utils.showSoftInput
|
||||||
|
import kotlinx.android.synthetic.main.fragment_login_recover.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LoginRecoverFragment : BaseFragment(), LoginRecoverView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: LoginRecoverPresenter
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = LoginRecoverFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var hostKeys: Array<String>
|
||||||
|
|
||||||
|
private lateinit var hostValues: Array<String>
|
||||||
|
|
||||||
|
override val recoverHostValue: String
|
||||||
|
get() = hostValues.getOrNull(hostKeys.indexOf(loginRecoverHost.text.toString())).orEmpty()
|
||||||
|
|
||||||
|
override val recoverNameValue: String
|
||||||
|
get() = loginRecoverName.text.toString().trim()
|
||||||
|
|
||||||
|
override val recoverSymbolValue: String
|
||||||
|
get() = loginRecoverSymbol.text.toString().trim()
|
||||||
|
|
||||||
|
override val emailHintString: String
|
||||||
|
get() = getString(R.string.login_email_hint)
|
||||||
|
|
||||||
|
override val loginPeselEmailHintString: String
|
||||||
|
get() = getString(R.string.login_login_pesel_email_hint)
|
||||||
|
|
||||||
|
override val invalidEmailString: String
|
||||||
|
get() = getString(R.string.login_invalid_email)
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_login_recover, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
presenter.onAttachView(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
loginRecoverWebView.setBackgroundColor(Color.TRANSPARENT)
|
||||||
|
hostKeys = resources.getStringArray(R.array.hosts_keys)
|
||||||
|
hostValues = resources.getStringArray(R.array.hosts_values)
|
||||||
|
|
||||||
|
loginRecoverName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() }
|
||||||
|
loginRecoverSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() }
|
||||||
|
loginRecoverHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
|
||||||
|
loginRecoverButton.setOnClickListener { presenter.onRecoverClick() }
|
||||||
|
loginRecoverErrorRetry.setOnClickListener { presenter.onRecoverClick() }
|
||||||
|
loginRecoverErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
loginRecoverLogin.setOnClickListener { (activity as LoginActivity).switchView(0) }
|
||||||
|
|
||||||
|
loginRecoverSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values)))
|
||||||
|
|
||||||
|
with(loginRecoverHost) {
|
||||||
|
setText(hostKeys.getOrNull(0).orEmpty())
|
||||||
|
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
|
||||||
|
setOnClickListener { if (loginRecoverFormContainer.visibility == GONE) dismissDropDown() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setDefaultCredentials(username: String) {
|
||||||
|
loginRecoverName.setText(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorNameRequired() {
|
||||||
|
with(loginRecoverNameLayout) {
|
||||||
|
requestFocus()
|
||||||
|
error = getString(R.string.login_field_required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUsernameHint(hint: String) {
|
||||||
|
loginRecoverNameLayout.hint = hint
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUsernameError(message: String) {
|
||||||
|
with(loginRecoverNameLayout) {
|
||||||
|
requestFocus()
|
||||||
|
error = message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSymbolError(focus: Boolean) {
|
||||||
|
with(loginRecoverSymbolLayout) {
|
||||||
|
if (focus) requestFocus()
|
||||||
|
error = getString(R.string.login_field_required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearUsernameError() {
|
||||||
|
loginRecoverNameLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearSymbolError() {
|
||||||
|
loginRecoverSymbolLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showSymbol(show: Boolean) {
|
||||||
|
loginRecoverSymbolLayout.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(show: Boolean) {
|
||||||
|
loginRecoverProgress.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showRecoverForm(show: Boolean) {
|
||||||
|
loginRecoverFormContainer.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showCaptcha(show: Boolean) {
|
||||||
|
loginRecoverCaptchaContainer.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showErrorView(show: Boolean) {
|
||||||
|
loginRecoverError.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorDetails(message: String) {
|
||||||
|
loginRecoverErrorMessage.text = message
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showSuccessView(show: Boolean) {
|
||||||
|
loginRecoverSuccess.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSuccessTitle(title: String) {
|
||||||
|
loginRecoverSuccessTitle.text = title
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSuccessMessage(message: String) {
|
||||||
|
loginRecoverSuccessMessage.text = message
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showSoftKeyboard() {
|
||||||
|
activity?.showSoftInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hideSoftKeyboard() {
|
||||||
|
activity?.hideSoftInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled", "AddJavascriptInterface")
|
||||||
|
override fun loadReCaptcha(siteKey: String, url: String) {
|
||||||
|
val html = """
|
||||||
|
<div style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);" id="recaptcha"></div>
|
||||||
|
<script src="https://www.google.com/recaptcha/api.js?onload=cl&render=explicit&hl=pl" async defer></script>
|
||||||
|
<script>var cl=()=>grecaptcha.render("recaptcha",{
|
||||||
|
sitekey:'$siteKey',
|
||||||
|
callback:e =>Android.captchaCallback(e)})</script>
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
with(loginRecoverWebView) {
|
||||||
|
settings.javaScriptEnabled = true
|
||||||
|
webViewClient = object : WebViewClient() {
|
||||||
|
private var recoverWebViewSuccess: Boolean = true
|
||||||
|
|
||||||
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
|
if (recoverWebViewSuccess) {
|
||||||
|
showCaptcha(true)
|
||||||
|
showProgress(false)
|
||||||
|
} else {
|
||||||
|
showProgress(false)
|
||||||
|
showErrorView(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
|
||||||
|
recoverWebViewSuccess = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDataWithBaseURL(url, html, "text/html", "UTF-8", null)
|
||||||
|
addJavascriptInterface(object {
|
||||||
|
@JavascriptInterface
|
||||||
|
fun captchaCallback(reCaptchaResponse: String) {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
presenter.onReCaptchaVerified(reCaptchaResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "Android")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
presenter.updateFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
loginRecoverWebView.destroy()
|
||||||
|
presenter.onDetachView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.login.recover
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.repositories.recover.RecoverRepository
|
||||||
|
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||||
|
import io.github.wulkanowy.utils.SchedulersProvider
|
||||||
|
import io.github.wulkanowy.utils.ifNullOrBlank
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LoginRecoverPresenter @Inject constructor(
|
||||||
|
schedulers: SchedulersProvider,
|
||||||
|
studentRepository: StudentRepository,
|
||||||
|
private val loginErrorHandler: RecoverErrorHandler,
|
||||||
|
private val analytics: FirebaseAnalyticsHelper,
|
||||||
|
private val recoverRepository: RecoverRepository
|
||||||
|
) : BasePresenter<LoginRecoverView>(loginErrorHandler, studentRepository, schedulers) {
|
||||||
|
|
||||||
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
|
override fun onAttachView(view: LoginRecoverView) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
view.initView()
|
||||||
|
|
||||||
|
with(loginErrorHandler) {
|
||||||
|
showErrorMessage = ::showErrorMessage
|
||||||
|
onInvalidUsername = ::onInvalidUsername
|
||||||
|
onInvalidCaptcha = ::onInvalidCaptcha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onNameTextChanged() {
|
||||||
|
view?.clearUsernameError()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSymbolTextChanged() {
|
||||||
|
view?.clearSymbolError()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onHostSelected() {
|
||||||
|
view?.run {
|
||||||
|
if ("fakelog" in recoverHostValue) setDefaultCredentials("jan@fakelog.cf")
|
||||||
|
clearUsernameError()
|
||||||
|
updateFields()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateFields() {
|
||||||
|
view?.run {
|
||||||
|
showSymbol("adfs" in recoverHostValue)
|
||||||
|
setUsernameHint(if ("standard" in recoverHostValue) emailHintString else loginPeselEmailHintString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRecoverClick() {
|
||||||
|
val username = view?.recoverNameValue.orEmpty()
|
||||||
|
val host = view?.recoverHostValue.orEmpty()
|
||||||
|
val symbol = view?.recoverSymbolValue.orEmpty()
|
||||||
|
|
||||||
|
if (!validateInput(username, host, symbol)) return
|
||||||
|
|
||||||
|
disposable.add(recoverRepository.getReCaptchaSiteKey(host, symbol.ifBlank { "Default" })
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.doOnSubscribe {
|
||||||
|
view?.run {
|
||||||
|
hideSoftKeyboard()
|
||||||
|
showRecoverForm(false)
|
||||||
|
showProgress(true)
|
||||||
|
showErrorView(false)
|
||||||
|
showCaptcha(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribe({ (resetUrl, siteKey) ->
|
||||||
|
view?.loadReCaptcha(siteKey, resetUrl)
|
||||||
|
}) {
|
||||||
|
Timber.e("Obtain captcha site key result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateInput(username: String, host: String, symbol: String): Boolean {
|
||||||
|
var isCorrect = true
|
||||||
|
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
view?.setErrorNameRequired()
|
||||||
|
isCorrect = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("standard" in host && "@" !in username) {
|
||||||
|
view?.setUsernameError(view?.invalidEmailString.orEmpty())
|
||||||
|
isCorrect = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("adfs" in host && symbol.isBlank()) {
|
||||||
|
view?.setSymbolError(focus = isCorrect)
|
||||||
|
isCorrect = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return isCorrect
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onReCaptchaVerified(reCaptchaResponse: String) {
|
||||||
|
val username = view?.recoverNameValue.orEmpty()
|
||||||
|
val host = view?.recoverHostValue.orEmpty()
|
||||||
|
val symbol = view?.recoverSymbolValue.ifNullOrBlank { "Default" }
|
||||||
|
|
||||||
|
with(disposable) {
|
||||||
|
clear()
|
||||||
|
add(recoverRepository.sendRecoverRequest(host, symbol, username, reCaptchaResponse)
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.doOnSubscribe {
|
||||||
|
view?.run {
|
||||||
|
showProgress(true)
|
||||||
|
showRecoverForm(false)
|
||||||
|
showCaptcha(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.doFinally {
|
||||||
|
view?.showProgress(false)
|
||||||
|
}
|
||||||
|
.subscribe({
|
||||||
|
view?.run {
|
||||||
|
showSuccessView(true)
|
||||||
|
setSuccessTitle(it.substringBefore(". "))
|
||||||
|
setSuccessMessage(it.substringAfter(". "))
|
||||||
|
}
|
||||||
|
|
||||||
|
analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to true)
|
||||||
|
}) {
|
||||||
|
Timber.e("Send recover request result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDetailsClick() {
|
||||||
|
view?.showErrorDetailsDialog(lastError)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showErrorMessage(message: String, error: Throwable) {
|
||||||
|
view?.run {
|
||||||
|
lastError = error
|
||||||
|
showProgress(false)
|
||||||
|
setErrorDetails(message)
|
||||||
|
showErrorView(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onInvalidUsername(message: String) {
|
||||||
|
view?.run {
|
||||||
|
setUsernameError(message)
|
||||||
|
showRecoverForm(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onInvalidCaptcha(message: String, error: Throwable) {
|
||||||
|
view?.run {
|
||||||
|
lastError = error
|
||||||
|
setErrorDetails(message)
|
||||||
|
showCaptcha(false)
|
||||||
|
showRecoverForm(false)
|
||||||
|
showErrorView(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.login.recover
|
||||||
|
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
|
interface LoginRecoverView : BaseView {
|
||||||
|
|
||||||
|
val recoverHostValue: String
|
||||||
|
|
||||||
|
val recoverNameValue: String
|
||||||
|
|
||||||
|
val recoverSymbolValue: String
|
||||||
|
|
||||||
|
val emailHintString: String
|
||||||
|
|
||||||
|
val loginPeselEmailHintString: String
|
||||||
|
|
||||||
|
val invalidEmailString: String
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun setDefaultCredentials(username: String)
|
||||||
|
|
||||||
|
fun clearUsernameError()
|
||||||
|
|
||||||
|
fun clearSymbolError()
|
||||||
|
|
||||||
|
fun showSymbol(show: Boolean)
|
||||||
|
|
||||||
|
fun setErrorNameRequired()
|
||||||
|
|
||||||
|
fun setUsernameHint(hint: String)
|
||||||
|
|
||||||
|
fun setUsernameError(message: String)
|
||||||
|
|
||||||
|
fun setSymbolError(focus: Boolean)
|
||||||
|
|
||||||
|
fun showSoftKeyboard()
|
||||||
|
|
||||||
|
fun hideSoftKeyboard()
|
||||||
|
|
||||||
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
fun showRecoverForm(show: Boolean)
|
||||||
|
|
||||||
|
fun showCaptcha(show: Boolean)
|
||||||
|
|
||||||
|
fun showErrorView(show: Boolean)
|
||||||
|
|
||||||
|
fun setErrorDetails(message: String)
|
||||||
|
|
||||||
|
fun showSuccessView(show: Boolean)
|
||||||
|
|
||||||
|
fun setSuccessMessage(message: String)
|
||||||
|
|
||||||
|
fun setSuccessTitle(title: String)
|
||||||
|
|
||||||
|
fun loadReCaptcha(siteKey: String, url: String)
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.login.recover
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import com.readystatesoftware.chuck.api.ChuckCollector
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.exception.InvalidCaptchaException
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.exception.InvalidEmailException
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.exception.NoAccountFoundException
|
||||||
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RecoverErrorHandler @Inject constructor(
|
||||||
|
resources: Resources,
|
||||||
|
chuckCollector: ChuckCollector
|
||||||
|
) : ErrorHandler(resources, chuckCollector) {
|
||||||
|
|
||||||
|
var onInvalidUsername: (String) -> Unit = {}
|
||||||
|
|
||||||
|
var onInvalidCaptcha: (String, Throwable) -> Unit = { _, _ -> }
|
||||||
|
|
||||||
|
override fun proceed(error: Throwable) {
|
||||||
|
when (error) {
|
||||||
|
is InvalidEmailException, is NoAccountFoundException -> onInvalidUsername(error.localizedMessage.orEmpty())
|
||||||
|
is InvalidCaptchaException -> onInvalidCaptcha(error.localizedMessage.orEmpty(), error)
|
||||||
|
else -> super.proceed(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
super.clear()
|
||||||
|
onInvalidUsername = {}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.login.studentselect
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
@ -11,7 +13,8 @@ import io.github.wulkanowy.data.db.entities.Student
|
|||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.item_login_student_select.*
|
import kotlinx.android.synthetic.main.item_login_student_select.*
|
||||||
|
|
||||||
class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
|
class LoginStudentSelectItem(val student: Student, val alreadySaved: Boolean) :
|
||||||
|
AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
|
||||||
|
|
||||||
override fun getLayoutRes() = R.layout.item_login_student_select
|
override fun getLayoutRes() = R.layout.item_login_student_select
|
||||||
|
|
||||||
@ -24,6 +27,10 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
|||||||
holder.apply {
|
holder.apply {
|
||||||
loginItemName.text = "${student.studentName} ${student.className}"
|
loginItemName.text = "${student.studentName} ${student.className}"
|
||||||
loginItemSchool.text = student.schoolName
|
loginItemSchool.text = student.schoolName
|
||||||
|
loginItemName.isEnabled = !alreadySaved
|
||||||
|
loginItemSchool.isEnabled = !alreadySaved
|
||||||
|
loginItemCheck.isEnabled = !alreadySaved
|
||||||
|
loginItemSignedIn.visibility = if (alreadySaved) VISIBLE else GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,6 +41,7 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
|||||||
other as LoginStudentSelectItem
|
other as LoginStudentSelectItem
|
||||||
|
|
||||||
if (student != other.student) return false
|
if (student != other.student) return false
|
||||||
|
if (alreadySaved != other.alreadySaved) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -42,7 +50,8 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
|||||||
return student.hashCode()
|
return student.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
class ItemViewHolder(view: View, val adapter: FlexibleAdapter<*>) :
|
||||||
|
FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||||
|
|
||||||
override val containerView: View
|
override val containerView: View
|
||||||
get() = itemView
|
get() = itemView
|
||||||
@ -53,7 +62,10 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
|||||||
|
|
||||||
override fun onClick(view: View?) {
|
override fun onClick(view: View?) {
|
||||||
super.onClick(view)
|
super.onClick(view)
|
||||||
loginItemCheck.apply { isChecked = !isChecked }
|
|
||||||
|
if (loginItemCheck.isEnabled) {
|
||||||
|
loginItemCheck.apply { isChecked = !isChecked }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
|||||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.SchedulersProvider
|
import io.github.wulkanowy.utils.SchedulersProvider
|
||||||
import io.github.wulkanowy.utils.ifNullOrBlank
|
import io.github.wulkanowy.utils.ifNullOrBlank
|
||||||
|
import io.reactivex.Single
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -50,7 +51,7 @@ class LoginStudentSelectPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
|
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||||
if (item is LoginStudentSelectItem) {
|
if (item is LoginStudentSelectItem && !item.alreadySaved) {
|
||||||
selectedStudents.removeAll { it == item.student }
|
selectedStudents.removeAll { it == item.student }
|
||||||
.let { if (!it) selectedStudents.add(item.student) }
|
.let { if (!it) selectedStudents.add(item.student) }
|
||||||
|
|
||||||
@ -58,11 +59,33 @@ class LoginStudentSelectPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun compareStudents(a: Student, b: Student): Boolean {
|
||||||
|
return a.email == b.email
|
||||||
|
&& a.symbol == b.symbol
|
||||||
|
&& a.studentId == b.studentId
|
||||||
|
&& a.schoolSymbol == b.schoolSymbol
|
||||||
|
&& a.classId == b.classId
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadData(students: List<Student>) {
|
private fun loadData(students: List<Student>) {
|
||||||
this.students = students
|
this.students = students
|
||||||
view?.apply {
|
disposable.add(studentRepository.getSavedStudents()
|
||||||
updateData(students.map { LoginStudentSelectItem(it) })
|
.map { savedStudents ->
|
||||||
}
|
students.map { student ->
|
||||||
|
Pair(student, savedStudents.any { compareStudents(student, it) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.subscribe({
|
||||||
|
view?.updateData(it.map { studentPair ->
|
||||||
|
LoginStudentSelectItem(studentPair.first, studentPair.second)
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
view?.updateData(students.map { student -> LoginStudentSelectItem(student, false) })
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerStudents(students: List<Student>) {
|
private fun registerStudents(students: List<Student>) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.modules.luckynumber
|
package io.github.wulkanowy.ui.modules.luckynumber
|
||||||
|
|
||||||
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
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.data.repositories.student.StudentRepository
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
@ -15,7 +14,6 @@ class LuckyNumberPresenter @Inject constructor(
|
|||||||
errorHandler: ErrorHandler,
|
errorHandler: ErrorHandler,
|
||||||
studentRepository: StudentRepository,
|
studentRepository: StudentRepository,
|
||||||
private val luckyNumberRepository: LuckyNumberRepository,
|
private val luckyNumberRepository: LuckyNumberRepository,
|
||||||
private val semesterRepository: SemesterRepository,
|
|
||||||
private val analytics: FirebaseAnalyticsHelper
|
private val analytics: FirebaseAnalyticsHelper
|
||||||
) : BasePresenter<LuckyNumberView>(errorHandler, studentRepository, schedulers) {
|
) : BasePresenter<LuckyNumberView>(errorHandler, studentRepository, schedulers) {
|
||||||
|
|
||||||
@ -38,7 +36,6 @@ class LuckyNumberPresenter @Inject constructor(
|
|||||||
disposable.apply {
|
disposable.apply {
|
||||||
clear()
|
clear()
|
||||||
add(studentRepository.getCurrentStudent()
|
add(studentRepository.getCurrentStudent()
|
||||||
.flatMap { semesterRepository.getCurrentSemester(it) }
|
|
||||||
.flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) }
|
.flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) }
|
||||||
.subscribeOn(schedulers.backgroundThread)
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
.observeOn(schedulers.mainThread)
|
.observeOn(schedulers.mainThread)
|
||||||
|
@ -1,21 +1,14 @@
|
|||||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||||
|
|
||||||
import android.annotation.TargetApi
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.appwidget.AppWidgetManager
|
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_HEIGHT
|
||||||
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
|
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH
|
||||||
import android.content.BroadcastReceiver
|
import android.appwidget.AppWidgetProvider
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Bundle
|
||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
@ -25,7 +18,6 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
|
|||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||||
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
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.data.repositories.student.StudentRepository
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
@ -34,14 +26,11 @@ import io.reactivex.Maybe
|
|||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var studentRepository: StudentRepository
|
lateinit var studentRepository: StudentRepository
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var semesterRepository: SemesterRepository
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var luckyNumberRepository: LuckyNumberRepository
|
lateinit var luckyNumberRepository: LuckyNumberRepository
|
||||||
|
|
||||||
@ -59,20 +48,20 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
|||||||
fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId"
|
fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId"
|
||||||
|
|
||||||
fun getThemeWidgetKey(appWidgetId: Int) = "lucky_number_widget_theme_$appWidgetId"
|
fun getThemeWidgetKey(appWidgetId: Int) = "lucky_number_widget_theme_$appWidgetId"
|
||||||
|
|
||||||
|
fun getHeightWidgetKey(appWidgetId: Int) = "lucky_number_widget_height_$appWidgetId"
|
||||||
|
|
||||||
|
fun getWidthWidgetKey(appWidgetId: Int) = "lucky_number_widget_width_$appWidgetId"
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
AndroidInjection.inject(this, context)
|
AndroidInjection.inject(this, context)
|
||||||
when (intent.action) {
|
super.onReceive(context, intent)
|
||||||
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) {
|
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray?) {
|
||||||
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
|
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||||
|
appWidgetIds?.forEach { appWidgetId ->
|
||||||
val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
|
val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
|
||||||
val layoutId = if (savedTheme == 0L) R.layout.widget_luckynumber else R.layout.widget_luckynumber_dark
|
val layoutId = if (savedTheme == 0L) R.layout.widget_luckynumber else R.layout.widget_luckynumber_dark
|
||||||
|
|
||||||
@ -85,14 +74,64 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
|||||||
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent)
|
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
setStyles(remoteView, intent)
|
setStyles(remoteView, appWidgetId)
|
||||||
appWidgetManager.updateAppWidget(appWidgetId, remoteView)
|
appWidgetManager.updateAppWidget(appWidgetId, remoteView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onDelete(intent: Intent) {
|
override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
|
||||||
val appWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0)
|
super.onDeleted(context, appWidgetIds)
|
||||||
if (appWidgetId != 0) sharedPref.delete(getStudentWidgetKey(appWidgetId))
|
appWidgetIds?.forEach { appWidgetId ->
|
||||||
|
if (appWidgetId != 0) sharedPref.delete(getStudentWidgetKey(appWidgetId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle?) {
|
||||||
|
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
|
||||||
|
|
||||||
|
val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
|
||||||
|
val layoutId = if (savedTheme == 0L) R.layout.widget_luckynumber else R.layout.widget_luckynumber_dark
|
||||||
|
|
||||||
|
val remoteView = RemoteViews(context.packageName, layoutId)
|
||||||
|
|
||||||
|
setStyles(remoteView, appWidgetId, newOptions)
|
||||||
|
appWidgetManager.updateAppWidget(appWidgetId, remoteView)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setStyles(views: RemoteViews, appWidgetId: Int, options: Bundle? = null) {
|
||||||
|
val width = options?.getInt(OPTION_APPWIDGET_MIN_WIDTH) ?: sharedPref.getLong(getWidthWidgetKey(appWidgetId), 74).toInt()
|
||||||
|
val height = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: sharedPref.getLong(getHeightWidgetKey(appWidgetId), 74).toInt()
|
||||||
|
|
||||||
|
sharedPref.putLong(getWidthWidgetKey(appWidgetId), width.toLong())
|
||||||
|
sharedPref.putLong(getHeightWidgetKey(appWidgetId), height.toLong())
|
||||||
|
|
||||||
|
val rows = getCellsForSize(height)
|
||||||
|
val cols = getCellsForSize(width)
|
||||||
|
|
||||||
|
Timber.d("New lucky number widget measurement: %dx%d", width, height)
|
||||||
|
Timber.d("Widget size: $cols x $rows")
|
||||||
|
|
||||||
|
when {
|
||||||
|
1 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = false)
|
||||||
|
1 == cols && 1 < rows -> views.setVisibility(imageTop = true, imageLeft = false)
|
||||||
|
1 < cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true)
|
||||||
|
1 == cols && 1 == rows -> views.setVisibility(imageTop = true, imageLeft = false)
|
||||||
|
2 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true)
|
||||||
|
else -> views.setVisibility(imageTop = false, imageLeft = false, title = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun RemoteViews.setVisibility(imageTop: Boolean, imageLeft: Boolean, title: Boolean = false) {
|
||||||
|
setViewVisibility(R.id.luckyNumberWidgetImageTop, if (imageTop) VISIBLE else GONE)
|
||||||
|
setViewVisibility(R.id.luckyNumberWidgetImageLeft, if (imageLeft) VISIBLE else GONE)
|
||||||
|
setViewVisibility(R.id.luckyNumberWidgetTitle, if (title) VISIBLE else GONE)
|
||||||
|
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCellsForSize(size: Int): Int {
|
||||||
|
var n = 2
|
||||||
|
while (74 * n - 30 < size) ++n
|
||||||
|
return n - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLuckyNumber(studentId: Long, appWidgetId: Int): LuckyNumber? {
|
private fun getLuckyNumber(studentId: Long, appWidgetId: Int): LuckyNumber? {
|
||||||
@ -113,7 +152,6 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
|||||||
else -> Maybe.empty()
|
else -> Maybe.empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
|
|
||||||
.flatMap { luckyNumberRepository.getLuckyNumber(it) }
|
.flatMap { luckyNumberRepository.getLuckyNumber(it) }
|
||||||
.subscribeOn(schedulers.backgroundThread)
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
.blockingGet()
|
.blockingGet()
|
||||||
@ -124,69 +162,4 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onOptionsChange(context: Context, intent: Intent) {
|
|
||||||
intent.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId ->
|
|
||||||
val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
|
|
||||||
val layoutId = if (savedTheme == 0L) R.layout.widget_luckynumber else R.layout.widget_luckynumber_dark
|
|
||||||
|
|
||||||
val remoteView = RemoteViews(context.packageName, layoutId)
|
|
||||||
|
|
||||||
setStyles(remoteView, intent)
|
|
||||||
appWidgetManager.updateAppWidget(appWidgetId, remoteView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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")
|
|
||||||
with(views) {
|
|
||||||
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")
|
|
||||||
with(views) {
|
|
||||||
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")
|
|
||||||
with(views) {
|
|
||||||
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")
|
|
||||||
with(views) {
|
|
||||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
|
||||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
|
||||||
setViewVisibility(R.id.luckyNumberWidgetTitle, VISIBLE)
|
|
||||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,8 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
|||||||
|
|
||||||
override val currentViewTitle get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { getString(it) }
|
override val currentViewTitle get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { getString(it) }
|
||||||
|
|
||||||
|
override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString
|
||||||
|
|
||||||
override var startMenuIndex = 0
|
override var startMenuIndex = 0
|
||||||
|
|
||||||
override var startMenuMoreIndex = -1
|
override var startMenuMoreIndex = -1
|
||||||
@ -152,6 +154,10 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
|||||||
supportActionBar?.title = title
|
supportActionBar?.title = title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setViewSubTitle(subtitle: String?) {
|
||||||
|
supportActionBar?.subtitle = subtitle
|
||||||
|
}
|
||||||
|
|
||||||
override fun showHomeArrow(show: Boolean) {
|
override fun showHomeArrow(show: Boolean) {
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(show)
|
supportActionBar?.setDisplayHomeAsUpEnabled(show)
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user