1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 01:49:09 -05:00

Merge branch 'release/0.13.0'

This commit is contained in:
Mikołaj Pich 2019-12-07 23:04:26 +01:00
commit ff1e794820
177 changed files with 2615 additions and 557 deletions

View File

@ -14,7 +14,7 @@ cache:
branches:
only:
- develop
- 0.12.0
- 0.13.0
android:
licenses:

View File

@ -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'

View File

@ -1,3 +0,0 @@
<resources>
<string name="app_name">Wulkanowy DEV</string>
</resources>

View File

@ -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

View File

@ -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) }
}

View File

@ -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<Attendance>): List<Long>
@Delete
fun deleteAll(exams: List<Attendance>)
interface AttendanceDao : BaseDao<Attendance> {
@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<List<Attendance>>

View File

@ -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<AttendanceSummary>): List<Long>
@Delete
fun deleteAll(exams: List<AttendanceSummary>)
interface AttendanceSummaryDao : BaseDao<AttendanceSummary> {
@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<List<AttendanceSummary>>

View File

@ -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<T> {
@Insert
fun insertAll(items: List<T>): List<Long>
@Update
fun updateAll(items: List<T>)
@Delete
fun deleteAll(items: List<T>)
}

View File

@ -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<CompletedLesson>)
@Delete
fun deleteAll(exams: List<CompletedLesson>)
interface CompletedLessonsDao : BaseDao<CompletedLesson> {
@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<List<CompletedLesson>>

View File

@ -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<Exam>): List<Long>
@Delete
fun deleteAll(exams: List<Exam>)
interface ExamDao : BaseDao<Exam> {
@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<List<Exam>>

View File

@ -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<Grade>)
@Update
fun updateAll(grade: List<Grade>)
@Delete
fun deleteAll(grades: List<Grade>)
interface GradeDao : BaseDao<Grade> {
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<Grade>>

View File

@ -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<GradePointsStatistics>)
@Delete
fun deleteAll(gradesStatistics: List<GradePointsStatistics>)
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe<GradePointsStatistics>

View File

@ -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<GradeStatistics>)
@Delete
fun deleteAll(gradesStatistics: List<GradeStatistics>)
interface GradeStatisticsDao : BaseDao<GradeStatistics> {
@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<List<GradeStatistics>>

View File

@ -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<GradeSummary>)
@Delete
fun deleteAll(gradesSummary: List<GradeSummary>)
interface GradeSummaryDao : BaseDao<GradeSummary> {
@Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>>

View File

@ -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<Homework>)
@Delete
fun deleteAll(homework: List<Homework>)
interface HomeworkDao : BaseDao<Homework> {
@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<List<Homework>>

View File

@ -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<LuckyNumber> {
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
fun load(studentId: Int, date: LocalDate): Maybe<LuckyNumber>
}

View File

@ -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<Message>)
@Delete
fun deleteAll(messages: List<Message>)
@Update
fun updateAll(messages: List<Message>)
interface MessagesDao : BaseDao<Message> {
@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<List<Message>>

View File

@ -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<MobileDevice>)
@Delete
fun deleteAll(devices: List<MobileDevice>)
interface MobileDeviceDao : BaseDao<MobileDevice> {
@Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC")
fun loadAll(studentId: Int): Maybe<List<MobileDevice>>

View File

@ -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<Note>)
@Update
fun updateAll(notes: List<Note>)
@Delete
fun deleteAll(notes: List<Note>)
interface NoteDao : BaseDao<Note> {
@Query("SELECT * FROM Notes WHERE student_id = :studentId")
fun loadAll(studentId: Int): Maybe<List<Note>>
}

View File

@ -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<Recipient>)
@Delete
fun deleteAll(messages: List<Recipient>)
interface RecipientDao : BaseDao<Recipient> {
@Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId")
fun load(studentId: Int, role: Int, unitId: Int): Maybe<List<Recipient>>

View File

@ -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<ReportingUnit>)
@Delete
fun deleteAll(reportingUnits: List<ReportingUnit>)
interface ReportingUnitDao : BaseDao<ReportingUnit> {
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId")
fun load(studentId: Int): Maybe<List<ReportingUnit>>

View File

@ -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<School> {
@Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId")
fun load(studentId: Int, classId: Int): Maybe<School>

View File

@ -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<Semester>)
@Delete
fun deleteAll(semester: List<Semester>)
interface SemesterDao : BaseDao<Semester> {
@Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId")
fun loadAll(studentId: Int, classId: Int): Maybe<List<Semester>>

View File

@ -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<Subject>): List<Long>
@Delete
fun deleteAll(subjects: List<Subject>)
interface SubjectDao : BaseDao<Subject> {
@Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId")
fun loadAll(diaryId: Int, studentId: Int): Maybe<List<Subject>>

View File

@ -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<Teacher>)
@Delete
fun deleteAll(teachers: List<Teacher>)
interface TeacherDao : BaseDao<Teacher> {
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId")
fun loadAll(studentId: Int, classId: Int): Maybe<List<Teacher>>

View File

@ -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<Timetable>): List<Long>
@Delete
fun deleteAll(exams: List<Timetable>)
interface TimetableDao : BaseDao<Timetable> {
@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<List<Timetable>>

View File

@ -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<LuckyNumber> {

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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<Recipient>) {
fun saveRecipients(recipients: List<Recipient>): List<Long> {
return recipientDb.insertAll(recipients)
}

View File

@ -18,7 +18,7 @@ class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: Report
return reportingUnitDb.loadOne(student.studentId, unitId)
}
fun saveReportingUnits(reportingUnits: List<ReportingUnit>) {
fun saveReportingUnits(reportingUnits: List<ReportingUnit>): List<Long> {
return reportingUnitDb.insertAll(reportingUnits)
}

View File

@ -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<School> {

View File

@ -24,7 +24,7 @@ class StudentLocal @Inject constructor(
fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {
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<Student> {

View File

@ -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()
}

View File

@ -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()

View File

@ -22,7 +22,8 @@ import io.github.wulkanowy.utils.FragmentLifecycleLogger
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView, HasAndroidInjector {
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView,
HasAndroidInjector {
@Inject
lateinit var androidInjector: DispatchingAndroidInjector<Any>
@ -53,13 +54,15 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>> : 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()

View File

@ -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())
}
}

View File

@ -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()

View File

@ -9,4 +9,6 @@ interface BaseView {
fun showExpiredDialog()
fun openClearLoginView()
fun showErrorDetailsDialog(error: Throwable)
}

View File

@ -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<String, String, Drawable?>?
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<String, String, Drawable?>?
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())
}

View File

@ -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) },

View File

@ -9,6 +9,8 @@ interface AboutView : BaseView {
val feedbackRes: Triple<String, String, Drawable?>?
val faqRes: Triple<String, String, Drawable?>?
val discordRes: Triple<String, String, Drawable?>?
val homepageRes: Triple<String, String, Drawable?>?
@ -25,6 +27,8 @@ interface AboutView : BaseView {
fun openEmailClient()
fun openFaqPage()
fun openHomepage()
fun openLicenses()

View File

@ -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()
}
}

View File

@ -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())
}

