diff --git a/.travis.yml b/.travis.yml index 8bda02d6..bf6f72f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.12.0 + - 0.13.0 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 090a643d..177f15b7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 16 targetSdkVersion 29 - versionCode 47 - versionName "0.12.0" + versionCode 48 + versionName "0.13.0" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -110,8 +110,8 @@ play { } ext { - work_manager = "2.3.0-alpha03" - room = "2.2.1" + work_manager = "2.3.0-beta01" + room = "2.2.2" dagger = "2.25.2" chucker = "2.0.4" mockk = "1.9.2" @@ -119,28 +119,27 @@ ext { configurations.all { resolutionStrategy.force "androidx.constraintlayout:constraintlayout:1.1.3" - resolutionStrategy.force "com.google.android.material:material:1.1.0-alpha07" } dependencies { - implementation "io.github.wulkanowy:api:0.12.0" + implementation "io.github.wulkanowy:api:0.13.0" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation "androidx.core:core-ktx:1.2.0-beta01" - implementation "androidx.activity:activity-ktx:1.1.0-rc01" + implementation "androidx.core:core-ktx:1.2.0-rc01" + implementation "androidx.activity:activity-ktx:1.1.0-rc03" implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0" - implementation "androidx.fragment:fragment-ktx:1.2.0-rc01" + implementation "androidx.fragment:fragment-ktx:1.2.0-rc03" implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.preference:preference-ktx:1.1.0" - implementation "androidx.recyclerview:recyclerview:1.1.0-rc01" + implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.viewpager:viewpager:1.0.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03" implementation "androidx.constraintlayout:constraintlayout:1.1.3" - implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0-rc01" - implementation "com.google.android.material:material:1.1.0-alpha07" + implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" + implementation "com.google.android.material:material:1.1.0-beta02" 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" @@ -157,17 +156,18 @@ dependencies { implementation "com.google.dagger:dagger-android-support:$dagger" kapt "com.google.dagger:dagger-compiler:$dagger" kapt "com.google.dagger:dagger-android-processor:$dagger" - implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0" - kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.5.0" + implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.2" + kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.5.2" implementation "eu.davidea:flexible-adapter:5.1.0" implementation "eu.davidea:flexible-adapter-ui:1.0.0" implementation "com.aurelhubert:ahbottomnavigation:2.3.4" implementation "com.ncapdevi:frag-nav:3.3.0" + implementation "com.github.YarikSOffice:lingver:1.1.0" implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6" implementation "io.reactivex.rxjava2:rxandroid:2.1.1" - implementation "io.reactivex.rxjava2:rxjava:2.2.13" + implementation "io.reactivex.rxjava2:rxjava:2.2.15" implementation "com.google.code.gson:gson:2.8.6" implementation "com.jakewharton.threetenabp:threetenabp:1.2.1" @@ -175,6 +175,7 @@ dependencies { implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "com.squareup.okhttp3:logging-interceptor:3.12.6" implementation "com.mikepenz:aboutlibraries:7.0.4" + implementation 'com.wdullaer:materialdatetimepicker:4.2.3' playImplementation "com.google.firebase:firebase-core:17.2.1" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" @@ -187,7 +188,7 @@ dependencies { testImplementation "junit:junit:4.12" testImplementation "io.mockk:mockk:$mockk" testImplementation "org.threeten:threetenbp:1.4.0" - testImplementation "org.mockito:mockito-inline:3.1.0" + testImplementation "org.mockito:mockito-inline:3.2.0" androidTestImplementation "androidx.test:core:1.2.0" androidTestImplementation "androidx.test:runner:1.2.0" @@ -195,7 +196,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.1.0" + androidTestImplementation "org.mockito:mockito-android:3.2.0" } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/debug/res/values-pl/strings.xml b/app/src/debug/res/values-pl/strings.xml deleted file mode 100644 index c90641dd..00000000 --- a/app/src/debug/res/values-pl/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Wulkanowy DEV - diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt index 72969059..eee70891 100644 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -6,10 +6,13 @@ import android.util.Log.VERBOSE import androidx.multidex.MultiDex import androidx.work.Configuration import com.jakewharton.threetenabp.AndroidThreeTen +import com.yariksoffice.lingver.Lingver import dagger.android.AndroidInjector import dagger.android.support.DaggerApplication import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.utils.Log +import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY import io.github.wulkanowy.di.DaggerAppComponent import io.github.wulkanowy.services.sync.SyncWorkerFactory import io.github.wulkanowy.ui.base.ThemeManager @@ -32,6 +35,9 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider { @Inject lateinit var themeManager: ThemeManager + @Inject + lateinit var sharedPrefProvider: SharedPrefProvider + @Inject lateinit var appInfo: AppInfo @@ -44,7 +50,9 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider { super.onCreate() AndroidThreeTen.init(this) RxJavaPlugins.setErrorHandler(::onError) + Lingver.init(this) themeManager.applyDefaultTheme() + migrateSharedPreferences() initLogging() initCrashlytics(this, appInfo) @@ -60,6 +68,13 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider { registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) } + private fun migrateSharedPreferences() { + if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1) < 48) { // #596 + sharedPrefProvider.delete(getString(R.string.pref_key_grade_modifier_plus)) + sharedPrefProvider.delete(getString(R.string.pref_key_grade_modifier_minus)) + } + } + private fun onError(error: Throwable) { //RxJava's too deep stack traces may cause SOE on older android devices val cause = error.cause diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt index a6d0a067..6a953395 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt @@ -8,6 +8,10 @@ import javax.inject.Singleton @Singleton class SharedPrefProvider @Inject constructor(private val sharedPref: SharedPreferences) { + companion object { + const val APP_VERSION_CODE_KEY = "app_version_code" + } + fun putLong(key: String, value: Long, sync: Boolean = false) { sharedPref.edit(sync) { putLong(key, value) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt index d3c4f146..3eb57473 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.Attendance import io.reactivex.Maybe @@ -11,13 +9,7 @@ import javax.inject.Singleton @Singleton @Dao -interface AttendanceDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) +interface AttendanceDao : BaseDao { @Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt index a7413de5..fd58533f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt @@ -1,20 +1,12 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.reactivex.Maybe @Dao -interface AttendanceSummaryDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) +interface AttendanceSummaryDao : BaseDao { @Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId") fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt new file mode 100644 index 00000000..32dbadb8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Update + +interface BaseDao { + + @Insert + fun insertAll(items: List): List + + @Update + fun updateAll(items: List) + + @Delete + fun deleteAll(items: List) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt index 6816ceaa..e13e569b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.CompletedLesson import io.reactivex.Maybe @@ -11,13 +9,7 @@ import javax.inject.Singleton @Singleton @Dao -interface CompletedLessonsDao { - - @Insert - fun insertAll(exams: List) - - @Delete - fun deleteAll(exams: List) +interface CompletedLessonsDao : BaseDao { @Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt index 06cd5613..ca6b32df 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.Exam import io.reactivex.Maybe @@ -11,13 +9,7 @@ import javax.inject.Singleton @Singleton @Dao -interface ExamDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) +interface ExamDao : BaseDao { @Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt index 0bd210b0..c74d1937 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt @@ -1,26 +1,14 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query -import androidx.room.Update import io.github.wulkanowy.data.db.entities.Grade import io.reactivex.Maybe import javax.inject.Singleton @Singleton @Dao -interface GradeDao { - - @Insert - fun insertAll(grades: List) - - @Update - fun updateAll(grade: List) - - @Delete - fun deleteAll(grades: List) +interface GradeDao : BaseDao { @Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId") fun loadAll(semesterId: Int, studentId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePointsStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePointsStatisticsDao.kt index 99a1269b..37609286 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePointsStatisticsDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePointsStatisticsDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.reactivex.Maybe @@ -10,13 +8,7 @@ import javax.inject.Singleton @Singleton @Dao -interface GradePointsStatisticsDao { - - @Insert - fun insertAll(gradesStatistics: List) - - @Delete - fun deleteAll(gradesStatistics: List) +interface GradePointsStatisticsDao : BaseDao { @Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName") fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt index 338c369f..6faa35d0 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.GradeStatistics import io.reactivex.Maybe @@ -10,13 +8,7 @@ import javax.inject.Singleton @Singleton @Dao -interface GradeStatisticsDao { - - @Insert - fun insertAll(gradesStatistics: List) - - @Delete - fun deleteAll(gradesStatistics: List) +interface GradeStatisticsDao : BaseDao { @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester") fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt index 3f2e87bd..1165ef07 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.GradeSummary import io.reactivex.Maybe @@ -10,13 +8,7 @@ import javax.inject.Singleton @Singleton @Dao -interface GradeSummaryDao { - - @Insert - fun insertAll(gradesSummary: List) - - @Delete - fun deleteAll(gradesSummary: List) +interface GradeSummaryDao : BaseDao { @Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId") fun loadAll(semesterId: Int, studentId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt index 253bdb11..1947a0df 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.Homework import io.reactivex.Maybe @@ -11,13 +9,7 @@ import javax.inject.Singleton @Singleton @Dao -interface HomeworkDao { - - @Insert - fun insertAll(homework: List) - - @Delete - fun deleteAll(homework: List) +interface HomeworkDao : BaseDao { @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end") fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt index afd7905c..f16c28d9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt @@ -1,10 +1,7 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query -import androidx.room.Update import io.github.wulkanowy.data.db.entities.LuckyNumber import io.reactivex.Maybe import org.threeten.bp.LocalDate @@ -12,18 +9,8 @@ import javax.inject.Singleton @Singleton @Dao -interface LuckyNumberDao { - - @Insert - fun insert(luckyNumber: LuckyNumber) - - @Update - fun update(luckyNumber: LuckyNumber) - - @Delete - fun delete(luckyNumber: LuckyNumber) +interface LuckyNumberDao : BaseDao { @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date") fun load(studentId: Int, date: LocalDate): Maybe - } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt index 4f72c6c9..6d14ad01 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -1,24 +1,12 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query -import androidx.room.Update import io.github.wulkanowy.data.db.entities.Message import io.reactivex.Maybe @Dao -interface MessagesDao { - - @Insert - fun insertAll(messages: List) - - @Delete - fun deleteAll(messages: List) - - @Update - fun updateAll(messages: List) +interface MessagesDao : BaseDao { @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC") fun loadAll(studentId: Int, folder: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt index d6b97f6a..b05b2d9c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt @@ -1,20 +1,12 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.MobileDevice import io.reactivex.Maybe @Dao -interface MobileDeviceDao { - - @Insert - fun insertAll(devices: List) - - @Delete - fun deleteAll(devices: List) +interface MobileDeviceDao : BaseDao { @Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC") fun loadAll(studentId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt index 867e06a2..ea2fc6eb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt @@ -1,28 +1,15 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query -import androidx.room.Update import io.github.wulkanowy.data.db.entities.Note import io.reactivex.Maybe import javax.inject.Singleton @Singleton @Dao -interface NoteDao { - - @Insert - fun insertAll(notes: List) - - @Update - fun updateAll(notes: List) - - @Delete - fun deleteAll(notes: List) +interface NoteDao : BaseDao { @Query("SELECT * FROM Notes WHERE student_id = :studentId") fun loadAll(studentId: Int): Maybe> - } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt index 7c5fd6ca..afb941b1 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.Recipient import io.reactivex.Maybe @@ -10,13 +8,7 @@ import javax.inject.Singleton @Singleton @Dao -interface RecipientDao { - - @Insert - fun insertAll(messages: List) - - @Delete - fun deleteAll(messages: List) +interface RecipientDao : BaseDao { @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId") fun load(studentId: Int, role: Int, unitId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt index 1898390a..6ddfd494 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.ReportingUnit import io.reactivex.Maybe @@ -10,13 +8,7 @@ import javax.inject.Singleton @Singleton @Dao -interface ReportingUnitDao { - - @Insert - fun insertAll(reportingUnits: List) - - @Delete - fun deleteAll(reportingUnits: List) +interface ReportingUnitDao : BaseDao { @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") fun load(studentId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolDao.kt index c2b2e0bc..e9bd6755 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.School import io.reactivex.Maybe @@ -10,13 +8,7 @@ import javax.inject.Singleton @Singleton @Dao -interface SchoolDao { - - @Insert - fun insert(school: School) - - @Delete - fun delete(school: School) +interface SchoolDao : BaseDao { @Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId") fun load(studentId: Int, classId: Int): Maybe diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt index 01841fb6..654b80f3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.Semester import io.reactivex.Maybe @@ -10,13 +8,7 @@ import javax.inject.Singleton @Singleton @Dao -interface SemesterDao { - - @Insert - fun insertAll(semester: List) - - @Delete - fun deleteAll(semester: List) +interface SemesterDao : BaseDao { @Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId") fun loadAll(studentId: Int, classId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt index 725a371a..525a7129 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt @@ -1,20 +1,12 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.Subject import io.reactivex.Maybe @Dao -interface SubjectDao { - - @Insert - fun insertAll(subjects: List): List - - @Delete - fun deleteAll(subjects: List) +interface SubjectDao : BaseDao { @Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId") fun loadAll(diaryId: Int, studentId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TeacherDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TeacherDao.kt index e7794248..5ea237a8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TeacherDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TeacherDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.Teacher import io.reactivex.Maybe @@ -10,13 +8,7 @@ import javax.inject.Singleton @Singleton @Dao -interface TeacherDao { - - @Insert - fun insertAll(teachers: List) - - @Delete - fun deleteAll(teachers: List) +interface TeacherDao : BaseDao { @Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId") fun loadAll(studentId: Int, classId: Int): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt index abe21361..6b62cc82 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import io.github.wulkanowy.data.db.entities.Timetable import io.reactivex.Maybe @@ -11,13 +9,7 @@ import javax.inject.Singleton @Singleton @Dao -interface TimetableDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) +interface TimetableDao : BaseDao { @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt index 115c8965..b461ca22 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt @@ -12,15 +12,15 @@ import javax.inject.Singleton class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumberDao) { fun saveLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.insert(luckyNumber) + luckyNumberDb.insertAll(listOf(luckyNumber)) } fun updateLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.update(luckyNumber) + luckyNumberDb.updateAll(listOf(luckyNumber)) } fun deleteLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.delete(luckyNumber) + luckyNumberDb.deleteAll(listOf(luckyNumber)) } fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt index b2bff1e7..90c510ef 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt @@ -11,7 +11,6 @@ import io.reactivex.Single import org.threeten.bp.LocalDateTime.now import javax.inject.Inject import javax.inject.Singleton -import io.github.wulkanowy.api.messages.Message as ApiMessage import io.github.wulkanowy.api.messages.Recipient as ApiRecipient @Singleton diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt index 8f0efa5b..473ffa7f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt @@ -3,7 +3,6 @@ package io.github.wulkanowy.data.repositories.mobiledevice import io.github.wulkanowy.data.db.dao.MobileDeviceDao import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student import io.reactivex.Maybe import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt index e1caf920..07a3654a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt @@ -33,6 +33,10 @@ class PreferencesRepository @Inject constructor( val gradeColorTheme: String get() = getString(R.string.pref_key_grade_color_scheme, R.string.pref_default_grade_color_scheme) + val appLanguageKey = context.getString(R.string.pref_key_app_language) + val appLanguage + get() = getString(appLanguageKey, R.string.pref_default_app_language) + val serviceEnableKey = context.getString(R.string.pref_key_services_enable) val isServiceEnabled: Boolean get() = getBoolean(serviceEnableKey, R.bool.pref_default_services_enable) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt index 6b8328ec..9b1d4ac2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt @@ -15,7 +15,7 @@ class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) return recipientDb.load(student.studentId, role, unit.realId).filter { !it.isEmpty() } } - fun saveRecipients(recipients: List) { + fun saveRecipients(recipients: List): List { return recipientDb.insertAll(recipients) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt index b4281cbf..6f9eec3f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt @@ -18,7 +18,7 @@ class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: Report return reportingUnitDb.loadOne(student.studentId, unitId) } - fun saveReportingUnits(reportingUnits: List) { + fun saveReportingUnits(reportingUnits: List): List { return reportingUnitDb.insertAll(reportingUnits) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolLocal.kt index 1692e39b..d8727287 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolLocal.kt @@ -9,11 +9,11 @@ import javax.inject.Inject class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) { fun saveSchool(school: School) { - schoolDb.insert(school) + schoolDb.insertAll(listOf(school)) } fun deleteSchool(school: School) { - schoolDb.delete(school) + schoolDb.deleteAll(listOf(school)) } fun getSchool(semester: Semester): Maybe { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt index e6d74421..f27199f2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt @@ -24,7 +24,7 @@ class StudentLocal @Inject constructor( fun getStudents(decryptPass: Boolean): Maybe> { return studentDb.loadAll() .map { list -> list.map { it.apply { if (decryptPass) password = decrypt(password) } } } - .filter { !it.isEmpty() } + .filter { it.isNotEmpty() } } fun getCurrentStudent(decryptPass: Boolean): Maybe { diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt index 42e50fa4..4f568385 100644 --- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt +++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.di import android.appwidget.AppWidgetManager import android.content.Context +import com.yariksoffice.lingver.Lingver import dagger.Module import dagger.Provides import eu.davidea.flexibleadapter.FlexibleAdapter @@ -32,4 +33,8 @@ internal class AppModule { @Singleton @Provides fun provideAppInfo() = AppInfo() + + @Singleton + @Provides + fun provideLingver() = Lingver.getInstance() } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt index 98fc3522..6b046977 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -11,6 +11,7 @@ import androidx.work.NetworkType.UNMETERED import androidx.work.PeriodicWorkRequestBuilder 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 @@ -32,10 +33,6 @@ class SyncManager @Inject constructor( appInfo: AppInfo ) { - companion object { - private const val APP_VERSION_CODE_KEY = "app_version_code" - } - init { if (now().isHolidays) stopSyncWorker() diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt index f0e51e31..ab67d8ac 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt @@ -22,7 +22,8 @@ import io.github.wulkanowy.utils.FragmentLifecycleLogger import io.github.wulkanowy.utils.getThemeAttrColor import javax.inject.Inject -abstract class BaseActivity> : AppCompatActivity(), BaseView, HasAndroidInjector { +abstract class BaseActivity> : AppCompatActivity(), BaseView, + HasAndroidInjector { @Inject lateinit var androidInjector: DispatchingAndroidInjector @@ -53,13 +54,15 @@ abstract class BaseActivity> : AppCompatActivity override fun showError(text: String, error: Throwable) { if (messageContainer != null) { Snackbar.make(messageContainer!!, text, LENGTH_LONG) - .setAction(R.string.all_details) { - ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString()) - } + .setAction(R.string.all_details) { showErrorDetailsDialog(error) } .show() } else showMessage(text) } + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString()) + } + override fun showMessage(text: String) { if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() else Toast.makeText(this, text, Toast.LENGTH_LONG).show() diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt new file mode 100644 index 00000000..fdc46371 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.ui.base + +import android.widget.Toast +import dagger.android.support.DaggerAppCompatDialogFragment + +abstract class BaseDialogFragment : DaggerAppCompatDialogFragment(), BaseView { + + override fun showError(text: String, error: Throwable) { + showMessage(text) + } + + override fun showMessage(text: String) { + Toast.makeText(context, text, Toast.LENGTH_LONG).show() + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*>)?.showExpiredDialog() + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*>)?.openClearLoginView() + } + + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt index 4b2ad053..2f5878d0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt @@ -13,15 +13,17 @@ abstract class BaseFragment : DaggerFragment(), BaseView { override fun showError(text: String, error: Throwable) { if (messageContainer != null) { Snackbar.make(messageContainer!!, text, LENGTH_LONG) - .setAction(R.string.all_details) { - if (isAdded) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) - } + .setAction(R.string.all_details) { if (isAdded) showErrorDetailsDialog(error) } .show() } else { (activity as? BaseActivity<*>)?.showError(text, error) } } + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } + override fun showMessage(text: String) { if (messageContainer != null) { Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt index 7681263b..0f4df92c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt @@ -9,4 +9,6 @@ interface BaseView { fun showExpiredDialog() fun openClearLoginView() + + fun showErrorDetailsDialog(error: Throwable) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt index 46dc5cfe..22276bdf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -47,6 +47,11 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback)) } + override val faqRes: Triple? + get() = context?.run { + Triple(getString(R.string.about_faq), getString(R.string.about_faq_summary), getCompatDrawable(R.drawable.ic_about_faq)) + } + override val discordRes: Triple? get() = context?.run { Triple(getString(R.string.about_discord), getString(R.string.about_discord_summary), getCompatDrawable(R.drawable.ic_about_discord)) @@ -130,6 +135,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { } } + override fun openFaqPage() { + context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania", ::showMessage) + } + override fun openLicenses() { (activity as? MainActivity)?.pushView(LicenseFragment.newInstance()) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 5303d361..8087c955 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -28,10 +28,15 @@ class AboutPresenter @Inject constructor( view?.run { when (item.title) { feedbackRes?.first -> { - Timber.i("Opening email client ") + Timber.i("Opening email client") openEmailClient() analytics.logEvent("about_open", "name" to "feedback") } + faqRes?.first -> { + Timber.i("Opening faq page") + openFaqPage() + analytics.logEvent("about_open", "name" to "faq") + } discordRes?.first -> { Timber.i("Opening discord") openDiscordInvite() @@ -61,6 +66,7 @@ class AboutPresenter @Inject constructor( updateData(AboutScrollableHeader(), listOfNotNull( versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, + faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, homepageRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, licensesRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt index 318a75e4..34850bae 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -9,6 +9,8 @@ interface AboutView : BaseView { val feedbackRes: Triple? + val faqRes: Triple? + val discordRes: Triple? val homepageRes: Triple? @@ -25,6 +27,8 @@ interface AboutView : BaseView { fun openEmailClient() + fun openFaqPage() + fun openHomepage() fun openLicenses() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt index f23a1eb5..cfff31c9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt @@ -7,18 +7,17 @@ import android.view.ViewGroup import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.appcompat.app.AlertDialog -import dagger.android.support.DaggerAppCompatDialogFragment import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.dialog_account.* import javax.inject.Inject -class AccountDialog : DaggerAppCompatDialogFragment(), AccountView { +class AccountDialog : BaseDialogFragment(), AccountView { @Inject lateinit var presenter: AccountPresenter @@ -77,14 +76,6 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView { } } - override fun showExpiredDialog() { - (activity as? BaseActivity<*>)?.showExpiredDialog() - } - - override fun openClearLoginView() { - (activity as? BaseActivity<*>)?.openClearLoginView() - } - override fun showConfirmDialog() { context?.let { AlertDialog.Builder(it) @@ -105,4 +96,3 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView { super.onDestroy() } } - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index fb6309f7..bc7c1cac 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -6,7 +6,11 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import android.view.ViewGroup +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.FlexibleItemDecoration import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager @@ -17,9 +21,11 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_attendance.* +import org.threeten.bp.LocalDate import javax.inject.Inject class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildView, @@ -70,7 +76,11 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie } attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + attendanceErrorRetry.setOnClickListener { presenter.onRetry() } + attendanceErrorDetails.setOnClickListener { presenter.onDetailsClick() } + attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() } + attendanceNavDate.setOnClickListener { presenter.onPickDate() } attendanceNextButton.setOnClickListener { presenter.onNextDay() } attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f)) @@ -110,11 +120,19 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie } override fun showEmpty(show: Boolean) { - attendanceEmpty.visibility = if (show) View.VISIBLE else View.GONE + attendanceEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + attendanceError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + attendanceErrorMessage.text = message } override fun showProgress(show: Boolean) { - attendanceProgress.visibility = if (show) View.VISIBLE else View.GONE + attendanceProgress.visibility = if (show) VISIBLE else GONE } override fun enableSwipe(enable: Boolean) { @@ -122,7 +140,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie } override fun showContent(show: Boolean) { - attendanceRecycler.visibility = if (show) View.VISIBLE else View.GONE + attendanceRecycler.visibility = if (show) VISIBLE else GONE } override fun hideRefresh() { @@ -130,17 +148,32 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie } override fun showPreButton(show: Boolean) { - attendancePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + attendancePreviousButton.visibility = if (show) VISIBLE else INVISIBLE } override fun showNextButton(show: Boolean) { - attendanceNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE } override fun showAttendanceDialog(lesson: Attendance) { (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) } + override fun showDatePickerDialog(currentDate: LocalDate) { + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> + presenter.onDateSet(year, month + 1, dayOfMonth) + } + val datePickerDialog = DatePickerDialog.newInstance(dateSetListener, + currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth) + + with(datePickerDialog) { + setDateRangeLimiter(SchooldaysRangeLimiter()) + version = DatePickerDialog.Version.VERSION_2 + scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL + show(this@AttendanceFragment.parentFragmentManager, null) + } + } + override fun openSummaryView() { (activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance()) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index b8114208..1490ee6e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.attendance +import android.annotation.SuppressLint import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository @@ -37,10 +38,13 @@ class AttendancePresenter @Inject constructor( lateinit var currentDate: LocalDate private set + private lateinit var lastError: Throwable + fun onAttachView(view: AttendanceView, date: Long?) { super.onAttachView(view) view.initView() Timber.i("Attendance view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData(ofEpochDay(date ?: baseDate.toEpochDay())) if (currentDate.isHolidays) setBaseDateOnHolidays() reloadView() @@ -56,11 +60,32 @@ class AttendancePresenter @Inject constructor( reloadView() } + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + loadData(LocalDate.of(year, month, day)) + reloadView() + } + fun onSwipeRefresh() { Timber.i("Force refreshing the attendance") loadData(currentDate, true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentDate, true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onViewReselected() { Timber.i("Attendance view is reselected") view?.also { view -> @@ -130,18 +155,29 @@ class AttendancePresenter @Inject constructor( view?.apply { updateData(it) showEmpty(it.isEmpty()) + showErrorView(false) showContent(it.isNotEmpty()) } analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading attendance result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } errorHandler.dispatch(it) } ) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + private fun reloadView() { Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}") view?.apply { @@ -149,11 +185,13 @@ class AttendancePresenter @Inject constructor( enableSwipe(false) showContent(false) showEmpty(false) + showErrorView(false) clearData() reloadNavigation() } } + @SuppressLint("DefaultLocale") private fun reloadNavigation() { view?.apply { showPreButton(!currentDate.minusDays(1).isHolidays) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index 04fe94a4..a6d0d4ba 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.attendance import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.ui.base.BaseView +import org.threeten.bp.LocalDate interface AttendanceView : BaseView { @@ -23,6 +24,10 @@ interface AttendanceView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) @@ -35,6 +40,8 @@ interface AttendanceView : BaseView { fun showAttendanceDialog(lesson: Attendance) + fun showDatePickerDialog(currentDate: LocalDate) + fun openSummaryView() fun popView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt index 4fe490d5..fc360184 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -57,6 +57,8 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie } attendanceSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh) + attendanceSummaryErrorRetry.setOnClickListener { presenter.onRetry() } + attendanceSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() } subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf()) subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) @@ -93,6 +95,14 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie attendanceSummaryEmpty.visibility = if (show) VISIBLE else GONE } + override fun showErrorView(show: Boolean) { + attendanceSummaryError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + attendanceSummaryErrorMessage.text = message + } + override fun showProgress(show: Boolean) { attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index 40339299..3ce85d81 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -33,10 +33,13 @@ class AttendanceSummaryPresenter @Inject constructor( var currentSubjectId = -1 private set + private lateinit var lastError: Throwable + fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) { super.onAttachView(view) view.initView() Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData(subjectId ?: -1) loadSubjects() } @@ -46,12 +49,26 @@ class AttendanceSummaryPresenter @Inject constructor( loadData(currentSubjectId, true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentSubjectId, true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onSubjectSelected(name: String?) { Timber.i("Select attendance summary subject $name") view?.run { showContent(false) showProgress(true) enableSwipe(false) + showEmpty(false) + showErrorView(false) clearView() } (subjects.singleOrNull { it.name == name }?.realId ?: -1).let { @@ -88,13 +105,23 @@ class AttendanceSummaryPresenter @Inject constructor( analytics.logEvent("load_attendance_summary", "items" to it.first.size, "force_refresh" to forceRefresh, "item_id" to subjectId) }) { Timber.i("Loading attendance summary result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } errorHandler.dispatch(it) } ) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + private fun loadSubjects() { Timber.i("Loading attendance summary subjects started") disposable.add(studentRepository.getCurrentStudent() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt index 50f03e20..b86f6590 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -18,6 +18,10 @@ interface AttendanceSummaryView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader) fun updateSubjects(data: ArrayList) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt index c762fa15..b880f465 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt @@ -61,6 +61,9 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView. } examSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + examErrorRetry.setOnClickListener { presenter.onRetry() } + examErrorDetails.setOnClickListener { presenter.onDetailsClick() } + examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } examNextButton.setOnClickListener { presenter.onNextWeek() } @@ -95,6 +98,14 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView. examEmpty.visibility = if (show) VISIBLE else GONE } + override fun showErrorView(show: Boolean) { + examError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + examErrorMessage.text = message + } + override fun showProgress(show: Boolean) { examProgress.visibility = if (show) VISIBLE else GONE } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt index 93109922..35cf5b94 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -36,10 +36,13 @@ class ExamPresenter @Inject constructor( lateinit var currentDate: LocalDate private set + private lateinit var lastError: Throwable + fun onAttachView(view: ExamView, date: Long?) { super.onAttachView(view) view.initView() Timber.i("Exam view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData(ofEpochDay(date ?: baseDate.toEpochDay())) if (currentDate.isHolidays) setBaseDateOnHolidays() reloadView() @@ -60,6 +63,18 @@ class ExamPresenter @Inject constructor( loadData(currentDate, true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentDate, true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onExamItemSelected(item: AbstractFlexibleItem<*>?) { if (item is ExamItem) { Timber.i("Select exam item ${item.exam.id}") @@ -116,17 +131,28 @@ class ExamPresenter @Inject constructor( view?.apply { updateData(it) showEmpty(it.isEmpty()) + showErrorView(false) showContent(it.isNotEmpty()) } analytics.logEvent("load_exam", "items" to it.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading exam result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } errorHandler.dispatch(it) }) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + private fun createExamItems(items: Map>): List { return items.flatMap { ExamHeader(it.key).let { header -> @@ -142,6 +168,7 @@ class ExamPresenter @Inject constructor( enableSwipe(false) showContent(false) showEmpty(false) + showErrorView(false) clearData() reloadNavigation() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt index 888cb05e..5f4a7430 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt @@ -21,6 +21,10 @@ interface ExamView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt index 68ae57ec..884b9f8c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt @@ -13,6 +13,7 @@ import androidx.appcompat.app.AlertDialog import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.base.ErrorDialog import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment @@ -83,7 +84,8 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie setElevationCompat(context.dpToPx(4f)) } - gradeSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + gradeErrorRetry.setOnClickListener { presenter.onRetry() } + gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() } } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -104,22 +106,18 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie gradeProgress.visibility = if (show) VISIBLE else INVISIBLE } - override fun showEmpty(show: Boolean) { - gradeEmpty.visibility = if (show) VISIBLE else INVISIBLE + override fun showErrorView(show: Boolean) { + gradeError.visibility = if (show) VISIBLE else INVISIBLE } - override fun showRefresh(show: Boolean) { - gradeSwipe.isRefreshing = show + override fun setErrorDetails(message: String) { + gradeErrorMessage.text = message } override fun showSemesterSwitch(show: Boolean) { semesterSwitchMenu?.isVisible = show } - override fun enableSwipe(enable: Boolean) { - gradeSwipe.isEnabled = enable - } - override fun showSemesterDialog(selectedIndex: Int) { val choices = arrayOf( getString(R.string.grade_semester, 1), diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt index 2ee69480..d8c20238 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -25,14 +25,14 @@ class GradePresenter @Inject constructor( private val loadedSemesterId = mutableMapOf() + private lateinit var lastError: Throwable + fun onAttachView(view: GradeView, savedIndex: Int?) { super.onAttachView(view) selectedIndex = savedIndex ?: 0 - view.run { - initView() - enableSwipe(false) - } + view.initView() Timber.i("Grade view was initialized with $selectedIndex index") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData() } @@ -71,7 +71,7 @@ class GradePresenter @Inject constructor( view?.apply { showContent(true) showProgress(false) - showEmpty(false) + showErrorView(false) loadedSemesterId[currentPageIndex] = semesterId } } @@ -80,10 +80,18 @@ class GradePresenter @Inject constructor( if (semesters.isNotEmpty()) loadChild(index) } - fun onSwipeRefresh() { + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } loadData() } + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + private fun loadData() { Timber.i("Loading grade data started") disposable.add(studentRepository.getCurrentStudent() @@ -96,25 +104,28 @@ class GradePresenter @Inject constructor( } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) - .doFinally { view?.showRefresh(false) } + .doFinally { view?.showProgress(false) } .subscribe({ view?.run { Timber.i("Loading grade result: Attempt load index $currentPageIndex") loadChild(currentPageIndex) - enableSwipe(false) + showErrorView(false) showSemesterSwitch(true) } }) { Timber.i("Loading grade result: An exception occurred") errorHandler.dispatch(it) - view?.run { - showProgress(false) - showEmpty(true) - enableSwipe(true) - } }) } + private fun showErrorViewOnError(message: String, error: Throwable) { + lastError = error + view?.run { + showErrorView(true) + setErrorDetails(message) + } + } + private fun loadChild(index: Int, forceRefresh: Boolean = false) { semesters.first { it.semesterName == selectedIndex }.semesterId.also { if (forceRefresh || loadedSemesterId[index] != it) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt index a37e6d67..bbdbc32e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt @@ -12,16 +12,14 @@ interface GradeView : BaseView { fun showProgress(show: Boolean) - fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) - fun showRefresh(show: Boolean) + fun setErrorDetails(message: String) fun showSemesterSwitch(show: Boolean) fun showSemesterDialog(selectedIndex: Int) - fun enableSwipe(enable: Boolean) - fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) fun notifyChildParentReselected(index: Int) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt index 8993760d..b3dd3587 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -18,6 +18,7 @@ import eu.davidea.flexibleadapter.items.IFlexible import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.base.ErrorDialog import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.ui.modules.main.MainActivity @@ -90,6 +91,8 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh ) } gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + gradeDetailsErrorRetry.setOnClickListener { presenter.onRetry() } + gradeDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() } } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -141,6 +144,14 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh gradeDetailsEmpty.visibility = if (show) VISIBLE else INVISIBLE } + override fun showErrorView(show: Boolean) { + gradeDetailsError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + gradeDetailsErrorMessage.text = message + } + override fun showRefresh(show: Boolean) { gradeDetailsSwipe.isRefreshing = show } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 96b55686..647047c6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.grade.details -import android.widget.Toast import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.repositories.grade.GradeRepository @@ -31,9 +30,12 @@ class GradeDetailsPresenter @Inject constructor( private var currentSemesterId = 0 + private lateinit var lastError: Throwable + override fun onAttachView(view: GradeDetailsView) { super.onAttachView(view) view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError } fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { @@ -90,6 +92,18 @@ class GradeDetailsPresenter @Inject constructor( view?.notifyParentRefresh() } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + view?.notifyParentRefresh() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onParentViewReselected() { view?.run { if (!isViewEmpty) { @@ -140,21 +154,32 @@ class GradeDetailsPresenter @Inject constructor( } .subscribe({ Timber.i("Loading grade details result: Success") - newGradesAmount = it.sumBy { gradeDetailsHeader -> gradeDetailsHeader.newGrades } + newGradesAmount = it.sumBy { gradeDetailsHeader -> gradeDetailsHeader.newGrades } updateMarkAsDoneButton() view?.run { showEmpty(it.isEmpty()) + showErrorView(false) showContent(it.isNotEmpty()) updateData(it) } analytics.logEvent("load_grade_details", "items" to it.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading grade details result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } errorHandler.dispatch(it) }) } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + private fun createGradeItems(items: Map>, averages: Map): List { val isGradeExpandable = preferencesRepository.isGradeExpandable val gradeColorTheme = preferencesRepository.gradeColorTheme diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt index fb806351..dad4ac6e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt @@ -38,6 +38,10 @@ interface GradeDetailsView : BaseView { fun showProgress(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun enableSwipe(enable: Boolean) fun showRefresh(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt index 94b5f474..5bcf167e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt @@ -111,9 +111,11 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G setOnItemSelectedListener { presenter.onSubjectSelected(it?.text?.toString()) } } - gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) - gradeStatisticsSubjectsContainer.setElevationCompat(requireContext().dpToPx(1f)) + + gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + gradeStatisticsErrorRetry.setOnClickListener { presenter.onRetry() } + gradeStatisticsErrorDetails.setOnClickListener { presenter.onDetailsClick() } } override fun updateSubjects(data: ArrayList) { @@ -228,6 +230,14 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE } + override fun showErrorView(show: Boolean) { + gradeStatisticsError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + gradeStatisticsErrorMessage.text = message + } + override fun showProgress(show: Boolean) { gradeStatisticsProgress.visibility = if (show) View.VISIBLE else View.GONE } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index 1e053701..9795c130 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -30,6 +30,8 @@ class GradeStatisticsPresenter @Inject constructor( private var currentSubjectName: String = "Wszystkie" + private lateinit var lastError: Throwable + var currentType: ViewType = ViewType.PARTIAL private set @@ -37,6 +39,7 @@ class GradeStatisticsPresenter @Inject constructor( super.onAttachView(view) currentType = type ?: ViewType.PARTIAL view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError } fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { @@ -51,6 +54,7 @@ class GradeStatisticsPresenter @Inject constructor( enableSwipe(false) showRefresh(false) showBarContent(false) + showErrorView(false) showEmpty(false) clearView() } @@ -62,6 +66,18 @@ class GradeStatisticsPresenter @Inject constructor( view?.notifyParentRefresh() } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + view?.notifyParentRefresh() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onSubjectSelected(name: String?) { Timber.i("Select grade stats subject $name") view?.run { @@ -70,6 +86,7 @@ class GradeStatisticsPresenter @Inject constructor( showProgress(true) enableSwipe(false) showEmpty(false) + showErrorView(false) clearView() } (subjects.singleOrNull { it.name == name }?.name)?.let { @@ -86,6 +103,7 @@ class GradeStatisticsPresenter @Inject constructor( showProgress(true) enableSwipe(false) showEmpty(false) + showErrorView(false) clearView() } loadDataByType(currentSemesterId, currentSubjectName, type) @@ -146,12 +164,12 @@ class GradeStatisticsPresenter @Inject constructor( showEmpty(it.isEmpty()) showBarContent(false) showPieContent(it.isNotEmpty()) + showErrorView(false) updatePieData(it, preferencesRepository.gradeColorTheme) } analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh) }) { Timber.e("Loading grade stats result: An exception occurred") - view?.run { showEmpty(isPieViewEmpty) } errorHandler.dispatch(it) }) } @@ -177,12 +195,12 @@ class GradeStatisticsPresenter @Inject constructor( 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") - view?.run { showEmpty(isBarViewEmpty) } errorHandler.dispatch(it) }, { Timber.d("Loading grade points stats result: No point stats found") @@ -193,4 +211,15 @@ class GradeStatisticsPresenter @Inject constructor( }) ) } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isBarViewEmpty || isPieViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt index d6e66fac..a214744c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt @@ -32,6 +32,10 @@ interface GradeStatisticsView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt index 7699a641..05fde522 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt @@ -56,6 +56,8 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh adapter = gradeSummaryAdapter } gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + gradeSummaryErrorRetry.setOnClickListener { presenter.onRetry() } + gradeSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() } } override fun updateData(data: List, header: GradeSummaryScrollableHeader) { @@ -82,6 +84,14 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh gradeSummaryEmpty.visibility = if (show) VISIBLE else INVISIBLE } + override fun showErrorView(show: Boolean) { + gradeSummaryError.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun setErrorDetails(message: String) { + gradeSummaryErrorMessage.text = message + } + override fun showProgress(show: Boolean) { gradeSummaryProgress.visibility = if (show) VISIBLE else GONE } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index 3f8fd0ed..1aaa9d09 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -25,9 +25,12 @@ class GradeSummaryPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { + private lateinit var lastError: Throwable + override fun onAttachView(view: GradeSummaryView) { super.onAttachView(view) view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError } fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { @@ -56,21 +59,44 @@ class GradeSummaryPresenter @Inject constructor( view?.run { showEmpty(gradeSummaryItems.isEmpty()) showContent(gradeSummaryItems.isNotEmpty()) + showErrorView(false) updateData(gradeSummaryItems, gradeSummaryHeader) } analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading grade summary result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } errorHandler.dispatch(it) }) } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + fun onSwipeRefresh() { Timber.i("Force refreshing the grade summary") view?.notifyParentRefresh() } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + view?.notifyParentRefresh() + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onParentViewReselected() { view?.run { if (!isViewEmpty) resetView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt index 9e9c6e58..cf318487 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt @@ -26,6 +26,10 @@ interface GradeSummaryView : BaseView { fun showContent(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showEmpty(show: Boolean) fun notifyParentDataLoaded(semesterId: Int) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt index 8195dd20..3f8f1359 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt @@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.homework 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 eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.FlexibleItemDecoration @@ -34,6 +36,8 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView { override val titleStringId get() = R.string.homework_title + override val isViewEmpty get() = homeworkAdapter.isEmpty + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_homework, container, false) } @@ -41,7 +45,7 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) messageContainer = homeworkRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(HomeworkFragment.SAVED_DATE_KEY)) + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) } override fun initView() { @@ -56,6 +60,9 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView { } homeworkSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + homeworkErrorRetry.setOnClickListener { presenter.onRetry() } + homeworkErrorDetails.setOnClickListener { presenter.onDetailsClick() } + homeworkPreviousButton.setOnClickListener { presenter.onPreviousDay() } homeworkNextButton.setOnClickListener { presenter.onNextDay() } @@ -74,18 +81,24 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView { homeworkNavDate.text = date } - override fun isViewEmpty() = homeworkAdapter.isEmpty - override fun hideRefresh() { homeworkSwipe.isRefreshing = false } override fun showEmpty(show: Boolean) { - homeworkEmpty.visibility = if (show) View.VISIBLE else View.GONE + homeworkEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + homeworkError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + homeworkErrorMessage.text = message } override fun showProgress(show: Boolean) { - homeworkProgress.visibility = if (show) View.VISIBLE else View.GONE + homeworkProgress.visibility = if (show) VISIBLE else GONE } override fun enableSwipe(enable: Boolean) { @@ -93,15 +106,15 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView { } override fun showContent(show: Boolean) { - homeworkRecycler.visibility = if (show) View.VISIBLE else View.GONE + homeworkRecycler.visibility = if (show) VISIBLE else GONE } override fun showPreButton(show: Boolean) { - homeworkPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + homeworkPreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE } override fun showNextButton(show: Boolean) { - homeworkNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + homeworkNextButton.visibility = if (show) VISIBLE else View.INVISIBLE } override fun showTimetableDialog(homework: Homework) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt index 6829031c..7e1da314 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -35,10 +35,13 @@ class HomeworkPresenter @Inject constructor( lateinit var currentDate: LocalDate private set + private lateinit var lastError: Throwable + fun onAttachView(view: HomeworkView, date: Long?) { super.onAttachView(view) view.initView() Timber.i("Homework view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData(ofEpochDay(date ?: baseDate.toEpochDay())) if (currentDate.isHolidays) setBaseDateOnHolidays() reloadView() @@ -59,6 +62,18 @@ class HomeworkPresenter @Inject constructor( loadData(currentDate, true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentDate, true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onHomeworkItemSelected(item: AbstractFlexibleItem<*>?) { if (item is HomeworkItem) { Timber.i("Select homework item ${item.homework.id}") @@ -105,17 +120,29 @@ class HomeworkPresenter @Inject constructor( view?.apply { updateData(it) showEmpty(it.isEmpty()) + showErrorView(false) showContent(it.isNotEmpty()) } analytics.logEvent("load_homework", "items" to it.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading homework result: An exception occurred") - view?.run { showEmpty(isViewEmpty()) } + errorHandler.dispatch(it) }) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + private fun createHomeworkItem(items: Map>): List { return items.flatMap { HomeworkHeader(it.key).let { header -> @@ -131,6 +158,7 @@ class HomeworkPresenter @Inject constructor( enableSwipe(false) showContent(false) showEmpty(false) + showErrorView(false) clearData() reloadNavigation() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt index 977a5b73..1d241df4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt @@ -5,6 +5,8 @@ import io.github.wulkanowy.ui.base.BaseView interface HomeworkView : BaseView { + val isViewEmpty: Boolean + fun initView() fun updateData(data: List) @@ -13,12 +15,14 @@ interface HomeworkView : BaseView { fun updateNavigationWeek(date: String) - fun isViewEmpty(): Boolean - fun hideRefresh() fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt index 1288e299..a3e70489 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -16,7 +16,7 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.openEmail +import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.showSoftInput import kotlinx.android.synthetic.main.fragment_login_form.* @@ -62,7 +62,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } loginFormSignIn.setOnClickListener { presenter.onSignInClick() } loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() } - loginFormContactDiscord.setOnClickListener { presenter.onDiscordClick() } + loginFormFaq.setOnClickListener { presenter.onFaqClick() } loginFormContactEmail.setOnClickListener { presenter.onEmailClick() } loginFormPass.setOnEditorActionListener { _, id, _ -> @@ -161,12 +161,12 @@ class LoginFormFragment : BaseFragment(), LoginFormView { presenter.onDetachView() } - override fun openDiscordInvite() { - context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) + override fun openFaqPage() { + context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania/dlaczego-nie-moge-sie-zalogowac", ::showMessage) } override fun openEmail() { - context?.openEmail( + context?.openEmailClient( requireContext().getString(R.string.login_email_intent_title), "wulkanowyinc@gmail.com", requireContext().getString(R.string.login_email_subject), diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index bdab1dad..10c0a663 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -91,8 +91,8 @@ class LoginFormPresenter @Inject constructor( })) } - fun onDiscordClick() { - view?.openDiscordInvite() + fun onFaqClick() { + view?.openFaqPage() } fun onEmailClick() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt index 7eec9477..91e8d86e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt @@ -45,7 +45,7 @@ interface LoginFormView : BaseView { fun showContact(show: Boolean) - fun openDiscordInvite() + fun openFaqPage() fun openEmail() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index dba0c951..8478f7cc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -14,7 +14,7 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.openEmail +import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_login_student_select.* @@ -102,7 +102,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { } override fun openEmail() { - context?.openEmail( + context?.openEmailClient( requireContext().getString(R.string.login_email_intent_title), "wulkanowyinc@gmail.com", requireContext().getString(R.string.login_email_subject), diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt index 724e3fbb..5e9ce3ba 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt @@ -16,7 +16,7 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.openEmail +import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.showSoftInput import kotlinx.android.synthetic.main.fragment_login_symbol.* @@ -50,7 +50,7 @@ class LoginSymbolFragment : BaseFragment(), LoginSymbolView { override fun initView() { loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) } - loginSymbolContactDiscord.setOnClickListener { presenter.onDiscordClick() } + loginSymbolFaq.setOnClickListener { presenter.onFaqClick() } loginSymbolContactEmail.setOnClickListener { presenter.onEmailClick() } loginSymbolName.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() } @@ -126,12 +126,12 @@ class LoginSymbolFragment : BaseFragment(), LoginSymbolView { presenter.onDetachView() } - override fun openDiscordInvite() { - context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) + override fun openFaqPage() { + context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania/co-to-jest-symbol", ::showMessage) } override fun openEmail() { - context?.openEmail( + context?.openEmailClient( requireContext().getString(R.string.login_email_intent_title), "wulkanowyinc@gmail.com", requireContext().getString(R.string.login_email_subject), diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt index 8e4dd3b0..49aabc92 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt @@ -89,8 +89,8 @@ class LoginSymbolPresenter @Inject constructor( } } - fun onDiscordClick() { - view?.openDiscordInvite() + fun onFaqClick() { + view?.openFaqPage() } fun onEmailClick() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt index 9b5340cf..1afc1532 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt @@ -29,7 +29,7 @@ interface LoginSymbolView : BaseView { fun showContact(show: Boolean) - fun openDiscordInvite() + fun openFaqPage() fun openEmail() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt index 00204a87..12bf1a13 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt @@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.luckynumber 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 io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.LuckyNumber @@ -23,17 +25,22 @@ class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView override val titleStringId: Int get() = R.string.lucky_number_title + override val isViewEmpty get() = luckyNumberText.text.isBlank() + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_lucky_number, container, false) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) + messageContainer = luckyNumberSwipe presenter.onAttachView(this) } override fun initView() { luckyNumberSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + luckyNumberErrorRetry.setOnClickListener { presenter.onRetry() } + luckyNumberErrorDetails.setOnClickListener { presenter.onDetailsClick() } } override fun updateData(data: LuckyNumber) { @@ -45,11 +52,19 @@ class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView } override fun showEmpty(show: Boolean) { - luckyNumberEmpty.visibility = if (show) View.VISIBLE else View.GONE + luckyNumberEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + luckyNumberError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + luckyNumberErrorMessage.text = message } override fun showProgress(show: Boolean) { - luckyNumberProgress.visibility = if (show) View.VISIBLE else View.GONE + luckyNumberProgress.visibility = if (show) VISIBLE else GONE } override fun enableSwipe(enable: Boolean) { @@ -57,11 +72,7 @@ class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView } override fun showContent(show: Boolean) { - luckyNumberContent.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun isViewEmpty(): Boolean { - return luckyNumberText.text.isBlank() + luckyNumberContent.visibility = if (show) VISIBLE else GONE } override fun onDestroyView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt index ee7260e6..63ced4b8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -19,6 +19,8 @@ class LuckyNumberPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { + private lateinit var lastError: Throwable + override fun onAttachView(view: LuckyNumberView) { super.onAttachView(view) view.run { @@ -27,6 +29,7 @@ class LuckyNumberPresenter @Inject constructor( enableSwipe(false) } Timber.i("Lucky number view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData() } @@ -52,25 +55,49 @@ class LuckyNumberPresenter @Inject constructor( updateData(it) showContent(true) showEmpty(false) + showErrorView(false) } analytics.logEvent("load_lucky_number", "lucky_number" to it.luckyNumber, "force_refresh" to forceRefresh) }, { Timber.i("Loading lucky number result: An exception occurred") - view?.run { showEmpty(isViewEmpty()) } errorHandler.dispatch(it) }, { Timber.i("Loading lucky number result: No lucky number found") view?.run { showContent(false) showEmpty(true) + showEmpty(false) } }) ) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + fun onSwipeRefresh() { Timber.i("Force refreshing the lucky number") loadData(true) } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt index 9ead2b1f..a680c83e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt @@ -5,6 +5,8 @@ import io.github.wulkanowy.ui.base.BaseView interface LuckyNumberView : BaseView { + val isViewEmpty: Boolean + fun initView() fun updateData(data: LuckyNumber) @@ -13,11 +15,13 @@ interface LuckyNumberView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) fun showContent(show: Boolean) - - fun isViewEmpty(): Boolean } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt index 3c8f18a0..6e4716bf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt @@ -58,7 +58,10 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor( .subscribe({ when { it.isEmpty() -> view?.openLoginView() - it.size == 1 -> registerStudent(it.single().student) + it.size == 1 -> { + selectedStudent = it.single().student + view?.showThemeDialog() + } else -> view?.updateData(it) } }, { errorHandler.dispatch(it) })) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index 4c7f7759..991c1d10 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -100,7 +100,7 @@ class MainActivity : BaseActivity(), MainView { override fun initView() { with(mainToolbar) { if (SDK_INT >= LOLLIPOP) stateListAnimator = null - setBackgroundColor(overlayProvider.get().getSurfaceColorWithOverlayIfNeeded(dpToPx(4f))) + setBackgroundColor(overlayProvider.get().compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(4f))) } with(mainBottomNav) { @@ -113,7 +113,7 @@ class MainActivity : BaseActivity(), MainView { )) accentColor = getThemeAttrColor(R.attr.colorPrimary) inactiveColor = ColorUtils.setAlphaComponent(getThemeAttrColor(R.attr.colorOnSurface), 153) - defaultBackgroundColor = overlayProvider.get().getSurfaceColorWithOverlayIfNeeded(dpToPx(8f)) + defaultBackgroundColor = overlayProvider.get().compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(8f)) titleState = ALWAYS_SHOW currentItem = startMenuIndex isBehaviorTranslationEnabled = false diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 4f881e8f..22231e42 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -65,6 +65,10 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getLong(MESSAGE_ID_KEY) ?: 0L) } + override fun initView() { + messagePreviewErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.action_menu_message_preview, menu) menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply) @@ -126,8 +130,16 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl menuDeleteButton?.setTitle(R.string.message_move_to_bin) } - override fun showMessageError() { - messagePreviewError.visibility = VISIBLE + override fun showErrorView(show: Boolean) { + messagePreviewError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + messagePreviewErrorMessage.text = message + } + + override fun setErrorRetryCallback(callback: () -> Unit) { + messagePreviewErrorRetry.setOnClickListener { callback() } } override fun openMessageReply(message: Message?) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index a230c0b3..c32bd611 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -23,11 +23,29 @@ class MessagePreviewPresenter @Inject constructor( private var message: Message? = null + private lateinit var lastError: Throwable + + private var retryCallback: () -> Unit = {} + fun onAttachView(view: MessagePreviewView, id: Long) { super.onAttachView(view) + view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError loadData(id) } + private fun onMessageLoadRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(messageId) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + private fun loadData(id: Long) { Timber.i("Loading message $id preview started") messageId = id @@ -55,7 +73,7 @@ class MessagePreviewPresenter @Inject constructor( analytics.logEvent("load_message_preview", "length" to message.content?.length) }) { Timber.i("Loading message $id preview result: An exception occurred ") - view?.showMessageError() + retryCallback = { onMessageLoadRetry() } errorHandler.dispatch(it) }) } @@ -85,6 +103,7 @@ class MessagePreviewPresenter @Inject constructor( showContent(false) showProgress(true) showOptions(false) + showErrorView(false) } } .doFinally { @@ -97,15 +116,24 @@ class MessagePreviewPresenter @Inject constructor( popView() } }, { error -> - view?.showMessageError() + retryCallback = { onMessageDelete() } errorHandler.dispatch(error) }, { - view?.showMessageError() + view?.showErrorView(true) }) ) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + lastError = error + setErrorDetails(message) + showErrorView(true) + setErrorRetryCallback { retryCallback() } + } + } + fun onMessageDelete(): Boolean { deleteMessage() return true diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index 373dee11..d5776627 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -9,6 +9,8 @@ interface MessagePreviewView : BaseView { val deleteMessageSuccessString: String + fun initView() + fun setSubject(subject: String) fun setRecipient(recipient: String) @@ -23,19 +25,23 @@ interface MessagePreviewView : BaseView { fun showContent(show: Boolean) + fun notifyParentMessageDeleted(message: Message) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun setErrorRetryCallback(callback: () -> Unit) + fun showOptions(show: Boolean) fun setDeletedOptionsLabels() fun setNotDeletedOptionsLabels() - fun showMessageError() - fun openMessageReply(message: Message?) fun openMessageForward(message: Message?) fun popView() - - fun notifyParentMessageDeleted(message: Message) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 326cd6a7..d6065983 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -72,6 +72,8 @@ class MessageTabFragment : BaseFragment(), MessageTabView { ) } messageTabSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + messageTabErrorRetry.setOnClickListener { presenter.onRetry() } + messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() } } override fun updateData(data: List) { @@ -102,6 +104,14 @@ class MessageTabFragment : BaseFragment(), MessageTabView { messageTabEmpty.visibility = if (show) VISIBLE else INVISIBLE } + override fun showErrorView(show: Boolean) { + messageTabError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + messageTabErrorMessage.text = message + } + override fun showRefresh(show: Boolean) { messageTabSwipe.isRefreshing = show } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index af576293..1123e7e4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -23,9 +23,12 @@ class MessageTabPresenter @Inject constructor( lateinit var folder: MessageFolder + private lateinit var lastError: Throwable + fun onAttachView(view: MessageTabView, folder: MessageFolder) { super.onAttachView(view) view.initView() + errorHandler.showErrorMessage = ::showErrorViewOnError this.folder = folder } @@ -34,6 +37,18 @@ class MessageTabPresenter @Inject constructor( onParentViewLoadData(true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onDeleteMessage() { loadData(false) } @@ -78,17 +93,28 @@ class MessageTabPresenter @Inject constructor( view?.run { showEmpty(it.isEmpty()) showContent(it.isNotEmpty()) + showErrorView(false) updateData(it) } analytics.logEvent("load_messages", "items" to it.size, "folder" to folder.name) }) { Timber.i("Loading $folder message result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } errorHandler.dispatch(it) }) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + private fun updateMessage(message: Message) { Timber.i("Attempt to update message ${message.id}") disposable.add(messageRepository.updateMessage(message) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt index 967863de..e5705c73 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -26,6 +26,10 @@ interface MessageTabView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showRefresh(show: Boolean) fun openMessage(messageId: Long) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt index 1d9104a8..d47574f6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt @@ -61,6 +61,8 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi onDeviceUnregisterListener = presenter::onUnregisterDevice } mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + mobileDevicesErrorRetry.setOnClickListener { presenter.onRetry() } + mobileDevicesErrorDetails.setOnClickListener { presenter.onDetailsClick() } mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() } } @@ -105,6 +107,14 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi mobileDevicesEmpty.visibility = if (show) VISIBLE else GONE } + override fun showErrorView(show: Boolean) { + mobileDevicesError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + mobileDevicesErrorMessage.text = message + } + override fun enableSwipe(enable: Boolean) { mobileDevicesSwipe.isEnabled = enable } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt index 71359a52..e95d2ce0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -20,10 +20,13 @@ class MobileDevicePresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { + private lateinit var lastError: Throwable + override fun onAttachView(view: MobileDeviceView) { super.onAttachView(view) view.initView() Timber.i("Mobile device view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData() } @@ -31,6 +34,18 @@ class MobileDevicePresenter @Inject constructor( loadData(true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading mobile devices data started") disposable.add(studentRepository.getCurrentStudent() @@ -51,6 +66,7 @@ class MobileDevicePresenter @Inject constructor( updateData(it) showContent(it.isNotEmpty()) showEmpty(it.isEmpty()) + showErrorView(false) } analytics.logEvent("load_devices", "items" to it.size, "force_refresh" to forceRefresh) }) { @@ -59,6 +75,17 @@ class MobileDevicePresenter @Inject constructor( }) } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + fun onRegisterDevice() { view?.showTokenDialog() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt index 58804e24..869b59bb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt @@ -25,6 +25,10 @@ interface MobileDeviceView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showUndo(position: Int, device: MobileDevice) fun showTokenDialog() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt index a1812ece..8b81156b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt @@ -12,15 +12,13 @@ import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.Toast import androidx.core.content.getSystemService -import dagger.android.support.DaggerDialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.pojos.MobileDeviceToken -import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRemote -import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.BaseDialogFragment import kotlinx.android.synthetic.main.dialog_mobile_device.* import javax.inject.Inject -class MobileDeviceTokenDialog : DaggerDialogFragment(), MobileDeviceTokenVIew { +class MobileDeviceTokenDialog : BaseDialogFragment(), MobileDeviceTokenVIew { @Inject lateinit var presenter: MobileDeviceTokenPresenter @@ -66,6 +64,12 @@ class MobileDeviceTokenDialog : DaggerDialogFragment(), MobileDeviceTokenVIew { }) } + private fun clickCopy(text: String) { + val clip = ClipData.newPlainText("wulkanowy", text) + activity?.getSystemService()?.setPrimaryClip(clip) + Toast.makeText(context, R.string.all_copied, Toast.LENGTH_LONG).show() + } + override fun hideLoading() { mobileDeviceDialogProgress.visibility = GONE } @@ -78,30 +82,8 @@ class MobileDeviceTokenDialog : DaggerDialogFragment(), MobileDeviceTokenVIew { dismiss() } - override fun showError(text: String, error: Throwable) { - showMessage(text) - } - - override fun showMessage(text: String) { - Toast.makeText(context, text, Toast.LENGTH_LONG).show() - } - - override fun showExpiredDialog() { - (activity as? BaseActivity<*>)?.showExpiredDialog() - } - - override fun openClearLoginView() { - (activity as? BaseActivity<*>)?.openClearLoginView() - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() } - - fun clickCopy(text: String) { - val clip = ClipData.newPlainText("wulkanowy", text) - activity?.getSystemService()?.setPrimaryClip(clip) - Toast.makeText(context, R.string.all_copied, Toast.LENGTH_LONG).show() - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt index 6117ae24..e5335e45 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt @@ -60,6 +60,8 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView { ) } noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + noteErrorRetry.setOnClickListener { presenter.onRetry() } + noteErrorDetails.setOnClickListener { presenter.onDetailsClick() } } override fun showNoteDialog(note: Note) { @@ -82,6 +84,14 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView { noteEmpty.visibility = if (show) VISIBLE else GONE } + override fun showErrorView(show: Boolean) { + noteError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + noteErrorMessage.text = message + } + override fun showProgress(show: Boolean) { noteProgress.visibility = if (show) VISIBLE else GONE } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt index df98ab88..7acf37a4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt @@ -21,10 +21,13 @@ class NotePresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { + private lateinit var lastError: Throwable + override fun onAttachView(view: NoteView) { super.onAttachView(view) view.initView() Timber.i("Note view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData() } @@ -33,6 +36,18 @@ class NotePresenter @Inject constructor( loadData(true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading note data started") disposable.add(studentRepository.getCurrentStudent() @@ -53,17 +68,28 @@ class NotePresenter @Inject constructor( view?.apply { updateData(it) showEmpty(it.isEmpty()) + showErrorView(false) showContent(it.isNotEmpty()) } analytics.logEvent("load_note", "items" to it.size, "force_refresh" to forceRefresh) }, { Timber.i("Loading note result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } errorHandler.dispatch(it) }) ) } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + fun onNoteItemSelected(item: AbstractFlexibleItem<*>?) { if (item is NoteItem) { Timber.i("Select note item ${item.note.id}") diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt index 9a2c12eb..a9c9f4f2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt @@ -18,6 +18,10 @@ interface NoteView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt index 92f74ca8..5a7c4cad 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt @@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.school 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 io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.School @@ -10,8 +12,8 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment -import io.github.wulkanowy.utils.dialPhone -import io.github.wulkanowy.utils.openMapLocation +import io.github.wulkanowy.utils.openDialer +import io.github.wulkanowy.utils.openNavigation import kotlinx.android.synthetic.main.fragment_school.* import javax.inject.Inject @@ -22,6 +24,8 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn override val titleStringId get() = R.string.school_title + override val isViewEmpty get() = schoolName.text.isBlank() + companion object { fun newInstance() = SchoolFragment() } @@ -37,6 +41,8 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn override fun initView() { schoolSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + schoolErrorRetry.setOnClickListener { presenter.onRetry() } + schoolErrorDetails.setOnClickListener { presenter.onDetailsClick() } schoolAddressButton.setOnClickListener { presenter.onAddressSelected() } schoolTelephoneButton.setOnClickListener { presenter.onTelephoneSelected() } @@ -45,19 +51,27 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn override fun updateData(data: School) { schoolName.text = data.name schoolAddress.text = data.address.ifBlank { "-" } - schoolAddressButton.visibility = if (data.address.isNotBlank()) View.VISIBLE else View.GONE + schoolAddressButton.visibility = if (data.address.isNotBlank()) VISIBLE else GONE schoolTelephone.text = data.contact.ifBlank { "-" } - schoolTelephoneButton.visibility = if (data.contact.isNotBlank()) View.VISIBLE else View.GONE + schoolTelephoneButton.visibility = if (data.contact.isNotBlank()) VISIBLE else GONE schoolHeadmaster.text = data.headmaster schoolPedagogue.text = data.pedagogue } override fun showEmpty(show: Boolean) { - schoolEmpty.visibility = if (show) View.VISIBLE else View.GONE + schoolEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + schoolError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + schoolErrorMessage.text = message } override fun showProgress(show: Boolean) { - schoolProgress.visibility = if (show) View.VISIBLE else View.GONE + schoolProgress.visibility = if (show) VISIBLE else GONE } override fun enableSwipe(enable: Boolean) { @@ -65,7 +79,7 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn } override fun showContent(show: Boolean) { - schoolContent.visibility = if (show) View.VISIBLE else View.GONE + schoolContent.visibility = if (show) VISIBLE else GONE } override fun hideRefresh() { @@ -86,10 +100,10 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn } override fun openMapsLocation(location: String) { - context?.openMapLocation(location) + context?.openNavigation(location) } override fun dialPhone(phone: String) { - context?.dialPhone(phone) + context?.openDialer(phone) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt index 2b0a37b1..d3299d90 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt @@ -23,10 +23,13 @@ class SchoolPresenter @Inject constructor( private var contact: String? = null + private lateinit var lastError: Throwable + override fun onAttachView(view: SchoolView) { super.onAttachView(view) view.initView() Timber.i("School view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData() } @@ -34,12 +37,24 @@ class SchoolPresenter @Inject constructor( loadData(true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onParentViewLoadData(forceRefresh: Boolean) { loadData(forceRefresh) } fun onAddressSelected() { - address?.let{ view?.openMapsLocation(it) } + address?.let { view?.openMapsLocation(it) } } fun onTelephoneSelected() { @@ -68,6 +83,7 @@ class SchoolPresenter @Inject constructor( updateData(it) showContent(true) showEmpty(false) + showErrorView(false) } analytics.logEvent("load_school", "force_refresh" to forceRefresh) }, { @@ -76,9 +92,22 @@ class SchoolPresenter @Inject constructor( }, { Timber.i("Loading school result: No school info found") view?.run { - showContent(false) - showEmpty(true) + showContent(!isViewEmpty) + showEmpty(isViewEmpty) + showErrorView(false) } })) } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + showContent(false) + } else showError(message, error) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt index 39e7b805..c42c2f91 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt @@ -6,12 +6,18 @@ import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildVi interface SchoolView : BaseView, SchoolAndTeachersChildView { + val isViewEmpty: Boolean + fun initView() fun updateData(data: School) fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt index 842e50fe..b6bb9c10 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt @@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.teacher 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 eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.FlexibleItemDecoration @@ -16,7 +18,8 @@ import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragmen import kotlinx.android.synthetic.main.fragment_teacher.* import javax.inject.Inject -class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, SchoolAndTeachersChildView { +class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, + SchoolAndTeachersChildView { @Inject lateinit var presenter: TeacherPresenter @@ -55,6 +58,8 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, School ) } teacherSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + teacherErrorRetry.setOnClickListener { presenter.onRetry() } + teacherErrorDetails.setOnClickListener { presenter.onDetailsClick() } } override fun updateData(data: List) { @@ -70,11 +75,19 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, School } override fun showEmpty(show: Boolean) { - teacherEmpty.visibility = if (show) View.VISIBLE else View.GONE + teacherEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + teacherError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + teacherErrorMessage.text = message } override fun showProgress(show: Boolean) { - teacherProgress.visibility = if (show) View.VISIBLE else View.GONE + teacherProgress.visibility = if (show) VISIBLE else GONE } override fun enableSwipe(enable: Boolean) { @@ -82,7 +95,7 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, School } override fun showContent(show: Boolean) { - teacherRecycler.visibility = if (show) View.VISIBLE else View.GONE + teacherRecycler.visibility = if (show) VISIBLE else GONE } override fun hideRefresh() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt index aabcff28..6aa4871d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt @@ -19,10 +19,13 @@ class TeacherPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { + private lateinit var lastError: Throwable + override fun onAttachView(view: TeacherView) { super.onAttachView(view) view.initView() Timber.i("Teacher view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData() } @@ -30,6 +33,18 @@ class TeacherPresenter @Inject constructor( loadData(true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onParentViewLoadData(forceRefresh: Boolean) { loadData(forceRefresh) } @@ -56,6 +71,7 @@ class TeacherPresenter @Inject constructor( updateData(it) showContent(it.isNotEmpty()) showEmpty(it.isEmpty()) + showErrorView(false) } analytics.logEvent("load_teachers", "items" to it.size, "force_refresh" to forceRefresh) }) { @@ -63,4 +79,15 @@ class TeacherPresenter @Inject constructor( errorHandler.dispatch(it) }) } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt index 2cc1139c..b16be8ff 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt @@ -27,4 +27,8 @@ interface TeacherView : BaseView, SchoolAndTeachersChildView { fun showContent(show: Boolean) fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt index cb172b98..03a89d27 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt @@ -5,14 +5,17 @@ import android.content.SharedPreferences import android.os.Bundle import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import com.yariksoffice.lingver.Lingver import dagger.android.support.AndroidSupportInjection import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.ErrorDialog import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AppInfo import javax.inject.Inject -class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener, +class SettingsFragment : PreferenceFragmentCompat(), + SharedPreferences.OnSharedPreferenceChangeListener, MainView.TitledView, SettingsView { @Inject @@ -21,6 +24,9 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP @Inject lateinit var appInfo: AppInfo + @Inject + lateinit var lingver: Lingver + companion object { fun newInstance() = SettingsFragment() } @@ -50,6 +56,10 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP activity?.recreate() } + override fun updateLanguage(langCode: String) { + lingver.setLocale(requireContext(), langCode) + } + override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) { findPreference(serviceEnablesKey)?.apply { summary = if (isHolidays) getString(R.string.pref_services_suspended) else "" @@ -73,6 +83,10 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP (activity as? BaseActivity<*>)?.openClearLoginView() } + override fun showErrorDetailsDialog(error: Throwable) { + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } + override fun onResume() { super.onResume() preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt index 88d38e0d..89ba0ee5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt @@ -6,6 +6,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.isHolidays @@ -20,7 +21,8 @@ class SettingsPresenter @Inject constructor( private val preferencesRepository: PreferencesRepository, private val analytics: FirebaseAnalyticsHelper, private val syncManager: SyncManager, - private val chuckCollector: ChuckCollector + private val chuckCollector: ChuckCollector, + private val appInfo: AppInfo ) : BasePresenter(errorHandler, studentRepository, schedulers) { override fun onAttachView(view: SettingsView) { @@ -38,6 +40,10 @@ class SettingsPresenter @Inject constructor( servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true) isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable) appThemeKey -> view?.recreateView() + appLanguageKey -> view?.run { + updateLanguage(if (appLanguage == "system") appInfo.systemLanguage else appLanguage) + recreateView() + } else -> Unit } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt index 1c4bf3b0..e50eb47b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt @@ -6,5 +6,7 @@ interface SettingsView : BaseView { fun recreateView() + fun updateLanguage(langCode: String) + fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt index a64dee8d..ef6057df 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -6,7 +6,10 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE import android.view.ViewGroup +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.FlexibleItemDecoration import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager @@ -17,9 +20,11 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment +import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_timetable.* +import org.threeten.bp.LocalDate import javax.inject.Inject class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, @@ -71,7 +76,11 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, } timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + timetableErrorRetry.setOnClickListener { presenter.onRetry() } + timetableErrorDetails.setOnClickListener { presenter.onDetailsClick() } + timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() } + timetableNavDate.setOnClickListener { presenter.onPickDate() } timetableNextButton.setOnClickListener { presenter.onNextDay() } timetableNavContainer.setElevationCompat(requireContext().dpToPx(8f)) @@ -115,11 +124,19 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, } override fun showEmpty(show: Boolean) { - timetableEmpty.visibility = if (show) View.VISIBLE else View.GONE + timetableEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + timetableError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + timetableErrorMessage.text = message } override fun showProgress(show: Boolean) { - timetableProgress.visibility = if (show) View.VISIBLE else View.GONE + timetableProgress.visibility = if (show) VISIBLE else GONE } override fun enableSwipe(enable: Boolean) { @@ -127,21 +144,36 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, } override fun showContent(show: Boolean) { - timetableRecycler.visibility = if (show) View.VISIBLE else View.GONE + timetableRecycler.visibility = if (show) VISIBLE else GONE } override fun showPreButton(show: Boolean) { - timetablePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + timetablePreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE } override fun showNextButton(show: Boolean) { - timetableNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + timetableNextButton.visibility = if (show) VISIBLE else View.INVISIBLE } override fun showTimetableDialog(lesson: Timetable) { (activity as? MainActivity)?.showDialogFragment(TimetableDialog.newInstance(lesson)) } + override fun showDatePickerDialog(currentDate: LocalDate) { + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> + presenter.onDateSet(year, month + 1, dayOfMonth) + } + val datePickerDialog = DatePickerDialog.newInstance(dateSetListener, + currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth) + + with(datePickerDialog) { + setDateRangeLimiter(SchooldaysRangeLimiter()) + version = DatePickerDialog.Version.VERSION_2 + scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL + show(this@TimetableFragment.parentFragmentManager, null) + } + } + override fun openCompletedLessonsView() { (activity as? MainActivity)?.pushView(CompletedLessonsFragment.newInstance()) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index cea5bf2f..4c30ecb7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -17,6 +17,7 @@ import io.github.wulkanowy.utils.previousSchoolDay import io.github.wulkanowy.utils.toFormattedString import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.of import org.threeten.bp.LocalDate.ofEpochDay import timber.log.Timber import java.util.concurrent.TimeUnit.MILLISECONDS @@ -36,10 +37,13 @@ class TimetablePresenter @Inject constructor( lateinit var currentDate: LocalDate private set + private lateinit var lastError: Throwable + fun onAttachView(view: TimetableView, date: Long?) { super.onAttachView(view) view.initView() Timber.i("Timetable was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError loadData(ofEpochDay(date ?: baseDate.toEpochDay())) if (currentDate.isHolidays) setBaseDateOnHolidays() reloadView() @@ -55,11 +59,32 @@ class TimetablePresenter @Inject constructor( reloadView() } + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + loadData(of(year, month, day)) + reloadView() + } + fun onSwipeRefresh() { Timber.i("Force refreshing the timetable") loadData(currentDate, true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentDate, true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onViewReselected() { Timber.i("Timetable view is reselected") view?.also { view -> @@ -125,17 +150,28 @@ class TimetablePresenter @Inject constructor( view?.apply { updateData(it) showEmpty(it.isEmpty()) + showErrorView(false) showContent(it.isNotEmpty()) } analytics.logEvent("load_timetable", "items" to it.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading timetable result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } errorHandler.dispatch(it) }) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + private fun reloadView() { Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") view?.apply { @@ -143,6 +179,7 @@ class TimetablePresenter @Inject constructor( enableSwipe(false) showContent(false) showEmpty(false) + showErrorView(false) clearData() reloadNavigation() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt index 84f28974..f730a271 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.ui.base.BaseView +import org.threeten.bp.LocalDate interface TimetableView : BaseView { @@ -23,6 +24,10 @@ interface TimetableView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) @@ -35,6 +40,8 @@ interface TimetableView : BaseView { fun showTimetableDialog(lesson: Timetable) + fun showDatePickerDialog(currentDate: LocalDate) + fun popView() fun openCompletedLessonsView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt index c7b5c6ca..60c16b2d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -3,7 +3,11 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.os.Bundle import android.view.LayoutInflater import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import android.view.ViewGroup +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -12,10 +16,12 @@ import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getCompatDrawable import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_timetable_completed.* +import org.threeten.bp.LocalDate import javax.inject.Inject class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.TitledView { @@ -55,7 +61,11 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. } completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + completedLessonErrorRetry.setOnClickListener { presenter.onRetry() } + completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() } + completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } + completedLessonsNavDate.setOnClickListener { presenter.onPickDate() } completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } completedLessonsNavContainer.setElevationCompat(requireContext().dpToPx(8f)) @@ -78,7 +88,15 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. } override fun showEmpty(show: Boolean) { - completedLessonsEmpty.visibility = if (show) View.VISIBLE else View.GONE + completedLessonsEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showErrorView(show: Boolean) { + completedLessonError.visibility = if (show) VISIBLE else GONE + } + + override fun setErrorDetails(message: String) { + completedLessonErrorMessage.text = message } override fun showFeatureDisabled() { @@ -87,7 +105,7 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. } override fun showProgress(show: Boolean) { - completedLessonsProgress.visibility = if (show) View.VISIBLE else View.GONE + completedLessonsProgress.visibility = if (show) VISIBLE else GONE } override fun enableSwipe(enable: Boolean) { @@ -95,21 +113,36 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. } override fun showContent(show: Boolean) { - completedLessonsRecycler.visibility = if (show) View.VISIBLE else View.GONE + completedLessonsRecycler.visibility = if (show) VISIBLE else GONE } override fun showPreButton(show: Boolean) { - completedLessonsPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + completedLessonsPreviousButton.visibility = if (show) VISIBLE else INVISIBLE } override fun showNextButton(show: Boolean) { - completedLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + completedLessonsNextButton.visibility = if (show) VISIBLE else INVISIBLE } override fun showCompletedLessonDialog(completedLesson: CompletedLesson) { (activity as? MainActivity)?.showDialogFragment(CompletedLessonDialog.newInstance(completedLesson)) } + override fun showDatePickerDialog(currentDate: LocalDate) { + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> + presenter.onDateSet(year, month + 1, dayOfMonth) + } + val datePickerDialog = DatePickerDialog.newInstance(dateSetListener, + currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth) + + with(datePickerDialog) { + setDateRangeLimiter(SchooldaysRangeLimiter()) + version = DatePickerDialog.Version.VERSION_2 + scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL + show(this@CompletedLessonsFragment.parentFragmentManager, null) + } + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index b936c657..e2b56950 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.timetable.completed +import android.annotation.SuppressLint import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository @@ -34,17 +35,20 @@ class CompletedLessonsPresenter @Inject constructor( lateinit var currentDate: LocalDate private set + private lateinit var lastError: Throwable + fun onAttachView(view: CompletedLessonsView, date: Long?) { super.onAttachView(view) Timber.i("Completed lessons is attached") view.initView() - loadData(ofEpochDay(date ?: baseDate.toEpochDay())) - if (currentDate.isHolidays) setBaseDateOnHolidays() - reloadView() + completedLessonsErrorHandler.showErrorMessage = ::showErrorViewOnError completedLessonsErrorHandler.onFeatureDisabled = { this.view?.showFeatureDisabled() Timber.i("Completed lessons feature disabled by school") } + loadData(ofEpochDay(date ?: baseDate.toEpochDay())) + if (currentDate.isHolidays) setBaseDateOnHolidays() + reloadView() } fun onPreviousDay() { @@ -57,11 +61,32 @@ class CompletedLessonsPresenter @Inject constructor( reloadView() } + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + loadData(LocalDate.of(year, month, day)) + reloadView() + } + fun onSwipeRefresh() { Timber.i("Force refreshing the completed lessons") loadData(currentDate, true) } + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentDate, true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + fun onCompletedLessonsItemSelected(item: AbstractFlexibleItem<*>?) { if (item is CompletedLessonItem) { Timber.i("Select completed lessons item ${item.completedLesson.id}") @@ -108,17 +133,28 @@ class CompletedLessonsPresenter @Inject constructor( view?.apply { updateData(it) showEmpty(it.isEmpty()) + showErrorView(false) showContent(it.isNotEmpty()) } analytics.logEvent("load_completed_lessons", "items" to it.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading completed lessons result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } completedLessonsErrorHandler.dispatch(it) }) } } + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + private fun reloadView() { Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}") view?.apply { @@ -126,11 +162,13 @@ class CompletedLessonsPresenter @Inject constructor( enableSwipe(false) showContent(false) showEmpty(false) + showErrorView(false) clearData() reloadNavigation() } } + @SuppressLint("DefaultLocale") private fun reloadNavigation() { view?.apply { showPreButton(!currentDate.minusDays(1).isHolidays) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt index a8bef66f..a6a327e2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable.completed import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.ui.base.BaseView +import org.threeten.bp.LocalDate interface CompletedLessonsView : BaseView { @@ -19,6 +20,10 @@ interface CompletedLessonsView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun showFeatureDisabled() fun showProgress(show: Boolean) @@ -32,4 +37,6 @@ interface CompletedLessonsView : BaseView { fun showNextButton(show: Boolean) fun showCompletedLessonDialog(completedLesson: CompletedLesson) + + fun showDatePickerDialog(currentDate: LocalDate) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt index cad26f2e..a04922e5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt @@ -33,7 +33,7 @@ class MaterialLinearLayout : LinearLayout { if (SDK_INT >= LOLLIPOP) { setElevation(elevation) } else { - setBackgroundColor(ElevationOverlayProvider(context).getSurfaceColorWithOverlayIfNeeded(elevation)) + setBackgroundColor(ElevationOverlayProvider(context).compositeOverlayWithThemeSurfaceColorIfNeeded(elevation)) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialTabLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialTabLayout.kt index 3bcaae7d..e19d0111 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialTabLayout.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialTabLayout.kt @@ -19,7 +19,7 @@ open class MaterialTabLayout : TabLayout { if (SDK_INT >= LOLLIPOP) { setElevation(elevation) } else { - setBackgroundColor(ElevationOverlayProvider(context).getSurfaceColorWithOverlayIfNeeded(elevation)) + setBackgroundColor(ElevationOverlayProvider(context).compositeOverlayWithThemeSurfaceColorIfNeeded(elevation)) } } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt index 3fa83a04..a444da6f 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.utils +import android.content.res.Resources import android.os.Build.MANUFACTURER import android.os.Build.MODEL import android.os.Build.VERSION.SDK_INT @@ -25,4 +26,8 @@ open class AppInfo { open val systemManufacturer: String get() = MANUFACTURER open val systemModel: String get() = MODEL + + @Suppress("DEPRECATION") + open val systemLanguage: String + get() = Resources.getSystem().configuration.locale.language } diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index 5110b067..a265378e 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -32,7 +32,7 @@ fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) - } } -fun Context.openEmail(chooserTitle: String, email: String, subject: String?, body: String?) { +fun Context.openEmailClient(chooserTitle: String, email: String, subject: String?, body: String?) { val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", email, null)) emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) if (subject != null) emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject) @@ -40,7 +40,7 @@ fun Context.openEmail(chooserTitle: String, email: String, subject: String?, bod startActivity(Intent.createChooser(emailIntent, chooserTitle)) } -fun Context.openMapLocation(location: String) { +fun Context.openNavigation(location: String) { val intentUri = Uri.parse("geo:0,0?q=${Uri.encode(location)}") val intent = Intent(Intent.ACTION_VIEW, intentUri) if (intent.resolveActivity(packageManager) != null) { @@ -48,7 +48,7 @@ fun Context.openMapLocation(location: String) { } } -fun Context.dialPhone(phone: String) { +fun Context.openDialer(phone: String) { val intentUri = Uri.parse("tel:$phone") val intent = Intent(Intent.ACTION_DIAL, intentUri) startActivity(intent) diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt index c863b030..22dbc22f 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -67,8 +67,8 @@ inline val Grade.colorStringId: Int fun Grade.changeModifier(plusModifier: Double, minusModifier: Double): Grade { return when { - modifier != .0 && plusModifier != .0 && modifier > 0 -> copy(modifier = plusModifier) - modifier != .0 && minusModifier != .0 && modifier < 0 -> copy(modifier = -minusModifier) + modifier > 0 -> copy(modifier = plusModifier) + modifier < 0 -> copy(modifier = -minusModifier) else -> this } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/SchooldaysRangeLimiter.kt b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysRangeLimiter.kt new file mode 100644 index 00000000..922aafbd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysRangeLimiter.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.utils + +import android.os.Parcel +import android.os.Parcelable +import com.wdullaer.materialdatetimepicker.date.DateRangeLimiter +import org.threeten.bp.DayOfWeek +import org.threeten.bp.LocalDate +import java.util.Calendar + +@Suppress("UNUSED_PARAMETER") +class SchooldaysRangeLimiter : DateRangeLimiter { + + private val now = LocalDate.now() + + override fun setToNearestDate(day: Calendar): Calendar = day + + override fun isOutOfRange(year: Int, month: Int, day: Int): Boolean { + val date = LocalDate.of(year, month + 1, day) + val dayOfWeek = date.dayOfWeek + return dayOfWeek == DayOfWeek.SUNDAY || dayOfWeek == DayOfWeek.SATURDAY || date.isHolidays + } + + override fun getStartDate(): Calendar { + val startYear = if (now.monthValue <= 6) now.year - 1 else now.year + val startOfSchoolYear = now.withYear(startYear).firstSchoolDay + + val calendar = Calendar.getInstance() + calendar.set(startOfSchoolYear.year, startOfSchoolYear.monthValue - 1, startOfSchoolYear.dayOfMonth) + return calendar + } + + override fun getEndDate(): Calendar { + val endYear = if (now.monthValue > 6) now.year + 1 else now.year + val endOfSchoolYear = now.withYear(endYear).lastSchoolDay + + val calendar = Calendar.getInstance() + calendar.set(endOfSchoolYear.year, endOfSchoolYear.monthValue - 1, endOfSchoolYear.dayOfMonth) + return calendar + } + + override fun writeToParcel(parcel: Parcel, flags: Int) {} + + override fun describeContents() = 0 + + companion object CREATOR : Parcelable.Creator { + + override fun createFromParcel(parcel: Parcel): SchooldaysRangeLimiter = SchooldaysRangeLimiter() + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } +} diff --git a/app/src/main/play/listings/pl-PL/full-description.txt b/app/src/main/play/listings/pl-PL/full-description.txt index d33cb56f..641b7706 100644 --- a/app/src/main/play/listings/pl-PL/full-description.txt +++ b/app/src/main/play/listings/pl-PL/full-description.txt @@ -1,4 +1,4 @@ -Aplikacja jest we wczesnej fazie rozwoju, ciągle pracujemy nad kolejnymi funkcjami. +Aplikacja jest przeznaczona dla użytkowników dziennika VULCAN UONET+. Wyróżnione cechy i funkcje: - obliczanie średniej ważonej, diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/1-grades.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/1-grades.png new file mode 100644 index 00000000..56768bf5 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/1-grades.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/2-timetable-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/2-timetable-dialog.png new file mode 100644 index 00000000..83d9353a Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/2-timetable-dialog.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/3-exams.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/3-exams.png new file mode 100644 index 00000000..f83a7af8 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/3-exams.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/4-timetable-widget.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/4-timetable-widget.png new file mode 100644 index 00000000..90d5fef4 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/4-timetable-widget.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/5-messages.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/5-messages.png new file mode 100644 index 00000000..eadbf2db Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/5-messages.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/6-account-switcher.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/6-account-switcher.png new file mode 100644 index 00000000..2a11a3db Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/6-account-switcher.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/7-class-grades.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/7-class-grades.png new file mode 100644 index 00000000..d9b2f1c7 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/7-class-grades.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png deleted file mode 100644 index 8a52d8de..00000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png deleted file mode 100644 index c6508574..00000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png deleted file mode 100644 index 376fc3c3..00000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png deleted file mode 100644 index 6b4089b0..00000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png deleted file mode 100644 index c3dc523f..00000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png deleted file mode 100644 index 511433d4..00000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png deleted file mode 100644 index 1d502470..00000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png and /dev/null differ diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 68608c38..fe3993f6 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,9 +1,9 @@ -Wersja 0.12.0 +Wersja 0.13.0 -- wsparcie dla dziennika Lubelskiego Portalu Oświatowego -- naprawa problemów z wyświetlaniem ocen punktowych -- automatyczne przełączanie motywu na androidzie 10 -- wyświetlanie pomocy przy problemach z logowaniem -- wyświetlanie informacji o szkole +- naprawiony plan lekcji po ostatniej aktualizacji VULCANa (19.11) +- naprawiona opcja wyboru motywu dla widżetu ze szczęśliwym numerkiem +- nowa opcja zmiany języka i język rosyjski +- możliwość łatwego nawigowania między dniami w planie lekcji przez kalendarz +- opcja ustawienia modyfikatora oceny na wartość 0,0 Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable/ic_about_faq.xml b/app/src/main/res/drawable/ic_about_faq.xml new file mode 100644 index 00000000..d6ab255b --- /dev/null +++ b/app/src/main/res/drawable/ic_about_faq.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_error.xml b/app/src/main/res/drawable/ic_error.xml new file mode 100644 index 00000000..bb4cb302 --- /dev/null +++ b/app/src/main/res/drawable/ic_error.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_attendance.xml b/app/src/main/res/layout/fragment_attendance.xml index 4877103e..daf7e67f 100644 --- a/app/src/main/res/layout/fragment_attendance.xml +++ b/app/src/main/res/layout/fragment_attendance.xml @@ -2,7 +2,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:context=".ui.modules.attendance.AttendanceFragment"> + android:layout_height="match_parent" + tools:listitem="@layout/item_attendance" /> + + + + + + + + + + + + + + + app:srcCompat="@drawable/ic_chevron_left" /> + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_attendance_summary.xml b/app/src/main/res/layout/fragment_attendance_summary.xml index 35bc0960..da0ce7f2 100644 --- a/app/src/main/res/layout/fragment_attendance_summary.xml +++ b/app/src/main/res/layout/fragment_attendance_summary.xml @@ -82,4 +82,55 @@ android:text="@string/attendance_no_items" android:textSize="20sp" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_exam.xml b/app/src/main/res/layout/fragment_exam.xml index adfbee32..1e1b0c99 100644 --- a/app/src/main/res/layout/fragment_exam.xml +++ b/app/src/main/res/layout/fragment_exam.xml @@ -2,7 +2,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:context=".ui.modules.exam.ExamFragment"> + android:layout_height="match_parent" + tools:listitem="@layout/header_exam" /> + + + + + + + + + + + + + + + app:srcCompat="@drawable/ic_chevron_left" /> + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_grade.xml b/app/src/main/res/layout/fragment_grade.xml index d134ec17..0bc864b2 100644 --- a/app/src/main/res/layout/fragment_grade.xml +++ b/app/src/main/res/layout/fragment_grade.xml @@ -13,23 +13,17 @@ android:visibility="invisible" app:tabMode="scrollable" app:tabSelectedTextColor="?colorPrimary" - app:tabTextColor="@color/mtrl_on_surface_emphasis_medium" + app:tabTextColor="@color/material_on_surface_emphasis_medium" tools:ignore="UnusedAttribute" tools:visibility="visible" /> - - - - + android:layout_marginTop="48dp" + android:visibility="invisible" + tools:visibility="visible" /> + + + + + + + diff --git a/app/src/main/res/layout/fragment_grade_details.xml b/app/src/main/res/layout/fragment_grade_details.xml index 125d43a9..a1faefe7 100644 --- a/app/src/main/res/layout/fragment_grade_details.xml +++ b/app/src/main/res/layout/fragment_grade_details.xml @@ -13,7 +13,8 @@ + android:layout_height="match_parent" + tools:listitem="@layout/item_grade_details" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_grade_statistics.xml b/app/src/main/res/layout/fragment_grade_statistics.xml index 6e88f8a8..10c8bb19 100644 --- a/app/src/main/res/layout/fragment_grade_statistics.xml +++ b/app/src/main/res/layout/fragment_grade_statistics.xml @@ -47,39 +47,45 @@ android:layout_height="wrap_content" android:orientation="vertical"> - + android:layout_gravity="center"> - + android:orientation="horizontal" + android:paddingStart="16dp" + android:paddingTop="5dp" + android:paddingEnd="16dp" + android:visibility="invisible" + tools:visibility="visible"> - + - - + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_grade_summary.xml b/app/src/main/res/layout/fragment_grade_summary.xml index 2d9e7dc6..3f21e61c 100644 --- a/app/src/main/res/layout/fragment_grade_summary.xml +++ b/app/src/main/res/layout/fragment_grade_summary.xml @@ -13,7 +13,8 @@ + android:layout_height="match_parent" + tools:listitem="@layout/item_grade_summary" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_homework.xml b/app/src/main/res/layout/fragment_homework.xml index 799d3f42..0fcde9c0 100644 --- a/app/src/main/res/layout/fragment_homework.xml +++ b/app/src/main/res/layout/fragment_homework.xml @@ -2,7 +2,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:context=".ui.modules.homework.HomeworkFragment"> + android:layout_height="match_parent" + tools:listitem="@layout/item_homework" /> + + + + + + + + + + + + + + + app:srcCompat="@drawable/ic_chevron_left" /> + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 29943515..9f7f01e0 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -2,7 +2,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:context=".ui.modules.login.form.LoginFormFragment"> + android:text="@string/about_faq" + app:icon="@drawable/ic_about_faq" /> + android:layout_height="match_parent" + tools:context=".ui.modules.login.symbol.LoginSymbolFragment"> + android:layout_marginRight="16dp" + android:orientation="horizontal"> + + android:layout_marginLeft="8dp" + android:layout_weight="1" + android:text="@string/about_faq" + app:icon="@drawable/ic_about_faq" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_message.xml b/app/src/main/res/layout/fragment_message.xml index d396276f..b4cc0c2f 100644 --- a/app/src/main/res/layout/fragment_message.xml +++ b/app/src/main/res/layout/fragment_message.xml @@ -16,7 +16,7 @@ app:tabMaxWidth="0dp" app:tabMode="fixed" app:tabSelectedTextColor="?colorPrimary" - app:tabTextColor="@color/mtrl_on_surface_emphasis_medium" + app:tabTextColor="@color/material_on_surface_emphasis_medium" tools:ignore="UnusedAttribute" tools:visibility="visible" /> @@ -35,8 +35,8 @@ android:layout_margin="16dp" android:clickable="true" android:focusable="true" - android:tint="?colorOnSecondary" android:text="@string/send_message_title" + android:tint="?colorOnSecondary" app:icon="@drawable/ic_menu_message_write" /> + + tools:ignore="UseCompoundDrawables" + tools:visibility="invisible"> + + + + + + + + android:layout_height="match_parent" + tools:listitem="@layout/item_message" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_mobile_device.xml b/app/src/main/res/layout/fragment_mobile_device.xml index b78dc067..cf11c283 100644 --- a/app/src/main/res/layout/fragment_mobile_device.xml +++ b/app/src/main/res/layout/fragment_mobile_device.xml @@ -14,6 +14,18 @@ android:indeterminate="true" tools:visibility="invisible" /> + + + + + - + android:layout_height="match_parent" + android:gravity="center" + android:orientation="vertical" + android:visibility="invisible" + tools:ignore="UseCompoundDrawables" + tools:visibility="invisible"> - + + + + - + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:gravity="center" + android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_school.xml b/app/src/main/res/layout/fragment_school.xml index 2570251b..a7e9a213 100644 --- a/app/src/main/res/layout/fragment_school.xml +++ b/app/src/main/res/layout/fragment_school.xml @@ -229,7 +229,58 @@ android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" - android:text="@string/teacher_no_items" + android:text="@string/school_no_info" android:textSize="20sp" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_schoolandteachers.xml b/app/src/main/res/layout/fragment_schoolandteachers.xml index 2b3f2203..6414abb9 100644 --- a/app/src/main/res/layout/fragment_schoolandteachers.xml +++ b/app/src/main/res/layout/fragment_schoolandteachers.xml @@ -16,7 +16,7 @@ app:tabMaxWidth="0dp" app:tabMode="fixed" app:tabSelectedTextColor="?colorPrimary" - app:tabTextColor="@color/mtrl_on_surface_emphasis_medium" + app:tabTextColor="@color/material_on_surface_emphasis_medium" tools:ignore="UnusedAttribute" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/fragment_teacher.xml b/app/src/main/res/layout/fragment_teacher.xml index 01cf8726..484bf065 100644 --- a/app/src/main/res/layout/fragment_teacher.xml +++ b/app/src/main/res/layout/fragment_teacher.xml @@ -49,4 +49,55 @@ android:text="@string/teacher_no_items" android:textSize="20sp" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_timetable.xml b/app/src/main/res/layout/fragment_timetable.xml index db5c6fc2..98ccabbf 100644 --- a/app/src/main/res/layout/fragment_timetable.xml +++ b/app/src/main/res/layout/fragment_timetable.xml @@ -2,7 +2,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:context=".ui.modules.timetable.TimetableFragment"> + android:layout_height="match_parent" + tools:listitem="@layout/item_timetable" /> + + + + + + + + + + + + + + + + app:srcCompat="@drawable/ic_chevron_left" /> + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_timetable_completed.xml b/app/src/main/res/layout/fragment_timetable_completed.xml index 7c093a4a..11067044 100644 --- a/app/src/main/res/layout/fragment_timetable_completed.xml +++ b/app/src/main/res/layout/fragment_timetable_completed.xml @@ -57,6 +57,57 @@ android:text="@string/completed_lessons_no_items" android:textSize="20sp" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_homework.xml b/app/src/main/res/layout/item_homework.xml index 5d03ed58..68169039 100644 --- a/app/src/main/res/layout/item_homework.xml +++ b/app/src/main/res/layout/item_homework.xml @@ -32,7 +32,7 @@ android:layout_toRightOf="@id/homeworkItemSubject" android:gravity="end" android:textSize="13sp" - tools:text="@tools:sample/lorem" /> + tools:text="@tools:sample/full_names" /> + tools:text="@tools:sample/lorem" /> diff --git a/app/src/main/res/menu/action_menu_attendance.xml b/app/src/main/res/menu/action_menu_attendance.xml index 86c2fed7..bb20c8ec 100644 --- a/app/src/main/res/menu/action_menu_attendance.xml +++ b/app/src/main/res/menu/action_menu_attendance.xml @@ -6,6 +6,6 @@ android:icon="@drawable/ic_menu_attendance_summary" android:orderInCategory="1" android:title="@string/attendance_summary_button" - app:iconTint="@color/mtrl_on_surface_emphasis_medium" + app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/action_menu_grade.xml b/app/src/main/res/menu/action_menu_grade.xml index 4438309d..6357cbb5 100644 --- a/app/src/main/res/menu/action_menu_grade.xml +++ b/app/src/main/res/menu/action_menu_grade.xml @@ -6,6 +6,6 @@ android:icon="@drawable/ic_menu_grade_semester" android:orderInCategory="1" android:title="@string/grade_switch_semester" - app:iconTint="@color/mtrl_on_surface_emphasis_medium" + app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/action_menu_grade_details.xml b/app/src/main/res/menu/action_menu_grade_details.xml index fbd28eec..62516cbd 100644 --- a/app/src/main/res/menu/action_menu_grade_details.xml +++ b/app/src/main/res/menu/action_menu_grade_details.xml @@ -6,6 +6,6 @@ android:enabled="false" android:orderInCategory="100" android:title="@string/grade_menu_read" - app:iconTint="@color/mtrl_on_surface_emphasis_medium" + app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="never" /> diff --git a/app/src/main/res/menu/action_menu_main.xml b/app/src/main/res/menu/action_menu_main.xml index 78b4ee4a..72bea25a 100644 --- a/app/src/main/res/menu/action_menu_main.xml +++ b/app/src/main/res/menu/action_menu_main.xml @@ -6,6 +6,6 @@ android:icon="@drawable/ic_all_account" android:orderInCategory="2" android:title="@string/main_account_picker" - app:iconTint="@color/mtrl_on_surface_emphasis_medium" + app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="always" /> diff --git a/app/src/main/res/menu/action_menu_message_preview.xml b/app/src/main/res/menu/action_menu_message_preview.xml index f7979780..dfc12e23 100644 --- a/app/src/main/res/menu/action_menu_message_preview.xml +++ b/app/src/main/res/menu/action_menu_message_preview.xml @@ -6,20 +6,20 @@ android:icon="@drawable/ic_menu_message_reply" android:orderInCategory="1" android:title="@string/message_reply" - app:iconTint="@color/mtrl_on_surface_emphasis_medium" + app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/action_menu_send_message.xml b/app/src/main/res/menu/action_menu_send_message.xml index 491f74f9..4b43764a 100644 --- a/app/src/main/res/menu/action_menu_send_message.xml +++ b/app/src/main/res/menu/action_menu_send_message.xml @@ -6,6 +6,6 @@ android:icon="@drawable/ic_menu_message_send" android:orderInCategory="1" android:title="@string/send_message_title" - app:iconTint="@color/mtrl_on_surface_emphasis_medium" + app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/menu/action_menu_timetable.xml b/app/src/main/res/menu/action_menu_timetable.xml index cfbdb626..c3d0222c 100644 --- a/app/src/main/res/menu/action_menu_timetable.xml +++ b/app/src/main/res/menu/action_menu_timetable.xml @@ -6,6 +6,6 @@ android:icon="@drawable/ic_menu_timetable_lessons_completed" android:orderInCategory="1" android:title="@string/completed_lessons_button" - app:iconTint="@color/mtrl_on_surface_emphasis_medium" + app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="ifRoom" /> diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 3e4a2466..49264538 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -10,6 +10,7 @@ ?android:textColorPrimary @android:color/black false + true + +