mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-02-20 21:14:44 +01:00
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/uiDesigner.xml
|
||||
.idea/runConfigurations.xml
|
||||
.idea/discord.xml
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
|
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
@ -1,9 +1,6 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<AndroidXmlCodeStyleSettings>
|
||||
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
|
||||
</AndroidXmlCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
|
10
.travis.yml
10
.travis.yml
@ -4,7 +4,7 @@ jdk: oraclejdk8
|
||||
env:
|
||||
global:
|
||||
- ANDROID_API_LEVEL=29
|
||||
- ANDROID_BUILD_TOOLS_VERSION=29.0.2
|
||||
- ANDROID_BUILD_TOOLS_VERSION=29.0.3
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@ -14,7 +14,7 @@ cache:
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- 0.15.0
|
||||
- 0.16.0
|
||||
|
||||
android:
|
||||
licenses:
|
||||
@ -49,9 +49,9 @@ script:
|
||||
- ./gradlew dependencies --stacktrace --daemon
|
||||
- fossa --no-ansi || true
|
||||
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
|
||||
- ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
|
||||
- ./gradlew createFdroidDebugCoverageReport --stacktrace --daemon
|
||||
- ./gradlew jacocoTestReport --stacktrace --daemon
|
||||
- ./gradlew -Pcoverage testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
|
||||
- ./gradlew -Pcoverage createFdroidDebugCoverageReport --stacktrace --daemon
|
||||
- ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon
|
||||
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
|
||||
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;
|
||||
|
@ -10,15 +10,15 @@ apply from: 'hooks.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.2'
|
||||
buildToolsVersion '29.0.3'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 16
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 29
|
||||
versionCode 52
|
||||
versionName "0.15.0"
|
||||
versionCode 53
|
||||
versionName "0.16.0"
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
@ -61,7 +61,7 @@ android {
|
||||
buildConfigField "boolean", "CRASHLYTICS_ENABLED", project.hasProperty("enableCrashlytics") ? "true" : "false"
|
||||
applicationIdSuffix ".dev"
|
||||
versionNameSuffix "-dev"
|
||||
testCoverageEnabled = true
|
||||
testCoverageEnabled = project.hasProperty('coverage')
|
||||
ext.enableCrashlytics = project.hasProperty("enableCrashlytics")
|
||||
}
|
||||
}
|
||||
@ -110,9 +110,9 @@ play {
|
||||
}
|
||||
|
||||
ext {
|
||||
work_manager = "2.3.0"
|
||||
room = "2.2.3"
|
||||
dagger = "2.25.4"
|
||||
work_manager = "2.3.2"
|
||||
room = "2.2.4"
|
||||
dagger = "2.26"
|
||||
chucker = "2.0.4"
|
||||
mockk = "1.9.2"
|
||||
}
|
||||
@ -122,14 +122,14 @@ configurations.all {
|
||||
}
|
||||
|
||||
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 "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.appcompat:appcompat: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.multidex:multidex:2.0.1"
|
||||
|
||||
@ -139,7 +139,7 @@ dependencies {
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
|
||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
||||
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.PhilJay:MPAndroidChart:v3.1.0"
|
||||
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
||||
@ -167,18 +167,19 @@ dependencies {
|
||||
|
||||
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
|
||||
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.jakewharton.threetenabp:threetenabp:1.2.2"
|
||||
implementation "com.jakewharton.timber:timber:4.7.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.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"
|
||||
|
||||
releaseImplementation "fr.o80.chucker:library-no-op:$chucker"
|
||||
@ -189,7 +190,7 @@ dependencies {
|
||||
testImplementation "junit:junit:4.13"
|
||||
testImplementation "io.mockk:mockk:$mockk"
|
||||
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:runner:1.2.0"
|
||||
@ -197,7 +198,7 @@ dependencies {
|
||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||
androidTestImplementation "androidx.room:room-testing:$room"
|
||||
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'
|
||||
|
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.assertTrue
|
||||
import org.junit.Test
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
@ -104,45 +103,45 @@ class Migration13Test : AbstractMigrationTest() {
|
||||
val db = helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
|
||||
|
||||
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 {
|
||||
assertFalse(isCurrent)
|
||||
assertEquals(1, semesterId)
|
||||
assertEquals(1, diaryId)
|
||||
assertFalse(second)
|
||||
assertEquals(1, first.semesterId)
|
||||
assertEquals(1, first.diaryId)
|
||||
}
|
||||
semesters1[2].run {
|
||||
assertFalse(isCurrent)
|
||||
assertEquals(3, semesterId)
|
||||
assertEquals(2, diaryId)
|
||||
assertFalse(second)
|
||||
assertEquals(3, first.semesterId)
|
||||
assertEquals(2, first.diaryId)
|
||||
}
|
||||
semesters1[3].run {
|
||||
assertTrue(isCurrent)
|
||||
assertEquals(4, semesterId)
|
||||
assertEquals(2, diaryId)
|
||||
assertTrue(second)
|
||||
assertEquals(4, first.semesterId)
|
||||
assertEquals(2, first.diaryId)
|
||||
}
|
||||
|
||||
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
|
||||
assertTrue { it.single { it.isCurrent }.isCurrent }
|
||||
assertEquals(1970, it[0].schoolYear)
|
||||
assertEquals(of(1970, 1, 1), it[0].end)
|
||||
assertEquals(of(1970, 1, 1), it[0].start)
|
||||
assertFalse(it[0].isCurrent)
|
||||
assertFalse(it[1].isCurrent)
|
||||
assertFalse(it[2].isCurrent)
|
||||
assertTrue(it[3].isCurrent)
|
||||
assertTrue { it.single { it.second }.second }
|
||||
assertEquals(1970, it[0].first.schoolYear)
|
||||
assertEquals(of(1970, 1, 1), it[0].first.end)
|
||||
assertEquals(of(1970, 1, 1), it[0].first.start)
|
||||
assertFalse(it[0].second)
|
||||
assertFalse(it[1].second)
|
||||
assertFalse(it[2].second)
|
||||
assertTrue(it[3].second)
|
||||
}
|
||||
|
||||
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
|
||||
assertTrue { it.single { it.isCurrent }.isCurrent }
|
||||
assertFalse(it[0].isCurrent)
|
||||
assertFalse(it[1].isCurrent)
|
||||
assertFalse(it[2].isCurrent)
|
||||
assertTrue(it[3].isCurrent)
|
||||
assertTrue { it.single { it.second }.second }
|
||||
assertFalse(it[0].second)
|
||||
assertFalse(it[1].second)
|
||||
assertFalse(it[2].second)
|
||||
assertTrue(it[3].second)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List<Semester> {
|
||||
val semesters = mutableListOf<Semester>()
|
||||
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List<Pair<Semester, Boolean>> {
|
||||
val semesters = mutableListOf<Pair<Semester, Boolean>>()
|
||||
|
||||
val cursor = db.query(query)
|
||||
if (cursor.moveToFirst()) {
|
||||
@ -153,13 +152,12 @@ class Migration13Test : AbstractMigrationTest() {
|
||||
diaryName = cursor.getString(3),
|
||||
semesterId = cursor.getInt(4),
|
||||
semesterName = cursor.getInt(5),
|
||||
isCurrent = cursor.getInt(6) == 1,
|
||||
classId = cursor.getInt(7),
|
||||
unitId = cursor.getInt(8),
|
||||
schoolYear = cursor.getInt(9),
|
||||
start = Converters().timestampToDate(cursor.getLong(10))!!,
|
||||
end = Converters().timestampToDate(cursor.getLong(11))!!
|
||||
))
|
||||
) to (cursor.getInt(6) == 1))
|
||||
} while (cursor.moveToNext())
|
||||
}
|
||||
return semesters.toList()
|
||||
|
@ -10,8 +10,8 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -35,19 +35,19 @@ class AttendanceLocalTest {
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
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, LocalDate.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, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name),
|
||||
Attendance(1, 2, 3, of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name),
|
||||
Attendance(1, 2, 3, of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name)
|
||||
))
|
||||
|
||||
val attendance = attendanceLocal
|
||||
.getAttendance(Semester(1, 2, "", 1, 3, 2019, true, now(), now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
.getAttendance(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||
of(2018, 9, 10),
|
||||
of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
assertEquals(2, attendance.size)
|
||||
assertEquals(attendance[0].date, LocalDate.of(2018, 9, 10))
|
||||
assertEquals(attendance[1].date, LocalDate.of(2018, 9, 14))
|
||||
assertEquals(attendance[0].date, of(2018, 9, 10))
|
||||
assertEquals(attendance[1].date, of(2018, 9, 14))
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -35,20 +37,20 @@ class CompletedLessonsLocalTest {
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
completedLessonsLocal.saveCompletedLessons(listOf(
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 10), 1),
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 14), 2),
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 17), 3)
|
||||
getCompletedLesson(of(2018, 9, 10), 1),
|
||||
getCompletedLesson(of(2018, 9, 14), 2),
|
||||
getCompletedLesson(of(2018, 9, 17), 3)
|
||||
))
|
||||
|
||||
val completed = completedLessonsLocal
|
||||
.getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
.getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||
of(2018, 9, 10),
|
||||
of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
assertEquals(2, completed.size)
|
||||
assertEquals(completed[0].date, LocalDate.of(2018, 9, 10))
|
||||
assertEquals(completed[1].date, LocalDate.of(2018, 9, 14))
|
||||
assertEquals(completed[0].date, of(2018, 9, 10))
|
||||
assertEquals(completed[1].date, of(2018, 9, 14))
|
||||
}
|
||||
|
||||
private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson {
|
||||
|
@ -10,7 +10,8 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -34,19 +35,19 @@ class ExamLocalTest {
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
examLocal.saveExams(listOf(
|
||||
Exam(1, 2, LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""),
|
||||
Exam(1, 2, LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""),
|
||||
Exam(1, 2, LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "")
|
||||
Exam(1, 2, of(2018, 9, 10), now(), "", "", "", "", "", ""),
|
||||
Exam(1, 2, of(2018, 9, 14), now(), "", "", "", "", "", ""),
|
||||
Exam(1, 2, of(2018, 9, 17), now(), "", "", "", "", "", "")
|
||||
))
|
||||
|
||||
val exams = examLocal
|
||||
.getExams(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
.getExams(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||
of(2018, 9, 10),
|
||||
of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
assertEquals(2, exams.size)
|
||||
assertEquals(exams[0].date, LocalDate.of(2018, 9, 10))
|
||||
assertEquals(exams[1].date, LocalDate.of(2018, 9, 14))
|
||||
assertEquals(exams[0].date, of(2018, 9, 10))
|
||||
assertEquals(exams[1].date, of(2018, 9, 14))
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class GradeLocalTest {
|
||||
createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2)
|
||||
))
|
||||
|
||||
val semester = Semester(1, 2, "", 2019, 2, 1, true, now(), now(), 1, 1)
|
||||
val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1)
|
||||
|
||||
val grades = gradeLocal
|
||||
.getGrades(semester)
|
||||
|
@ -6,7 +6,6 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SdkSuppress
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.SdkHelper
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
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.every
|
||||
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 org.junit.After
|
||||
import org.junit.Before
|
||||
|
@ -11,7 +11,7 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -40,10 +40,7 @@ class GradeStatisticsLocalTest {
|
||||
getGradeStatistics("Fizyka", 1, 2)
|
||||
))
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
|
||||
"Matematyka"
|
||||
).blockingGet()
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Matematyka").blockingGet()
|
||||
assertEquals(1, stats.size)
|
||||
assertEquals(stats[0].subject, "Matematyka")
|
||||
}
|
||||
@ -56,12 +53,11 @@ class GradeStatisticsLocalTest {
|
||||
getGradeStatistics("Fizyka", 1, 2)
|
||||
))
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
|
||||
"Wszystkie"
|
||||
).blockingGet()
|
||||
assertEquals(1, stats.size)
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Wszystkie").blockingGet()
|
||||
assertEquals(3, stats.size)
|
||||
assertEquals(stats[0].subject, "Wszystkie")
|
||||
assertEquals(stats[1].subject, "Matematyka")
|
||||
assertEquals(stats[2].subject, "Chemia")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -72,11 +68,8 @@ class GradeStatisticsLocalTest {
|
||||
getGradePointsStatistics("Fizyka", 1, 2)
|
||||
))
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
"Matematyka"
|
||||
).blockingGet()
|
||||
with(stats) {
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet()
|
||||
with(stats[0]) {
|
||||
assertEquals(subject, "Matematyka")
|
||||
assertEquals(others, 5.0)
|
||||
assertEquals(student, 5.0)
|
||||
@ -87,10 +80,7 @@ class GradeStatisticsLocalTest {
|
||||
fun saveAndRead_subjectEmpty() {
|
||||
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
"Matematyka"
|
||||
).blockingGet()
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet()
|
||||
assertEquals(null, stats)
|
||||
}
|
||||
|
||||
@ -98,13 +88,14 @@ class GradeStatisticsLocalTest {
|
||||
fun saveAndRead_allEmpty() {
|
||||
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
"Wszystkie"
|
||||
).blockingGet()
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Wszystkie").blockingGet()
|
||||
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 {
|
||||
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.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDateTime.now
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -36,7 +38,7 @@ class LuckyNumberLocalTest {
|
||||
fun saveAndReadTest() {
|
||||
luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14))
|
||||
|
||||
val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
val luckyNumber = luckyNumberLocal.getLuckyNumber(Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", 1, false, now()),
|
||||
LocalDate.of(2019, 1, 20)
|
||||
).blockingGet()
|
||||
|
||||
|
@ -42,7 +42,7 @@ class RecipientLocalTest {
|
||||
))
|
||||
|
||||
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,
|
||||
ReportingUnit(1, 4, "", 0, "", emptyList())
|
||||
).blockingGet()
|
||||
|
@ -39,7 +39,7 @@ class StudentLocalTest {
|
||||
|
||||
@Test
|
||||
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()
|
||||
|
||||
val student = studentLocal.getCurrentStudent(true).blockingGet()
|
||||
|
@ -21,7 +21,7 @@ fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", s
|
||||
teacher = teacher,
|
||||
teacherOld = "",
|
||||
info = "",
|
||||
studentPlan = true,
|
||||
isStudentPlan = true,
|
||||
changes = changes,
|
||||
canceled = false
|
||||
)
|
||||
|
@ -41,7 +41,7 @@ class TimetableLocalTest {
|
||||
))
|
||||
|
||||
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, 14)
|
||||
).blockingGet()
|
||||
|
@ -5,13 +5,12 @@ package io.github.wulkanowy.utils
|
||||
import android.content.Context
|
||||
import timber.log.Timber
|
||||
|
||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
||||
// do nothing
|
||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {}
|
||||
|
||||
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?) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
class CrashlyticsExceptionTree : TimberTreeNoOp()
|
||||
|
@ -43,8 +43,8 @@
|
||||
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/send_message_title"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar" />
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
android:excludeFromRecents="true"
|
||||
@ -96,6 +96,16 @@
|
||||
android:exported="false"
|
||||
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
|
||||
android:name="io.fabric.ApiKey"
|
||||
android:value="${fabric_api_key}" />
|
||||
|
@ -20,7 +20,7 @@
|
||||
"githubUsername": "doteq"
|
||||
},
|
||||
{
|
||||
"displayName": "Pavuloff",
|
||||
"displayName": "Paweł Krzyś",
|
||||
"githubUsername": "pavuloff"
|
||||
},
|
||||
{
|
||||
@ -30,5 +30,9 @@
|
||||
{
|
||||
"displayName": "Dinolek",
|
||||
"githubUsername": "Dinolek"
|
||||
},
|
||||
{
|
||||
"displayName": "Mateusz Idziejczak",
|
||||
"githubUsername": "PanTajemnic"
|
||||
}
|
||||
]
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log.DEBUG
|
||||
import android.util.Log.INFO
|
||||
import android.util.Log.VERBOSE
|
||||
import androidx.multidex.MultiDex
|
||||
@ -11,11 +12,13 @@ import dagger.android.AndroidInjector
|
||||
import dagger.android.support.DaggerApplication
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.utils.Log
|
||||
import fr.bipi.tressence.file.FileLoggerTree
|
||||
import io.github.wulkanowy.di.DaggerAppComponent
|
||||
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
||||
import io.github.wulkanowy.ui.base.ThemeManager
|
||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.CrashlyticsExceptionTree
|
||||
import io.github.wulkanowy.utils.CrashlyticsTree
|
||||
import io.github.wulkanowy.utils.DebugLogTree
|
||||
import io.github.wulkanowy.utils.initCrashlytics
|
||||
@ -54,9 +57,17 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
|
||||
|
||||
private fun initLogging() {
|
||||
if (appInfo.isDebug) {
|
||||
Timber.plant(DebugLogTree())
|
||||
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 {
|
||||
Timber.plant(CrashlyticsExceptionTree())
|
||||
Timber.plant(CrashlyticsTree())
|
||||
}
|
||||
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.Migration20
|
||||
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.Migration4
|
||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||
@ -103,7 +104,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 21
|
||||
const val VERSION_SCHEMA = 22
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
||||
return arrayOf(
|
||||
@ -126,7 +127,8 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
Migration18(),
|
||||
Migration19(sharedPrefProvider),
|
||||
Migration20(),
|
||||
Migration21()
|
||||
Migration21(),
|
||||
Migration22()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import javax.inject.Singleton
|
||||
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
|
||||
|
||||
@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")
|
||||
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradePointsStatistics>>
|
||||
|
@ -4,6 +4,7 @@ import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
|
||||
@Dao
|
||||
interface MessagesDao : BaseDao<Message> {
|
||||
@ -12,7 +13,7 @@ interface MessagesDao : BaseDao<Message> {
|
||||
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>
|
||||
|
||||
@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")
|
||||
fun loadDeleted(studentId: Int): Maybe<List<Message>>
|
||||
|
@ -27,9 +27,6 @@ data class Semester(
|
||||
@ColumnInfo(name = "semester_name")
|
||||
val semesterName: Int,
|
||||
|
||||
@ColumnInfo(name = "is_current")
|
||||
val isCurrent: Boolean,
|
||||
|
||||
val start: LocalDate,
|
||||
|
||||
val end: LocalDate,
|
||||
@ -43,4 +40,8 @@ data class Semester(
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
|
||||
@ColumnInfo(name = "is_current")
|
||||
var current: Boolean = false
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ data class Student(
|
||||
@ColumnInfo(name = "school_id")
|
||||
val schoolSymbol: String,
|
||||
|
||||
@ColumnInfo(name ="school_short")
|
||||
val schoolShortName: String,
|
||||
|
||||
@ColumnInfo(name = "school_name")
|
||||
val schoolName: String,
|
||||
|
||||
|
@ -41,7 +41,7 @@ data class Timetable(
|
||||
val info: String,
|
||||
|
||||
@ColumnInfo(name = "student_plan")
|
||||
val studentPlan: Boolean,
|
||||
val isStudentPlan: 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>> {
|
||||
return Single.fromCallable<List<AppCreator>> {
|
||||
Gson().fromJson(
|
||||
assets.open("creators.json").bufferedReader().use { it.readText() },
|
||||
assets.open("contributors.json").bufferedReader().use { it.readText() },
|
||||
Array<AppCreator>::class.java
|
||||
).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.GradeStatistics
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.roundToDecimalPlaces
|
||||
import io.reactivex.Maybe
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -30,23 +29,17 @@ class GradeStatisticsLocal @Inject constructor(
|
||||
list.groupBy { it.grade }.map {
|
||||
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
|
||||
it.value.fold(0) { acc, e -> acc + e.amount }, false)
|
||||
}
|
||||
} + list
|
||||
}
|
||||
else -> gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)
|
||||
}.filter { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String): Maybe<GradePointsStatistics> {
|
||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String): Maybe<List<GradePointsStatistics>> {
|
||||
return when (subjectName) {
|
||||
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).flatMap { list ->
|
||||
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)
|
||||
))
|
||||
}
|
||||
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
|
||||
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
|
||||
}
|
||||
}.filter { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
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.GradeStatistics
|
||||
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.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -19,8 +20,8 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
private val remote: GradeStatisticsRemote
|
||||
) {
|
||||
|
||||
fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatistics>> {
|
||||
return local.getGradesStatistics(semester, isSemester, subjectName).filter { !forceRefresh }
|
||||
fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatisticsItem>> {
|
||||
return local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.filter { !forceRefresh }
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMap {
|
||||
if (it) remote.getGradeStatistics(semester, isSemester)
|
||||
@ -31,21 +32,43 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
local.deleteGradesStatistics(old.uniqueSubtract(new))
|
||||
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> {
|
||||
return local.getGradesPointsStatistics(semester, subjectName).filter { !forceRefresh }
|
||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Single<List<GradeStatisticsItem>> {
|
||||
return local.getGradesPointsStatistics(semester, subjectName).map { it.mapToStatisticsItem() }.filter { !forceRefresh }
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMapMaybe {
|
||||
if (it) remote.getGradePointsStatistics(semester).toMaybe()
|
||||
else Maybe.error(UnknownHostException())
|
||||
.flatMap {
|
||||
if (it) remote.getGradePointsStatistics(semester)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { new ->
|
||||
local.getGradesPointsStatistics(semester).defaultIfEmpty(emptyList())
|
||||
local.getGradesPointsStatistics(semester).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteGradesPointsStatistics(old.uniqueSubtract(new))
|
||||
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.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
import javax.inject.Inject
|
||||
@ -23,7 +23,7 @@ class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumbe
|
||||
luckyNumberDb.deleteAll(listOf(luckyNumber))
|
||||
}
|
||||
|
||||
fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe<LuckyNumber> {
|
||||
return luckyNumberDb.load(semester.studentId, date)
|
||||
fun getLuckyNumber(student: Student, date: LocalDate): Maybe<LuckyNumber> {
|
||||
return luckyNumberDb.load(student.studentId, date)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package io.github.wulkanowy.data.repositories.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.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
@ -11,14 +11,13 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) {
|
||||
|
||||
fun getLuckyNumber(semester: Semester): Maybe<LuckyNumber> {
|
||||
return sdk.getLuckyNumber()
|
||||
.map {
|
||||
LuckyNumber(
|
||||
studentId = semester.studentId,
|
||||
date = LocalDate.now(),
|
||||
luckyNumber = it
|
||||
)
|
||||
}
|
||||
fun getLuckyNumber(student: Student): Maybe<LuckyNumber> {
|
||||
return sdk.getLuckyNumber(student.schoolShortName).map {
|
||||
LuckyNumber(
|
||||
studentId = student.studentId,
|
||||
date = LocalDate.now(),
|
||||
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.internet.observing.InternetObservingSettings
|
||||
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.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
@ -18,14 +18,14 @@ class LuckyNumberRepository @Inject constructor(
|
||||
private val remote: LuckyNumberRemote
|
||||
) {
|
||||
|
||||
fun getLuckyNumber(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> {
|
||||
return local.getLuckyNumber(semester, LocalDate.now()).filter { !forceRefresh }
|
||||
fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> {
|
||||
return local.getLuckyNumber(student, LocalDate.now()).filter { !forceRefresh }
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMapMaybe {
|
||||
if (it) remote.getLuckyNumber(semester)
|
||||
if (it) remote.getLuckyNumber(student)
|
||||
else Maybe.error(UnknownHostException())
|
||||
}.flatMap { new ->
|
||||
local.getLuckyNumber(semester, LocalDate.now())
|
||||
local.getLuckyNumber(student, LocalDate.now())
|
||||
.doOnSuccess { old ->
|
||||
if (new != old) {
|
||||
local.deleteLuckyNumber(old)
|
||||
@ -39,13 +39,13 @@ class LuckyNumberRepository @Inject constructor(
|
||||
if (notify) isNotified = false
|
||||
})
|
||||
}
|
||||
}.flatMap({ local.getLuckyNumber(semester, LocalDate.now()) }, { Maybe.error(it) },
|
||||
{ local.getLuckyNumber(semester, LocalDate.now()) })
|
||||
}.flatMap({ local.getLuckyNumber(student, LocalDate.now()) }, { Maybe.error(it) },
|
||||
{ local.getLuckyNumber(student, LocalDate.now()) })
|
||||
)
|
||||
}
|
||||
|
||||
fun getNotNotifiedLuckyNumber(semester: Semester): Maybe<LuckyNumber> {
|
||||
return local.getLuckyNumber(semester, LocalDate.now()).filter { !it.isNotified }
|
||||
fun getNotNotifiedLuckyNumber(student: Student): Maybe<LuckyNumber> {
|
||||
return local.getLuckyNumber(student, LocalDate.now()).filter { !it.isNotified }
|
||||
}
|
||||
|
||||
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.repositories.message.MessageFolder.TRASHED
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -23,7 +24,7 @@ class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) {
|
||||
messagesDb.deleteAll(messages)
|
||||
}
|
||||
|
||||
fun getMessage(id: Long): Maybe<Message> {
|
||||
fun getMessage(id: Long): Single<Message> {
|
||||
return messagesDb.load(id)
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -51,21 +52,26 @@ class MessageRepository @Inject constructor(
|
||||
return Single.just(sdkHelper.init(student))
|
||||
.flatMap { _ ->
|
||||
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)
|
||||
.flatMap {
|
||||
if (it) local.getMessage(messageDbId).toSingle()
|
||||
if (it) local.getMessage(messageDbId)
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
.flatMap { dbMessage ->
|
||||
remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
|
||||
local.updateMessages(listOf(dbMessage.copy(unread = false).apply {
|
||||
local.updateMessages(listOf(dbMessage.copy(unread = !markAsRead).apply {
|
||||
id = dbMessage.id
|
||||
content = content.ifBlank { it }
|
||||
}))
|
||||
Timber.d("Message $messageDbId with blank content: ${dbMessage.content.isBlank()}, marked as read")
|
||||
}
|
||||
}.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>> {
|
||||
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,
|
||||
semesterId = it.semesterId,
|
||||
semesterName = it.semesterNumber,
|
||||
isCurrent = it.current,
|
||||
start = it.start,
|
||||
end = it.end,
|
||||
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.db.entities.Semester
|
||||
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.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -21,28 +22,30 @@ class SemesterRepository @Inject constructor(
|
||||
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))
|
||||
.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)
|
||||
.flatMap {
|
||||
if (it) remote.getSemesters(student) else Single.error(UnknownHostException())
|
||||
if (it) remote.getSemesters(student)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { new ->
|
||||
val currentSemesters = new.filter { it.isCurrent }
|
||||
if (currentSemesters.size == 1) {
|
||||
local.getSemesters(student).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteSemesters(old.uniqueSubtract(new))
|
||||
local.saveSemesters(new.uniqueSubtract(old))
|
||||
}
|
||||
} else {
|
||||
Timber.i("Current semesters list:\n${new.joinToString(separator = "\n")}")
|
||||
throw IllegalArgumentException("Current semester can be only one.")
|
||||
if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!")
|
||||
|
||||
local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old ->
|
||||
local.deleteSemesters(old.uniqueSubtract(new))
|
||||
local.saveSemesters(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
|
||||
}
|
||||
|
||||
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,
|
||||
studentName = student.studentName,
|
||||
schoolSymbol = student.schoolSymbol,
|
||||
schoolShortName = student.schoolShortName,
|
||||
schoolName = student.schoolName,
|
||||
className = student.className,
|
||||
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 {
|
||||
if (it) remote.getStudentsScrapper(email, password, endpoint, symbol)
|
||||
else Single.error(UnknownHostException("No internet connection"))
|
||||
|
@ -30,7 +30,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) {
|
||||
teacher = it.teacher,
|
||||
teacherOld = it.teacherOld,
|
||||
info = it.info,
|
||||
studentPlan = it.studentPlan,
|
||||
isStudentPlan = it.studentPlan,
|
||||
changes = it.changes,
|
||||
canceled = it.canceled
|
||||
)
|
||||
|
@ -9,6 +9,12 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
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.AttendanceWork
|
||||
import io.github.wulkanowy.services.sync.works.CompletedLessonWork
|
||||
@ -29,11 +35,10 @@ import javax.inject.Singleton
|
||||
|
||||
@Suppress("unused")
|
||||
@AssistedModule
|
||||
@Module(includes = [AssistedInject_ServicesModule::class, ServicesModule.Static::class])
|
||||
@Module(includes = [AssistedInject_ServicesModule::class])
|
||||
abstract class ServicesModule {
|
||||
|
||||
@Module
|
||||
object Static {
|
||||
companion object {
|
||||
|
||||
@Provides
|
||||
fun provideWorkManager(context: Context) = WorkManager.getInstance(context)
|
||||
@ -101,4 +106,24 @@ abstract class ServicesModule {
|
||||
@Binds
|
||||
@IntoSet
|
||||
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_CODES.O
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.work.BackoffPolicy.EXPONENTIAL
|
||||
import androidx.work.Constraints
|
||||
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.Companion.APP_VERSION_CODE_KEY
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.Channel
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.isHolidays
|
||||
import org.threeten.bp.LocalDate.now
|
||||
@ -27,9 +27,9 @@ import javax.inject.Singleton
|
||||
class SyncManager @Inject constructor(
|
||||
private val workManager: WorkManager,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
channels: Set<@JvmSuppressWildcards Channel>,
|
||||
notificationManager: NotificationManagerCompat,
|
||||
sharedPrefProvider: SharedPrefProvider,
|
||||
newEntriesChannel: NewEntriesChannel,
|
||||
debugChannel: DebugChannel,
|
||||
appInfo: AppInfo
|
||||
) {
|
||||
|
||||
@ -37,8 +37,8 @@ class SyncManager @Inject constructor(
|
||||
if (now().isHolidays) stopSyncWorker()
|
||||
|
||||
if (SDK_INT > O) {
|
||||
newEntriesChannel.create()
|
||||
if (appInfo.isDebug) debugChannel.create()
|
||||
channels.forEach { it.create() }
|
||||
notificationManager.deleteNotificationChannel("new_entries_channel")
|
||||
}
|
||||
|
||||
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 androidx.core.app.NotificationManagerCompat
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class DebugChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val context: Context
|
||||
) {
|
||||
private val context: Context,
|
||||
private val appInfo: AppInfo
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "debug_channel"
|
||||
}
|
||||
|
||||
fun create() {
|
||||
override fun create() {
|
||||
if (appInfo.isDebug) return
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT)
|
||||
.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
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification.VISIBILITY_PUBLIC
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager.IMPORTANCE_HIGH
|
||||
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 NewEntriesChannel @Inject constructor(
|
||||
class NewNotesChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val context: Context
|
||||
) {
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "new_entries_channel"
|
||||
const val CHANNEL_ID = "new_notes_channel"
|
||||
}
|
||||
|
||||
fun create() {
|
||||
override fun create() {
|
||||
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 {
|
||||
enableLights(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.repositories.grade.GradeRepository
|
||||
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.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
@ -38,7 +38,7 @@ class GradeWork @Inject constructor(
|
||||
}
|
||||
|
||||
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))
|
||||
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
|
||||
.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.repositories.luckynumber.LuckyNumberRepository
|
||||
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.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
@ -29,8 +29,8 @@ class LuckyNumberWork @Inject constructor(
|
||||
) : Work {
|
||||
|
||||
override fun create(student: Student, semester: Semester): Completable {
|
||||
return luckyNumberRepository.getLuckyNumber(semester, true, preferencesRepository.isNotificationsEnable)
|
||||
.flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(semester) }
|
||||
return luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable)
|
||||
.flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(student) }
|
||||
.flatMapCompletable {
|
||||
notify(it)
|
||||
luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
|
||||
@ -38,7 +38,7 @@ class LuckyNumberWork @Inject constructor(
|
||||
}
|
||||
|
||||
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))
|
||||
.setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.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.MessageRepository
|
||||
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.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
@ -39,7 +39,7 @@ class MessageWork @Inject constructor(
|
||||
}
|
||||
|
||||
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))
|
||||
.setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size))
|
||||
.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.repositories.note.NoteRepository
|
||||
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.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
@ -38,7 +38,7 @@ class NoteWork @Inject constructor(
|
||||
}
|
||||
|
||||
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))
|
||||
.setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
|
||||
.setSmallIcon(R.drawable.ic_stat_note)
|
||||
|
@ -4,6 +4,7 @@ import android.content.Intent
|
||||
import android.widget.RemoteViewsService
|
||||
import dagger.android.AndroidInjection
|
||||
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.student.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
||||
@ -22,6 +23,9 @@ class TimetableWidgetService : RemoteViewsService() {
|
||||
@Inject
|
||||
lateinit var semesterRepo: SemesterRepository
|
||||
|
||||
@Inject
|
||||
lateinit var prefRepository: PreferencesRepository
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPref: SharedPrefProvider
|
||||
|
||||
@ -30,6 +34,6 @@ class TimetableWidgetService : RemoteViewsService() {
|
||||
|
||||
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
|
||||
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 io.github.wulkanowy.R
|
||||
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.logviewer.LogViewerFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
@ -45,7 +46,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
|
||||
override val creatorsRes: Triple<String, String, Drawable?>?
|
||||
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?>?
|
||||
@ -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() {
|
||||
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
|
||||
}
|
||||
@ -150,7 +155,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
}
|
||||
|
||||
override fun openCreators() {
|
||||
(activity as? MainActivity)?.pushView(CreatorFragment.newInstance())
|
||||
(activity as? MainActivity)?.pushView(ContributorFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun openPrivacyPolicy() {
|
||||
|
@ -27,6 +27,11 @@ class AboutPresenter @Inject constructor(
|
||||
if (item !is AboutItem) return
|
||||
view?.run {
|
||||
when (item.title) {
|
||||
versionRes?.first -> {
|
||||
Timber.i("Opening log viewer")
|
||||
openLogViewer()
|
||||
analytics.logEvent("about_open", "name" to "log_viewer")
|
||||
}
|
||||
feedbackRes?.first -> {
|
||||
Timber.i("Opening email client")
|
||||
openEmailClient()
|
||||
|
@ -25,6 +25,8 @@ interface AboutView : BaseView {
|
||||
|
||||
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
|
||||
|
||||
fun openLogViewer()
|
||||
|
||||
fun openDiscordInvite()
|
||||
|
||||
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.view.LayoutInflater
|
||||
@ -18,18 +18,18 @@ import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_creator.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView {
|
||||
class ContributorFragment : BaseFragment(), ContributorView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: CreatorPresenter
|
||||
lateinit var presenter: ContributorPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var creatorsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
override val titleStringId get() = R.string.creators_title
|
||||
override val titleStringId get() = R.string.contributors_title
|
||||
|
||||
companion object {
|
||||
fun newInstance() = CreatorFragment()
|
||||
fun newInstance() = ContributorFragment()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
@ -53,7 +53,7 @@ class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView {
|
||||
creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<CreatorItem>) {
|
||||
override fun updateData(data: List<ContributorItem>) {
|
||||
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 coil.api.load
|
||||
@ -10,11 +10,12 @@ import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.AppCreator
|
||||
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)
|
||||
|
||||
@ -33,7 +34,7 @@ class CreatorItem(val creator: AppCreator) : AbstractFlexibleItem<CreatorItem.Vi
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CreatorItem
|
||||
other as ContributorItem
|
||||
|
||||
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 io.github.wulkanowy.data.pojos.AppCreator
|
||||
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
|
||||
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 io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreatorPresenter @Inject constructor(
|
||||
class ContributorPresenter @Inject constructor(
|
||||
schedulers: SchedulersProvider,
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
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)
|
||||
view.initView()
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item !is CreatorItem) return
|
||||
if (item !is ContributorItem) return
|
||||
view?.openUserGithubPage(item.creator.githubUsername)
|
||||
}
|
||||
|
||||
@ -34,7 +32,7 @@ class CreatorPresenter @Inject constructor(
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(appCreatorRepository.getAppCreators()
|
||||
.map { it.map { creator -> CreatorItem(creator) } }
|
||||
.map { it.map { creator -> ContributorItem(creator) } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.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
|
||||
|
||||
interface CreatorView : BaseView {
|
||||
interface ContributorView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<CreatorItem>)
|
||||
fun updateData(data: List<ContributorItem>)
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
override val totalString get() = getString(R.string.attendance_summary_total)
|
||||
|
||||
override val titleStringId get() = R.string.attendance_title
|
||||
|
||||
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.calculatePercentage
|
||||
import io.github.wulkanowy.utils.getFormattedName
|
||||
import org.threeten.bp.Month
|
||||
import timber.log.Timber
|
||||
import java.lang.String.format
|
||||
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> {
|
||||
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(
|
||||
month = it.month.getFormattedName(),
|
||||
percentage = formatPercentage(it.calculatePercentage()),
|
||||
|
@ -4,6 +4,8 @@ import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface AttendanceSummaryView : BaseView {
|
||||
|
||||
val totalString: String
|
||||
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
fun initView()
|
||||
|
@ -40,6 +40,8 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie
|
||||
|
||||
override val titleStringId get() = R.string.grade_title
|
||||
|
||||
override var subtitleString = ""
|
||||
|
||||
override val currentPageIndex get() = gradeViewPager.currentItem
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -133,6 +135,11 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie
|
||||
.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() {
|
||||
presenter.onChildViewRefresh()
|
||||
}
|
||||
|
@ -11,11 +11,10 @@ import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
|
||||
|
||||
@Suppress("unused")
|
||||
@Module(includes = [GradeModule.Static::class])
|
||||
@Module
|
||||
abstract class GradeModule {
|
||||
|
||||
@Module
|
||||
object Static {
|
||||
companion object {
|
||||
|
||||
@PerFragment
|
||||
@Provides
|
||||
|
@ -7,7 +7,9 @@ import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import javax.inject.Inject
|
||||
|
||||
class GradePresenter @Inject constructor(
|
||||
@ -21,6 +23,8 @@ class GradePresenter @Inject constructor(
|
||||
var selectedIndex = 0
|
||||
private set
|
||||
|
||||
private var schoolYear = 0
|
||||
|
||||
private var semesters = emptyList<Semester>()
|
||||
|
||||
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
||||
@ -56,6 +60,7 @@ class GradePresenter @Inject constructor(
|
||||
selectedIndex = index + 1
|
||||
loadedSemesterId.clear()
|
||||
view?.let {
|
||||
it.setCurrentSemesterName(index + 1, schoolYear)
|
||||
notifyChildrenSemesterChange()
|
||||
loadChild(it.currentPageIndex)
|
||||
}
|
||||
@ -95,17 +100,17 @@ class GradePresenter @Inject constructor(
|
||||
private fun loadData() {
|
||||
Timber.i("Loading grade data started")
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it) }
|
||||
.doOnSuccess {
|
||||
it.first { item -> item.isCurrent }.also { current ->
|
||||
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
|
||||
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
||||
}
|
||||
}
|
||||
.flatMap { semesterRepository.getSemesters(it, refreshOnNoCurrent = true) }
|
||||
.delay(200, MILLISECONDS)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally { view?.showProgress(false) }
|
||||
.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 {
|
||||
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
|
||||
loadChild(currentPageIndex)
|
||||
@ -121,6 +126,7 @@ class GradePresenter @Inject constructor(
|
||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||
lastError = error
|
||||
view?.run {
|
||||
showProgress(false)
|
||||
showErrorView(true)
|
||||
setErrorDetails(message)
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ interface GradeView : BaseView {
|
||||
|
||||
fun showSemesterDialog(selectedIndex: Int)
|
||||
|
||||
fun setCurrentSemesterName(semester: Int, schoolYear: Int)
|
||||
|
||||
fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean)
|
||||
|
||||
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
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.Color.WHITE
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
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 androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
||||
import kotlinx.android.synthetic.main.fragment_grade_statistics.*
|
||||
import javax.inject.Inject
|
||||
@ -35,6 +22,9 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
||||
@Inject
|
||||
lateinit var presenter: GradeStatisticsPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var statisticsAdapter: GradeStatisticsAdapter
|
||||
|
||||
private lateinit var subjectsAdapter: ArrayAdapter<String>
|
||||
|
||||
companion object {
|
||||
@ -43,9 +33,7 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
||||
fun newInstance() = GradeStatisticsFragment()
|
||||
}
|
||||
|
||||
override val isPieViewEmpty get() = gradeStatisticsChart.isEmpty
|
||||
|
||||
override val isBarViewEmpty get() = gradeStatisticsChartPoints.isEmpty
|
||||
override val isViewEmpty get() = statisticsAdapter.items.isEmpty()
|
||||
|
||||
override val currentType
|
||||
get() = when (gradeStatisticsTypeSwitch.checkedRadioButtonId) {
|
||||
@ -54,35 +42,6 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
||||
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? {
|
||||
return inflater.inflate(R.layout.fragment_grade_statistics, container, false)
|
||||
}
|
||||
@ -94,31 +53,9 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(gradeStatisticsChart) {
|
||||
description.isEnabled = false
|
||||
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
|
||||
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
|
||||
}
|
||||
with(gradeStatisticsRecycler) {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = statisticsAdapter
|
||||
}
|
||||
|
||||
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) {
|
||||
gradeColors = when (theme) {
|
||||
"vulcan" -> vulcanGradeColors
|
||||
else -> materialGradeColors
|
||||
}
|
||||
|
||||
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 updateData(items: List<GradeStatisticsItem>, theme: String) {
|
||||
statisticsAdapter.theme = theme
|
||||
statisticsAdapter.items = items
|
||||
statisticsAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun showSubjects(show: Boolean) {
|
||||
@ -232,16 +93,15 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
||||
}
|
||||
|
||||
override fun clearView() {
|
||||
gradeStatisticsChart.clear()
|
||||
gradeStatisticsChartPoints.clear()
|
||||
statisticsAdapter.items = emptyList()
|
||||
}
|
||||
|
||||
override fun showPieContent(show: Boolean) {
|
||||
gradeStatisticsChart.visibility = if (show) View.VISIBLE else View.GONE
|
||||
override fun resetView() {
|
||||
gradeStatisticsScroll.scrollTo(0, 0)
|
||||
}
|
||||
|
||||
override fun showBarContent(show: Boolean) {
|
||||
gradeStatisticsChartPoints.visibility = if (show) View.VISIBLE else View.GONE
|
||||
override fun showContent(show: Boolean) {
|
||||
gradeStatisticsRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun showEmpty(show: Boolean) {
|
||||
@ -273,7 +133,7 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
||||
}
|
||||
|
||||
override fun onParentReselected() {
|
||||
//
|
||||
presenter.onParentViewReselected()
|
||||
}
|
||||
|
||||
override fun onParentChangeSemester() {
|
||||
|
@ -48,12 +48,19 @@ class GradeStatisticsPresenter @Inject constructor(
|
||||
loadDataByType(semesterId, currentSubjectName, currentType, forceRefresh)
|
||||
}
|
||||
|
||||
|
||||
fun onParentViewReselected() {
|
||||
view?.run {
|
||||
if (!isViewEmpty) resetView()
|
||||
}
|
||||
}
|
||||
|
||||
fun onParentViewChangeSemester() {
|
||||
view?.run {
|
||||
showProgress(true)
|
||||
enableSwipe(false)
|
||||
showRefresh(false)
|
||||
showBarContent(false)
|
||||
showContent(false)
|
||||
showErrorView(false)
|
||||
showEmpty(false)
|
||||
clearView()
|
||||
@ -81,8 +88,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
||||
fun onSubjectSelected(name: String?) {
|
||||
Timber.i("Select grade stats subject $name")
|
||||
view?.run {
|
||||
showBarContent(false)
|
||||
showPieContent(false)
|
||||
showContent(false)
|
||||
showProgress(true)
|
||||
enableSwipe(false)
|
||||
showEmpty(false)
|
||||
@ -99,8 +105,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
||||
Timber.i("Select grade stats semester: $type")
|
||||
disposable.clear()
|
||||
view?.run {
|
||||
showBarContent(false)
|
||||
showPieContent(false)
|
||||
showContent(false)
|
||||
showProgress(true)
|
||||
enableSwipe(false)
|
||||
showEmpty(false)
|
||||
@ -135,20 +140,22 @@ class GradeStatisticsPresenter @Inject constructor(
|
||||
private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) {
|
||||
currentSubjectName = subjectName
|
||||
currentType = type
|
||||
when (type) {
|
||||
ViewType.SEMESTER -> loadData(semesterId, subjectName, true, forceRefresh)
|
||||
ViewType.PARTIAL -> loadData(semesterId, subjectName, false, forceRefresh)
|
||||
ViewType.POINTS -> loadPointsData(semesterId, subjectName, forceRefresh)
|
||||
}
|
||||
loadData(semesterId, subjectName, type, 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")
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it) }
|
||||
.flatMap { gradeStatisticsRepository.getGradesStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, isSemester, forceRefresh) }
|
||||
.map { list -> list.sortedByDescending { it.grade } }
|
||||
.map { list -> list.filter { it.amount != 0 } }
|
||||
.flatMap {
|
||||
val semester = it.first { item -> item.semesterId == semesterId }
|
||||
|
||||
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)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
@ -163,10 +170,9 @@ class GradeStatisticsPresenter @Inject constructor(
|
||||
Timber.i("Loading grade stats result: Success")
|
||||
view?.run {
|
||||
showEmpty(it.isEmpty())
|
||||
showBarContent(false)
|
||||
showPieContent(it.isNotEmpty())
|
||||
showContent(it.isNotEmpty())
|
||||
showErrorView(false)
|
||||
updatePieData(it, preferencesRepository.gradeColorTheme)
|
||||
updateData(it, preferencesRepository.gradeColorTheme)
|
||||
}
|
||||
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) {
|
||||
view?.run {
|
||||
if ((isBarViewEmpty && currentType == ViewType.POINTS) || (isPieViewEmpty) && currentType != ViewType.POINTS) {
|
||||
if (isViewEmpty) {
|
||||
lastError = error
|
||||
setErrorDetails(message)
|
||||
showErrorView(true)
|
||||
|
@ -1,14 +1,11 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.statistics
|
||||
|
||||
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.ui.base.BaseView
|
||||
|
||||
interface GradeStatisticsView : BaseView {
|
||||
|
||||
val isPieViewEmpty: Boolean
|
||||
|
||||
val isBarViewEmpty: Boolean
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
val currentType: ViewType
|
||||
|
||||
@ -16,9 +13,7 @@ interface GradeStatisticsView : BaseView {
|
||||
|
||||
fun updateSubjects(data: ArrayList<String>)
|
||||
|
||||
fun updatePieData(items: List<GradeStatistics>, theme: String)
|
||||
|
||||
fun updateBarData(item: GradePointsStatistics)
|
||||
fun updateData(items: List<GradeStatisticsItem>, theme: String)
|
||||
|
||||
fun showSubjects(show: Boolean)
|
||||
|
||||
@ -28,9 +23,9 @@ interface GradeStatisticsView : BaseView {
|
||||
|
||||
fun clearView()
|
||||
|
||||
fun showPieContent(show: Boolean)
|
||||
fun resetView()
|
||||
|
||||
fun showBarContent(show: Boolean)
|
||||
fun showContent(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.modules.login.advanced.LoginAdvancedFragment
|
||||
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.symbol.LoginSymbolFragment
|
||||
import io.github.wulkanowy.utils.setOnSelectPageListener
|
||||
@ -52,7 +53,8 @@ class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
|
||||
LoginFormFragment.newInstance(),
|
||||
LoginSymbolFragment.newInstance(),
|
||||
LoginStudentSelectFragment.newInstance(),
|
||||
LoginAdvancedFragment.newInstance()
|
||||
LoginAdvancedFragment.newInstance(),
|
||||
LoginRecoverFragment.newInstance()
|
||||
))
|
||||
}
|
||||
|
||||
@ -99,4 +101,8 @@ class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
|
||||
fun onAdvancedLoginClick() {
|
||||
presenter.onAdvancedLoginClick()
|
||||
}
|
||||
|
||||
fun onRecoverClick() {
|
||||
presenter.onRecoverClick()
|
||||
}
|
||||
}
|
||||
|
@ -43,5 +43,8 @@ class LoginErrorHandler @Inject constructor(
|
||||
super.clear()
|
||||
onBadCredentials = {}
|
||||
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.modules.login.advanced.LoginAdvancedFragment
|
||||
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.symbol.LoginSymbolFragment
|
||||
|
||||
@Suppress("unused")
|
||||
@Module(includes = [LoginModule.Static::class])
|
||||
@Module
|
||||
internal abstract class LoginModule {
|
||||
|
||||
@Module
|
||||
object Static {
|
||||
companion object {
|
||||
|
||||
@PerActivity
|
||||
@Provides
|
||||
@ -38,4 +38,8 @@ internal abstract class LoginModule {
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLoginSelectStudentFragment(): LoginStudentSelectFragment
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLoginRecoverFragment(): LoginRecoverFragment
|
||||
}
|
||||
|
@ -49,11 +49,15 @@ class LoginPresenter @Inject constructor(
|
||||
view?.switchView(3)
|
||||
}
|
||||
|
||||
fun onRecoverClick() {
|
||||
view?.switchView(4)
|
||||
}
|
||||
|
||||
fun onViewSelected(index: Int) {
|
||||
view?.apply {
|
||||
when (index) {
|
||||
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")
|
||||
view?.apply {
|
||||
when (currentViewIndex) {
|
||||
1, 2, 3 -> switchView(0)
|
||||
1, 2, 3, 4 -> switchView(0)
|
||||
else -> default()
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ package io.github.wulkanowy.ui.modules.login.advanced
|
||||
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.view.inputmethod.EditorInfo
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import io.github.wulkanowy.R
|
||||
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.form.LoginSymbolAdapter
|
||||
import io.github.wulkanowy.utils.hideSoftInput
|
||||
import io.github.wulkanowy.utils.setOnEditorDoneSignIn
|
||||
import io.github.wulkanowy.utils.showSoftInput
|
||||
import kotlinx.android.synthetic.main.fragment_login_advanced.*
|
||||
import javax.inject.Inject
|
||||
@ -35,8 +36,8 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
||||
else -> "HYBRID"
|
||||
}
|
||||
|
||||
override val formNameValue: String
|
||||
get() = loginFormName.text.toString().trim()
|
||||
override val formUsernameValue: String
|
||||
get() = loginFormUsername.text.toString().trim()
|
||||
|
||||
override val formPassValue: String
|
||||
get() = loginFormPass.text.toString().trim()
|
||||
@ -45,8 +46,13 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
||||
|
||||
private lateinit var hostValues: Array<String>
|
||||
|
||||
override val formHostValue: String?
|
||||
get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString()))
|
||||
private lateinit var hostSymbols: Array<String>
|
||||
|
||||
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
|
||||
get() = loginFormPin.text.toString().trim()
|
||||
@ -57,6 +63,12 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
||||
override val formTokenValue: String
|
||||
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? {
|
||||
return inflater.inflate(R.layout.fragment_login_advanced, container, false)
|
||||
}
|
||||
@ -69,8 +81,9 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
||||
override fun initView() {
|
||||
hostKeys = resources.getStringArray(R.array.hosts_keys)
|
||||
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() }
|
||||
loginFormPin.doOnTextChanged { _, _, _, _ -> presenter.onPinTextChanged() }
|
||||
loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() }
|
||||
@ -86,33 +99,48 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
||||
})
|
||||
}
|
||||
|
||||
loginFormPin.setOnEditorDoneSignIn()
|
||||
loginFormPass.setOnEditorDoneSignIn()
|
||||
loginFormPin.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() }
|
||||
loginFormPass.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() }
|
||||
|
||||
loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values)))
|
||||
|
||||
with(loginFormHost) {
|
||||
setText(hostKeys.getOrElse(0) { "" })
|
||||
setText(hostKeys.getOrNull(0).orEmpty())
|
||||
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
|
||||
setOnClickListener { if (loginFormContainer.visibility == GONE) dismissDropDown() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun AppCompatEditText.setOnEditorDoneSignIn() {
|
||||
setOnEditorActionListener { _, id, _ ->
|
||||
if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) loginFormSignIn.callOnClick() else false
|
||||
}
|
||||
override fun showMobileApiWarningMessage() {
|
||||
loginFormAdvancedWarningInfo.text = getString(R.string.login_advanced_warning_mobile_api)
|
||||
}
|
||||
|
||||
override fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String) {
|
||||
loginFormName.setText(name)
|
||||
override fun showScraperWarningMessage() {
|
||||
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)
|
||||
loginFormToken.setText(token)
|
||||
loginFormSymbol.setText(symbol)
|
||||
loginFormPin.setText(pin)
|
||||
}
|
||||
|
||||
override fun setErrorNameRequired() {
|
||||
with(loginFormNameLayout) {
|
||||
override fun setUsernameLabel(label: String) {
|
||||
loginFormUsernameLayout.hint = label
|
||||
}
|
||||
|
||||
override fun setSymbol(symbol: String) {
|
||||
loginFormSymbol.setText(symbol)
|
||||
}
|
||||
|
||||
override fun setErrorUsernameRequired() {
|
||||
with(loginFormUsernameLayout) {
|
||||
requestFocus()
|
||||
error = getString(R.string.login_field_required)
|
||||
}
|
||||
@ -181,8 +209,8 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearNameError() {
|
||||
loginFormNameLayout.error = null
|
||||
override fun clearUsernameError() {
|
||||
loginFormUsernameLayout.error = null
|
||||
}
|
||||
|
||||
override fun clearPassError() {
|
||||
@ -202,30 +230,30 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
||||
}
|
||||
|
||||
override fun showOnlyHybridModeInputs() {
|
||||
loginFormNameLayout.visibility = View.VISIBLE
|
||||
loginFormPassLayout.visibility = View.VISIBLE
|
||||
loginFormHostLayout.visibility = View.VISIBLE
|
||||
loginFormPinLayout.visibility = View.GONE
|
||||
loginFormSymbolLayout.visibility = View.VISIBLE
|
||||
loginFormTokenLayout.visibility = View.GONE
|
||||
loginFormUsernameLayout.visibility = VISIBLE
|
||||
loginFormPassLayout.visibility = VISIBLE
|
||||
loginFormHostLayout.visibility = VISIBLE
|
||||
loginFormPinLayout.visibility = GONE
|
||||
loginFormSymbolLayout.visibility = VISIBLE
|
||||
loginFormTokenLayout.visibility = GONE
|
||||
}
|
||||
|
||||
override fun showOnlyScrapperModeInputs() {
|
||||
loginFormNameLayout.visibility = View.VISIBLE
|
||||
loginFormPassLayout.visibility = View.VISIBLE
|
||||
loginFormHostLayout.visibility = View.VISIBLE
|
||||
loginFormPinLayout.visibility = View.GONE
|
||||
loginFormSymbolLayout.visibility = View.VISIBLE
|
||||
loginFormTokenLayout.visibility = View.GONE
|
||||
loginFormUsernameLayout.visibility = VISIBLE
|
||||
loginFormPassLayout.visibility = VISIBLE
|
||||
loginFormHostLayout.visibility = VISIBLE
|
||||
loginFormPinLayout.visibility = GONE
|
||||
loginFormSymbolLayout.visibility = VISIBLE
|
||||
loginFormTokenLayout.visibility = GONE
|
||||
}
|
||||
|
||||
override fun showOnlyMobileApiModeInputs() {
|
||||
loginFormNameLayout.visibility = View.GONE
|
||||
loginFormPassLayout.visibility = View.GONE
|
||||
loginFormHostLayout.visibility = View.GONE
|
||||
loginFormPinLayout.visibility = View.VISIBLE
|
||||
loginFormSymbolLayout.visibility = View.VISIBLE
|
||||
loginFormTokenLayout.visibility = View.VISIBLE
|
||||
loginFormUsernameLayout.visibility = GONE
|
||||
loginFormPassLayout.visibility = GONE
|
||||
loginFormHostLayout.visibility = GONE
|
||||
loginFormPinLayout.visibility = VISIBLE
|
||||
loginFormSymbolLayout.visibility = VISIBLE
|
||||
loginFormTokenLayout.visibility = VISIBLE
|
||||
}
|
||||
|
||||
override fun showSoftKeyboard() {
|
||||
@ -237,21 +265,26 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
|
||||
}
|
||||
|
||||
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) {
|
||||
loginFormContainer.visibility = if (show) View.VISIBLE else View.GONE
|
||||
loginFormContainer.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun notifyParentAccountLogged(students: List<Student>) {
|
||||
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
|
||||
loginFormName.text.toString(),
|
||||
loginFormUsername.text.toString(),
|
||||
loginFormPass.text.toString(),
|
||||
resources.getStringArray(R.array.hosts_values)[1]
|
||||
))
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
presenter.updateUsernameLabel()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
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() {
|
||||
view?.apply {
|
||||
clearPassError()
|
||||
clearNameError()
|
||||
if (formHostValue?.contains("fakelog") == true) {
|
||||
clearUsernameError()
|
||||
if (formHostValue.contains("fakelog")) {
|
||||
setDefaultCredentials("jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999")
|
||||
}
|
||||
setSymbol(formHostSymbol)
|
||||
updateUsernameLabel()
|
||||
}
|
||||
}
|
||||
|
||||
fun onLoginModeSelected(type: Sdk.Mode) {
|
||||
view?.run {
|
||||
when (type) {
|
||||
Sdk.Mode.API -> showOnlyMobileApiModeInputs()
|
||||
Sdk.Mode.SCRAPPER -> showOnlyScrapperModeInputs()
|
||||
Sdk.Mode.HYBRID -> showOnlyHybridModeInputs()
|
||||
Sdk.Mode.API -> {
|
||||
showOnlyMobileApiModeInputs()
|
||||
showMobileApiWarningMessage()
|
||||
}
|
||||
Sdk.Mode.SCRAPPER -> {
|
||||
showOnlyScrapperModeInputs()
|
||||
showScraperWarningMessage()
|
||||
}
|
||||
Sdk.Mode.HYBRID -> {
|
||||
showOnlyHybridModeInputs()
|
||||
showHybridWarningMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,8 +106,8 @@ class LoginAdvancedPresenter @Inject constructor(
|
||||
view?.clearPassError()
|
||||
}
|
||||
|
||||
fun onNameTextChanged() {
|
||||
view?.clearNameError()
|
||||
fun onUsernameTextChanged() {
|
||||
view?.clearUsernameError()
|
||||
}
|
||||
|
||||
fun onPinTextChanged() {
|
||||
@ -137,7 +154,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
private fun getStudentsAppropriatesToLoginType(): Single<List<Student>> {
|
||||
val email = view?.formNameValue.orEmpty()
|
||||
val email = view?.formUsernameValue.orEmpty()
|
||||
val password = view?.formPassValue.orEmpty()
|
||||
val endpoint = view?.formHostValue.orEmpty()
|
||||
|
||||
@ -153,7 +170,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
private fun validateCredentials(): Boolean {
|
||||
val login = view?.formNameValue.orEmpty()
|
||||
val login = view?.formUsernameValue.orEmpty()
|
||||
val password = view?.formPassValue.orEmpty()
|
||||
|
||||
val pin = view?.formPinValue.orEmpty()
|
||||
@ -181,7 +198,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
||||
}
|
||||
Sdk.Mode.SCRAPPER -> {
|
||||
if (login.isEmpty()) {
|
||||
view?.setErrorNameRequired()
|
||||
view?.setErrorUsernameRequired()
|
||||
isCorrect = false
|
||||
}
|
||||
|
||||
@ -197,7 +214,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
||||
}
|
||||
Sdk.Mode.HYBRID -> {
|
||||
if (login.isEmpty()) {
|
||||
view?.setErrorNameRequired()
|
||||
view?.setErrorUsernameRequired()
|
||||
isCorrect = false
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,13 @@ import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface LoginAdvancedView : BaseView {
|
||||
|
||||
val formNameValue: String
|
||||
val formUsernameValue: String
|
||||
|
||||
val formPassValue: String
|
||||
|
||||
val formHostValue: String?
|
||||
val formHostValue: String
|
||||
|
||||
val formHostSymbol: String
|
||||
|
||||
val formLoginType: String
|
||||
|
||||
@ -19,11 +21,25 @@ interface LoginAdvancedView : BaseView {
|
||||
|
||||
val formTokenValue: String
|
||||
|
||||
val nicknameLabel: String
|
||||
|
||||
val emailLabel: String
|
||||
|
||||
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)
|
||||
|
||||
@ -31,7 +47,7 @@ interface LoginAdvancedView : BaseView {
|
||||
|
||||
fun setErrorPassIncorrect()
|
||||
|
||||
fun clearNameError()
|
||||
fun clearUsernameError()
|
||||
|
||||
fun clearPassError()
|
||||
|
||||
|
@ -7,8 +7,7 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
|
||||
import android.view.inputmethod.EditorInfo.IME_NULL
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import io.github.wulkanowy.R
|
||||
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.openEmailClient
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.setOnEditorDoneSignIn
|
||||
import io.github.wulkanowy.utils.showSoftInput
|
||||
import kotlinx.android.synthetic.main.fragment_login_form.*
|
||||
import javax.inject.Inject
|
||||
@ -34,16 +34,33 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
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 hostValues: Array<String>
|
||||
|
||||
private lateinit var hostSymbols: Array<String>
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_login_form, container, false)
|
||||
}
|
||||
@ -56,38 +73,61 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
override fun initView() {
|
||||
hostKeys = resources.getStringArray(R.array.hosts_keys)
|
||||
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() }
|
||||
loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() }
|
||||
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
|
||||
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
|
||||
loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() }
|
||||
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
|
||||
loginFormFaq.setOnClickListener { presenter.onFaqClick() }
|
||||
loginFormContactEmail.setOnClickListener { presenter.onEmailClick() }
|
||||
loginFormRecoverLink.setOnClickListener { presenter.onRecoverClick() }
|
||||
loginFormPass.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() }
|
||||
loginFormSymbol.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() }
|
||||
|
||||
loginFormPass.setOnEditorActionListener { _, id, _ ->
|
||||
if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false
|
||||
}
|
||||
loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values)))
|
||||
|
||||
with(loginFormHost) {
|
||||
setText(hostKeys.getOrElse(0) { "" })
|
||||
setText(hostKeys.getOrNull(0).orEmpty())
|
||||
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
|
||||
setOnClickListener { if (loginFormContainer.visibility == GONE) dismissDropDown() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun setCredentials(name: String, pass: String) {
|
||||
loginFormName.setText(name)
|
||||
override fun setCredentials(username: String, pass: String) {
|
||||
loginFormUsername.setText(username)
|
||||
loginFormPass.setText(pass)
|
||||
}
|
||||
|
||||
override fun setErrorNameRequired() {
|
||||
with(loginFormNameLayout) {
|
||||
override fun setSymbol(symbol: String) {
|
||||
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()
|
||||
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) {
|
||||
with(loginFormPassLayout) {
|
||||
if (focus) requestFocus()
|
||||
@ -109,14 +149,18 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearNameError() {
|
||||
loginFormNameLayout.error = null
|
||||
override fun clearUsernameError() {
|
||||
loginFormUsernameLayout.error = null
|
||||
}
|
||||
|
||||
override fun clearPassError() {
|
||||
loginFormPassLayout.error = null
|
||||
}
|
||||
|
||||
override fun clearSymbolError() {
|
||||
loginFormSymbolLayout.error = null
|
||||
}
|
||||
|
||||
override fun showSoftKeyboard() {
|
||||
activity?.showSoftInput()
|
||||
}
|
||||
@ -154,6 +198,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
(activity as? LoginActivity)?.onAdvancedLoginClick()
|
||||
}
|
||||
|
||||
override fun onRecoverClick() {
|
||||
(activity as? LoginActivity)?.onRecoverClick()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
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)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
with(presenter) {
|
||||
updateUsernameLabel()
|
||||
updateSymbolInputVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openEmail() {
|
||||
context?.openEmailClient(
|
||||
requireContext().getString(R.string.login_email_intent_title),
|
||||
|
@ -42,10 +42,25 @@ class LoginFormPresenter @Inject constructor(
|
||||
fun onHostSelected() {
|
||||
view?.apply {
|
||||
clearPassError()
|
||||
clearNameError()
|
||||
if (formHostValue?.contains("fakelog") == true) {
|
||||
clearUsernameError()
|
||||
if (formHostValue.contains("fakelog")) {
|
||||
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()
|
||||
}
|
||||
|
||||
fun onNameTextChanged() {
|
||||
view?.clearNameError()
|
||||
fun onUsernameTextChanged() {
|
||||
view?.clearUsernameError()
|
||||
}
|
||||
|
||||
fun onSymbolTextChanged() {
|
||||
view?.clearSymbolError()
|
||||
}
|
||||
|
||||
fun onSignInClick() {
|
||||
val email = view?.formNameValue.orEmpty().trim()
|
||||
val email = view?.formUsernameValue.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)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
@ -83,11 +103,11 @@ class LoginFormPresenter @Inject constructor(
|
||||
}
|
||||
.subscribe({
|
||||
Timber.i("Login result: Success")
|
||||
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "scrapperBaseUrl" to endpoint, "error" to "No error")
|
||||
view?.notifyParentAccountLogged(it, Triple(email, password, endpoint))
|
||||
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "scrapperBaseUrl" to host, "error" to "No error")
|
||||
view?.notifyParentAccountLogged(it, Triple(email, password, host))
|
||||
}, {
|
||||
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)
|
||||
view?.showContact(true)
|
||||
}))
|
||||
@ -101,11 +121,15 @@ class LoginFormPresenter @Inject constructor(
|
||||
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
|
||||
|
||||
if (login.isEmpty()) {
|
||||
view?.setErrorNameRequired()
|
||||
view?.setErrorUsernameRequired()
|
||||
isCorrect = false
|
||||
}
|
||||
|
||||
@ -118,6 +142,12 @@ class LoginFormPresenter @Inject constructor(
|
||||
view?.setErrorPassInvalid(focus = isCorrect)
|
||||
isCorrect = false
|
||||
}
|
||||
|
||||
if ("standard" !in host && symbol.isBlank()) {
|
||||
view?.setErrorSymbolRequired(focus = isCorrect)
|
||||
isCorrect = false
|
||||
}
|
||||
|
||||
return isCorrect
|
||||
}
|
||||
}
|
||||
|
@ -7,15 +7,31 @@ interface LoginFormView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
val formNameValue: String
|
||||
val formUsernameValue: 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)
|
||||
|
||||
@ -23,10 +39,12 @@ interface LoginFormView : BaseView {
|
||||
|
||||
fun setErrorPassIncorrect()
|
||||
|
||||
fun clearNameError()
|
||||
fun clearUsernameError()
|
||||
|
||||
fun clearPassError()
|
||||
|
||||
fun clearSymbolError()
|
||||
|
||||
fun showSoftKeyboard()
|
||||
|
||||
fun hideSoftKeyboard()
|
||||
@ -48,4 +66,6 @@ interface LoginFormView : BaseView {
|
||||
fun openEmail()
|
||||
|
||||
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.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
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.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
|
||||
|
||||
@ -24,6 +27,10 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
||||
holder.apply {
|
||||
loginItemName.text = "${student.studentName} ${student.className}"
|
||||
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
|
||||
|
||||
if (student != other.student) return false
|
||||
if (alreadySaved != other.alreadySaved) return false
|
||||
|
||||
return true
|
||||
}
|
||||
@ -42,7 +50,8 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
||||
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
|
||||
get() = itemView
|
||||
@ -53,7 +62,10 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
||||
|
||||
override fun onClick(view: 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.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.ifNullOrBlank
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.io.Serializable
|
||||
import javax.inject.Inject
|
||||
@ -50,7 +51,7 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is LoginStudentSelectItem) {
|
||||
if (item is LoginStudentSelectItem && !item.alreadySaved) {
|
||||
selectedStudents.removeAll { it == 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>) {
|
||||
this.students = students
|
||||
view?.apply {
|
||||
updateData(students.map { LoginStudentSelectItem(it) })
|
||||
}
|
||||
disposable.add(studentRepository.getSavedStudents()
|
||||
.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>) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumber
|
||||
|
||||
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
@ -15,7 +14,6 @@ class LuckyNumberPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val luckyNumberRepository: LuckyNumberRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BasePresenter<LuckyNumberView>(errorHandler, studentRepository, schedulers) {
|
||||
|
||||
@ -38,7 +36,6 @@ class LuckyNumberPresenter @Inject constructor(
|
||||
disposable.apply {
|
||||
clear()
|
||||
add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||
.flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
|
@ -1,21 +1,14 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OPTIONS
|
||||
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
|
||||
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
|
||||
import android.content.BroadcastReceiver
|
||||
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
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.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
@ -34,14 +26,11 @@ import io.reactivex.Maybe
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
||||
class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
|
||||
@Inject
|
||||
lateinit var studentRepository: StudentRepository
|
||||
|
||||
@Inject
|
||||
lateinit var semesterRepository: SemesterRepository
|
||||
|
||||
@Inject
|
||||
lateinit var luckyNumberRepository: LuckyNumberRepository
|
||||
|
||||
@ -59,20 +48,20 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
||||
fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$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) {
|
||||
AndroidInjection.inject(this, context)
|
||||
when (intent.action) {
|
||||
ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent)
|
||||
ACTION_APPWIDGET_DELETED -> onDelete(intent)
|
||||
ACTION_APPWIDGET_OPTIONS_CHANGED -> onOptionsChange(context, intent)
|
||||
}
|
||||
super.onReceive(context, intent)
|
||||
}
|
||||
|
||||
private fun onUpdate(context: Context, intent: Intent) {
|
||||
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray?) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
appWidgetIds?.forEach { appWidgetId ->
|
||||
val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
|
||||
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)
|
||||
}
|
||||
|
||||
setStyles(remoteView, intent)
|
||||
setStyles(remoteView, appWidgetId)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, remoteView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDelete(intent: Intent) {
|
||||
val appWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0)
|
||||
if (appWidgetId != 0) sharedPref.delete(getStudentWidgetKey(appWidgetId))
|
||||
override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
|
||||
super.onDeleted(context, appWidgetIds)
|
||||
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? {
|
||||
@ -113,7 +152,6 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
||||
else -> Maybe.empty()
|
||||
}
|
||||
}
|
||||
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
|
||||
.flatMap { luckyNumberRepository.getLuckyNumber(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet()
|
||||
@ -124,69 +162,4 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
||||
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 currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString
|
||||
|
||||
override var startMenuIndex = 0
|
||||
|
||||
override var startMenuMoreIndex = -1
|
||||
@ -152,6 +154,10 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
||||
supportActionBar?.title = title
|
||||
}
|
||||
|
||||
override fun setViewSubTitle(subtitle: String?) {
|
||||
supportActionBar?.subtitle = subtitle
|
||||
}
|
||||
|
||||
override fun showHomeArrow(show: Boolean) {
|
||||
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