View File

@ -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)

View File

@ -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()

View File

@ -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
}

View File

@ -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()

View File

@ -18,6 +18,10 @@ interface AttendanceSummaryView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun updateDataSet(data: List<AttendanceSummaryItem>, header: AttendanceSummaryScrollableHeader)
fun updateSubjects(data: ArrayList<String>)

View File

@ -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
}

View File

@ -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<LocalDate, List<Exam>>): List<ExamItem> {
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()
}

View File

@ -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)

View File

@ -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),

View File

@ -25,14 +25,14 @@ class GradePresenter @Inject constructor(
private val loadedSemesterId = mutableMapOf<Int, Int>()
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) {

View File

@ -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)

View File

@ -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
}

View File

@ -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<String, List<Grade>>, averages: Map<String, Double>): List<GradeDetailsHeader> {
val isGradeExpandable = preferencesRepository.isGradeExpandable
val gradeColorTheme = preferencesRepository.gradeColorTheme

View File

@ -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)

View File

@ -111,9 +111,11 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
setOnItemSelectedListener<TextView> { 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<String>) {
@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)

View File

@ -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<GradeSummaryItem>, 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
}

View File

@ -25,9 +25,12 @@ class GradeSummaryPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<GradeSummaryView>(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()

View File

@ -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)

View File

@ -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) {

View File

@ -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<LocalDate, List<Homework>>): List<HomeworkItem> {
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()
}

View File

@ -5,6 +5,8 @@ import io.github.wulkanowy.ui.base.BaseView
interface HomeworkView : BaseView {
val isViewEmpty: Boolean
fun initView()
fun updateData(data: List<HomeworkItem>)
@ -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)

View File

@ -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),

View File

@ -91,8 +91,8 @@ class LoginFormPresenter @Inject constructor(
}))
}
fun onDiscordClick() {
view?.openDiscordInvite()
fun onFaqClick() {
view?.openFaqPage()
}
fun onEmailClick() {

View File

@ -45,7 +45,7 @@ interface LoginFormView : BaseView {
fun showContact(show: Boolean)
fun openDiscordInvite()
fun openFaqPage()
fun openEmail()
}

View File

@ -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),

View File

@ -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),

View File

@ -89,8 +89,8 @@ class LoginSymbolPresenter @Inject constructor(
}
}
fun onDiscordClick() {
view?.openDiscordInvite()
fun onFaqClick() {
view?.openFaqPage()
}
fun onEmailClick() {

View File

@ -29,7 +29,7 @@ interface LoginSymbolView : BaseView {
fun showContact(show: Boolean)
fun openDiscordInvite()
fun openFaqPage()
fun openEmail()
}

View File

@ -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() {

View File

@ -19,6 +19,8 @@ class LuckyNumberPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LuckyNumberView>(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)
}
}

View File

@ -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
}

View File

@ -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) }))

View File

@ -100,7 +100,7 @@ class MainActivity : BaseActivity<MainPresenter>(), 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<MainPresenter>(), 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

View File

@ -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?) {

View File

@ -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

View File

@ -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)
}

View File

@ -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<MessageItem>) {
@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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
}

View File

@ -20,10 +20,13 @@ class MobileDevicePresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MobileDeviceView>(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()
}

View File

@ -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()

View File

@ -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<ClipboardManager>()?.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<ClipboardManager>()?.setPrimaryClip(clip)
Toast.makeText(context, R.string.all_copied, Toast.LENGTH_LONG).show()
}
}

View File

@ -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
}

View File

@ -21,10 +21,13 @@ class NotePresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<NoteView>(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}")

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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)

View File

@ -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<TeacherItem>) {
@ -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() {

View File

@ -19,10 +19,13 @@ class TeacherPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<TeacherView>(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)
}
}
}

View File

@ -27,4 +27,8 @@ interface TeacherView : BaseView, SchoolAndTeachersChildView {
fun showContent(show: Boolean)
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
}

View File

@ -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<Preference>(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)

Some files were not shown because too many files have changed in this diff Show More