diff --git a/.gitignore b/.gitignore
index cd5ff714..921bd0a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -119,3 +119,4 @@ Thumbs.db
app/src/release/agconnect-services.json
app/src/release/agconnect-credentials.json
.idea/deploymentTargetDropDown.xml
+.idea/kotlinc.xml
diff --git a/app/build.gradle b/app/build.gradle
index 255e0098..bcca5587 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 33
- versionCode 121
- versionName "1.9.2"
+ versionCode 122
+ versionName "2.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -177,36 +177,36 @@ huaweiPublish {
}
ext {
- work_manager = "2.7.1"
+ work_manager = "2.8.1"
android_hilt = "1.0.0"
- room = "2.4.3"
+ room = "2.5.1"
chucker = "3.5.2"
- mockk = "1.13.3"
+ mockk = "1.13.5"
coroutines = "1.6.4"
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.9.2"
+ implementation "io.github.wulkanowy:sdk:2.0.0"
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
- implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
- implementation "androidx.core:core-ktx:1.9.0"
- implementation 'androidx.core:core-splashscreen:1.0.0'
- implementation "androidx.activity:activity-ktx:1.6.1"
- implementation "androidx.appcompat:appcompat:1.5.1"
- implementation "androidx.fragment:fragment-ktx:1.5.5"
- implementation "androidx.annotation:annotation:1.5.0"
+ implementation "androidx.core:core-ktx:1.10.0"
+ implementation 'androidx.core:core-splashscreen:1.0.1'
+ implementation "androidx.activity:activity-ktx:1.7.1"
+ implementation "androidx.appcompat:appcompat:1.6.1"
+ implementation "androidx.fragment:fragment-ktx:1.5.7"
+ implementation "androidx.annotation:annotation:1.6.0"
implementation "androidx.preference:preference-ktx:1.2.0"
- implementation "androidx.recyclerview:recyclerview:1.2.1"
+ implementation "androidx.recyclerview:recyclerview:1.3.0"
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
- implementation "com.google.android.material:material:1.7.0"
+ implementation "com.google.android.material:material:1.8.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation 'com.github.lopspower:CircularImageView:4.3.0'
@@ -214,7 +214,7 @@ dependencies {
implementation "androidx.work:work-runtime-ktx:$work_manager"
playImplementation "androidx.work:work-gcm:$work_manager"
- implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-ktx:$room"
@@ -229,30 +229,30 @@ dependencies {
implementation "com.github.YarikSOffice:lingver:1.3.0"
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
- implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
- implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
+ implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
+ implementation "com.squareup.okhttp3:logging-interceptor:4.11.0"
implementation "com.jakewharton.timber:timber:5.0.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
- implementation "io.coil-kt:coil:2.2.2"
+ implementation "io.coil-kt:coil:2.3.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
- implementation 'com.fredporciuncula:flow-preferences:1.8.0'
+ implementation 'com.fredporciuncula:flow-preferences:1.9.1'
implementation 'org.apache.commons:commons-text:1.10.0'
- playImplementation platform('com.google.firebase:firebase-bom:31.1.1')
+ playImplementation platform('com.google.firebase:firebase-bom:31.5.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
playImplementation 'com.google.firebase:firebase-config-ktx'
playImplementation 'com.google.android.play:core:1.10.3'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
- playImplementation 'com.google.android.gms:play-services-ads:21.4.0'
+ playImplementation 'com.google.android.gms:play-services-ads:22.0.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.9.0.301'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.302'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.9.1.200'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.0.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
@@ -265,17 +265,17 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
- testImplementation 'org.robolectric:robolectric:4.9.2'
- testImplementation "androidx.test:runner:1.5.1"
- testImplementation "androidx.test.ext:junit:1.1.4"
+ testImplementation 'org.robolectric:robolectric:4.10'
+ testImplementation "androidx.test:runner:1.5.2"
+ testImplementation "androidx.test.ext:junit:1.1.5"
testImplementation "androidx.test:core:1.5.0"
testImplementation "androidx.room:room-testing:$room"
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
androidTestImplementation "androidx.test:core:1.5.0"
- androidTestImplementation "androidx.test:runner:1.5.1"
- androidTestImplementation "androidx.test.ext:junit:1.1.4"
+ androidTestImplementation "androidx.test:runner:1.5.2"
+ androidTestImplementation "androidx.test.ext:junit:1.1.5"
androidTestImplementation "io.mockk:mockk-android:$mockk"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
index b7b756b9..9c21d49d 100644
--- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3773093b..174c9a1f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -72,7 +72,7 @@
android:name=".ui.modules.message.send.SendMessageActivity"
android:configChanges="orientation|screenSize"
android:label="@string/send_message_title"
- android:theme="@style/WulkanowyTheme.MessageSend"
+ android:theme="@style/WulkanowyTheme.NoActionBar"
android:windowSoftInputMode="adjustResize" />
): List
@Delete
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt
index 50299e60..2292c3e6 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt
@@ -22,6 +22,7 @@ data class Exam(
val subject: String,
+ @Deprecated("not available anymore")
val group: String,
val type: String,
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt
index 17a9e5cd..add6439d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt
@@ -10,9 +10,9 @@ fun List.mapToEntities(semester: Semester) = map {
diaryId = semester.diaryId,
agenda = it.agenda,
conferenceId = it.id,
- date = it.dateZoned.toInstant(),
+ date = it.date.toInstant(),
presentOnConference = it.presentOnConference,
- subject = it.subject,
- title = it.title
+ subject = it.topic,
+ title = it.place,
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt
index bdb5efbb..173dfebf 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt
@@ -11,7 +11,7 @@ fun List.mapToEntities(semester: Semester) = map {
date = it.date,
entryDate = it.entryDate,
subject = it.subject,
- group = it.group,
+ group = "",
type = it.type,
description = it.description,
teacher = it.teacher,
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
index 6fc5dc95..a26d7665 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
@@ -26,7 +26,7 @@ fun List.mapToEntities(
messageId = it.id,
correspondents = it.correspondents,
subject = it.subject.trim(),
- date = it.dateZoned.toInstant(),
+ date = it.date.toInstant(),
folderId = it.folderId,
unread = it.unread,
unreadBy = it.unreadBy,
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
index 1a1c501f..1f4178fa 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
@@ -9,7 +9,7 @@ import io.github.wulkanowy.sdk.pojo.Token as SdkToken
fun List.mapToEntities(student: Student) = map {
MobileDevice(
userLoginId = student.userLoginId,
- date = it.createDateZoned.toInstant(),
+ date = it.createDate.toInstant(),
deviceId = it.id,
name = it.name
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt
index 2dfd7e06..bcf26a5e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt
@@ -3,22 +3,24 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.pojos.*
-import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.sdk.mapper.mapSemesters
import java.time.Instant
-import io.github.wulkanowy.sdk.scrapper.register.RegisterStudent as SdkRegisterStudent
-import io.github.wulkanowy.sdk.scrapper.register.RegisterUser as SdkRegisterUser
+import io.github.wulkanowy.sdk.pojo.RegisterStudent as SdkRegisterStudent
+import io.github.wulkanowy.sdk.pojo.RegisterUser as SdkRegisterUser
-fun SdkRegisterUser.mapToPojo(password: String) = RegisterUser(
+fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser(
email = email,
login = login,
password = password,
- baseUrl = baseUrl,
+ scrapperBaseUrl = scrapperBaseUrl,
+ loginMode = loginMode,
loginType = loginType,
symbols = symbols.map { registerSymbol ->
RegisterSymbol(
symbol = registerSymbol.symbol,
error = registerSymbol.error,
+ hebeBaseUrl = registerSymbol.hebeBaseUrl,
+ keyId = registerSymbol.keyId,
+ privatePem = registerSymbol.privatePem,
userName = registerSymbol.userName,
schools = registerSymbol.schools.map {
RegisterUnit(
@@ -42,14 +44,13 @@ fun SdkRegisterUser.mapToPojo(password: String) = RegisterUser(
classId = registerSubject.classId,
isParent = registerSubject.isParent,
semesters = registerSubject.semesters
- .mapSemesters()
.mapToEntities(registerSubject.studentId),
)
},
)
}
)
- }
+ },
)
fun RegisterStudent.mapToStudentWithSemesters(
@@ -68,17 +69,17 @@ fun RegisterStudent.mapToStudentWithSemesters(
classId = classId,
studentId = studentId,
symbol = symbol.symbol,
- loginType = user.loginType.name,
+ loginType = user.loginType?.name.orEmpty(),
schoolName = unit.schoolName,
schoolShortName = unit.schoolShortName,
schoolSymbol = unit.schoolId,
studentName = "$studentName $studentSurname",
- loginMode = Sdk.Mode.SCRAPPER.name,
- scrapperBaseUrl = user.baseUrl,
- mobileBaseUrl = "",
- certificateKey = "",
- privateKey = "",
- password = user.password,
+ loginMode = user.loginMode.name,
+ scrapperBaseUrl = user.scrapperBaseUrl.orEmpty(),
+ mobileBaseUrl = symbol.hebeBaseUrl.orEmpty(),
+ certificateKey = symbol.keyId.orEmpty(),
+ privateKey = symbol.privatePem.orEmpty(),
+ password = user.password.orEmpty(),
isCurrent = false,
registrationDate = Instant.now(),
).apply {
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt
deleted file mode 100644
index a2110d7f..00000000
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.github.wulkanowy.data.mappers
-
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.db.entities.StudentWithSemesters
-import java.time.Instant
-import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
-
-fun List.mapToEntities(password: String = "", colors: List) = map {
- StudentWithSemesters(
- student = Student(
- email = it.email,
- password = password,
- isParent = it.isParent,
- symbol = it.symbol,
- studentId = it.studentId,
- userLoginId = it.userLoginId,
- userName = it.userName,
- studentName = it.studentName + " " + it.studentSurname,
- schoolSymbol = it.schoolSymbol,
- schoolShortName = it.schoolShortName,
- schoolName = it.schoolName,
- className = it.className,
- classId = it.classId,
- scrapperBaseUrl = it.scrapperBaseUrl,
- loginType = it.loginType.name,
- isCurrent = false,
- registrationDate = Instant.now(),
- mobileBaseUrl = it.mobileBaseUrl,
- privateKey = it.privateKey,
- certificateKey = it.certificateKey,
- loginMode = it.loginMode.name,
- ).apply {
- avatarColor = colors.random()
- },
- semesters = it.semesters.mapToEntities(it.studentId)
- )
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt
index e55aa3cf..ee525e10 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt
@@ -5,10 +5,10 @@ import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.pojos.TimetableFull
-import io.github.wulkanowy.sdk.pojo.TimetableFull as SdkTimetableFull
+import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetableFull
import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader
-import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable
-import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional
+import io.github.wulkanowy.sdk.pojo.Lesson as SdkLesson
+import io.github.wulkanowy.sdk.pojo.LessonAdditional as SdkTimetableAdditional
fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
lessons = lessons.mapToEntities(semester),
@@ -16,13 +16,13 @@ fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
headers = headers.mapToEntities(semester)
)
-fun List.mapToEntities(semester: Semester) = map {
+fun List.mapToEntities(semester: Semester) = map {
Timetable(
studentId = semester.studentId,
diaryId = semester.diaryId,
number = it.number,
- start = it.startZoned.toInstant(),
- end = it.endZoned.toInstant(),
+ start = it.start.toInstant(),
+ end = it.end.toInstant(),
date = it.date,
subject = it.subject,
subjectOld = it.subjectOld,
@@ -45,8 +45,8 @@ fun List.mapToEntities(semester: Semester) = map {
diaryId = semester.diaryId,
subject = it.subject,
date = it.date,
- start = it.startZoned.toInstant(),
- end = it.endZoned.toInstant(),
+ start = it.start.toInstant(),
+ end = it.end.toInstant(),
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt
index 4aea3377..98bf1402 100644
--- a/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt
@@ -1,20 +1,25 @@
package io.github.wulkanowy.data.pojos
import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.scrapper.Scrapper
data class RegisterUser(
val email: String,
- val password: String,
+ val password: String?,
val login: String, // may be the same as email
- val baseUrl: String,
- val loginType: Scrapper.LoginType,
+ val scrapperBaseUrl: String?,
+ val loginType: Scrapper.LoginType?,
+ val loginMode: Sdk.Mode,
val symbols: List,
) : java.io.Serializable
data class RegisterSymbol(
val symbol: String,
val error: Throwable?,
+ val hebeBaseUrl: String?,
+ val keyId: String?,
+ val privatePem: String?,
val userName: String,
val schools: List,
) : java.io.Serializable
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt
index cbaa12bd..bec2797d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt
@@ -19,7 +19,6 @@ class AppCreatorRepository @Inject constructor(
) {
@OptIn(ExperimentalSerializationApi::class)
- @Suppress("BlockingMethodInNonBlockingContext")
suspend fun getAppCreators() = withContext(dispatchers.io) {
val inputStream = context.assets.open("contributors.json").buffered()
json.decodeFromStream>(inputStream)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
index fd5d8bd1..3afb9907 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
@@ -59,7 +59,7 @@ class AttendanceRepository @Inject constructor(
}
sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
- .getAttendance(start.monday, end.sunday, semester.semesterId)
+ .getAttendance(start.monday, end.sunday)
.mapToEntities(semester, lessons)
},
saveFetchResult = { old, new ->
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
index faa80b93..013c0951 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
@@ -52,7 +52,7 @@ class ExamRepository @Inject constructor(
fetch = {
sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
- .getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
+ .getExams(start.startExamsDay, start.endExamsDay)
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
index e5d7bc5c..4101803f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
@@ -42,7 +42,7 @@ class NoteRepository @Inject constructor(
fetch = {
sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
- .getNotes(semester.semesterId)
+ .getNotes()
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
index afc26286..348a4054 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
@@ -2,14 +2,17 @@ package io.github.wulkanowy.data.repositories
import android.content.Context
import android.content.SharedPreferences
+import androidx.annotation.StringRes
import androidx.core.content.edit
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
import com.fredporciuncula.flow.preferences.Preference
+import com.fredporciuncula.flow.preferences.Serializer
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.enums.*
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
+import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
@@ -28,29 +31,35 @@ class PreferencesRepository @Inject constructor(
private val json: Json,
) {
- val startMenuIndex: Int
- get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt()
-
val isShowPresent: Boolean
get() = getBoolean(
R.string.pref_key_attendance_present,
R.bool.pref_default_attendance_present
)
- val gradeAverageMode: GradeAverageMode
- get() = GradeAverageMode.getByValue(
- getString(
- R.string.pref_key_grade_average_mode,
- R.string.pref_default_grade_average_mode
- )
+ private val gradeAverageModePref: Preference
+ get() = getObjectFlow(
+ R.string.pref_key_grade_average_mode,
+ R.string.pref_default_grade_average_mode,
+ object : Serializer {
+ override fun serialize(value: GradeAverageMode) = value.value
+ override fun deserialize(serialized: String) =
+ GradeAverageMode.getByValue(serialized)
+ },
)
- val gradeAverageForceCalc: Boolean
- get() = getBoolean(
- R.string.pref_key_grade_average_force_calc,
- R.bool.pref_default_grade_average_force_calc
+ val gradeAverageModeFlow: Flow
+ get() = gradeAverageModePref.asFlow()
+
+ private val gradeAverageForceCalcPref: Preference
+ get() = flowSharedPref.getBoolean(
+ context.getString(R.string.pref_key_grade_average_force_calc),
+ context.resources.getBoolean(R.bool.pref_default_grade_average_force_calc)
)
+ val gradeAverageForceCalcFlow: Flow
+ get() = gradeAverageForceCalcPref.asFlow()
+
val gradeExpandMode: GradeExpandMode
get() = GradeExpandMode.getByValue(
getString(
@@ -140,12 +149,24 @@ class PreferencesRepository @Inject constructor(
R.string.pref_default_grade_modifier_plus
).toDouble()
+ val gradePlusModifierFlow: Flow
+ get() = getStringFlow(
+ R.string.pref_key_grade_modifier_plus,
+ R.string.pref_default_grade_modifier_plus
+ ).asFlow().map { it.toDouble() }
+
val gradeMinusModifier: Double
get() = getString(
R.string.pref_key_grade_modifier_minus,
R.string.pref_default_grade_modifier_minus
).toDouble()
+ val gradeMinusModifierFlow: Flow
+ get() = getStringFlow(
+ R.string.pref_key_grade_modifier_minus,
+ R.string.pref_default_grade_modifier_minus
+ ).asFlow().map { it.toDouble() }
+
val fillMessageContent: Boolean
get() = getBoolean(
R.string.pref_key_fill_message_content,
@@ -180,24 +201,17 @@ class PreferencesRepository @Inject constructor(
R.bool.pref_default_timetable_show_timers
)
- var isHomeworkFullscreen: Boolean
- get() = getBoolean(
- R.string.pref_key_homework_fullscreen,
- R.bool.pref_default_homework_fullscreen
- )
- set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply()
-
val showSubjectsWithoutGrades: Boolean
get() = getBoolean(
R.string.pref_key_subjects_without_grades,
R.bool.pref_default_subjects_without_grades
)
- val isOptionalArithmeticAverage: Boolean
- get() = getBoolean(
- R.string.pref_key_optional_arithmetic_average,
- R.bool.pref_default_optional_arithmetic_average
- )
+ val isOptionalArithmeticAverageFlow: Flow
+ get() = flowSharedPref.getBoolean(
+ context.getString(R.string.pref_key_optional_arithmetic_average),
+ context.resources.getBoolean(R.bool.pref_default_optional_arithmetic_average)
+ ).asFlow()
var lasSyncDate: Instant?
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
@@ -315,6 +329,20 @@ class PreferencesRepository @Inject constructor(
putBoolean(context.getString(R.string.pref_key_ads_enabled), value)
}
+ var appMenuItemOrder: List
+ get() {
+ val value = sharedPref.getString(PREF_KEY_APP_MENU_ITEM_ORDER, null)
+ ?: return AppMenuItem.defaultAppMenuItemList
+
+ return json.decodeFromString(value)
+ }
+ set(value) = sharedPref.edit {
+ putString(
+ PREF_KEY_APP_MENU_ITEM_ORDER,
+ json.encodeToString(value)
+ )
+ }
+
var installationId: String
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
@@ -330,6 +358,21 @@ class PreferencesRepository @Inject constructor(
private fun getLong(id: String, default: Int) =
sharedPref.getLong(id, context.resources.getString(default).toLong())
+ private fun getStringFlow(id: Int, default: Int) =
+ flowSharedPref.getString(context.getString(id), context.getString(default))
+
+ private fun getObjectFlow(
+ @StringRes id: Int,
+ @StringRes default: Int,
+ serializer: Serializer
+ ): Preference = flowSharedPref.getObject(
+ key = context.getString(id),
+ serializer = serializer,
+ defaultValue = serializer.deserialize(
+ flowSharedPref.getString(context.getString(default)).get()
+ )
+ )
+
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
private fun getString(id: String, default: Int) =
@@ -341,6 +384,7 @@ class PreferencesRepository @Inject constructor(
sharedPref.getBoolean(id, context.resources.getBoolean(default))
private companion object {
+ private const val PREF_KEY_APP_MENU_ITEM_ORDER = "app_menu_item_order"
private const val PREF_KEY_INSTALLATION_ID = "installation_id"
private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position"
private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count"
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
index 96f01922..92bb3708 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
@@ -40,7 +40,7 @@ class SemesterRepository @Inject constructor(
val isNoSemesters = semesters.isEmpty()
val isRefreshOnModeChangeRequired = when {
- Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> {
+ Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE -> {
semesters.firstOrNull { it.isCurrent }?.let {
0 == it.diaryId && 0 == it.kindergartenDiaryId
} == true
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
index b1d1ba83..4c7069ef 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
@@ -10,11 +10,9 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
-import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.mappers.mapToPojo
import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.security.decrypt
import io.github.wulkanowy.utils.security.encrypt
@@ -29,37 +27,35 @@ class StudentRepository @Inject constructor(
private val studentDb: StudentDao,
private val semesterDb: SemesterDao,
private val sdk: Sdk,
- private val appInfo: AppInfo,
private val appDatabase: AppDatabase
) {
- suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty()
-
suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false
suspend fun getStudentsApi(
pin: String,
symbol: String,
token: String
- ): List =
- sdk.getStudentsFromMobileApi(token, pin, symbol, "")
- .mapToEntities(colors = appInfo.defaultColorsForAvatar)
+ ): RegisterUser = sdk
+ .getStudentsFromHebe(token, pin, symbol, "")
+ .mapToPojo(null)
suspend fun getStudentsScrapper(
email: String,
password: String,
scrapperBaseUrl: String,
symbol: String
- ): List =
- sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol)
- .mapToEntities(password, appInfo.defaultColorsForAvatar)
+ ): RegisterUser = sdk
+ .getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
+ .mapToPojo(password)
suspend fun getUserSubjectsFromScrapper(
email: String,
password: String,
scrapperBaseUrl: String,
symbol: String
- ): RegisterUser = sdk.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
+ ): RegisterUser = sdk
+ .getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
.mapToPojo(password)
suspend fun getStudentsHybrid(
@@ -67,15 +63,15 @@ class StudentRepository @Inject constructor(
password: String,
scrapperBaseUrl: String,
symbol: String
- ): List =
- sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
- .mapToEntities(password, appInfo.defaultColorsForAvatar)
+ ): RegisterUser = sdk
+ .getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
+ .mapToPojo(password)
suspend fun getSavedStudents(decryptPass: Boolean = true) =
studentDb.loadStudentsWithSemesters()
.map {
it.apply {
- if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
+ if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) {
decrypt(student.password)
}
@@ -85,7 +81,7 @@ class StudentRepository @Inject constructor(
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) =
studentDb.loadStudentWithSemestersById(id)?.apply {
- if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
+ if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) {
decrypt(student.password)
}
@@ -95,7 +91,7 @@ class StudentRepository @Inject constructor(
suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student {
val student = studentDb.loadById(id) ?: throw NoCurrentStudentException()
- if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
+ if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) {
decrypt(student.password)
}
@@ -106,7 +102,7 @@ class StudentRepository @Inject constructor(
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
- if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
+ if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) {
decrypt(student.password)
}
@@ -119,7 +115,7 @@ class StudentRepository @Inject constructor(
val students = studentsWithSemesters.map { it.student }
.map {
it.apply {
- if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) {
+ if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.HEBE) {
password = withContext(dispatchers.io) {
encrypt(password, context)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
index acd71e1f..4e3b40f9 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
@@ -40,7 +40,7 @@ class TeacherRepository @Inject constructor(
fetch = {
sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
- .getTeachers(semester.semesterId)
+ .getTeachers()
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
index 3145c2a2..136fb8d5 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
@@ -13,6 +13,7 @@ import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.sync.Mutex
+import java.time.Instant
import java.time.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@@ -65,7 +66,7 @@ class TimetableRepository @Inject constructor(
fetch = {
val timetableFull = sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
- .getTimetableFull(start.monday, end.sunday)
+ .getTimetable(start.monday, end.sunday)
timetableFull.mapToEntities(semester)
},
@@ -164,6 +165,11 @@ class TimetableRepository @Inject constructor(
timetableHeaderDb.insertAll(new uniqueSubtract old)
}
+ fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {
+ val refreshKey = getRefreshKey(cacheKey, semester, start, end)
+ return refreshHelper.getLastRefreshTimestamp(refreshKey)
+ }
+
suspend fun saveAdditionalList(additionalList: List) =
timetableAdditionalDb.insertAll(additionalList)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt
index c1bed4dd..e0a136f9 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt
@@ -4,18 +4,12 @@ import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.O
import androidx.core.app.NotificationManagerCompat
import androidx.lifecycle.asFlow
+import androidx.work.*
import androidx.work.BackoffPolicy.EXPONENTIAL
-import androidx.work.Constraints
-import androidx.work.Data
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
-import androidx.work.ExistingPeriodicWorkPolicy.REPLACE
-import androidx.work.ExistingWorkPolicy
+import androidx.work.ExistingPeriodicWorkPolicy.UPDATE
import androidx.work.NetworkType.CONNECTED
import androidx.work.NetworkType.UNMETERED
-import androidx.work.OneTimeWorkRequestBuilder
-import androidx.work.PeriodicWorkRequestBuilder
-import androidx.work.WorkInfo
-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.PreferencesRepository
@@ -60,7 +54,7 @@ class SyncManager @Inject constructor(
val serviceInterval = preferencesRepository.servicesInterval
workManager.enqueueUniquePeriodicWork(
- SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP,
+ SyncWorker::class.java.simpleName, if (restart) UPDATE else KEEP,
PeriodicWorkRequestBuilder(serviceInterval, MINUTES)
.setInitialDelay(10, MINUTES)
.setBackoffCriteria(EXPONENTIAL, 30, MINUTES)
diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt
index 45cd2b04..d48556fa 100644
--- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt
@@ -4,7 +4,6 @@ import android.content.Intent
import android.widget.RemoteViewsService
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.SharedPrefProvider
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
@@ -24,14 +23,13 @@ class TimetableWidgetService : RemoteViewsService() {
@Inject
lateinit var semesterRepo: SemesterRepository
- @Inject
- lateinit var prefRepository: PreferencesRepository
-
@Inject
lateinit var sharedPref: SharedPrefProvider
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
Timber.d("TimetableWidgetFactory created")
- return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, applicationContext, intent)
+ return TimetableWidgetFactory(
+ timetableRepo, studentRepo, semesterRepo, sharedPref, applicationContext, intent
+ )
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
index 075557a5..7914df81 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
@@ -4,9 +4,9 @@ import android.app.ActivityManager
import android.os.Bundle
import android.view.View
import android.widget.Toast
-import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
import io.github.wulkanowy.R
@@ -30,6 +30,8 @@ abstract class BaseActivity, VB : ViewBinding> :
protected var messageContainer: View? = null
+ protected var messageAnchor: View? = null
+
abstract var presenter: T
override fun onCreate(savedInstanceState: Bundle?) {
@@ -48,6 +50,7 @@ abstract class BaseActivity, VB : ViewBinding> :
if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
.setAction(R.string.all_details) { showErrorDetailsDialog(error) }
+ .apply { messageAnchor?.let { anchorView = it } }
.show()
} else showMessage(text)
}
@@ -57,12 +60,15 @@ abstract class BaseActivity, VB : ViewBinding> :
}
override fun showMessage(text: String) {
- if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
- else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
+ if (messageContainer != null) {
+ Snackbar.make(messageContainer!!, text, LENGTH_LONG)
+ .apply { messageAnchor?.let { anchorView = it } }
+ .show()
+ } else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
}
override fun showExpiredDialog() {
- AlertDialog.Builder(this)
+ MaterialAlertDialogBuilder(this)
.setTitle(R.string.main_session_expired)
.setMessage(R.string.main_session_relogin)
.setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() }
@@ -74,6 +80,7 @@ abstract class BaseActivity, VB : ViewBinding> :
messageContainer?.let {
Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG)
.setAction(R.string.all_change) { openInternetBrowser(redirectUrl) }
+ .apply { messageAnchor?.let { anchorView = it } }
.show()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt
index 25a53395..561d181a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt
@@ -1,8 +1,14 @@
package io.github.wulkanowy.ui.base
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
import android.widget.Toast
+import androidx.annotation.CallSuper
import androidx.fragment.app.DialogFragment
import androidx.viewbinding.ViewBinding
+import com.google.android.material.elevation.SurfaceColors
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.lifecycleAwareVariable
import javax.inject.Inject
@@ -38,6 +44,19 @@ abstract class BaseDialogFragment : DialogFragment(), BaseView
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
+ @CallSuper
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ view.setBackgroundColor(SurfaceColors.SURFACE_3.getColor(requireContext()))
+ }
+
+ @CallSuper
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = binding.root
+
override fun onResume() {
super.onResume()
analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
index fe0e6469..679d904a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
@@ -4,13 +4,13 @@ import android.app.Dialog
import android.content.ClipData
import android.content.ClipboardManager
import android.os.Bundle
+import android.view.View
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.app.AlertDialog
import androidx.core.content.getSystemService
import androidx.core.os.bundleOf
import androidx.core.view.isGone
-import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@@ -20,7 +20,7 @@ import io.github.wulkanowy.utils.*
import javax.inject.Inject
@AndroidEntryPoint
-class ErrorDialog : DialogFragment() {
+class ErrorDialog : BaseDialogFragment() {
@Inject
lateinit var appInfo: AppInfo
@@ -28,6 +28,8 @@ class ErrorDialog : DialogFragment() {
@Inject
lateinit var preferencesRepository: PreferencesRepository
+ private lateinit var error: Throwable
+
companion object {
private const val ARGUMENT_KEY = "error"
@@ -36,32 +38,31 @@ class ErrorDialog : DialogFragment() {
}
}
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val error = requireArguments().serializable(ARGUMENT_KEY)
-
- val binding = DialogErrorBinding.inflate(layoutInflater)
- binding.bindErrorDetails(error)
-
- return getAlertDialog(binding, error).apply {
- enableReportButtonIfErrorIsReportable(error)
- }
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ error = requireArguments().serializable(ARGUMENT_KEY)
}
- private fun getAlertDialog(binding: DialogErrorBinding, error: Throwable): AlertDialog {
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return MaterialAlertDialogBuilder(requireContext()).apply {
val errorStacktrace = error.stackTraceToString()
setTitle(R.string.all_details)
- setView(binding.root)
+ setView(DialogErrorBinding.inflate(layoutInflater).apply { binding = this }.root)
setNeutralButton(R.string.about_feedback) { _, _ ->
openConfirmDialog { openEmailClient(errorStacktrace) }
}
setNegativeButton(android.R.string.cancel) { _, _ -> }
setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) }
- }.create()
+ }.create().apply {
+ setOnShowListener {
+ getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported()
+ }
+ }
}
- private fun DialogErrorBinding.bindErrorDetails(error: Throwable) {
- return with(this) {
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ with(binding) {
errorDialogHumanizedMessage.text = resources.getErrorString(error)
errorDialogErrorMessage.text = error.localizedMessage
errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank()
@@ -70,12 +71,6 @@ class ErrorDialog : DialogFragment() {
}
}
- private fun AlertDialog.enableReportButtonIfErrorIsReportable(error: Throwable) {
- setOnShowListener {
- getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported()
- }
- }
-
private fun copyErrorToClipboard(errorStacktrace: String) {
val clip = ClipData.newPlainText("Error details", errorStacktrace)
requireActivity().getSystemService()?.setPrimaryClip(clip)
@@ -83,7 +78,7 @@ class ErrorDialog : DialogFragment() {
}
private fun openConfirmDialog(callback: () -> Unit) {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.dialog_error_check_update)
.setMessage(R.string.dialog_error_check_update_message)
.setNeutralButton(R.string.about_feedback) { _, _ -> callback() }
@@ -113,8 +108,4 @@ class ErrorDialog : DialogFragment() {
}
)
}
-
- private fun showMessage(text: String) {
- Toast.makeText(requireContext(), text, LENGTH_LONG).show()
- }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt
index e1c23457..f42f315c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt
@@ -6,15 +6,14 @@ import android.content.pm.PackageManager.GET_ACTIVITIES
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
-import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
-import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
-import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
+import com.google.android.material.color.DynamicColors
import io.github.wulkanowy.R
import io.github.wulkanowy.data.enums.AppTheme
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.ui.modules.login.LoginActivity
+import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
import io.github.wulkanowy.ui.modules.main.MainActivity
-import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
+import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
import javax.inject.Inject
import javax.inject.Singleton
@@ -28,18 +27,19 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
when (activity) {
is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black)
is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black)
- is SendMessageActivity -> activity.setTheme(R.style.WulkanowyTheme_MessageSend_Black)
}
}
+ } else if (activity is TimetableWidgetConfigureActivity || activity is LuckyNumberWidgetConfigureActivity) {
+ DynamicColors.applyToActivityIfAvailable(activity)
}
}
fun applyDefaultTheme() {
AppCompatDelegate.setDefaultNightMode(
when (preferencesRepository.appTheme) {
- AppTheme.LIGHT -> MODE_NIGHT_NO
- AppTheme.DARK, AppTheme.BLACK -> MODE_NIGHT_YES
- AppTheme.SYSTEM -> MODE_NIGHT_FOLLOW_SYSTEM
+ AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
+ AppTheme.DARK, AppTheme.BLACK -> AppCompatDelegate.MODE_NIGHT_YES
+ AppTheme.SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
)
}
@@ -52,7 +52,6 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
.let {
it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar
|| it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black
- || it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_Black
}
@Suppress("DEPRECATION")
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
index 561419a0..f0969fac 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
@@ -9,11 +9,14 @@ import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
+import io.github.wulkanowy.ui.modules.luckynumber.history.LuckyNumberHistoryFragment
import io.github.wulkanowy.ui.modules.message.MessageFragment
+import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
-import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment
+import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
+import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import kotlinx.serialization.Serializable
import java.time.LocalDate
@@ -39,10 +42,13 @@ sealed class Destination {
NOTE(Note),
CONFERENCE(Conference),
SCHOOL_ANNOUNCEMENT(SchoolAnnouncement),
- SCHOOL(School),
- LUCKY_NUMBER(More),
+ SCHOOL_AND_TEACHERS(SchoolAndTeachers),
+ LUCKY_NUMBER(LuckyNumber),
+ LUCKY_NUMBER_HISTORY(LuckyNumberHistory),
MORE(More),
- MESSAGE(Message);
+ MESSAGE(Message),
+ MOBILE_DEVICE(MobileDevice),
+ SETTINGS(Settings);
}
@Serializable
@@ -103,9 +109,9 @@ sealed class Destination {
}
@Serializable
- object School : Destination() {
- override val destinationType get() = Type.SCHOOL
- override val destinationFragment get() = SchoolFragment.newInstance()
+ object SchoolAndTeachers : Destination() {
+ override val destinationType get() = Type.SCHOOL_AND_TEACHERS
+ override val destinationFragment get() = SchoolAndTeachersFragment.newInstance()
}
@Serializable
@@ -114,6 +120,12 @@ sealed class Destination {
override val destinationFragment get() = LuckyNumberFragment.newInstance()
}
+ @Serializable
+ object LuckyNumberHistory : Destination() {
+ override val destinationType get() = Type.LUCKY_NUMBER_HISTORY
+ override val destinationFragment get() = LuckyNumberHistoryFragment.newInstance()
+ }
+
@Serializable
object More : Destination() {
override val destinationType get() = Type.MORE
@@ -125,4 +137,16 @@ sealed class Destination {
override val destinationType get() = Type.MESSAGE
override val destinationFragment get() = MessageFragment.newInstance()
}
+
+ @Serializable
+ object MobileDevice : Destination() {
+ override val destinationType get() = Type.MOBILE_DEVICE
+ override val destinationFragment get() = MobileDeviceFragment.newInstance()
+ }
+
+ @Serializable
+ object Settings : Destination() {
+ override val destinationType get() = Type.SETTINGS
+ override val destinationFragment get() = SettingsFragment.newInstance()
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt
index 41b97b07..d6bc6154 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt
@@ -9,6 +9,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf
import androidx.core.view.get
import androidx.core.view.isVisible
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
@@ -114,7 +115,7 @@ class AccountDetailsFragment :
override fun showLogoutConfirmDialog() {
context?.let {
- AlertDialog.Builder(it)
+ MaterialAlertDialogBuilder(it)
.setTitle(R.string.account_logout_student)
.setMessage(R.string.account_confirm)
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt
index 6e2bc8c4..4229579c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt
@@ -1,11 +1,11 @@
package io.github.wulkanowy.ui.modules.account.accountedit
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.GridLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.databinding.DialogAccountEditBinding
@@ -31,16 +31,12 @@ class AccountEditDialog : BaseDialogFragment(), Accoun
}
}
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
- }
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogAccountEditBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt
index d23978f5..2d2dccec 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt
@@ -1,11 +1,11 @@
package io.github.wulkanowy.ui.modules.account.accountquick
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.DialogAccountQuickBinding
@@ -36,19 +36,17 @@ class AccountQuickDialog : BaseDialogFragment(), Acco
}
}
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(
+ DialogAccountQuickBinding.inflate(layoutInflater)
+ .apply { binding = this }.root
+ )
+ .create()
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root
-
- @Suppress("UNCHECKED_CAST")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
val studentsWithSemesters = requireArguments()
.serializable>(STUDENTS_ARGUMENT_KEY).toList()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt
index eab24f91..c0026bee 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt
@@ -1,21 +1,20 @@
package io.github.wulkanowy.ui.modules.attendance
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.os.bundleOf
-import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.databinding.DialogAttendanceBinding
+import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.descriptionRes
-import io.github.wulkanowy.utils.lifecycleAwareVariable
import io.github.wulkanowy.utils.serializable
import io.github.wulkanowy.utils.toFormattedString
-class AttendanceDialog : DialogFragment() {
-
- private var binding: DialogAttendanceBinding by lifecycleAwareVariable()
+@AndroidEntryPoint
+class AttendanceDialog : BaseDialogFragment() {
private lateinit var attendance: Attendance
@@ -30,15 +29,14 @@ class AttendanceDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
attendance = requireArguments().serializable(ARGUMENT_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogAttendanceBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt
index 21f30b04..a73c2606 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt
@@ -4,10 +4,10 @@ import android.content.DialogInterface.BUTTON_POSITIVE
import android.os.Bundle
import android.view.*
import android.view.View.*
-import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance
@@ -124,7 +124,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag
attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() }
- attendanceNavContainer.elevation = requireContext().dpToPx(8f)
+ attendanceNavContainer.elevation = requireContext().dpToPx(3f)
}
}
@@ -228,7 +228,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag
override fun showExcuseDialog() {
val dialogBinding = DialogExcuseBinding.inflate(LayoutInflater.from(context))
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.attendance_excuse_title)
.setView(dialogBinding.root)
.setNegativeButton(android.R.string.cancel) { _, _ -> }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt
index 7834b6e8..c532377e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt
@@ -1,21 +1,20 @@
package io.github.wulkanowy.ui.modules.conference
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
-import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.databinding.DialogConferenceBinding
-import io.github.wulkanowy.utils.lifecycleAwareVariable
+import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.serializable
import io.github.wulkanowy.utils.toFormattedString
-class ConferenceDialog : DialogFragment() {
-
- private var binding: DialogConferenceBinding by lifecycleAwareVariable()
+@AndroidEntryPoint
+class ConferenceDialog : BaseDialogFragment() {
private lateinit var conference: Conference
@@ -30,15 +29,14 @@ class ConferenceDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
conference = requireArguments().serializable(ARGUMENT_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogConferenceBinding.inflate(inflater).also { binding = it }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogConferenceBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt
index b9642b1c..0cd3150c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt
@@ -16,7 +16,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class ConferenceFragment : BaseFragment(R.layout.fragment_conference),
- ConferenceView, MainView.TitledView {
+ ConferenceView, MainView.TitledView, MainView.MainChildView {
@Inject
lateinit var presenter: ConferencePresenter
@@ -109,6 +109,14 @@ class ConferenceFragment : BaseFragment(R.layout.frag
(activity as? MainActivity)?.showDialogFragment(ConferenceDialog.newInstance(conference))
}
+ override fun onFragmentReselected() {
+ if (::presenter.isInitialized) presenter.onFragmentReselected()
+ }
+
+ override fun resetView() {
+ binding.conferenceRecycler.smoothScrollToPosition(0)
+ }
+
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt
index f5364893..1178c720 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt
@@ -96,4 +96,11 @@ class ConferencePresenter @Inject constructor(
.onResourceError(errorHandler::dispatch)
.launch()
}
+
+ fun onFragmentReselected() {
+ Timber.i("Conference is reselected")
+ if (view?.isViewEmpty == false) {
+ view?.resetView()
+ }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt
index 4f73394d..3299a1f0 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt
@@ -28,4 +28,6 @@ interface ConferenceView : BaseView {
fun showContent(show: Boolean)
fun openConferenceDialog(conference: Conference)
+
+ fun resetView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
index cd66d6c2..ce17c763 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
@@ -11,6 +11,7 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.FragmentDashboardBinding
@@ -148,7 +149,7 @@ class DashboardFragment : BaseFragment(R.layout.fragme
val values = requireContext().resources.getStringArray(R.array.dashboard_tile_values)
val selectedItemsState = values.map { value -> selectedItems.any { it.name == value } }
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.pref_dashboard_appearance_tiles_title)
.setMultiChoiceItems(entries, selectedItemsState.toBooleanArray()) { _, _, _ -> }
.setPositiveButton(android.R.string.ok) { dialog, _ ->
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index 22b0d267..ac2c896d 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -606,7 +606,7 @@ class DashboardPresenter @Inject constructor(
}
is Resource.Error -> {
Timber.i("Loading dashboard admin message result: An exception occurred")
- errorHandler.dispatch(it.error)
+ Timber.e(it.error)
updateData(
dashboardItem = DashboardItem.AdminMessages(
adminMessage = null,
@@ -748,7 +748,7 @@ class DashboardPresenter @Inject constructor(
itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null
val isGeneralError =
filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError
- val firstError = itemsLoadedList.mapNotNull { it.error }.firstOrNull()
+ val firstError = itemsLoadedList.firstNotNullOfOrNull { it.error }
val filteredOriginalLoadedList =
dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt
index d00df9d4..d821de53 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt
@@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.dashboard.adapters
+import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
@@ -8,6 +9,7 @@ import io.github.wulkanowy.data.enums.GradeColorTheme
import io.github.wulkanowy.databinding.SubitemDashboardGradesBinding
import io.github.wulkanowy.databinding.SubitemDashboardSmallGradeBinding
import io.github.wulkanowy.utils.getBackgroundColor
+import io.github.wulkanowy.utils.getCompatColor
class DashboardGradesAdapter : RecyclerView.Adapter() {
@@ -37,7 +39,9 @@ class DashboardGradesAdapter : RecyclerView.Adapter() {
private lateinit var exam: Exam
@@ -32,15 +31,14 @@ class ExamDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
exam = requireArguments().serializable(ARGUMENT_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogExamBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogExamBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt
index ddd0e4a1..0123e234 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt
@@ -2,9 +2,7 @@ package io.github.wulkanowy.ui.modules.exam
import android.os.Bundle
import android.view.View
-import android.view.View.GONE
-import android.view.View.INVISIBLE
-import android.view.View.VISIBLE
+import android.view.View.*
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@@ -20,7 +18,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class ExamFragment : BaseFragment(R.layout.fragment_exam), ExamView,
- MainView.TitledView {
+ MainView.TitledView, MainView.MainChildView {
@Inject
lateinit var presenter: ExamPresenter
@@ -64,7 +62,7 @@ class ExamFragment : BaseFragment(R.layout.fragment_exam),
examPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
examNextButton.setOnClickListener { presenter.onNextWeek() }
- examNavContainer.elevation = requireContext().dpToPx(8f)
+ examNavContainer.elevation = requireContext().dpToPx(3f)
}
}
@@ -126,6 +124,14 @@ class ExamFragment : BaseFragment(R.layout.fragment_exam),
(activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam))
}
+ override fun onFragmentReselected() {
+ if (::presenter.isInitialized) presenter.onViewReselected()
+ }
+
+ override fun resetView() {
+ binding.examRecycler.smoothScrollToPosition(0)
+ }
+
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
index 99b0bcb8..85814072 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
@@ -175,4 +175,17 @@ class ExamPresenter @Inject constructor(
)
}
}
+
+ fun onViewReselected() {
+ Timber.i("Exam view is reselected")
+
+ baseDate = now().nextOrSameSchoolDay
+
+ if (currentDate != baseDate) {
+ reloadView(baseDate)
+ loadData()
+ } else if (view?.isViewEmpty == false) {
+ view?.resetView()
+ }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt
index 45b9e788..677fac40 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt
@@ -34,4 +34,6 @@ interface ExamView : BaseView {
fun showPreButton(show: Boolean)
fun showExamDialog(exam: Exam)
+
+ fun resetView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
index b6733d4f..2d63aae4 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
@@ -12,70 +12,92 @@ import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier
-import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
import javax.inject.Inject
-@OptIn(FlowPreview::class)
+@OptIn(ExperimentalCoroutinesApi::class)
class GradeAverageProvider @Inject constructor(
private val semesterRepository: SemesterRepository,
private val gradeRepository: GradeRepository,
private val preferencesRepository: PreferencesRepository
) {
- private val plusModifier get() = preferencesRepository.gradePlusModifier
+ private data class AverageCalcParams(
+ val gradeAverageMode: GradeAverageMode,
+ val forceAverageCalc: Boolean,
+ val isOptionalArithmeticAverage: Boolean,
+ val plusModifier: Double,
+ val minusModifier: Double,
+ )
- private val minusModifier get() = preferencesRepository.gradeMinusModifier
-
- private val isOptionalArithmeticAverage get() = preferencesRepository.isOptionalArithmeticAverage
-
- fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) =
+ fun getGradesDetailsWithAverage(
+ student: Student,
+ semesterId: Int,
+ forceRefresh: Boolean
+ ): Flow>> = combine(
+ flow = preferencesRepository.gradeAverageModeFlow,
+ flow2 = preferencesRepository.gradeAverageForceCalcFlow,
+ flow3 = preferencesRepository.isOptionalArithmeticAverageFlow,
+ flow4 = preferencesRepository.gradePlusModifierFlow,
+ flow5 = preferencesRepository.gradeMinusModifierFlow,
+ ) { gradeAverageMode, forceAverageCalc, isOptionalArithmeticAverage, plusModifier, minusModifier ->
+ AverageCalcParams(
+ gradeAverageMode = gradeAverageMode,
+ forceAverageCalc = forceAverageCalc,
+ isOptionalArithmeticAverage = isOptionalArithmeticAverage,
+ plusModifier = plusModifier,
+ minusModifier = minusModifier,
+ )
+ }.flatMapLatest { params ->
flatResourceFlow {
val semesters = semesterRepository.getSemesters(student)
-
- when (preferencesRepository.gradeAverageMode) {
+ when (params.gradeAverageMode) {
ONE_SEMESTER -> getGradeSubjects(
student = student,
semester = semesters.single { it.semesterId == semesterId },
- forceRefresh = forceRefresh
+ forceRefresh = forceRefresh,
+ params = params,
)
BOTH_SEMESTERS -> calculateCombinedAverage(
student = student,
semesters = semesters,
semesterId = semesterId,
forceRefresh = forceRefresh,
- averageMode = BOTH_SEMESTERS
+ config = params,
)
ALL_YEAR -> calculateCombinedAverage(
student = student,
semesters = semesters,
semesterId = semesterId,
forceRefresh = forceRefresh,
- averageMode = ALL_YEAR
+ config = params,
)
}
- }.distinctUntilChanged()
+ }
+ }.distinctUntilChanged()
private fun calculateCombinedAverage(
student: Student,
semesters: List,
semesterId: Int,
forceRefresh: Boolean,
- averageMode: GradeAverageMode
+ config: AverageCalcParams,
): Flow>> {
- val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
val selectedSemester = semesters.single { it.semesterId == semesterId }
val firstSemester =
semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
val selectedSemesterGradeSubjects =
- getGradeSubjects(student, selectedSemester, forceRefresh)
+ getGradeSubjects(student, selectedSemester, forceRefresh, config)
if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects
- val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh)
+ val firstSemesterGradeSubjects =
+ getGradeSubjects(student, firstSemester, forceRefresh, config)
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
if (firstSemesterGradeSubject.errorOrNull != null) {
@@ -91,21 +113,21 @@ class GradeAverageProvider @Inject constructor(
val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty()
.singleOrNull { it.subject == secondSemesterSubject.subject }
- val updatedAverage = if (averageMode == ALL_YEAR) {
+ val updatedAverage = if (config.gradeAverageMode == ALL_YEAR) {
calculateAllYearAverage(
student = student,
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
- isGradeAverageForceCalc = isGradeAverageForceCalc,
secondSemesterSubject = secondSemesterSubject,
- firstSemesterSubject = firstSemesterSubject
+ firstSemesterSubject = firstSemesterSubject,
+ config = config,
)
} else {
calculateBothSemestersAverage(
student = student,
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
- isGradeAverageForceCalc = isGradeAverageForceCalc,
secondSemesterSubject = secondSemesterSubject,
- firstSemesterSubject = firstSemesterSubject
+ firstSemesterSubject = firstSemesterSubject,
+ config = config
)
}
secondSemesterSubject.copy(average = updatedAverage)
@@ -117,17 +139,17 @@ class GradeAverageProvider @Inject constructor(
private fun calculateAllYearAverage(
student: Student,
isAnyVulcanAverage: Boolean,
- isGradeAverageForceCalc: Boolean,
secondSemesterSubject: GradeSubject,
- firstSemesterSubject: GradeSubject?
- ) = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
- val updatedSecondSemesterGrades =
- secondSemesterSubject.grades.updateModifiers(student)
- val updatedFirstSemesterGrades =
- firstSemesterSubject?.grades?.updateModifiers(student).orEmpty()
+ firstSemesterSubject: GradeSubject?,
+ config: AverageCalcParams,
+ ) = if (!isAnyVulcanAverage || config.forceAverageCalc) {
+ val updatedSecondSemesterGrades = secondSemesterSubject.grades
+ .updateModifiers(student, config)
+ val updatedFirstSemesterGrades = firstSemesterSubject?.grades
+ ?.updateModifiers(student, config).orEmpty()
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
- isOptionalArithmeticAverage
+ config.isOptionalArithmeticAverage
)
} else {
secondSemesterSubject.average
@@ -136,32 +158,35 @@ class GradeAverageProvider @Inject constructor(
private fun calculateBothSemestersAverage(
student: Student,
isAnyVulcanAverage: Boolean,
- isGradeAverageForceCalc: Boolean,
secondSemesterSubject: GradeSubject,
- firstSemesterSubject: GradeSubject?
- ): Double = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
- val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
+ firstSemesterSubject: GradeSubject?,
+ config: AverageCalcParams,
+ ): Double {
+ return if (!isAnyVulcanAverage || config.forceAverageCalc) {
+ val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
+ val secondSemesterAverage = secondSemesterSubject.grades
+ .updateModifiers(student, config)
+ .calcAverage(config.isOptionalArithmeticAverage)
+ val firstSemesterAverage = firstSemesterSubject?.grades
+ ?.updateModifiers(student, config)
+ ?.calcAverage(config.isOptionalArithmeticAverage) ?: secondSemesterAverage
- val secondSemesterAverage = secondSemesterSubject.grades.updateModifiers(student)
- .calcAverage(isOptionalArithmeticAverage)
- val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
- ?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage
+ (secondSemesterAverage + firstSemesterAverage) / divider
+ } else {
+ val divider = if (secondSemesterSubject.average > 0) 2 else 1
- (secondSemesterAverage + firstSemesterAverage) / divider
- } else {
- val divider = if (secondSemesterSubject.average > 0) 2 else 1
-
- (secondSemesterSubject.average + (firstSemesterSubject?.average
- ?: secondSemesterSubject.average)) / divider
+ secondSemesterSubject.average.plus(
+ (firstSemesterSubject?.average ?: secondSemesterSubject.average)
+ ) / divider
+ }
}
private fun getGradeSubjects(
student: Student,
semester: Semester,
- forceRefresh: Boolean
+ forceRefresh: Boolean,
+ params: AverageCalcParams,
): Flow>> {
- val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
-
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
.mapResourceData { res ->
val (details, summaries) = res
@@ -172,13 +197,15 @@ class GradeAverageProvider @Inject constructor(
student = student,
semester = semester,
grades = allGrades.toList(),
- calcAverage = isAnyAverage
+ calcAverage = isAnyAverage,
+ params = params,
).map { summary ->
val grades = allGrades[summary.subject].orEmpty()
GradeSubject(
subject = summary.subject,
- average = if (!isAnyAverage || isGradeAverageForceCalc) {
- grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage)
+ average = if (!isAnyAverage || params.forceAverageCalc) {
+ grades.updateModifiers(student, params)
+ .calcAverage(params.isOptionalArithmeticAverage)
} else summary.average,
points = summary.pointsSum,
summary = summary,
@@ -195,7 +222,8 @@ class GradeAverageProvider @Inject constructor(
student: Student,
semester: Semester,
grades: List>>,
- calcAverage: Boolean
+ calcAverage: Boolean,
+ params: AverageCalcParams,
): List {
if (isNotEmpty() && size > grades.size) return this
@@ -211,15 +239,16 @@ class GradeAverageProvider @Inject constructor(
proposedPoints = "",
finalPoints = "",
pointsSum = "",
- average = if (calcAverage) details.updateModifiers(student)
- .calcAverage(isOptionalArithmeticAverage) else .0
+ average = if (calcAverage) details.updateModifiers(student, params)
+ .calcAverage(params.isOptionalArithmeticAverage) else .0
)
}
}
- private fun List.updateModifiers(student: Student): List {
- return if (student.loginMode == Sdk.Mode.SCRAPPER.name) {
- map { it.changeModifier(plusModifier, minusModifier) }
- } else this
- }
+ private fun List.updateModifiers(
+ student: Student,
+ params: AverageCalcParams,
+ ): List = if (student.loginMode == Sdk.Mode.SCRAPPER.name) {
+ map { it.changeModifier(params.plusModifier, params.minusModifier) }
+ } else this
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt
index 15df47a1..7ce07eb6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt
@@ -8,6 +8,7 @@ import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.appcompat.app.AlertDialog
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@@ -141,7 +142,7 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade
val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) }
.toTypedArray()
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setSingleChoiceItems(choices, selectedIndex) { dialog, which ->
presenter.onSemesterSelected(which)
dialog.dismiss()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt
index e5c3bb63..15b5db03 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt
@@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.grade.details
import android.annotation.SuppressLint
+import android.content.res.ColorStateList
import android.content.res.Resources
import android.view.LayoutInflater
import android.view.View
@@ -17,9 +18,10 @@ import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding
import io.github.wulkanowy.databinding.ItemGradeDetailsBinding
import io.github.wulkanowy.ui.base.BaseExpandableAdapter
import io.github.wulkanowy.utils.getBackgroundColor
+import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.toFormattedString
import timber.log.Timber
-import java.util.BitSet
+import java.util.*
import javax.inject.Inject
class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter() {
@@ -203,7 +205,9 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter grade.description
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt
index a1ef2ec5..39f72f8b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt
@@ -1,22 +1,23 @@
package io.github.wulkanowy.ui.modules.grade.details
+import android.app.Dialog
+import android.content.res.ColorStateList
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
-import android.view.ViewGroup
+import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
-import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.enums.GradeColorTheme
import io.github.wulkanowy.databinding.DialogGradeBinding
+import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.*
-
-class GradeDetailsDialog : DialogFragment() {
-
- private var binding: DialogGradeBinding by lifecycleAwareVariable()
+@AndroidEntryPoint
+class GradeDetailsDialog : BaseDialogFragment() {
private lateinit var grade: Grade
@@ -38,16 +39,15 @@ class GradeDetailsDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
grade = requireArguments().serializable(ARGUMENT_KEY)
gradeColorTheme = requireArguments().serializable(COLOR_THEME_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogGradeBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogGradeBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -55,10 +55,9 @@ class GradeDetailsDialog : DialogFragment() {
with(binding) {
gradeDialogSubject.text = grade.subject
- gradeDialogColorAndWeightValue.run {
- text = context.getString(R.string.grade_weight_value, grade.weight)
- setBackgroundResource(grade.getGradeColor())
- }
+ gradeDialogWeightValue.text = grade.weight
+ gradeDialogWeightLayout.backgroundTintList =
+ ColorStateList.valueOf(requireContext().getCompatColor(grade.getGradeColor()))
gradeDialogDateValue.text = grade.date.toFormattedString()
gradeDialogColorValue.text = getString(grade.colorStringId)
@@ -72,7 +71,12 @@ class GradeDetailsDialog : DialogFragment() {
gradeDialogValue.run {
text = grade.entry
- setBackgroundResource(grade.getBackgroundColor(gradeColorTheme))
+ backgroundTintList = ColorStateList.valueOf(
+ ContextCompat.getColor(
+ requireContext(),
+ grade.getBackgroundColor(gradeColorTheme)
+ )
+ )
}
gradeDialogTeacherValue.text = grade.teacher.ifBlank { getString(R.string.all_no_data) }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt
index fd0ac547..3fce8d57 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt
@@ -116,7 +116,9 @@ class GradeStatisticsAdapter @Inject constructor() :
}
)
- binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
+ binding.gradeStatisticsTypeSwitch.addOnButtonCheckedListener { _, checkedId, isChecked ->
+ if (!isChecked) return@addOnButtonCheckedListener
+
currentDataType = when (checkedId) {
R.id.gradeStatisticsTypePartial -> GradeStatisticsItem.DataType.PARTIAL
R.id.gradeStatisticsTypeSemester -> GradeStatisticsItem.DataType.SEMESTER
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt
index 3810902f..abd0b13c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt
@@ -7,6 +7,7 @@ import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradeSummary
@@ -118,7 +119,7 @@ class GradeSummaryFragment :
}
override fun showCalculatedAverageHelpDialog() {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.grade_summary_calculated_average_help_dialog_title)
.setMessage(R.string.grade_summary_calculated_average_help_dialog_message)
.setPositiveButton(R.string.all_close) { _, _ -> }
@@ -126,7 +127,7 @@ class GradeSummaryFragment :
}
override fun showFinalAverageHelpDialog() {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.grade_summary_final_average_help_dialog_title)
.setMessage(R.string.grade_summary_final_average_help_dialog_message)
.setPositiveButton(R.string.all_close) { _, _ -> }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt
index d4eaade2..0381acf3 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt
@@ -21,7 +21,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class HomeworkFragment : BaseFragment(R.layout.fragment_homework),
- HomeworkView, MainView.TitledView {
+ HomeworkView, MainView.TitledView, MainView.MainChildView {
@Inject
lateinit var presenter: HomeworkPresenter
@@ -67,7 +67,7 @@ class HomeworkFragment : BaseFragment(R.layout.fragment
openAddHomeworkButton.setOnClickListener { presenter.onHomeworkAddButtonClicked() }
- homeworkNavContainer.elevation = requireContext().dpToPx(8f)
+ homeworkNavContainer.elevation = requireContext().dpToPx(3f)
}
}
@@ -133,6 +133,14 @@ class HomeworkFragment : BaseFragment(R.layout.fragment
(activity as? MainActivity)?.showDialogFragment(HomeworkAddDialog())
}
+ override fun onFragmentReselected() {
+ if (::presenter.isInitialized) presenter.onViewReselected()
+ }
+
+ override fun resetView() {
+ binding.homeworkRecycler.smoothScrollToPosition(0)
+ }
+
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt
index 2ac552b4..6b263e26 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt
@@ -177,8 +177,21 @@ class HomeworkPresenter @Inject constructor(
showNextButton(!currentDate.plusDays(7).isHolidays)
updateNavigationWeek(
"${currentDate.monday.toFormattedString("dd.MM")} - " +
- currentDate.sunday.toFormattedString("dd.MM")
+ currentDate.sunday.toFormattedString("dd.MM")
)
}
}
+
+ fun onViewReselected() {
+ Timber.i("Homework view is reselected")
+
+ baseDate = LocalDate.now().nextOrSameSchoolDay
+
+ if (currentDate != baseDate) {
+ reloadView(baseDate)
+ loadData()
+ } else if (view?.isViewEmpty == false) {
+ view?.resetView()
+ }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt
index 7c05ab86..56ba6c89 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt
@@ -36,4 +36,6 @@ interface HomeworkView : BaseView {
fun showHomeworkDialog(homework: Homework)
fun showAddHomeworkDialog()
+
+ fun resetView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
index c2aff2b1..c51370ea 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
@@ -1,10 +1,10 @@
package io.github.wulkanowy.ui.modules.homework.add
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.widget.doOnTextChanged
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogHomeworkAddBinding
@@ -21,20 +21,15 @@ class HomeworkAddDialog : BaseDialogFragment(), Homewo
@Inject
lateinit var presenter: HomeworkAddPresenter
- // todo: move it to presenter
+ //todo: move it to presenter
private var date: LocalDate? = null
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogHomeworkAddBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogHomeworkAddBinding.inflate(inflater).apply { binding = this }.root
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt
index e03707a5..1ad2a0e3 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt
@@ -31,14 +31,8 @@ class HomeworkDetailsAdapter @Inject constructor() :
attachments = value?.attachments.orEmpty()
}
- var isHomeworkFullscreen = false
-
var onAttachmentClickListener: (url: String) -> Unit = {}
- var onFullScreenClickListener = {}
-
- var onFullScreenExitClickListener = {}
-
var onDeleteClickListener: (homework: Homework) -> Unit = {}
override fun getItemCount() = 1 + if (attachments.isNotEmpty()) attachments.size + 1 else 0
@@ -82,18 +76,6 @@ class HomeworkDetailsAdapter @Inject constructor() :
homeworkDialogTeacher.text = homework?.teacher.ifNullOrBlank { noDataString }
homeworkDialogContent.text = homework?.content.ifNullOrBlank { noDataString }
homeworkDialogDelete.visibility = if (homework?.isAddedByUser == true) VISIBLE else GONE
- homeworkDialogFullScreen.visibility = if (isHomeworkFullscreen) GONE else VISIBLE
- homeworkDialogFullScreenExit.visibility = if (isHomeworkFullscreen) VISIBLE else GONE
- homeworkDialogFullScreen.setOnClickListener {
- homeworkDialogFullScreen.visibility = GONE
- homeworkDialogFullScreenExit.visibility = VISIBLE
- onFullScreenClickListener()
- }
- homeworkDialogFullScreenExit.setOnClickListener {
- homeworkDialogFullScreen.visibility = VISIBLE
- homeworkDialogFullScreenExit.visibility = GONE
- onFullScreenExitClickListener()
- }
homeworkDialogDelete.setOnClickListener {
onDeleteClickListener(homework!!)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt
index 5e2cc65d..1f9bc881 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt
@@ -1,14 +1,12 @@
package io.github.wulkanowy.ui.modules.homework.details
import android.annotation.SuppressLint
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Homework
@@ -43,15 +41,14 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
homework = requireArguments().serializable(ARGUMENT_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogHomeworkBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogHomeworkBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -67,26 +64,11 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew
homeworkDialogClose.setOnClickListener { dismiss() }
}
- if (presenter.isHomeworkFullscreen) {
- dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
- } else {
- dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
- }
-
with(binding.homeworkDialogRecycler) {
layoutManager = LinearLayoutManager(context)
adapter = detailsAdapter.apply {
onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) }
- onFullScreenClickListener = {
- dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
- presenter.isHomeworkFullscreen = true
- }
- onFullScreenExitClickListener = {
- dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
- presenter.isHomeworkFullscreen = false
- }
onDeleteClickListener = { homework -> presenter.deleteHomework(homework) }
- isHomeworkFullscreen = presenter.isHomeworkFullscreen
homework = this@HomeworkDetailsDialog.homework
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt
index e76df6bd..84933f06 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt
@@ -5,7 +5,6 @@ import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.HomeworkRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
@@ -19,15 +18,8 @@ class HomeworkDetailsPresenter @Inject constructor(
studentRepository: StudentRepository,
private val homeworkRepository: HomeworkRepository,
private val analytics: AnalyticsHelper,
- private val preferencesRepository: PreferencesRepository
) : BasePresenter(errorHandler, studentRepository) {
- var isHomeworkFullscreen
- get() = preferencesRepository.isHomeworkFullscreen
- set(value) {
- preferencesRepository.isHomeworkFullscreen = value
- }
-
override fun onAttachView(view: HomeworkDetailsView) {
super.onAttachView(view)
view.initView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt
index 37ab71dc..4f709438 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt
@@ -4,13 +4,15 @@ import android.content.Context
import android.database.sqlite.SQLiteConstraintException
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
-import io.github.wulkanowy.sdk.mobile.exception.InvalidPinException
-import io.github.wulkanowy.sdk.mobile.exception.InvalidSymbolException
-import io.github.wulkanowy.sdk.mobile.exception.InvalidTokenException
-import io.github.wulkanowy.sdk.mobile.exception.TokenDeadException
+import io.github.wulkanowy.sdk.hebe.exception.InvalidPinException
+import io.github.wulkanowy.sdk.hebe.exception.InvalidTokenException
+import io.github.wulkanowy.sdk.hebe.exception.TokenDeadException
+import io.github.wulkanowy.sdk.hebe.exception.UnknownTokenException
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
import io.github.wulkanowy.ui.base.ErrorHandler
import javax.inject.Inject
+import io.github.wulkanowy.sdk.hebe.exception.InvalidSymbolException as InvalidHebeSymbolException
+import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException as InvalidScrapperSymbolException
class LoginErrorHandler @Inject constructor(
@ApplicationContext context: Context,
@@ -32,9 +34,11 @@ class LoginErrorHandler @Inject constructor(
is BadCredentialsException -> onBadCredentials(error.message)
is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student))
is TokenDeadException -> onInvalidToken(resources.getString(R.string.login_expired_token))
+ is UnknownTokenException,
is InvalidTokenException -> onInvalidToken(resources.getString(R.string.login_invalid_token))
is InvalidPinException -> onInvalidPin(resources.getString(R.string.login_invalid_pin))
- is InvalidSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol))
+ is InvalidScrapperSymbolException,
+ is InvalidHebeSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol))
else -> super.proceed(error)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt
index 8c90623e..ead2d71a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt
@@ -34,9 +34,9 @@ class LoginAdvancedFragment :
override val formLoginType: String
get() = when (binding.loginTypeSwitch.checkedRadioButtonId) {
- R.id.loginTypeApi -> "API"
- R.id.loginTypeScrapper -> "SCRAPPER"
- else -> "HYBRID"
+ R.id.loginTypeApi -> Sdk.Mode.HEBE.name
+ R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER.name
+ else -> Sdk.Mode.HYBRID.name
}
override val formUsernameValue: String
@@ -99,7 +99,7 @@ class LoginAdvancedFragment :
loginTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
presenter.onLoginModeSelected(
when (checkedId) {
- R.id.loginTypeApi -> Sdk.Mode.API
+ R.id.loginTypeApi -> Sdk.Mode.HEBE
R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER
else -> Sdk.Mode.HYBRID
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt
index 33a76e5f..ab56bd78 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt
@@ -1,17 +1,12 @@
package io.github.wulkanowy.ui.modules.login.advanced
import io.github.wulkanowy.data.Resource
-import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceNotLoading
-import io.github.wulkanowy.data.pojos.RegisterStudent
-import io.github.wulkanowy.data.pojos.RegisterSymbol
-import io.github.wulkanowy.data.pojos.RegisterUnit
import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.sdk.scrapper.Scrapper
import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginData
@@ -97,14 +92,16 @@ class LoginAdvancedPresenter @Inject constructor(
fun onLoginModeSelected(type: Sdk.Mode) {
view?.run {
when (type) {
- Sdk.Mode.API -> {
+ Sdk.Mode.HEBE -> {
showOnlyMobileApiModeInputs()
showMobileApiWarningMessage()
}
+
Sdk.Mode.SCRAPPER -> {
showOnlyScrapperModeInputs()
showScraperWarningMessage()
}
+
Sdk.Mode.HYBRID -> {
showOnlyHybridModeInputs()
showHybridWarningMessage()
@@ -145,11 +142,12 @@ class LoginAdvancedPresenter @Inject constructor(
showProgress(true)
showContent(false)
}
+
is Resource.Success -> {
analytics.logEvent(
"registration_form",
"success" to true,
- "students" to it.data.size,
+ "scrapperBaseUrl" to view?.formHostValue.orEmpty(),
"error" to "No error"
)
val loginData = LoginData(
@@ -158,14 +156,15 @@ class LoginAdvancedPresenter @Inject constructor(
baseUrl = view?.formHostValue.orEmpty().trim(),
symbol = view?.formSymbolValue.orEmpty().trim().getNormalizedSymbol(),
)
- when (it.data.size) {
+ when (it.data.symbols.size) {
0 -> view?.navigateToSymbol(loginData)
else -> view?.navigateToStudentSelect(
loginData = loginData,
- registerUser = it.data.toRegisterUser(loginData),
+ registerUser = it.data,
)
}
}
+
is Resource.Error -> {
analytics.logEvent(
"registration_form",
@@ -183,59 +182,7 @@ class LoginAdvancedPresenter @Inject constructor(
}.launch("login")
}
- private fun List.toRegisterUser(loginData: LoginData) = RegisterUser(
- email = loginData.login,
- password = loginData.password,
- login = loginData.login,
- baseUrl = loginData.baseUrl,
- loginType = firstOrNull()?.student?.loginType?.let(
- Scrapper.LoginType::valueOf
- ) ?: Scrapper.LoginType.AUTO,
- symbols = this
- .groupBy { students -> students.student.symbol }
- .map { (symbol, students) ->
- RegisterSymbol(
- symbol = symbol,
- error = null,
- userName = "",
- schools = students
- .groupBy { student ->
- Triple(
- first = student.student.schoolSymbol,
- second = student.student.userLoginId,
- third = student.student.schoolShortName
- )
- }
- .map { (groupKey, students) ->
- val (schoolId, loginId, schoolName) = groupKey
- RegisterUnit(
- students = students.map {
- RegisterStudent(
- studentId = it.student.studentId,
- studentName = it.student.studentName,
- studentSecondName = it.student.studentName,
- studentSurname = it.student.studentName,
- className = it.student.className,
- classId = it.student.classId,
- isParent = it.student.isParent,
- semesters = it.semesters,
- )
- },
- userLoginId = loginId,
- schoolId = schoolId,
- schoolName = schoolName,
- schoolShortName = schoolName,
- parentIds = listOf(),
- studentIds = listOf(),
- employeeIds = listOf(),
- error = null
- )
- }
- )
- },
- )
-
- private suspend fun getStudentsAppropriatesToLoginType(): List {
+ private suspend fun getStudentsAppropriatesToLoginType(): RegisterUser {
val email = view?.formUsernameValue.orEmpty()
val password = view?.formPassValue.orEmpty()
val endpoint = view?.formHostValue.orEmpty()
@@ -245,10 +192,11 @@ class LoginAdvancedPresenter @Inject constructor(
val token = view?.formTokenValue.orEmpty()
return when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) {
- Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token)
+ Sdk.Mode.HEBE -> studentRepository.getStudentsApi(pin, symbol, token)
Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(
email, password, endpoint, symbol
)
+
Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(
email, password, endpoint, symbol
)
@@ -267,8 +215,8 @@ class LoginAdvancedPresenter @Inject constructor(
var isCorrect = true
- when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) {
- Sdk.Mode.API -> {
+ when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) {
+ Sdk.Mode.HEBE -> {
if (pin.isEmpty()) {
view?.setErrorPinRequired()
isCorrect = false
@@ -284,17 +232,17 @@ class LoginAdvancedPresenter @Inject constructor(
isCorrect = false
}
}
+
Sdk.Mode.HYBRID, Sdk.Mode.SCRAPPER -> {
if (login.isEmpty()) {
view?.setErrorUsernameRequired()
isCorrect = false
} else {
- if ("@" in login && "standard" !in host) {
+ if ("@" in login && "login" in host) {
view?.setErrorLoginRequired()
isCorrect = false
}
-
- if ("@" !in login && "standard" in host) {
+ if ("@" !in login && "email" in host) {
view?.setErrorEmailRequired()
isCorrect = false
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt
index 824fa028..34062d93 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt
@@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.login.advanced
-import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.ui.base.BaseView
import io.github.wulkanowy.ui.modules.login.LoginData
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt
index a0e7608d..43ba3fe1 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt
@@ -15,12 +15,7 @@ import io.github.wulkanowy.databinding.FragmentLoginFormBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginData
-import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.hideSoftInput
-import io.github.wulkanowy.utils.openEmailClient
-import io.github.wulkanowy.utils.openInternetBrowser
-import io.github.wulkanowy.utils.setOnEditorDoneSignIn
-import io.github.wulkanowy.utils.showSoftInput
+import io.github.wulkanowy.utils.*
import javax.inject.Inject
@AndroidEntryPoint
@@ -149,12 +144,14 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
override fun setErrorPassRequired(focus: Boolean) {
with(binding.loginFormPassLayout) {
error = getString(R.string.error_field_required)
+ setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError))
}
}
override fun setErrorPassInvalid(focus: Boolean) {
with(binding.loginFormPassLayout) {
error = getString(R.string.login_invalid_password)
+ setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError))
}
}
@@ -162,6 +159,7 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
with(binding) {
loginFormUsernameLayout.error = " "
loginFormPassLayout.error = " "
+ loginFormPassLayout.setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError))
loginFormHostLayout.error = " "
loginFormErrorBox.text = message ?: getString(R.string.login_incorrect_password_default)
loginFormErrorBox.isVisible = true
@@ -181,6 +179,7 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
override fun clearPassError() {
binding.loginFormPassLayout.error = null
+ binding.loginFormPassLayout.setEndIconTintList(null)
binding.loginFormErrorBox.isVisible = false
}
@@ -205,6 +204,10 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
binding.loginFormContainer.visibility = if (show) VISIBLE else GONE
}
+ override fun showOtherOptionsButton(show: Boolean) {
+ binding.loginFormAdvancedButton.isVisible = show
+ }
+
@SuppressLint("SetTextI18n")
override fun showVersion() {
binding.loginFormVersion.text = "v${appInfo.versionName}"
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt
index 8035ea0a..ed70eb12 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt
@@ -7,6 +7,7 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.ifNullOrBlank
import timber.log.Timber
import java.net.URL
@@ -15,6 +16,7 @@ import javax.inject.Inject
class LoginFormPresenter @Inject constructor(
studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler,
+ private val appInfo: AppInfo,
private val analytics: AnalyticsHelper
) : BasePresenter(loginErrorHandler, studentRepository) {
@@ -25,6 +27,7 @@ class LoginFormPresenter @Inject constructor(
view.run {
initView()
showContact(false)
+ showOtherOptionsButton(appInfo.isDebug)
showVersion()
loginErrorHandler.onBadCredentials = {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt
index 5a816fb3..e5c680d6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt
@@ -56,6 +56,8 @@ interface LoginFormView : BaseView {
fun showContent(show: Boolean)
+ fun showOtherOptionsButton(show: Boolean)
+
fun showVersion()
fun navigateToSymbol(loginData: LoginData)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
index 16970215..c33d12fa 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
@@ -55,7 +55,6 @@ class LoginStudentSelectFragment :
}
}
- @Suppress("UNCHECKED_CAST")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentLoginStudentSelectBinding.bind(view)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt
index 0a73fe15..a6c75c1d 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt
@@ -18,7 +18,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class LuckyNumberFragment :
BaseFragment(R.layout.fragment_lucky_number), LuckyNumberView,
- MainView.TitledView {
+ MainView.TitledView, MainView.MainChildView {
@Inject
lateinit var presenter: LuckyNumberPresenter
@@ -86,6 +86,14 @@ class LuckyNumberFragment :
(activity as? MainActivity)?.pushView(LuckyNumberHistoryFragment.newInstance())
}
+ override fun onFragmentReselected() {
+ if (::presenter.isInitialized) presenter.onViewReselected()
+ }
+
+ override fun popView() {
+ (activity as? MainActivity)?.popView()
+ }
+
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt
index 6f5c8e74..2039624b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt
@@ -99,4 +99,9 @@ class LuckyNumberPresenter @Inject constructor(
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
+
+ fun onViewReselected() {
+ Timber.i("Luckynumber view is reselected")
+ view?.popView()
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt
index 0c05a156..3d34ebc8 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt
@@ -26,4 +26,6 @@ interface LuckyNumberView : BaseView {
fun showContent(show: Boolean)
fun openLuckyNumberHistory()
+
+ fun popView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
index 53f06cac..a78ce5dd 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
@@ -61,7 +61,7 @@ class LuckyNumberHistoryFragment :
luckyNumberHistoryPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
luckyNumberHistoryNextButton.setOnClickListener { presenter.onNextWeek() }
- luckyNumberHistoryNavContainer.elevation = requireContext().dpToPx(8f)
+ luckyNumberHistoryNavContainer.elevation = requireContext().dpToPx(3f)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt
index 024beff8..a2d23e54 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt
@@ -1,16 +1,12 @@
package io.github.wulkanowy.ui.modules.luckynumberwidget
-import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
-import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
-import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
+import android.appwidget.AppWidgetManager.*
import android.content.Intent
-import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
-import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding
import io.github.wulkanowy.ui.base.BaseActivity
@@ -41,7 +37,6 @@ class LuckyNumberWidgetConfigureActivity :
setContentView(
ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root
)
-
intent.extras.let {
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID))
}
@@ -56,22 +51,6 @@ class LuckyNumberWidgetConfigureActivity :
configureAdapter.onClickListener = presenter::onItemSelect
}
- override fun showThemeDialog() {
- var items = arrayOf(
- getString(R.string.widget_timetable_theme_light),
- getString(R.string.widget_timetable_theme_dark)
- )
- if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += (getString(R.string.widget_timetable_theme_system))
-
- dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher)
- .setTitle(R.string.widget_timetable_theme_title)
- .setOnDismissListener { presenter.onDismissThemeView() }
- .setSingleChoiceItems(items, -1) { _, which ->
- presenter.onThemeSelect(which)
- }
- .show()
- }
-
override fun updateData(data: List, selectedStudentId: Long) {
with(configureAdapter) {
selectedId = selectedStudentId
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt
index cac648da..7e53dad0 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt
@@ -8,7 +8,6 @@ import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey
-import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getThemeWidgetKey
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -32,20 +31,9 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
fun onItemSelect(student: Student) {
selectedStudent = student
- view?.showThemeDialog()
- }
-
- fun onThemeSelect(index: Int) {
- appWidgetId?.let {
- sharedPref.putLong(getThemeWidgetKey(it), index.toLong())
- }
registerStudent(selectedStudent)
}
- fun onDismissThemeView() {
- view?.finishView()
- }
-
private fun loadData() {
resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
when (it) {
@@ -56,10 +44,7 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
} ?: -1
when {
it.data.isEmpty() -> view?.openLoginView()
- it.data.size == 1 -> {
- selectedStudent = it.data.single().student
- view?.showThemeDialog()
- }
+ it.data.size == 1 -> onItemSelect(it.data.single().student)
else -> view?.updateData(it.data, selectedStudentId)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt
index b4556f7e..df13b993 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt
@@ -7,8 +7,6 @@ interface LuckyNumberWidgetConfigureView : BaseView {
fun initView()
- fun showThemeDialog()
-
fun updateData(data: List, selectedStudentId: Long)
fun updateLuckyNumberWidget(widgetId: Int)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
index e03e3e90..bafb2d7e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
@@ -2,14 +2,12 @@ package io.github.wulkanowy.ui.modules.luckynumberwidget
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
-import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
-import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
-import android.view.View.GONE
-import android.view.View.VISIBLE
+import android.util.TypedValue.COMPLEX_UNIT_SP
+import android.view.View
import android.widget.RemoteViews
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@@ -17,7 +15,6 @@ import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.LuckyNumber
-import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.toFirstResult
@@ -41,16 +38,12 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
lateinit var sharedPref: SharedPrefProvider
companion object {
+ private const val LUCKY_NUMBER_WIDGET_MAX_SIZE = 196
- const val LUCKY_NUMBER_PENDING_INTENT_ID = 200
+ private const val LUCKY_NUMBER_PENDING_INTENT_ID = 300
+ private const val LUCKY_NUMBER_HISTORY_PENDING_INTENT_ID = 301
fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId"
-
- fun getThemeWidgetKey(appWidgetId: Int) = "lucky_number_widget_theme_$appWidgetId"
-
- fun getHeightWidgetKey(appWidgetId: Int) = "lucky_number_widget_height_$appWidgetId"
-
- fun getWidthWidgetKey(appWidgetId: Int) = "lucky_number_widget_width_$appWidgetId"
}
override fun onUpdate(
@@ -59,107 +52,86 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
appWidgetIds: IntArray?
) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
- appWidgetIds?.forEach { appWidgetId ->
- val luckyNumber =
- getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
- val appIntent = PendingIntent.getActivity(
- context,
- LUCKY_NUMBER_PENDING_INTENT_ID,
- SplashActivity.getStartIntent(context, Destination.LuckyNumber),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
- )
- if (luckyNumber is Resource.Error) {
- Timber.e("Error loading lucky number for widget", luckyNumber.error)
- }
+ val appIntent = PendingIntent.getActivity(
+ context,
+ LUCKY_NUMBER_PENDING_INTENT_ID,
+ SplashActivity.getStartIntent(context, Destination.LuckyNumber),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+ )
- val remoteView =
- RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context))
- .apply {
- setTextViewText(
- R.id.luckyNumberWidgetNumber,
- luckyNumber.dataOrNull?.luckyNumber?.toString() ?: "#"
- )
- setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent)
- }
+ val historyIntent = PendingIntent.getActivity(
+ context,
+ LUCKY_NUMBER_HISTORY_PENDING_INTENT_ID,
+ SplashActivity.getStartIntent(context, Destination.LuckyNumberHistory),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+ )
- setStyles(remoteView, appWidgetId)
- appWidgetManager.updateAppWidget(appWidgetId, remoteView)
+ appWidgetIds?.forEach { widgetId ->
+ val studentId = sharedPref.getLong(getStudentWidgetKey(widgetId), 0)
+ val luckyNumberResource = getLuckyNumber(studentId, widgetId)
+ val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber?.toString()
+ val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber)
+ .apply {
+ setTextViewText(R.id.luckyNumberWidgetValue, luckyNumber ?: "-")
+ setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent)
+ setOnClickPendingIntent(R.id.luckyNumberWidgetHistoryButton, historyIntent)
+ }
+
+ resizeWidget(context, appWidgetManager.getAppWidgetOptions(widgetId), remoteView)
+ appWidgetManager.updateAppWidget(widgetId, remoteView)
+ }
+ }
+
+ override fun onAppWidgetOptionsChanged(
+ context: Context?,
+ appWidgetManager: AppWidgetManager?,
+ appWidgetId: Int,
+ newOptions: Bundle?
+ ) {
+ super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
+
+ if (context == null || newOptions == null || appWidgetManager == null) {
+ return
+ }
+
+ val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber)
+ resizeWidget(context, newOptions, remoteView)
+ appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteView)
+ }
+
+ private fun resizeWidget(context: Context, options: Bundle, remoteViews: RemoteViews) {
+ val (width, height) = options.getWidgetSize(context)
+ val size = minOf(width, height, LUCKY_NUMBER_WIDGET_MAX_SIZE).toFloat()
+ resizeWidgetContents(size, remoteViews)
+ Timber.v("LuckyNumberWidget resized: ${width}x${height} ($size)")
+ }
+
+ private fun resizeWidgetContents(size: Float, remoteViews: RemoteViews) {
+ var historyButtonVisibility = View.VISIBLE
+ var luckyNumberTextSize = 72f
+
+ if (size < 150) {
+ luckyNumberTextSize = 44f
+ historyButtonVisibility = View.GONE
+ }
+ if (size < 75) {
+ luckyNumberTextSize = 26f
+ }
+
+ remoteViews.apply {
+ setTextViewTextSize(R.id.luckyNumberWidgetValue, COMPLEX_UNIT_SP, luckyNumberTextSize)
+ setViewVisibility(R.id.luckyNumberWidgetHistoryButton, historyButtonVisibility)
}
}
override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
super.onDeleted(context, appWidgetIds)
appWidgetIds?.forEach { appWidgetId ->
- with(sharedPref) {
- delete(getHeightWidgetKey(appWidgetId))
- delete(getStudentWidgetKey(appWidgetId))
- delete(getThemeWidgetKey(appWidgetId))
- delete(getWidthWidgetKey(appWidgetId))
- }
+ sharedPref.delete(getStudentWidgetKey(appWidgetId))
}
}
- override fun onAppWidgetOptionsChanged(
- context: Context,
- appWidgetManager: AppWidgetManager,
- appWidgetId: Int,
- newOptions: Bundle?
- ) {
- super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
-
- val remoteView = RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context))
-
- setStyles(remoteView, appWidgetId, newOptions)
- appWidgetManager.updateAppWidget(appWidgetId, remoteView)
- }
-
- private fun setStyles(views: RemoteViews, appWidgetId: Int, options: Bundle? = null) {
- val width = options?.getInt(OPTION_APPWIDGET_MIN_WIDTH) ?: sharedPref.getLong(
- getWidthWidgetKey(appWidgetId), 74
- ).toInt()
- val height = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: sharedPref.getLong(
- getHeightWidgetKey(appWidgetId), 74
- ).toInt()
-
- with(sharedPref) {
- putLong(getWidthWidgetKey(appWidgetId), width.toLong())
- putLong(getHeightWidgetKey(appWidgetId), height.toLong())
- }
-
- val rows = getCellsForSize(height)
- val cols = getCellsForSize(width)
-
- Timber.d("New lucky number widget measurement: %dx%d", width, height)
- Timber.d("Widget size: $cols x $rows")
-
- when {
- 1 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = false)
- 1 == cols && 1 < rows -> views.setVisibility(imageTop = true, imageLeft = false)
- 1 < cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true)
- 1 == cols && 1 == rows -> views.setVisibility(imageTop = true, imageLeft = false)
- 2 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true)
- else -> views.setVisibility(imageTop = false, imageLeft = false, title = true)
- }
- }
-
- private fun RemoteViews.setVisibility(
- imageTop: Boolean,
- imageLeft: Boolean,
- title: Boolean = false
- ) {
- setViewVisibility(R.id.luckyNumberWidgetImageTop, if (imageTop) VISIBLE else GONE)
- setViewVisibility(R.id.luckyNumberWidgetImageLeft, if (imageLeft) VISIBLE else GONE)
- setViewVisibility(R.id.luckyNumberWidgetTitle, if (title) VISIBLE else GONE)
- setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
- }
-
- private fun getCellsForSize(size: Int): Int {
- var n = 2
- while (74 * n - 30 < size) ++n
- return n - 1
- }
-
private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking {
try {
val students = studentRepository.getSavedStudents()
@@ -181,22 +153,24 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
Resource.Success(null)
}
} catch (e: Exception) {
- if (e.cause !is NoCurrentStudentException) {
- Timber.e(e, "An error has occurred in lucky number provider")
- }
+ Timber.e(e, "An error has occurred in lucky number provider")
Resource.Error(e)
}
}
- private fun getCorrectLayoutId(appWidgetId: Int, context: Context): Int {
- val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
- val isSystemDarkMode =
- context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
+ private fun Bundle.getWidgetSize(context: Context): Pair {
+ val minWidth = getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
+ val maxWidth = getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
+ val minHeight = getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
+ val maxHeight = getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
- return if (savedTheme == 1L || (savedTheme == 2L && isSystemDarkMode)) {
- R.layout.widget_luckynumber_dark
+ val orientation = context.resources.configuration.orientation
+ val isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT
+
+ return if (isPortrait) {
+ minWidth to maxHeight
} else {
- R.layout.widget_luckynumber
+ maxWidth to minHeight
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt
index d332ee35..091080a5 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt
@@ -2,14 +2,14 @@ package io.github.wulkanowy.ui.modules.main
import android.content.Context
import android.content.Intent
-import android.os.Build.VERSION_CODES.P
+import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
+import android.view.ViewGroup.MarginLayoutParams
import androidx.activity.OnBackPressedCallback
import androidx.activity.addCallback
-import androidx.core.view.ViewCompat
-import androidx.core.view.isVisible
+import androidx.core.view.*
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.preference.Preference
@@ -27,6 +27,7 @@ import io.github.wulkanowy.databinding.DialogAdsConsentBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
+import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
import io.github.wulkanowy.utils.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@@ -89,8 +90,16 @@ class MainActivity : BaseActivity(), MainVie
super.onCreate(savedInstanceState)
setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root)
setSupportActionBar(binding.mainToolbar)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ binding.mainAppBar.isLifted = true
+ }
+ initializeFragmentContainer()
+
this.savedInstanceState = savedInstanceState
messageContainer = binding.mainMessageContainer
+ messageAnchor = binding.mainMessageContainer
updateHelper.messageContainer = binding.mainFragmentContainer
onBackCallback = onBackPressedDispatcher.addCallback(this, enabled = false) {
presenter.onBackPressed()
@@ -124,13 +133,20 @@ class MainActivity : BaseActivity(), MainVie
return true
}
- override fun initView(startMenuIndex: Int, rootDestinations: List) {
+ override fun initView(
+ startMenuIndex: Int,
+ rootAppMenuItems: List,
+ rootUpdatedDestinations: List
+ ) {
initializeToolbar()
- initializeBottomNavigation(startMenuIndex)
- initializeNavController(startMenuIndex, rootDestinations)
+ initializeBottomNavigation(startMenuIndex, rootAppMenuItems)
+ initializeNavController(startMenuIndex, rootUpdatedDestinations)
}
- private fun initializeNavController(startMenuIndex: Int, rootDestinations: List) {
+ private fun initializeNavController(
+ startMenuIndex: Int,
+ rootUpdatedDestinations: List
+ ) {
with(navController) {
setOnViewChangeListener { destinationView ->
presenter.onViewChange(destinationView)
@@ -140,7 +156,7 @@ class MainActivity : BaseActivity(), MainVie
)
}
fragmentHideStrategy = HIDE
- rootFragments = rootDestinations.map { it.destinationFragment }
+ rootFragments = rootUpdatedDestinations.map { it.destinationFragment }
initialize(startMenuIndex, savedInstanceState)
}
@@ -156,17 +172,16 @@ class MainActivity : BaseActivity(), MainVie
}
}
- private fun initializeBottomNavigation(startMenuIndex: Int) {
+ private fun initializeBottomNavigation(
+ startMenuIndex: Int,
+ rootAppMenuItems: List
+ ) {
with(binding.mainBottomNav) {
with(menu) {
- add(Menu.NONE, 0, Menu.NONE, R.string.dashboard_title)
- .setIcon(R.drawable.ic_main_dashboard)
- add(Menu.NONE, 1, Menu.NONE, R.string.grade_title)
- .setIcon(R.drawable.ic_main_grade)
- add(Menu.NONE, 2, Menu.NONE, R.string.attendance_title)
- .setIcon(R.drawable.ic_main_attendance)
- add(Menu.NONE, 3, Menu.NONE, R.string.timetable_title)
- .setIcon(R.drawable.ic_main_timetable)
+ rootAppMenuItems.forEachIndexed { index, item ->
+ add(Menu.NONE, index, Menu.NONE, item.title)
+ .setIcon(item.icon)
+ }
add(Menu.NONE, 4, Menu.NONE, R.string.more_title)
.setIcon(R.drawable.ic_main_more)
}
@@ -180,6 +195,17 @@ class MainActivity : BaseActivity(), MainVie
}
}
+ private fun initializeFragmentContainer() {
+ ViewCompat.setOnApplyWindowInsetsListener(binding.mainFragmentContainer) { view, insets ->
+ val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
+
+ view.updateLayoutParams {
+ bottomMargin = if (binding.mainBottomNav.isVisible) 0 else bottomInsets.bottom
+ }
+ WindowInsetsCompat.CONSUMED
+ }
+ }
+
override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat,
pref: Preference
@@ -224,20 +250,9 @@ class MainActivity : BaseActivity(), MainVie
showDialogFragment(AccountQuickDialog.newInstance(studentWithSemesters))
}
- override fun showActionBarElevation(show: Boolean) {
- ViewCompat.setElevation(binding.mainToolbar, if (show) dpToPx(4f) else 0f)
- }
-
override fun showBottomNavigation(show: Boolean) {
binding.mainBottomNav.isVisible = show
-
- if (appInfo.systemVersion >= P) {
- window.navigationBarColor = if (show) {
- getThemeAttrColor(android.R.attr.navigationBarColor)
- } else {
- getThemeAttrColor(R.attr.colorSurface)
- }
- }
+ binding.mainFragmentContainer.requestApplyInsets()
}
override fun openMoreDestination(destination: Destination) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt
index 458e966d..ae05ecf2 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt
@@ -14,9 +14,6 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.account.AccountView
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView
-import io.github.wulkanowy.ui.modules.grade.GradeView
-import io.github.wulkanowy.ui.modules.message.MessageView
-import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
import io.github.wulkanowy.utils.AdsHelper
import io.github.wulkanowy.utils.AnalyticsHelper
@@ -42,17 +39,16 @@ class MainPresenter @Inject constructor(
private var studentsWitSemesters: List? = null
- private val rootDestinationTypeList = listOf(
- Destination.Type.DASHBOARD,
- Destination.Type.GRADE,
- Destination.Type.ATTENDANCE,
- Destination.Type.TIMETABLE,
- Destination.Type.MORE
- )
+ private val rootAppMenuItems = preferencesRepository.appMenuItemOrder
+ .sortedBy { it.order }
+ .take(4)
+
+ private val rootDestinationTypeList = rootAppMenuItems.map { it.destinationType }
+ .plus(Destination.Type.MORE)
private val Destination?.startMenuIndex
get() = when {
- this == null -> preferencesRepository.startMenuIndex
+ this == null -> 0
destinationType in rootDestinationTypeList -> {
rootDestinationTypeList.indexOf(destinationType)
}
@@ -69,7 +65,7 @@ class MainPresenter @Inject constructor(
if (it == initDestination?.destinationType) initDestination else it.defaultDestination
}
- view.initView(startMenuIndex, destinations)
+ view.initView(startMenuIndex, rootAppMenuItems, destinations)
if (initDestination != null && startMenuIndex == 4) {
view.openMoreDestination(initDestination)
}
@@ -101,7 +97,6 @@ class MainPresenter @Inject constructor(
fun onViewChange(destinationView: BaseView) {
view?.apply {
showBottomNavigation(shouldShowBottomNavigation(destinationView))
- showActionBarElevation(shouldShowActionBarElevation(destinationView))
currentViewTitle?.let { setViewTitle(it) }
currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) }
currentStackSize?.let {
@@ -111,13 +106,6 @@ class MainPresenter @Inject constructor(
}
}
- private fun shouldShowActionBarElevation(destination: BaseView) = when (destination) {
- is GradeView,
- is MessageView,
- is SchoolAndTeachersView -> false
- else -> true
- }
-
private fun shouldShowBottomNavigation(destination: BaseView) = when (destination) {
is AccountView,
is StudentInfoView,
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt
index 3d018e3d..62436f3b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt
@@ -4,6 +4,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
import io.github.wulkanowy.ui.modules.Destination
+import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
interface MainView : BaseView {
@@ -15,7 +16,11 @@ interface MainView : BaseView {
val currentStackSize: Int?
- fun initView(startMenuIndex: Int, rootDestinations: List)
+ fun initView(
+ startMenuIndex: Int,
+ rootAppMenuItems: List,
+ rootUpdatedDestinations: List
+ )
fun switchMenuView(position: Int)
@@ -23,8 +28,6 @@ interface MainView : BaseView {
fun showAccountPicker(studentWithSemesters: List)
- fun showActionBarElevation(show: Boolean)
-
fun showBottomNavigation(show: Boolean)
fun notifyMenuViewReselected()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt
index 4607793c..4317fb7f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt
@@ -15,6 +15,7 @@ import io.github.wulkanowy.data.enums.MessageFolder.*
import io.github.wulkanowy.databinding.FragmentMessageBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
+import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment
@@ -24,7 +25,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class MessageFragment : BaseFragment(R.layout.fragment_message),
- MessageView, MainView.TitledView {
+ MessageView, MainView.TitledView, MainView.MainChildView {
@Inject
lateinit var presenter: MessagePresenter
@@ -123,8 +124,12 @@ class MessageFragment : BaseFragment(R.layout.fragment_m
presenter.onChildViewShowNewMessage(show)
}
- fun onFragmentChanged() {
- presenter.onFragmentChanged()
+ override fun onFragmentReselected() {
+ if (::presenter.isInitialized) presenter.onFragmentReselected()
+ }
+
+ override fun onFragmentChanged() {
+ if (::presenter.isInitialized) presenter.onFragmentChanged()
}
override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) {
@@ -139,10 +144,19 @@ class MessageFragment : BaseFragment(R.layout.fragment_m
}
}
+ override fun notifyChildParentReselected(index: Int) {
+ (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)
+ ?.onParentReselected()
+ }
+
override fun openSendMessage() {
context?.let { it.startActivity(SendMessageActivity.getStartIntent(it)) }
}
+ override fun popView() {
+ (activity as? MainActivity)?.popView()
+ }
+
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt
index 68bdc4b7..cf6bad19 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt
@@ -39,6 +39,14 @@ class MessagePresenter @Inject constructor(
view?.notifyChildrenFinishActionMode()
}
+ fun onFragmentReselected() {
+ Timber.i("Message view is reselected")
+ view?.run {
+ popView()
+ notifyChildParentReselected(currentPageIndex)
+ }
+ }
+
fun onChildViewLoaded() {
view?.apply {
showContent(true)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt
index e0cc5098..def4a275 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt
@@ -20,5 +20,9 @@ interface MessageView : BaseView {
fun notifyChildrenFinishActionMode()
+ fun notifyChildParentReselected(index: Int)
+
fun openSendMessage()
+
+ fun popView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt
index 37f9a19b..8bd84f2b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt
@@ -1,11 +1,11 @@
package io.github.wulkanowy.ui.modules.message.mailboxchooser
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.fragment.app.setFragmentResult
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.databinding.DialogMailboxChooserBinding
@@ -37,19 +37,19 @@ class MailboxChooserDialog : BaseDialogFragment(),
}
}
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(
+ DialogMailboxChooserBinding.inflate(layoutInflater).apply { binding = this }.root
+ )
+ .create()
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogMailboxChooserBinding.inflate(inflater).apply { binding = this }.root
@Suppress("UNCHECKED_CAST")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(
view = this,
requireMailbox = requireArguments().getBoolean(REQUIRED_KEY, false),
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
index 14f3d718..28147fae 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
@@ -1,10 +1,10 @@
package io.github.wulkanowy.ui.modules.message.send
import android.annotation.SuppressLint
-import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.graphics.Rect
+import android.os.Build
import android.os.Bundle
import android.text.Spanned
import android.view.Menu
@@ -12,11 +12,14 @@ import android.view.MenuItem
import android.view.TouchDelegate
import android.view.View.GONE
import android.view.View.VISIBLE
+import android.view.ViewGroup
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.core.text.parseAsHtml
import androidx.core.text.toHtml
+import androidx.core.view.*
import androidx.core.widget.doOnTextChanged
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Mailbox
@@ -24,8 +27,8 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.databinding.ActivitySendMessageBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog
-import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.MAILBOX_KEY
import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.LISTENER_KEY
+import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.MAILBOX_KEY
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.nullableSerializable
@@ -99,6 +102,13 @@ class SendMessageActivity : BaseActivity= Build.VERSION_CODES.R) {
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ binding.sendAppBar.isLifted = true
+ }
+ initializeMessageContainer()
+
messageContainer = binding.sendMessageContainer
formRecipientsData = binding.sendMessageTo.addedChipItems as List
@@ -130,6 +140,17 @@ class SendMessageActivity : BaseActivity
+ val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
+
+ view.updateLayoutParams {
+ bottomMargin = bottomInsets.bottom
+ }
+ WindowInsetsCompat.CONSUMED
+ }
+ }
+
private fun onMessageSubjectChange(text: CharSequence?) {
formSubjectValue = text.toString()
presenter.onMessageContentChange()
@@ -252,7 +273,7 @@ class SendMessageActivity : BaseActivity presenter.restoreMessageParts() }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt
index 6df6153c..9792c708 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt
@@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.message.tab
+import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.Typeface
import android.view.LayoutInflater
@@ -68,21 +69,23 @@ class MessageTabAdapter @Inject constructor() :
}
}
+ @SuppressLint("PrivateResource")
private fun bindHeaderViewHolder(holder: HeaderViewHolder, position: Int) {
val item = items[position] as MessageTabDataItem.FilterHeader
+ val context = holder.binding.root.context
with(holder.binding) {
- chipMailbox.text = item.selectedMailbox
- ?: root.context.getString(R.string.message_chip_all_mailboxes)
+ chipMailbox.text =
+ item.selectedMailbox ?: context.getString(R.string.message_chip_all_mailboxes)
chipMailbox.chipBackgroundColor = ColorStateList.valueOf(
if (item.selectedMailbox == null) {
- root.context.getCompatColor(R.color.mtrl_choice_chip_background_color)
- } else root.context.getThemeAttrColor(android.R.attr.colorPrimary, 64)
+ context.getCompatColor(R.color.m3_elevated_chip_background_color)
+ } else context.getThemeAttrColor(R.attr.colorPrimary, 64)
)
chipMailbox.setTextColor(
if (item.selectedMailbox == null) {
- root.context.getThemeAttrColor(android.R.attr.textColorPrimary)
- } else root.context.getThemeAttrColor(android.R.attr.colorPrimary)
+ context.getThemeAttrColor(R.attr.colorOnSurfaceVariant)
+ } else context.getThemeAttrColor(R.attr.colorPrimary)
)
chipMailbox.setOnClickListener { onMailboxClickListener() }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt
index c78ccc6e..592cbd60 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt
@@ -235,6 +235,10 @@ class MessageTabFragment : BaseFragment(R.layout.frag
presenter.onParentFinishActionMode()
}
+ fun onParentReselected() {
+ presenter.onParentReselected()
+ }
+
private fun onChipChecked(chip: CompoundButton, isChecked: Boolean) {
when (chip.id) {
R.id.chip_unread -> presenter.onUnreadFilterSelected(isChecked)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
index ea142db2..ec92e9c2 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
@@ -83,6 +83,14 @@ class MessageTabPresenter @Inject constructor(
view?.showActionMode(false)
}
+ fun onParentReselected() {
+ view?.run {
+ if (!isViewEmpty) {
+ resetListPosition()
+ }
+ }
+ }
+
fun onDestroyActionMode() {
isActionMode = false
messagesToDelete.clear()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt
index f8e367c5..1afe773c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt
@@ -22,7 +22,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class MobileDeviceFragment :
BaseFragment(R.layout.fragment_mobile_device), MobileDeviceView,
- MainView.TitledView {
+ MainView.TitledView, MainView.MainChildView {
@Inject
lateinit var presenter: MobileDevicePresenter
@@ -135,6 +135,14 @@ class MobileDeviceFragment :
(activity as? MainActivity)?.showDialogFragment(MobileDeviceTokenDialog.newInstance())
}
+ override fun onFragmentReselected() {
+ if (::presenter.isInitialized) presenter.onFragmentReselected()
+ }
+
+ override fun resetView() {
+ binding.mobileDevicesRecycler.smoothScrollToPosition(0)
+ }
+
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt
index 36a720e5..56785dbf 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt
@@ -129,4 +129,10 @@ class MobileDevicePresenter @Inject constructor(
.onResourceError(errorHandler::dispatch)
.launch("unregister")
}
+
+ fun onFragmentReselected() {
+ if (view?.isViewEmpty == false) {
+ view?.resetView()
+ }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt
index b94646a7..973cb123 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt
@@ -32,4 +32,6 @@ interface MobileDeviceView : BaseView {
fun setErrorDetails(message: String)
fun showTokenDialog()
+
+ fun resetView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt
index eb420a6a..2cc2a2aa 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt
@@ -1,17 +1,17 @@
package io.github.wulkanowy.ui.modules.mobiledevice.token
+import android.app.Dialog
import android.content.ClipData
import android.content.ClipboardManager
import android.graphics.BitmapFactory
import android.os.Bundle
import android.util.Base64
-import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
-import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.getSystemService
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.pojos.MobileDeviceToken
@@ -31,17 +31,14 @@ class MobileDeviceTokenDialog : BaseDialogFragment(),
fun newInstance() = MobileDeviceTokenDialog()
}
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(
+ DialogMobileDeviceBinding.inflate(layoutInflater).apply { binding = this }.root
+ )
+ .create()
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogMobileDeviceBinding.inflate(inflater).apply { binding = this }.root
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt
index 70587b0c..697eab33 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt
@@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.more
-import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
@@ -9,9 +8,9 @@ import javax.inject.Inject
class MoreAdapter @Inject constructor() : RecyclerView.Adapter() {
- var items = emptyList>()
+ var items = emptyList()
- var onClickListener: (name: String) -> Unit = {}
+ var onClickListener: (moreItem: MoreItem) -> Unit = {}
override fun getItemCount() = items.size
@@ -20,13 +19,14 @@ class MoreAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragment_more),
override val titleStringId: Int
get() = R.string.more_title
- override val messagesRes: Pair?
- get() = context?.run { getString(R.string.message_title) to getCompatDrawable(R.drawable.ic_more_messages) }
-
- override val homeworkRes: Pair?
- get() = context?.run { getString(R.string.homework_title) to getCompatDrawable(R.drawable.ic_more_homework) }
-
- override val noteRes: Pair?
- get() = context?.run { getString(R.string.note_title) to getCompatDrawable(R.drawable.ic_more_note) }
-
- override val conferencesRes: Pair?
- get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) }
-
- override val schoolAnnouncementRes: Pair?
- get() = context?.run { getString(R.string.school_announcement_title) to getCompatDrawable(R.drawable.ic_all_about) }
-
- override val schoolAndTeachersRes: Pair?
- get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) }
-
- override val mobileDevicesRes: Pair?
- get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) }
-
- override val settingsRes: Pair?
- get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) }
-
- override val examRes: Pair?
- get() = context?.run { getString(R.string.exam_title) to getCompatDrawable(R.drawable.ic_main_exam) }
-
- override val luckyNumberRes: Pair?
- get() = context?.run { getString(R.string.lucky_number_title) to getCompatDrawable(R.drawable.ic_more_lucky_number) }
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentMoreBinding.bind(view)
@@ -94,57 +54,21 @@ class MoreFragment : BaseFragment(R.layout.fragment_more),
?.onFragmentChanged()
}
- override fun updateData(data: List>) {
+ override fun updateData(data: List) {
with(moreAdapter) {
items = data
notifyDataSetChanged()
}
}
- override fun openMessagesView() {
- (activity as? MainActivity)?.pushView(MessageFragment.newInstance())
- }
-
- override fun openHomeworkView() {
- (activity as? MainActivity)?.pushView(HomeworkFragment.newInstance())
- }
-
- override fun openNoteView() {
- (activity as? MainActivity)?.pushView(NoteFragment.newInstance())
- }
-
- override fun openSchoolAnnouncementView() {
- (activity as? MainActivity)?.pushView(SchoolAnnouncementFragment.newInstance())
- }
-
- override fun openConferencesView() {
- (activity as? MainActivity)?.pushView(ConferenceFragment.newInstance())
- }
-
- override fun openSchoolAndTeachersView() {
- (activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance())
- }
-
- override fun openMobileDevicesView() {
- (activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance())
- }
-
- override fun openSettingsView() {
- (activity as? MainActivity)?.pushView(SettingsFragment.newInstance())
- }
-
- override fun openExamView() {
- (activity as? MainActivity)?.pushView(ExamFragment.newInstance())
- }
-
- override fun openLuckyNumberView() {
- (activity as? MainActivity)?.pushView(LuckyNumberFragment.newInstance())
- }
-
override fun popView(depth: Int) {
(activity as? MainActivity)?.popView(depth)
}
+ override fun openView(destination: Destination) {
+ (activity as? MainActivity)?.pushView(destination.destinationFragment)
+ }
+
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt
new file mode 100644
index 00000000..d544f041
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt
@@ -0,0 +1,12 @@
+package io.github.wulkanowy.ui.modules.more
+
+import io.github.wulkanowy.ui.modules.Destination
+
+data class MoreItem(
+
+ val icon: Int,
+
+ val title: Int,
+
+ val destination: Destination
+)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt
index 92551d6e..0ebaf4c7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt
@@ -1,16 +1,24 @@
package io.github.wulkanowy.ui.modules.more
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.ui.modules.Destination
import timber.log.Timber
import javax.inject.Inject
class MorePresenter @Inject constructor(
errorHandler: ErrorHandler,
- studentRepository: StudentRepository
+ studentRepository: StudentRepository,
+ preferencesRepository: PreferencesRepository
) : BasePresenter(errorHandler, studentRepository) {
+ private val moreAppMenuItem = preferencesRepository.appMenuItemOrder
+ .sortedBy { it.order }
+ .drop(4)
+
override fun onAttachView(view: MoreView) {
super.onAttachView(view)
view.initView()
@@ -18,22 +26,10 @@ class MorePresenter @Inject constructor(
loadData()
}
- fun onItemSelected(title: String) {
- Timber.i("Select more item \"${title}\"")
- view?.run {
- when (title) {
- messagesRes?.first -> openMessagesView()
- examRes?.first -> openExamView()
- homeworkRes?.first -> openHomeworkView()
- noteRes?.first -> openNoteView()
- conferencesRes?.first -> openConferencesView()
- schoolAnnouncementRes?.first -> openSchoolAnnouncementView()
- schoolAndTeachersRes?.first -> openSchoolAndTeachersView()
- mobileDevicesRes?.first -> openMobileDevicesView()
- settingsRes?.first -> openSettingsView()
- luckyNumberRes?.first -> openLuckyNumberView()
- }
- }
+ fun onItemSelected(moreItem: MoreItem) {
+ Timber.i("Select more item \"${moreItem.destination.destinationType}\"")
+
+ view?.openView(moreItem.destination)
}
fun onViewReselected() {
@@ -43,19 +39,21 @@ class MorePresenter @Inject constructor(
private fun loadData() {
Timber.i("Load items for more view")
- view?.run {
- updateData(listOfNotNull(
- messagesRes,
- examRes,
- homeworkRes,
- noteRes,
- luckyNumberRes,
- conferencesRes,
- schoolAnnouncementRes,
- schoolAndTeachersRes,
- mobileDevicesRes,
- settingsRes
- ))
+ val moreItems = moreAppMenuItem.map {
+ MoreItem(
+ icon = it.icon,
+ title = it.title,
+ destination = it.destinationType.defaultDestination
+ )
}
+ .plus(
+ MoreItem(
+ icon = R.drawable.ic_more_settings,
+ title = R.string.settings_title,
+ destination = Destination.Settings
+ )
+ )
+
+ view?.updateData(moreItems)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt
index cb895de2..fbca97ed 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt
@@ -1,53 +1,15 @@
package io.github.wulkanowy.ui.modules.more
-import android.graphics.drawable.Drawable
import io.github.wulkanowy.ui.base.BaseView
+import io.github.wulkanowy.ui.modules.Destination
interface MoreView : BaseView {
- val messagesRes: Pair?
-
- val homeworkRes: Pair?
-
- val noteRes: Pair?
-
- val conferencesRes: Pair?
-
- val schoolAnnouncementRes: Pair?
-
- val schoolAndTeachersRes: Pair?
-
- val mobileDevicesRes: Pair?
-
- val settingsRes: Pair?
-
- val examRes: Pair?
-
- val luckyNumberRes: Pair?
-
fun initView()
- fun updateData(data: List>)
-
- fun openSettingsView()
+ fun updateData(data: List)
fun popView(depth: Int)
- fun openMessagesView()
-
- fun openHomeworkView()
-
- fun openNoteView()
-
- fun openSchoolAnnouncementView()
-
- fun openConferencesView()
-
- fun openSchoolAndTeachersView()
-
- fun openMobileDevicesView()
-
- fun openExamView()
-
- fun openLuckyNumberView()
+ fun openView(destination: Destination)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt
index e46ab42c..0592e924 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt
@@ -1,25 +1,24 @@
package io.github.wulkanowy.ui.modules.note
import android.annotation.SuppressLint
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
-import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.databinding.DialogNoteBinding
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
+import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.lifecycleAwareVariable
import io.github.wulkanowy.utils.serializable
import io.github.wulkanowy.utils.toFormattedString
-class NoteDialog : DialogFragment() {
-
- private var binding: DialogNoteBinding by lifecycleAwareVariable()
+@AndroidEntryPoint
+class NoteDialog : BaseDialogFragment() {
private lateinit var note: Note
@@ -34,15 +33,14 @@ class NoteDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
note = requireArguments().serializable(ARGUMENT_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogNoteBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogNoteBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
+ }
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt
index dd622344..cb54b384 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt
@@ -18,7 +18,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class NoteFragment : BaseFragment(R.layout.fragment_note), NoteView,
- MainView.TitledView {
+ MainView.TitledView, MainView.MainChildView {
@Inject
lateinit var presenter: NotePresenter
@@ -112,6 +112,14 @@ class NoteFragment : BaseFragment(R.layout.fragment_note),
binding.noteSwipe.isRefreshing = show
}
+ override fun onFragmentReselected() {
+ if (::presenter.isInitialized) presenter.onFragmentReselected()
+ }
+
+ override fun resetView() {
+ binding.noteRecycler.smoothScrollToPosition(0)
+ }
+
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt
index 440565e1..62ad347f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt
@@ -121,4 +121,10 @@ class NotePresenter @Inject constructor(
}
.launch("update_note")
}
+
+ fun onFragmentReselected() {
+ if (view?.isViewEmpty == false) {
+ view?.resetView()
+ }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt
index 9fc0be94..b813b315 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt
@@ -30,4 +30,6 @@ interface NoteView : BaseView {
fun showRefresh(show: Boolean)
fun showNoteDialog(note: Note)
+
+ fun resetView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt
index 163ba8cd..0763d4fa 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt
@@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.modules.notifications
import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
-import androidx.appcompat.app.AlertDialog
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.FragmentNotificationsBinding
@@ -41,7 +41,7 @@ class NotificationsFragment :
}
private fun showSettingsDialog() {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.notifications_header_title)
.setMessage(R.string.notifications_header_description)
.setNegativeButton(R.string.notifications_skip) { dialog, _ ->
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt
index e33a48f0..c1c58441 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt
@@ -1,21 +1,20 @@
package io.github.wulkanowy.ui.modules.schoolannouncement
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.os.bundleOf
-import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import io.github.wulkanowy.databinding.DialogSchoolAnnouncementBinding
-import io.github.wulkanowy.utils.lifecycleAwareVariable
+import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.parseUonetHtml
import io.github.wulkanowy.utils.serializable
import io.github.wulkanowy.utils.toFormattedString
-class SchoolAnnouncementDialog : DialogFragment() {
-
- private var binding: DialogSchoolAnnouncementBinding by lifecycleAwareVariable()
+@AndroidEntryPoint
+class SchoolAnnouncementDialog : BaseDialogFragment() {
private lateinit var announcement: SchoolAnnouncement
@@ -30,15 +29,17 @@ class SchoolAnnouncementDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
announcement = requireArguments().serializable(ARGUMENT_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogSchoolAnnouncementBinding.inflate(inflater).also { binding = it }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(
+ DialogSchoolAnnouncementBinding.inflate(layoutInflater)
+ .apply { binding = this }.root
+ )
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt
new file mode 100644
index 00000000..f522913a
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt
@@ -0,0 +1,206 @@
+package io.github.wulkanowy.ui.modules.settings.appearance.menuorder
+
+import io.github.wulkanowy.R
+import io.github.wulkanowy.ui.modules.Destination
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.Transient
+
+@Serializable
+sealed class AppMenuItem {
+
+ companion object {
+ val defaultAppMenuItemList = setOf(
+ DashboardAppMenuItem(),
+ GradeAppMenuItem(),
+ TimetableAppMenuItem(),
+ AttendanceAppMenuItem(),
+ ExamsAppMenuItem(),
+ HomeworkAppMenuItem(),
+ NoteAppMenuItem(),
+ LuckyNumberAppMenuItem(),
+ SchoolAnnouncementsAppMenuItem(),
+ SchoolAndTeachersAppMenuItem(),
+ MobileDevicesAppMenuItem(),
+ ConferenceAppMenuItem(),
+ MessageAppMenuItem()
+ ).sortedBy { it.order }
+ }
+
+ // https://youtrack.jetbrains.com/issue/KT-38958
+ abstract var order: Int
+
+ abstract val icon: Int
+
+ abstract val title: Int
+
+ abstract val destinationType: Destination.Type
+
+ @Serializable
+ data class DashboardAppMenuItem(override var order: Int = 0) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_main_dashboard
+
+ @Transient
+ override val title = R.string.dashboard_title
+
+ @Transient
+ override val destinationType = Destination.Type.DASHBOARD
+ }
+
+ @Serializable
+ data class GradeAppMenuItem(override var order: Int = 1) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_main_grade
+
+ @Transient
+ override val title = R.string.grade_title
+
+ @Transient
+ override val destinationType = Destination.Type.GRADE
+ }
+
+ @Serializable
+ data class AttendanceAppMenuItem(override var order: Int = 2) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_main_attendance
+
+ @Transient
+ override val title = R.string.attendance_title
+
+ @Transient
+ override val destinationType = Destination.Type.ATTENDANCE
+ }
+
+ @Serializable
+ data class TimetableAppMenuItem(override var order: Int = 3) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_main_timetable
+
+ @Transient
+ override val title = R.string.timetable_title
+
+ @Transient
+ override val destinationType = Destination.Type.TIMETABLE
+ }
+
+ @Serializable
+ data class MessageAppMenuItem(override var order: Int = 4) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_more_messages
+
+ @Transient
+ override val title = R.string.message_title
+
+ @Transient
+ override val destinationType = Destination.Type.MESSAGE
+ }
+
+ @Serializable
+ data class ExamsAppMenuItem(override var order: Int = 5) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_main_exam
+
+ @Transient
+ override val title = R.string.exam_title
+
+ @Transient
+ override val destinationType = Destination.Type.EXAM
+ }
+
+ @Serializable
+ data class HomeworkAppMenuItem(override var order: Int = 6) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_more_homework
+
+ @Transient
+ override val title = R.string.homework_title
+
+ @Transient
+ override val destinationType = Destination.Type.HOMEWORK
+ }
+
+ @Serializable
+ data class NoteAppMenuItem(override var order: Int = 7) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_more_note
+
+ @Transient
+ override val title = R.string.note_title
+
+ @Transient
+ override val destinationType = Destination.Type.NOTE
+ }
+
+ @Serializable
+ data class LuckyNumberAppMenuItem(override var order: Int = 8) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_more_lucky_number
+
+ @Transient
+ override val title = R.string.lucky_number_title
+
+ @Transient
+ override val destinationType = Destination.Type.LUCKY_NUMBER
+ }
+
+ @Serializable
+ data class ConferenceAppMenuItem(override var order: Int = 9) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_more_conferences
+
+ @Transient
+ override val title = R.string.conferences_title
+
+ @Transient
+ override val destinationType = Destination.Type.CONFERENCE
+ }
+
+ @Serializable
+ data class SchoolAnnouncementsAppMenuItem(override var order: Int = 10) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_all_about
+
+ @Transient
+ override val title = R.string.school_announcement_title
+
+ @Transient
+ override val destinationType = Destination.Type.SCHOOL_ANNOUNCEMENT
+ }
+
+ @Serializable
+ data class SchoolAndTeachersAppMenuItem(override var order: Int = 11) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_more_schoolandteachers
+
+ @Transient
+ override val title = R.string.schoolandteachers_title
+
+ @Transient
+ override val destinationType = Destination.Type.SCHOOL_AND_TEACHERS
+ }
+
+ @Serializable
+ data class MobileDevicesAppMenuItem(override var order: Int = 12) : AppMenuItem() {
+
+ @Transient
+ override val icon = R.drawable.ic_more_mobile_devices
+
+ @Transient
+ override val title = R.string.mobile_devices_title
+
+ @Transient
+ override val destinationType = Destination.Type.MOBILE_DEVICE
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt
new file mode 100644
index 00000000..49196fad
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt
@@ -0,0 +1,44 @@
+package io.github.wulkanowy.ui.modules.settings.appearance.menuorder
+
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.RecyclerView
+import java.util.*
+
+class MenuItemMoveCallback(
+ private val menuOrderAdapter: MenuOrderAdapter,
+ private var onUserInteractionEndListener: (List) -> Unit = {}
+) : ItemTouchHelper.Callback() {
+
+ override fun isLongPressDragEnabled() = true
+
+ override fun isItemViewSwipeEnabled() = false
+
+ override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
+ //Not implemented
+ }
+
+ override fun getMovementFlags(
+ recyclerView: RecyclerView,
+ viewHolder: RecyclerView.ViewHolder
+ ) = makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0)
+
+ override fun onMove(
+ recyclerView: RecyclerView,
+ viewHolder: RecyclerView.ViewHolder,
+ target: RecyclerView.ViewHolder
+ ): Boolean {
+ val list = menuOrderAdapter.items.toMutableList()
+
+ Collections.swap(list, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
+
+ menuOrderAdapter.submitList(list)
+ return true
+ }
+
+ override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
+ super.clearView(recyclerView, viewHolder)
+
+ onUserInteractionEndListener(menuOrderAdapter.items.toList())
+ }
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt
new file mode 100644
index 00000000..6bdd2fe0
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt
@@ -0,0 +1,58 @@
+package io.github.wulkanowy.ui.modules.settings.appearance.menuorder
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import io.github.wulkanowy.databinding.ItemMenuOrderBinding
+import javax.inject.Inject
+
+class MenuOrderAdapter @Inject constructor() :
+ RecyclerView.Adapter() {
+
+ val items = mutableListOf()
+
+ fun submitList(newItems: List) {
+ val diffResult = DiffUtil.calculateDiff(DiffCallback(newItems, items.toMutableList()))
+
+ with(items) {
+ clear()
+ addAll(newItems)
+ }
+
+ diffResult.dispatchUpdatesTo(this)
+ }
+
+ override fun getItemCount() = items.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
+ ItemMenuOrderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ )
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = items[position].appMenuItem
+
+ with(holder.binding) {
+ menuOrderItemTitle.setText(item.title)
+ menuOrderItemIcon.setImageResource(item.icon)
+ }
+ }
+
+ class ViewHolder(val binding: ItemMenuOrderBinding) : RecyclerView.ViewHolder(binding.root)
+
+ private class DiffCallback(
+ private val oldList: List,
+ private val newList: List
+ ) : DiffUtil.Callback() {
+
+ override fun getNewListSize() = newList.size
+
+ override fun getOldListSize() = oldList.size
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
+ oldList[oldItemPosition] == newList[newItemPosition]
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
+ oldList[oldItemPosition].appMenuItem.destinationType == newList[newItemPosition].appMenuItem.destinationType
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt
new file mode 100644
index 00000000..21a7f5e8
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt
@@ -0,0 +1,50 @@
+package io.github.wulkanowy.ui.modules.settings.appearance.menuorder
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.drawable.ShapeDrawable
+import android.view.View
+import androidx.core.graphics.drawable.DrawableCompat
+import androidx.core.view.forEach
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.RecyclerView
+import io.github.wulkanowy.R
+import io.github.wulkanowy.utils.getThemeAttrColor
+
+class MenuOrderDividerItemDecoration(private val context: Context) :
+ DividerItemDecoration(context, VERTICAL) {
+
+ private val dividerDrawable = ShapeDrawable()
+ .apply {
+ DrawableCompat.setTint(this, context.getThemeAttrColor(R.attr.colorDivider))
+ }
+
+ override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
+ canvas.save()
+ val dividerLeft = parent.paddingLeft
+ val dividerRight = parent.width - parent.paddingRight
+
+ parent.forEach {
+ if (parent.getChildAdapterPosition(it) == 3) {
+ val params = it.layoutParams as RecyclerView.LayoutParams
+ val dividerTop = it.bottom + params.bottomMargin
+ val dividerBottom = dividerTop + dividerDrawable.intrinsicHeight
+
+ dividerDrawable.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom + 30)
+ dividerDrawable.draw(canvas)
+ }
+ }
+
+ canvas.restore()
+ }
+
+ override fun getItemOffsets(
+ outRect: Rect, view: View, parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ if (parent.getChildAdapterPosition(view) == 3) {
+ outRect.bottom = dividerDrawable.intrinsicHeight + 30
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt
new file mode 100644
index 00000000..e08fc5dd
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt
@@ -0,0 +1,101 @@
+package io.github.wulkanowy.ui.modules.settings.appearance.menuorder
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.view.View
+import androidx.activity.addCallback
+import androidx.core.view.MenuProvider
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.SimpleItemAnimator
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.databinding.FragmentMenuOrderBinding
+import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.ui.widgets.DividerItemDecoration
+import javax.inject.Inject
+
+
+@AndroidEntryPoint
+class MenuOrderFragment : BaseFragment(R.layout.fragment_menu_order),
+ MenuOrderView, MainView.TitledView {
+
+ @Inject
+ lateinit var presenter: MenuOrderPresenter
+
+ @Inject
+ lateinit var menuOrderAdapter: MenuOrderAdapter
+
+ override val titleStringId = R.string.menu_order_title
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding = FragmentMenuOrderBinding.bind(view)
+ presenter.onAttachView(this)
+ }
+
+ override fun initView() {
+ val itemTouchHelper = ItemTouchHelper(
+ MenuItemMoveCallback(menuOrderAdapter, presenter::onDragAndDropEnd)
+ )
+
+ itemTouchHelper.attachToRecyclerView(binding.menuOrderRecycler)
+
+ with(binding.menuOrderRecycler) {
+ layoutManager = LinearLayoutManager(context)
+ adapter = menuOrderAdapter
+ addItemDecoration(MenuOrderDividerItemDecoration(context))
+ addItemDecoration(DividerItemDecoration(context))
+ (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
+ }
+
+ requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
+ presenter.onBackSelected()
+ }
+
+ initializeToolbar()
+ }
+
+ private fun initializeToolbar() {
+ requireActivity().addMenuProvider(object : MenuProvider {
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ }
+
+ override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
+ if (menuItem.itemId == android.R.id.home) {
+ presenter.onBackSelected()
+ return true
+ }
+ return false
+ }
+
+ }, viewLifecycleOwner)
+ }
+
+ override fun updateData(data: List) {
+ menuOrderAdapter.submitList(data)
+ }
+
+ override fun restartApp() {
+ startActivity(MainActivity.getStartIntent(requireContext()))
+ requireActivity().finishAffinity()
+ }
+
+ override fun popView() {
+ (activity as? MainActivity?)?.popView()
+ }
+
+ override fun showRestartConfirmationDialog() {
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(R.string.menu_order_confirm_title)
+ .setMessage(R.string.menu_order_confirm_content)
+ .setPositiveButton(R.string.menu_order_confirm_restart) { _, _ -> presenter.onConfirmRestart() }
+ .setNegativeButton(R.string.all_cancel) { _, _ -> presenter.onCancelRestart() }
+ .show()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt
new file mode 100644
index 00000000..b82bc1bd
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt
@@ -0,0 +1,6 @@
+package io.github.wulkanowy.ui.modules.settings.appearance.menuorder
+
+data class MenuOrderItem(
+ val appMenuItem: AppMenuItem,
+ val order: Int
+)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt
new file mode 100644
index 00000000..1c90cc5e
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt
@@ -0,0 +1,64 @@
+package io.github.wulkanowy.ui.modules.settings.appearance.menuorder
+
+import io.github.wulkanowy.data.repositories.PreferencesRepository
+import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import timber.log.Timber
+import javax.inject.Inject
+
+class MenuOrderPresenter @Inject constructor(
+ studentRepository: StudentRepository,
+ errorHandler: ErrorHandler,
+ private val preferencesRepository: PreferencesRepository
+) : BasePresenter(errorHandler, studentRepository) {
+
+ private var updatedMenuOrderItems = emptyList()
+
+ override fun onAttachView(view: MenuOrderView) {
+ super.onAttachView(view)
+ view.initView()
+ Timber.i("Menu order view was initialized")
+ loadData()
+ }
+
+ private fun loadData() {
+ val savedMenuItemList = (preferencesRepository.appMenuItemOrder)
+ .sortedBy { it.order }
+ .map { MenuOrderItem(it, it.order) }
+
+ view?.updateData(savedMenuItemList)
+ }
+
+ fun onDragAndDropEnd(list: List) {
+ val updatedList = list.mapIndexed { index, menuOrderItem ->
+ menuOrderItem.copy(order = index)
+ }
+
+ updatedMenuOrderItems = updatedList
+ view?.updateData(updatedList)
+ }
+
+ fun onBackSelected() {
+ if (updatedMenuOrderItems.isNotEmpty()) {
+ view?.showRestartConfirmationDialog()
+ } else {
+ view?.popView()
+ }
+ }
+
+ fun onConfirmRestart() {
+ updatedMenuOrderItems.forEach {
+ it.appMenuItem.apply {
+ order = it.order
+ }
+ }
+
+ preferencesRepository.appMenuItemOrder = updatedMenuOrderItems.map { it.appMenuItem }
+ view?.restartApp()
+ }
+
+ fun onCancelRestart() {
+ view?.popView()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt
new file mode 100644
index 00000000..264e68cc
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt
@@ -0,0 +1,16 @@
+package io.github.wulkanowy.ui.modules.settings.appearance.menuorder
+
+import io.github.wulkanowy.ui.base.BaseView
+
+interface MenuOrderView : BaseView {
+
+ fun initView()
+
+ fun updateData(data: List)
+
+ fun restartApp()
+
+ fun showRestartConfirmationDialog()
+
+ fun popView()
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt
index 77a3c6cf..98ac1573 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt
@@ -8,13 +8,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AlertDialog
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.thelittlefireman.appkillermanager.AppKillerManager
import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException
import dagger.hilt.android.AndroidEntryPoint
@@ -149,7 +149,7 @@ class NotificationsFragment : PreferenceFragmentCompat(),
}
override fun showFixSyncDialog() {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.pref_notify_fix_sync_issues)
.setMessage(R.string.pref_notify_fix_sync_issues_message)
.setNegativeButton(android.R.string.cancel) { _, _ -> }
@@ -177,7 +177,7 @@ class NotificationsFragment : PreferenceFragmentCompat(),
}
override fun openNotificationsPermissionDialog() {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.notifications_header_title)
.setMessage(R.string.notifications_header_description)
.setPositiveButton(R.string.pref_notification_go_to_settings) { _, _ ->
@@ -191,7 +191,7 @@ class NotificationsFragment : PreferenceFragmentCompat(),
}
override fun openNotificationPiggyBackPermissionDialog() {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.pref_notification_piggyback_popup_title))
.setMessage(getString(R.string.pref_notification_piggyback_popup_description))
.setPositiveButton(getString(R.string.pref_notification_go_to_settings)) { _, _ ->
@@ -205,7 +205,7 @@ class NotificationsFragment : PreferenceFragmentCompat(),
}
override fun openNotificationExactAlarmSettings() {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.pref_notification_exact_alarm_popup_title))
.setMessage(getString(R.string.pref_notification_exact_alarm_popup_descriptions))
.setPositiveButton(getString(R.string.pref_notification_go_to_settings)) { _, _ ->
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
index 8477e322..2a804d9f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
@@ -5,6 +5,7 @@ import android.os.Bundle
import android.view.View
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
+import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
@@ -75,7 +76,11 @@ class SyncFragment : PreferenceFragmentCompat(),
}
override fun showMessage(text: String) {
- (activity as? BaseActivity<*, *>)?.showMessage(text)
+ Snackbar.make(requireView(), text, Snackbar.LENGTH_LONG)
+ .apply {
+ anchorView = requireActivity().findViewById(R.id.main_bottom_nav)
+ show()
+ }
}
override fun showExpiredDialog() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
index 2f0d697f..d917e7d5 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
@@ -160,7 +160,7 @@ class TimetableAdapter @Inject constructor() :
timetableSmallItemDescription.setTextColor(
root.context.getThemeAttrColor(
- if (lesson.canceled) R.attr.colorPrimary
+ if (lesson.canceled) R.attr.colorTimetableCanceled
else R.attr.colorTimetableChange
)
)
@@ -185,7 +185,7 @@ class TimetableAdapter @Inject constructor() :
timetableItemDescription.setTextColor(
root.context.getThemeAttrColor(
- if (lesson.canceled) R.attr.colorPrimary
+ if (lesson.canceled) R.attr.colorTimetableCanceled
else R.attr.colorTimetableChange
)
)
@@ -228,8 +228,8 @@ class TimetableAdapter @Inject constructor() :
}
private fun updateNumberAndSubjectCanceledColor(numberView: TextView, subjectView: TextView) {
- numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorPrimary))
- subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorPrimary))
+ numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorTimetableCanceled))
+ subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorTimetableCanceled))
}
private fun updateNumberColor(numberView: TextView, lesson: Timetable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt
index 4f5547d2..e8a85347 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt
@@ -1,24 +1,24 @@
package io.github.wulkanowy.ui.modules.timetable
import android.annotation.SuppressLint
+import android.app.Dialog
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
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 androidx.core.os.bundleOf
-import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.databinding.DialogTimetableBinding
+import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.*
import java.time.Instant
-class TimetableDialog : DialogFragment() {
-
- private var binding: DialogTimetableBinding by lifecycleAwareVariable()
+@AndroidEntryPoint
+class TimetableDialog : BaseDialogFragment() {
private lateinit var lesson: Timetable
@@ -33,15 +33,14 @@ class TimetableDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
lesson = requireArguments().serializable(ARGUMENT_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogTimetableBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(DialogTimetableBinding.inflate(layoutInflater).apply { binding = this }.root)
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -82,12 +81,12 @@ class TimetableDialog : DialogFragment() {
if (canceled) {
timetableDialogChangesTitle.setTextColor(
requireContext().getThemeAttrColor(
- R.attr.colorPrimary
+ R.attr.colorTimetableCanceled
)
)
timetableDialogChangesValue.setTextColor(
requireContext().getThemeAttrColor(
- R.attr.colorPrimary
+ R.attr.colorTimetableCanceled
)
)
} else {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
index e95d6f82..ebc16239 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
@@ -87,7 +87,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme
timetableNavDate.setOnClickListener { presenter.onPickDate() }
timetableNextButton.setOnClickListener { presenter.onNextDay() }
- timetableNavContainer.elevation = requireContext().dpToPx(8f)
+ timetableNavContainer.elevation = requireContext().dpToPx(3f)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
index 043fa1f7..faa833c2 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
@@ -2,8 +2,8 @@ package io.github.wulkanowy.ui.modules.timetable.additional
import android.os.Bundle
import android.view.View
-import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.TimetableAdditional
@@ -13,11 +13,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.timetable.additional.add.AdditionalLessonAddDialog
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.dpToPx
-import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.openMaterialDatePicker
+import io.github.wulkanowy.utils.*
import java.time.LocalDate
import javax.inject.Inject
@@ -73,7 +69,7 @@ class AdditionalLessonsFragment :
openAddAdditionalLessonButton.setOnClickListener { presenter.onAdditionalLessonAddButtonClicked() }
- additionalLessonsNavContainer.elevation = requireContext().dpToPx(8f)
+ additionalLessonsNavContainer.elevation = requireContext().dpToPx(3f)
}
}
@@ -154,7 +150,7 @@ class AdditionalLessonsFragment :
}
override fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional) {
- AlertDialog.Builder(requireContext())
+ MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.additional_lessons_delete_title))
.setItems(
arrayOf(
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
index f82d6483..13471997 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
@@ -1,10 +1,10 @@
package io.github.wulkanowy.ui.modules.timetable.additional.add
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.widget.doOnTextChanged
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import dagger.hilt.android.AndroidEntryPoint
@@ -29,16 +29,14 @@ class AdditionalLessonAddDialog : BaseDialogFragment
fun newInstance() = AdditionalLessonAddDialog()
}
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
- }
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogAdditionalAddBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(
+ DialogAdditionalAddBinding.inflate(layoutInflater).apply { binding = this }.root
+ )
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt
index ddd7488e..d937d4dd 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt
@@ -1,19 +1,18 @@
package io.github.wulkanowy.ui.modules.timetable.completed
+import android.app.Dialog
import android.os.Bundle
-import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.os.bundleOf
-import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.databinding.DialogLessonCompletedBinding
-import io.github.wulkanowy.utils.lifecycleAwareVariable
+import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.serializable
-class CompletedLessonDialog : DialogFragment() {
-
- private var binding: DialogLessonCompletedBinding by lifecycleAwareVariable()
+@AndroidEntryPoint
+class CompletedLessonDialog : BaseDialogFragment() {
private lateinit var completedLesson: CompletedLesson
@@ -28,15 +27,16 @@ class CompletedLessonDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
completedLesson = requireArguments().serializable(ARGUMENT_KEY)
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogLessonCompletedBinding.inflate(inflater).apply { binding = this }.root
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return MaterialAlertDialogBuilder(requireContext(), theme)
+ .setView(
+ DialogLessonCompletedBinding.inflate(layoutInflater).apply { binding = this }.root
+ )
+ .create()
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
index 34a69e6a..77a7bbd5 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
@@ -2,9 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable.completed
import android.os.Bundle
import android.view.View
-import android.view.View.GONE
-import android.view.View.INVISIBLE
-import android.view.View.VISIBLE
+import android.view.View.*
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@@ -14,12 +12,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.dpToPx
-import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.getCompatDrawable
-import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.openMaterialDatePicker
+import io.github.wulkanowy.utils.*
import java.time.LocalDate
import javax.inject.Inject
@@ -73,7 +66,7 @@ class CompletedLessonsFragment :
completedLessonsNavDate.setOnClickListener { presenter.onPickDate() }
completedLessonsNextButton.setOnClickListener { presenter.onNextDay() }
- completedLessonsNavContainer.elevation = requireContext().dpToPx(8f)
+ completedLessonsNavContainer.elevation = requireContext().dpToPx(3f)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
index 6ef6cfc9..672dbe72 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
@@ -2,14 +2,11 @@ package io.github.wulkanowy.ui.modules.timetablewidget
import android.appwidget.AppWidgetManager.*
import android.content.Intent
-import android.os.Build
import android.os.Bundle
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
-import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
-import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding
import io.github.wulkanowy.ui.base.BaseActivity
@@ -34,8 +31,6 @@ class TimetableWidgetConfigureActivity :
@Inject
lateinit var appInfo: AppInfo
- private var dialog: AlertDialog? = null
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setResult(RESULT_CANCELED)
@@ -61,23 +56,6 @@ class TimetableWidgetConfigureActivity :
configureAdapter.onClickListener = presenter::onItemSelect
}
- override fun showThemeDialog() {
- var items = arrayOf(
- getString(R.string.widget_timetable_theme_light),
- getString(R.string.widget_timetable_theme_dark)
- )
-
- if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += getString(R.string.widget_timetable_theme_system)
-
- dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher)
- .setTitle(R.string.widget_timetable_theme_title)
- .setOnDismissListener { presenter.onDismissThemeView() }
- .setSingleChoiceItems(items, -1) { _, which ->
- presenter.onThemeSelect(which)
- }
- .show()
- }
-
override fun updateData(data: List, selectedStudentId: Long) {
with(configureAdapter) {
selectedId = selectedStudentId
@@ -110,9 +88,4 @@ class TimetableWidgetConfigureActivity :
override fun openLoginView() {
startActivity(LoginActivity.getStartIntent(this))
}
-
- override fun onDestroy() {
- super.onDestroy()
- dialog?.dismiss()
- }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt
index dc2a7c6c..87e89336 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt
@@ -8,7 +8,6 @@ import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
-import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getThemeWidgetKey
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -39,22 +38,9 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
fun onItemSelect(student: Student) {
selectedStudent = student
-
- if (isFromProvider) registerStudent(selectedStudent)
- else view?.showThemeDialog()
- }
-
- fun onThemeSelect(index: Int) {
- appWidgetId?.let {
- sharedPref.putLong(getThemeWidgetKey(it), index.toLong())
- }
registerStudent(selectedStudent)
}
- fun onDismissThemeView() {
- view?.finishView()
- }
-
private fun loadData() {
resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
when (it) {
@@ -65,10 +51,7 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
} ?: -1
when {
it.data.isEmpty() -> view?.openLoginView()
- it.data.size == 1 && !isFromProvider -> {
- selectedStudent = it.data.single().student
- view?.showThemeDialog()
- }
+ it.data.size == 1 && !isFromProvider -> onItemSelect(it.data.single().student)
else -> view?.updateData(it.data, selectedStudentId)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt
index accdc28d..7740b9bb 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt
@@ -11,8 +11,6 @@ interface TimetableWidgetConfigureView : BaseView {
fun updateTimetableWidget(widgetId: Int)
- fun showThemeDialog()
-
fun setSuccessResult(widgetId: Int)
fun finishView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
index 664086bc..9c5abe1c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
@@ -1,27 +1,25 @@
package io.github.wulkanowy.ui.modules.timetablewidget
-import android.annotation.SuppressLint
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
import android.content.Context
import android.content.Intent
+import android.content.res.Configuration
import android.graphics.Paint.ANTI_ALIAS_FLAG
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.View.GONE
import android.view.View.VISIBLE
-import android.widget.AdapterView.INVALID_POSITION
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import io.github.wulkanowy.R
import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.SharedPrefProvider
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
-import io.github.wulkanowy.data.enums.TimetableMode
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.data.toFirstResult
-import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getCurrentThemeWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey
@@ -29,13 +27,13 @@ import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.runBlocking
import timber.log.Timber
+import java.time.Instant
import java.time.LocalDate
class TimetableWidgetFactory(
private val timetableRepository: TimetableRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
- private val prefRepository: PreferencesRepository,
private val sharedPref: SharedPrefProvider,
private val context: Context,
private val intent: Intent?
@@ -43,19 +41,22 @@ class TimetableWidgetFactory(
private var lessons = emptyList()
- private var savedCurrentTheme: Long? = null
-
- private var primaryColor: Int? = null
+ private var timetableCanceledColor: Int? = null
private var textColor: Int? = null
private var timetableChangeColor: Int? = null
+ private var lastSyncInstant: Instant? = null
+
override fun getLoadingView() = null
override fun hasStableIds() = true
- override fun getCount() = lessons.size
+ override fun getCount() = when {
+ lessons.isEmpty() -> 0
+ else -> lessons.size + 1
+ }
override fun getViewTypeCount() = 2
@@ -70,195 +71,170 @@ class TimetableWidgetFactory(
val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0))
val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0)
- updateTheme(appWidgetId)
- lessons = getLessons(date, studentId)
-
- val todayLastLessonEndTimestamp = lessons.maxOfOrNull { it.end }
- if (date == LocalDate.now() && todayLastLessonEndTimestamp != null) {
- sharedPref.putLong(
- key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId),
- value = todayLastLessonEndTimestamp.epochSecond,
- sync = true
- )
- }
- }
- }
-
- private fun updateTheme(appWidgetId: Int) {
- savedCurrentTheme = sharedPref.getLong(getCurrentThemeWidgetKey(appWidgetId), 0)
-
- if (savedCurrentTheme == 0L) {
- primaryColor = R.color.colorPrimary
- textColor = android.R.color.black
- timetableChangeColor = R.color.timetable_change_dark
- } else {
- primaryColor = R.color.colorPrimaryLight
- textColor = android.R.color.white
- timetableChangeColor = R.color.timetable_change_light
- }
- }
-
- private fun getItemLayout(lesson: Timetable): Int {
- return when {
- prefRepository.showWholeClassPlan == TimetableMode.SMALL_OTHER_GROUP && !lesson.isStudentPlan -> {
- if (savedCurrentTheme == 0L) R.layout.item_widget_timetable_small
- else R.layout.item_widget_timetable_small_dark
- }
- savedCurrentTheme == 1L -> R.layout.item_widget_timetable_dark
- else -> R.layout.item_widget_timetable
- }
- }
-
- private fun getLessons(date: LocalDate, studentId: Long) = try {
- runBlocking {
- if (!studentRepository.isStudentSaved()) return@runBlocking emptyList()
-
- val students = studentRepository.getSavedStudents()
- val student = students.singleOrNull { it.student.id == studentId }?.student
- ?: return@runBlocking emptyList()
-
- val semester = semesterRepository.getCurrentSemester(student)
- timetableRepository.getTimetable(student, semester, date, date, false)
- .toFirstResult().dataOrNull?.lessons.orEmpty()
- .sortedWith(compareBy({ it.number }, { !it.isStudentPlan }))
- .filter {
- if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
- it.isStudentPlan
- } else true
+ runCatching {
+ runBlocking {
+ val student = getStudent(studentId) ?: return@runBlocking
+ val semester = semesterRepository.getCurrentSemester(student)
+ lessons = getLessons(student, semester, date)
+ lastSyncInstant =
+ timetableRepository.getLastRefreshTimestamp(semester, date, date)
+ if (date == LocalDate.now()) {
+ updateTodayLastLessonEnd(appWidgetId)
+ }
}
+ }.onFailure {
+ Timber.e(it, "An error has occurred in timetable widget factory")
+ }
}
- } catch (e: Exception) {
- Timber.e(e, "An error has occurred in timetable widget factory")
- emptyList()
}
- @SuppressLint("DefaultLocale")
+ private suspend fun getStudent(studentId: Long): Student? {
+ val students = studentRepository.getSavedStudents()
+ return students.singleOrNull { it.student.id == studentId }?.student
+ }
+
+ private suspend fun getLessons(
+ student: Student, semester: Semester, date: LocalDate
+ ): List {
+ val timetable = timetableRepository.getTimetable(student, semester, date, date, false)
+ val lessons = timetable.toFirstResult().dataOrNull?.lessons.orEmpty()
+ return lessons.sortedBy { it.number }
+ }
+
+ private fun updateTodayLastLessonEnd(appWidgetId: Int) {
+ val todayLastLessonEnd = lessons.maxOfOrNull { it.end } ?: return
+ val key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId)
+ sharedPref.putLong(key, todayLastLessonEnd.epochSecond, true)
+ }
+
+ companion object {
+ const val TIME_FORMAT_STYLE = "HH:mm"
+ }
+
override fun getViewAt(position: Int): RemoteViews? {
- if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null
-
- val lesson = lessons[position]
- return RemoteViews(context.packageName, getItemLayout(lesson)).apply {
- setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
- setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString())
- setTextViewText(
- R.id.timetableWidgetItemTimeStart,
- lesson.start.toFormattedString("HH:mm")
- )
- setTextViewText(
- R.id.timetableWidgetItemTimeFinish,
- lesson.end.toFormattedString("HH:mm")
- )
-
- updateDescription(this, lesson)
-
- if (lesson.canceled) {
- updateStylesCanceled(this)
- } else {
- updateStylesNotCanceled(this, lesson)
+ if (position == lessons.size) {
+ val synchronizationInstant = lastSyncInstant ?: Instant.MIN
+ val synchronizationText = getSynchronizationInfoText(synchronizationInstant)
+ return RemoteViews(context.packageName, R.layout.item_widget_timetable_footer).apply {
+ setTextViewText(R.id.timetableWidgetSynchronizationTime, synchronizationText)
}
+ }
+ val lesson = lessons.getOrNull(position) ?: return null
+
+ val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE)
+ val lessonEndTime = lesson.end.toFormattedString(TIME_FORMAT_STYLE)
+ val roomText = "${context.getString(R.string.timetable_room)} ${lesson.room}"
+
+ val remoteViews = RemoteViews(context.packageName, R.layout.item_widget_timetable).apply {
+ setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString())
+ setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime)
+ setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime)
+ setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
+ setTextViewText(R.id.timetableWidgetItemRoom, roomText)
+ setTextViewText(R.id.timetableWidgetItemTeacher, lesson.teacher)
+ setTextViewText(R.id.timetableWidgetItemDescription, lesson.info)
setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent())
}
+
+ updateTheme()
+ clearLessonStyles(remoteViews)
+
+ when {
+ lesson.canceled -> applyCancelledLessonStyles(remoteViews)
+ lesson.changes or lesson.info.isNotBlank() -> applyChangedLessonStyles(
+ remoteViews, lesson
+ )
+ }
+
+ return remoteViews
}
- private fun updateDescription(remoteViews: RemoteViews, lesson: Timetable) {
- with(remoteViews) {
- if (lesson.info.isNotBlank() && !lesson.changes) {
- setTextViewText(R.id.timetableWidgetItemDescription, lesson.info)
- setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE)
- setViewVisibility(R.id.timetableWidgetItemRoom, GONE)
- setViewVisibility(R.id.timetableWidgetItemTeacher, GONE)
- } else {
- setViewVisibility(R.id.timetableWidgetItemDescription, GONE)
- setViewVisibility(R.id.timetableWidgetItemRoom, VISIBLE)
- setViewVisibility(R.id.timetableWidgetItemTeacher, VISIBLE)
+ private fun updateTheme() {
+ when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
+ Configuration.UI_MODE_NIGHT_YES -> {
+ textColor = android.R.color.white
+ timetableChangeColor = R.color.timetable_change_dark
+ timetableCanceledColor = R.color.timetable_canceled_dark
+ }
+
+ else -> {
+ textColor = android.R.color.black
+ timetableChangeColor = R.color.timetable_change_light
+ timetableCanceledColor = R.color.timetable_canceled_light
}
}
}
- private fun updateStylesCanceled(remoteViews: RemoteViews) {
- with(remoteViews) {
- setInt(
- R.id.timetableWidgetItemSubject, "setPaintFlags",
- STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG
- )
- setTextColor(R.id.timetableWidgetItemNumber, context.getCompatColor(primaryColor!!))
- setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(primaryColor!!))
- setTextColor(
- R.id.timetableWidgetItemDescription,
- context.getCompatColor(primaryColor!!)
- )
- }
- }
+ private fun clearLessonStyles(remoteViews: RemoteViews) {
+ val defaultTextColor = context.getCompatColor(textColor ?: 0)
- private fun updateStylesNotCanceled(remoteViews: RemoteViews, lesson: Timetable) {
- with(remoteViews) {
+ remoteViews.apply {
setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG)
- setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(textColor!!))
- setTextColor(
- R.id.timetableWidgetItemDescription,
- context.getCompatColor(timetableChangeColor!!)
- )
-
- updateNotCanceledLessonNumberColor(this, lesson)
- updateNotCanceledSubjectColor(this, lesson)
-
- val teacherChange = lesson.teacherOld.isNotBlank()
- updateNotCanceledRoom(this, lesson, teacherChange)
- updateNotCanceledTeacher(this, lesson, teacherChange)
+ setViewVisibility(R.id.timetableWidgetItemRoom, VISIBLE)
+ setViewVisibility(R.id.timetableWidgetItemTeacher, VISIBLE)
+ setViewVisibility(R.id.timetableWidgetItemIcon, GONE)
+ setViewVisibility(R.id.timetableWidgetItemDescription, GONE)
+ setTextColor(R.id.timetableWidgetItemNumber, defaultTextColor)
+ setTextColor(R.id.timetableWidgetItemSubject, defaultTextColor)
+ setTextColor(R.id.timetableWidgetItemRoom, defaultTextColor)
+ setTextColor(R.id.timetableWidgetItemTeacher, defaultTextColor)
+ setTextColor(R.id.timetableWidgetItemDescription, defaultTextColor)
}
}
- private fun updateNotCanceledLessonNumberColor(remoteViews: RemoteViews, lesson: Timetable) {
- remoteViews.setTextColor(
- R.id.timetableWidgetItemNumber, context.getCompatColor(
- if (lesson.changes || (lesson.info.isNotBlank() && !lesson.canceled)) timetableChangeColor!!
- else textColor!!
- )
- )
- }
+ private fun applyCancelledLessonStyles(remoteViews: RemoteViews) {
+ val cancelledThemeColor = context.getCompatColor(timetableCanceledColor ?: 0)
+ val strikeThroughPaintFlags = STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG
- private fun updateNotCanceledSubjectColor(remoteViews: RemoteViews, lesson: Timetable) {
- remoteViews.setTextColor(
- R.id.timetableWidgetItemSubject, context.getCompatColor(
- if (lesson.subjectOld.isNotBlank() && lesson.subject != lesson.subjectOld) timetableChangeColor!!
- else textColor!!
- )
- )
- }
-
- private fun updateNotCanceledRoom(
- remoteViews: RemoteViews,
- lesson: Timetable,
- teacherChange: Boolean
- ) {
- with(remoteViews) {
- if (lesson.room.isNotBlank()) {
- setTextViewText(
- R.id.timetableWidgetItemRoom,
- if (teacherChange) lesson.room
- else "${context.getString(R.string.timetable_room)} ${lesson.room}"
- )
-
- setTextColor(
- R.id.timetableWidgetItemRoom, context.getCompatColor(
- if (lesson.roomOld.isNotBlank() && lesson.room != lesson.roomOld) timetableChangeColor!!
- else textColor!!
- )
- )
- } else setTextViewText(R.id.timetableWidgetItemRoom, "")
+ remoteViews.apply {
+ setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", strikeThroughPaintFlags)
+ setTextColor(R.id.timetableWidgetItemNumber, cancelledThemeColor)
+ setTextColor(R.id.timetableWidgetItemSubject, cancelledThemeColor)
+ setTextColor(R.id.timetableWidgetItemDescription, cancelledThemeColor)
+ setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE)
+ setViewVisibility(R.id.timetableWidgetItemRoom, GONE)
+ setViewVisibility(R.id.timetableWidgetItemTeacher, GONE)
}
}
- private fun updateNotCanceledTeacher(
- remoteViews: RemoteViews,
- lesson: Timetable,
- teacherChange: Boolean
- ) {
- remoteViews.setTextViewText(
- R.id.timetableWidgetItemTeacher,
- if (teacherChange) lesson.teacher
- else ""
- )
+ private fun applyChangedLessonStyles(remoteViews: RemoteViews, lesson: Timetable) {
+ val changesTextColor = context.getCompatColor(timetableChangeColor ?: 0)
+
+ remoteViews.apply {
+ setTextColor(R.id.timetableWidgetItemNumber, changesTextColor)
+ setTextColor(R.id.timetableWidgetItemDescription, changesTextColor)
+ setViewVisibility(R.id.timetableWidgetItemIcon, VISIBLE)
+ setImageViewResource(R.id.timetableWidgetItemIcon, R.drawable.ic_timetable_widget_swap)
+ }
+
+ if (lesson.subject != lesson.subjectOld) {
+ remoteViews.setTextColor(R.id.timetableWidgetItemSubject, changesTextColor)
+ }
+
+ if (lesson.room != lesson.roomOld) {
+ remoteViews.setTextColor(R.id.timetableWidgetItemRoom, changesTextColor)
+ }
+
+ if (lesson.teacher != lesson.teacherOld) {
+ remoteViews.setTextColor(R.id.timetableWidgetItemTeacher, changesTextColor)
+ }
+
+ if (lesson.info.isNotBlank() && !lesson.changes) {
+ remoteViews.setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE)
+ remoteViews.setViewVisibility(R.id.timetableWidgetItemRoom, GONE)
+ remoteViews.setViewVisibility(R.id.timetableWidgetItemTeacher, GONE)
+ }
}
+
+ private fun getSynchronizationInfoText(synchronizationInstant: Instant) =
+ synchronizationInstant.run {
+ val synchronizationTime = toFormattedString(TIME_FORMAT_STYLE)
+ val synchronizationDate = toFormattedString()
+ context.getString(
+ R.string.widget_timetable_last_synchronization,
+ synchronizationDate,
+ synchronizationTime,
+ )
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt
index 3ba2ae94..624ca30f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt
@@ -8,10 +8,10 @@ import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
-import android.content.res.Configuration
-import android.graphics.Bitmap
-import android.graphics.Canvas
import android.widget.RemoteViews
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.core.graphics.drawable.DrawableCompat
+import androidx.core.graphics.drawable.toBitmap
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.SharedPrefProvider
@@ -70,11 +70,6 @@ class TimetableWidgetProvider : BroadcastReceiver() {
"timetable_widget_today_last_lesson_end_date_time_$appWidgetId"
fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId"
-
- fun getThemeWidgetKey(appWidgetId: Int) = "timetable_widget_theme_$appWidgetId"
-
- fun getCurrentThemeWidgetKey(appWidgetId: Int) =
- "timetable_widget_current_theme_$appWidgetId"
}
@OptIn(DelicateCoroutinesApi::class)
@@ -109,8 +104,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE)
val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0)
val student = getStudent(
- sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0),
- toggledWidgetId
+ sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), toggledWidgetId
)
val savedDate =
LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0))
@@ -122,8 +116,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
}
if (!buttonType.isNullOrBlank()) {
analytics.logEvent(
- "changed_timetable_widget_day",
- "button" to buttonType
+ "changed_timetable_widget_day", "button" to buttonType
)
}
updateWidget(context, toggledWidgetId, date, student)
@@ -137,49 +130,21 @@ class TimetableWidgetProvider : BroadcastReceiver() {
with(sharedPref) {
delete(getStudentWidgetKey(appWidgetId))
delete(getDateWidgetKey(appWidgetId))
- delete(getThemeWidgetKey(appWidgetId))
- delete(getCurrentThemeWidgetKey(appWidgetId))
}
}
}
private fun updateWidget(
- context: Context,
- appWidgetId: Int,
- date: LocalDate,
- student: Student?
+ context: Context, appWidgetId: Int, date: LocalDate, student: Student?
) {
- val savedConfigureTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
- val isSystemDarkMode =
- context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
- var currentTheme = 0L
- var layoutId = R.layout.widget_timetable
-
- if (savedConfigureTheme == 1L || (savedConfigureTheme == 2L && isSystemDarkMode)) {
- currentTheme = 1L
- layoutId = R.layout.widget_timetable_dark
- }
-
val nextNavIntent = createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT)
val prevNavIntent = createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV)
val resetNavIntent =
createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET)
- val adapterIntent = Intent(context, TimetableWidgetService::class.java)
- .apply {
- putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
- //make Intent unique
- action = appWidgetId.toString()
- }
- val accountIntent = PendingIntent.getActivity(
- context,
- -Int.MAX_VALUE + appWidgetId,
- Intent(context, TimetableWidgetConfigureActivity::class.java).apply {
- addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
- putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
- putExtra(EXTRA_FROM_PROVIDER, true)
- }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
-
- )
+ val adapterIntent = Intent(context, TimetableWidgetService::class.java).apply {
+ putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
+ action = appWidgetId.toString() //make Intent unique
+ }
val appIntent = PendingIntent.getActivity(
context,
TIMETABLE_PENDING_INTENT_ID,
@@ -187,56 +152,41 @@ class TimetableWidgetProvider : BroadcastReceiver() {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
- val remoteView = RemoteViews(context.packageName, layoutId).apply {
+ val formattedDate = date.toFormattedString("EEE, dd.MM").capitalise()
+ val remoteView = RemoteViews(context.packageName, R.layout.widget_timetable).apply {
setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty)
- setTextViewText(
- R.id.timetableWidgetDate,
- date.toFormattedString("EEEE, dd.MM").capitalise()
- )
- setTextViewText(
- R.id.timetableWidgetName,
- student?.nickOrName ?: context.getString(R.string.all_no_data)
- )
-
- student?.let {
- setImageViewBitmap(R.id.timetableWidgetAccount, context.createAvatarBitmap(it))
- }
-
+ setTextViewText(R.id.timetableWidgetDate, formattedDate)
setRemoteAdapter(R.id.timetableWidgetList, adapterIntent)
setOnClickPendingIntent(R.id.timetableWidgetNext, nextNavIntent)
setOnClickPendingIntent(R.id.timetableWidgetPrev, prevNavIntent)
setOnClickPendingIntent(R.id.timetableWidgetDate, resetNavIntent)
- setOnClickPendingIntent(R.id.timetableWidgetName, resetNavIntent)
- setOnClickPendingIntent(R.id.timetableWidgetAccount, accountIntent)
setPendingIntentTemplate(R.id.timetableWidgetList, appIntent)
}
+ student?.let {
+ setupAccountView(context, student, remoteView, appWidgetId)
+ }
+
with(sharedPref) {
- putLong(getCurrentThemeWidgetKey(appWidgetId), currentTheme)
putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true)
}
with(appWidgetManager) {
- updateAppWidget(appWidgetId, remoteView)
+ partiallyUpdateAppWidget(appWidgetId, remoteView)
notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList)
- Timber.d("TimetableWidgetProvider updated")
}
+
+ Timber.d("TimetableWidgetProvider updated")
}
private fun createNavIntent(
- context: Context,
- code: Int,
- appWidgetId: Int,
- buttonType: String
+ context: Context, code: Int, appWidgetId: Int, buttonType: String
) = PendingIntent.getBroadcast(
- context,
- code,
- Intent(context, TimetableWidgetProvider::class.java).apply {
+ context, code, Intent(context, TimetableWidgetProvider::class.java).apply {
action = ACTION_APPWIDGET_UPDATE
putExtra(EXTRA_BUTTON_TYPE, buttonType)
putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId)
- },
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+ }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try {
@@ -258,31 +208,6 @@ class TimetableWidgetProvider : BroadcastReceiver() {
null
}
- private fun Context.createAvatarBitmap(student: Student): Bitmap {
- val avatarColor = if (student.avatarColor == -2937041L) {
- getCompatColor(R.color.colorPrimaryLight).toLong()
- } else {
- student.avatarColor
- }
- val avatarDrawable = createNameInitialsDrawable(student.nickOrName, avatarColor, 0.5f)
-
- val avatarBitmap =
- if (avatarDrawable.intrinsicWidth <= 0 || avatarDrawable.intrinsicHeight <= 0) {
- Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
- } else {
- Bitmap.createBitmap(
- avatarDrawable.intrinsicWidth,
- avatarDrawable.intrinsicHeight,
- Bitmap.Config.ARGB_8888
- )
- }
-
- val canvas = Canvas(avatarBitmap)
- avatarDrawable.setBounds(0, 0, canvas.width, canvas.height)
- avatarDrawable.draw(canvas)
- return avatarBitmap
- }
-
private fun getWidgetDefaultDateToLoad(appWidgetId: Int): LocalDate {
val lastLessonEndTimestamp =
sharedPref.getLong(getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), 0)
@@ -299,4 +224,44 @@ class TimetableWidgetProvider : BroadcastReceiver() {
todayDate.nextOrSameSchoolDay
}
}
+
+ private fun setupAccountView(
+ context: Context,
+ student: Student,
+ remoteViews: RemoteViews,
+ appWidgetId: Int
+ ) {
+ val accountInitials = student.nickOrName
+ .split(" ")
+ .mapNotNull { it.firstOrNull() }.take(2)
+ .joinToString(separator = "").uppercase()
+
+ val accountPickerIntent = PendingIntent.getActivity(
+ context,
+ -Int.MAX_VALUE + appWidgetId,
+ Intent(context, TimetableWidgetConfigureActivity::class.java).apply {
+ addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
+ putExtra(EXTRA_FROM_PROVIDER, true)
+ },
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
+ )
+
+ // Create background bitmap
+ val avatarDrawableResource = R.drawable.background_timetable_widget_avatar
+ AppCompatResources.getDrawable(context, avatarDrawableResource)?.let { drawable ->
+ val screenDensity = context.resources.displayMetrics.density
+ val avatarSize = (48 * screenDensity).toInt()
+ val backgroundBitmap = DrawableCompat.wrap(drawable).run {
+ DrawableCompat.setTint(this, student.avatarColor.toInt())
+ toBitmap(avatarSize, avatarSize)
+ }
+ remoteViews.setImageViewBitmap(R.id.timetableWidgetAccountBackground, backgroundBitmap)
+ }
+
+ remoteViews.apply {
+ setTextViewText(R.id.timetableWidgetAccountInitials, accountInitials)
+ setOnClickPendingIntent(R.id.timetableWidgetAccount, accountPickerIntent)
+ }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt
index d0d47025..d3c9f800 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.utils
import android.content.Intent
import android.os.Build
import android.os.Bundle
+import android.os.Parcelable
import java.io.Serializable
inline fun Bundle.serializable(key: String): T = when {
@@ -15,10 +16,10 @@ inline fun Bundle.nullableSerializable(key: String):
else -> @Suppress("DEPRECATION") getSerializable(key) as T?
}
-@Suppress("DEPRECATION", "UNCHECKED_CAST")
-inline fun Bundle.parcelableArray(key: String): Array? = when {
+@Suppress("UNCHECKED_CAST")
+inline fun Bundle.parcelableArray(key: String): Array? = when {
Build.VERSION.SDK_INT >= 33 -> getParcelableArray(key, T::class.java)
- else -> getParcelableArray(key) as Array?
+ else -> @Suppress("DEPRECATION") getParcelableArray(key) as Array?
}
inline fun Intent.serializable(key: String): T = when {
diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
index cc4c5aaa..77f3eb64 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
@@ -6,7 +6,6 @@ import android.content.res.ColorStateList
import android.graphics.*
import android.text.TextPaint
import android.util.DisplayMetrics.DENSITY_DEFAULT
-import android.widget.ImageView
import androidx.annotation.*
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
@@ -14,7 +13,6 @@ import androidx.core.graphics.applyCanvas
import androidx.core.graphics.drawable.RoundedBitmapDrawable
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.core.graphics.drawable.toBitmap
-import androidx.core.widget.ImageViewCompat
@ColorInt
@@ -89,6 +87,6 @@ fun Context.createNameInitialsDrawable(
.apply { isCircular = true }
}
-fun ImageView.setTint(@ColorInt color: Int) {
- ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(color))
+fun Context.getAttrColorStateList(@AttrRes color: Int): ColorStateList {
+ return ColorStateList.valueOf(getThemeAttrColor(color))
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt
index 032e2d28..76ce66dc 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.utils
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
@@ -29,18 +30,18 @@ class LifecycleAwareVariable : ReadWriteProperty, DefaultL
}
}
-class LifecycleAwareVariableActivity : ReadWriteProperty,
+class LifecycleAwareVariableComponent : ReadWriteProperty,
DefaultLifecycleObserver {
private var _value: T? = null
- override fun setValue(thisRef: AppCompatActivity, property: KProperty<*>, value: T) {
+ override fun setValue(thisRef: LifecycleOwner, property: KProperty<*>, value: T) {
thisRef.lifecycle.removeObserver(this)
_value = value
thisRef.lifecycle.addObserver(this)
}
- override fun getValue(thisRef: AppCompatActivity, property: KProperty<*>) = _value
+ override fun getValue(thisRef: LifecycleOwner, property: KProperty<*>) = _value
?: throw IllegalStateException("Trying to call an lifecycle-aware value outside of the view lifecycle, or the value has not been initialized")
override fun onDestroy(owner: LifecycleOwner) {
@@ -53,4 +54,8 @@ class LifecycleAwareVariableActivity : ReadWriteProperty Fragment.lifecycleAwareVariable() = LifecycleAwareVariable()
-fun lifecycleAwareVariable() = LifecycleAwareVariableActivity()
+@Suppress("unused")
+fun DialogFragment.lifecycleAwareVariable() = LifecycleAwareVariableComponent()
+
+@Suppress("unused")
+fun AppCompatActivity.lifecycleAwareVariable() = LifecycleAwareVariableComponent()
diff --git a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
index 93e67be0..72129751 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
@@ -49,4 +49,9 @@ class AutoRefreshHelper @Inject constructor(
fun updateLastRefreshTimestamp(key: String) {
sharedPref.putLong(key, Instant.now().toEpochMilli())
}
+
+ fun getLastRefreshTimestamp(key: String): Instant {
+ val refreshTimestampMilli = sharedPref.getLong(key, 0)
+ return Instant.ofEpochMilli(refreshTimestampMilli)
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt
index 63a30db8..481cad11 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt
@@ -12,18 +12,17 @@ fun Sdk.init(student: Student): Sdk {
studentId = student.studentId
classId = student.classId
- if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
+ if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
+ mobileBaseUrl = student.mobileBaseUrl
+ } else {
scrapperBaseUrl = student.scrapperBaseUrl
loginType = Sdk.ScrapperLoginType.valueOf(student.loginType)
}
- loginId = student.userLoginId
mode = Sdk.Mode.valueOf(student.loginMode)
mobileBaseUrl = student.mobileBaseUrl
- certKey = student.certificateKey
- privateKey = student.privateKey
-
- emptyCookieJarInterceptor = true
+ keyId = student.certificateKey
+ privatePem = student.privateKey
Timber.d("Sdk in ${student.loginMode} mode reinitialized")
diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt
index b3fec438..c5ec9de3 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,7 +1,8 @@
-Wersja 1.9.2
+Wersja 2.0.0
-- naprawiliśmy oznaczanie wiadomości jako odczytanych (problem dotyczył głównie kont rodziców z wieloma dziećmi w tej samej szkole)
-- naprawiliśmy zapisywanie załączników do wiadomości w sytuacji, gdy ten sam załącznik był dodany do więcej niż jednej wiadomości
-- usprawniliśmy ekran z wyborem uczniów i wpisywaniem symbolu przy pierwszym logowaniu
+— zaktualizowaliśmy wygląd aplikacji na (częściowo) zgodny z wytycznymi Material 3
+— dodaliśmy możliwość zmiany kolejności pozycji w menu dolnym
+— poprawiliśmy sposób wyświetlania błędu o nieprawidłowym haśle na ekranie logowania
+— od teraz zmiana ustawień liczenia średniej automatycznie odświeży listę ocen
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
diff --git a/app/src/main/res/drawable-night/background_header_note.xml b/app/src/main/res/drawable-night/background_header_note.xml
deleted file mode 100644
index 6b594e7c..00000000
--- a/app/src/main/res/drawable-night/background_header_note.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/background_grade_details_rounded.xml b/app/src/main/res/drawable/background_grade_details_rounded.xml
new file mode 100644
index 00000000..e24088a0
--- /dev/null
+++ b/app/src/main/res/drawable/background_grade_details_rounded.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/background_grade_details_weight_rounded.xml b/app/src/main/res/drawable/background_grade_details_weight_rounded.xml
new file mode 100644
index 00000000..4b210912
--- /dev/null
+++ b/app/src/main/res/drawable/background_grade_details_weight_rounded.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/background_widget_timetable_dark.xml b/app/src/main/res/drawable/background_grade_rounded.xml
similarity index 74%
rename from app/src/main/res/drawable/background_widget_timetable_dark.xml
rename to app/src/main/res/drawable/background_grade_rounded.xml
index 6fe7d0ab..52c10c2f 100644
--- a/app/src/main/res/drawable/background_widget_timetable_dark.xml
+++ b/app/src/main/res/drawable/background_grade_rounded.xml
@@ -1,5 +1,5 @@
-
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/drawable/background_widget_item_timetable_dark.xml b/app/src/main/res/drawable/background_grade_small_rounded.xml
similarity index 51%
rename from app/src/main/res/drawable/background_widget_item_timetable_dark.xml
rename to app/src/main/res/drawable/background_grade_small_rounded.xml
index e432a648..dd50417f 100644
--- a/app/src/main/res/drawable/background_widget_item_timetable_dark.xml
+++ b/app/src/main/res/drawable/background_grade_small_rounded.xml
@@ -1,5 +1,5 @@
-
-
-
\ No newline at end of file
+
+
+
diff --git a/app/src/main/res/drawable/background_header_note.xml b/app/src/main/res/drawable/background_header_note.xml
index c21e55c6..8cf84a1c 100644
--- a/app/src/main/res/drawable/background_header_note.xml
+++ b/app/src/main/res/drawable/background_header_note.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/app/src/main/res/drawable/background_luckynumber_widget.xml b/app/src/main/res/drawable/background_luckynumber_widget_button.xml
similarity index 65%
rename from app/src/main/res/drawable/background_luckynumber_widget.xml
rename to app/src/main/res/drawable/background_luckynumber_widget_button.xml
index 367c5527..66b1685f 100644
--- a/app/src/main/res/drawable/background_luckynumber_widget.xml
+++ b/app/src/main/res/drawable/background_luckynumber_widget_button.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/background_luckynumber_widget_dark.xml b/app/src/main/res/drawable/background_luckynumber_widget_dark.xml
deleted file mode 100644
index cb094b57..00000000
--- a/app/src/main/res/drawable/background_luckynumber_widget_dark.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/background_material_alert_dialog.xml b/app/src/main/res/drawable/background_material_alert_dialog.xml
new file mode 100644
index 00000000..5ab8a350
--- /dev/null
+++ b/app/src/main/res/drawable/background_material_alert_dialog.xml
@@ -0,0 +1,26 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/background_timetable_widget_avatar.xml b/app/src/main/res/drawable/background_timetable_widget_avatar.xml
new file mode 100644
index 00000000..7f64c4eb
--- /dev/null
+++ b/app/src/main/res/drawable/background_timetable_widget_avatar.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/background_widget_header_timetable.xml b/app/src/main/res/drawable/background_widget_header_timetable.xml
deleted file mode 100644
index 98eec700..00000000
--- a/app/src/main/res/drawable/background_widget_header_timetable.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/background_widget_header_timetable_dark.xml b/app/src/main/res/drawable/background_widget_header_timetable_dark.xml
deleted file mode 100644
index 616a9127..00000000
--- a/app/src/main/res/drawable/background_widget_header_timetable_dark.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/background_widget_item_timetable.xml b/app/src/main/res/drawable/background_widget_item_timetable.xml
index 08854fba..09635758 100644
--- a/app/src/main/res/drawable/background_widget_item_timetable.xml
+++ b/app/src/main/res/drawable/background_widget_item_timetable.xml
@@ -1,5 +1,5 @@
-
-
-
\ No newline at end of file
+
+
+
diff --git a/app/src/main/res/drawable/background_widget_timetable.xml b/app/src/main/res/drawable/background_widget_timetable.xml
index 2267587d..b589ad29 100644
--- a/app/src/main/res/drawable/background_widget_timetable.xml
+++ b/app/src/main/res/drawable/background_widget_timetable.xml
@@ -1,5 +1,5 @@
-
-
-
\ No newline at end of file
+
+
+
diff --git a/app/src/main/res/drawable/ic_chevron_left.xml b/app/src/main/res/drawable/ic_chevron_left.xml
index ee3ff4be..4250fae4 100644
--- a/app/src/main/res/drawable/ic_chevron_left.xml
+++ b/app/src/main/res/drawable/ic_chevron_left.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/app/src/main/res/drawable/ic_chevron_right.xml
index a6d73497..de5037cf 100644
--- a/app/src/main/res/drawable/ic_chevron_right.xml
+++ b/app/src/main/res/drawable/ic_chevron_right.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/app/src/main/res/drawable/ic_history.xml b/app/src/main/res/drawable/ic_history.xml
new file mode 100644
index 00000000..f20e2094
--- /dev/null
+++ b/app/src/main/res/drawable/ic_history.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_order_drag.xml b/app/src/main/res/drawable/ic_menu_order_drag.xml
new file mode 100644
index 00000000..623c67d2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_order_drag.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_scale_balance.xml b/app/src/main/res/drawable/ic_scale_balance.xml
new file mode 100644
index 00000000..c65467a6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_scale_balance.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_timetable_widget_swap.xml b/app/src/main/res/drawable/ic_timetable_widget_swap.xml
new file mode 100644
index 00000000..2f91489a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_timetable_widget_swap.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_widget_chevron.png b/app/src/main/res/drawable/ic_widget_chevron.png
deleted file mode 100644
index 34345521..00000000
Binary files a/app/src/main/res/drawable/ic_widget_chevron.png and /dev/null differ
diff --git a/app/src/main/res/drawable/ic_widget_chevron.xml b/app/src/main/res/drawable/ic_widget_chevron.xml
new file mode 100644
index 00000000..2c88f818
--- /dev/null
+++ b/app/src/main/res/drawable/ic_widget_chevron.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/img_luckynumber_widget_preview.png b/app/src/main/res/drawable/img_luckynumber_widget_preview.png
index 539b0a59..26701855 100644
Binary files a/app/src/main/res/drawable/img_luckynumber_widget_preview.png and b/app/src/main/res/drawable/img_luckynumber_widget_preview.png differ
diff --git a/app/src/main/res/drawable/img_timetable_widget_preview.png b/app/src/main/res/drawable/img_timetable_widget_preview.png
index 84943018..a81bcf36 100755
Binary files a/app/src/main/res/drawable/img_timetable_widget_preview.png and b/app/src/main/res/drawable/img_timetable_widget_preview.png differ
diff --git a/app/src/main/res/drawable/shape_badge.xml b/app/src/main/res/drawable/shape_badge.xml
new file mode 100644
index 00000000..3153f592
--- /dev/null
+++ b/app/src/main/res/drawable/shape_badge.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout-v31/widget_timetable_preview.xml b/app/src/main/res/layout-v31/widget_timetable_preview.xml
new file mode 100644
index 00000000..bc556f50
--- /dev/null
+++ b/app/src/main/res/layout-v31/widget_timetable_preview.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 11844e24..d14de50a 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,24 +1,31 @@
-
+ android:fitsSystemWindows="true"
+ app:layout_constraintBaseline_toTopOf="parent">
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/main_app_bar"
+ tools:layout="@layout/fragment_dashboard" />
-
+ android:fitsSystemWindows="true"
+ app:layout_constraintBaseline_toTopOf="parent">
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/send_app_bar">
+ app:layout_constraintTop_toBottomOf="@id/send_app_bar" />
diff --git a/app/src/main/res/layout/dialog_account_edit.xml b/app/src/main/res/layout/dialog_account_edit.xml
index 9f617e44..2ab4ccc6 100644
--- a/app/src/main/res/layout/dialog_account_edit.xml
+++ b/app/src/main/res/layout/dialog_account_edit.xml
@@ -1,19 +1,14 @@
-
-
+ android:layout_height="match_parent">
-
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp">
+ android:textAppearance="?attr/textAppearanceHeadlineSmall"
+ android:textColor="?attr/colorOnSurface"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
-
+ android:text="@string/additional_lessons_repeat"
+ app:layout_constraintTop_toBottomOf="@id/additionalLessonDialogDate" />
+ android:hint="@string/all_subject"
+ app:layout_constraintTop_toBottomOf="@id/additionalLessonDialogEnd">
-
+
-
-
-
-
-
+
+
diff --git a/app/src/main/res/layout/dialog_ads_consent.xml b/app/src/main/res/layout/dialog_ads_consent.xml
index 81607478..118fb9c1 100644
--- a/app/src/main/res/layout/dialog_ads_consent.xml
+++ b/app/src/main/res/layout/dialog_ads_consent.xml
@@ -63,7 +63,7 @@
-
-
-
+ android:layout_height="match_parent">
-
-
+ app:layout_constraintEnd_toStartOf="@+id/examDialogClose" />
diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml
index 94facb23..f47f6108 100644
--- a/app/src/main/res/layout/dialog_grade.xml
+++ b/app/src/main/res/layout/dialog_grade.xml
@@ -32,43 +32,57 @@
android:id="@+id/gradeDialogValue"
android:layout_width="match_parent"
android:layout_height="86dp"
- android:layout_marginStart="0dp"
android:layout_marginEnd="16dp"
- android:background="@color/grade_material_default"
+ android:background="@drawable/background_grade_details_rounded"
+ android:backgroundTint="@color/grade_material_default"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="30sp"
tools:text="6" />
-
+ android:background="@drawable/background_grade_details_weight_rounded"
+ android:backgroundTint="@color/grade_black"
+ android:gravity="center_horizontal">
+
+
+
+
+
+ android:textAppearance="?attr/textAppearanceHeadlineSmall"
+ android:textColor="?attr/colorOnSurface"
+ android:textIsSelectable="true" />
+ android:text="@string/all_no_description"
+ android:textAppearance="?attr/textAppearanceBodyLarge"
+ android:textColor="?attr/colorOnSurface"
+ android:textIsSelectable="true" />
+ android:textAppearance="?attr/textAppearanceBodySmall"
+ android:textColor="?attr/colorOnSurfaceVariant" />
+ android:textAppearance="?attr/textAppearanceBodyLarge"
+ android:textColor="?attr/colorOnSurface"
+ android:textIsSelectable="true" />
+ android:textAppearance="?attr/textAppearanceBodySmall"
+ android:textColor="?attr/colorOnSurfaceVariant" />
+ android:textAppearance="?attr/textAppearanceBodyLarge"
+ android:textColor="?attr/colorOnSurface"
+ android:textIsSelectable="true" />
+ android:textAppearance="?attr/textAppearanceBodySmall"
+ android:textColor="?attr/colorOnSurfaceVariant" />
+ android:textAppearance="?attr/textAppearanceBodyLarge"
+ android:textColor="?attr/colorOnSurface"
+ android:textIsSelectable="true" />
+ android:textAppearance="?attr/textAppearanceBodySmall"
+ android:textColor="?attr/colorOnSurfaceVariant" />
+ android:textAppearance="?attr/textAppearanceBodyLarge"
+ android:textColor="?attr/colorOnSurface"
+ android:textIsSelectable="true" />
-
-
diff --git a/app/src/main/res/layout/dialog_homework.xml b/app/src/main/res/layout/dialog_homework.xml
index 341cec54..8c6cf0a7 100644
--- a/app/src/main/res/layout/dialog_homework.xml
+++ b/app/src/main/res/layout/dialog_homework.xml
@@ -1,71 +1,56 @@
-
+ android:layout_height="wrap_content">
+ android:background="@drawable/ic_all_divider"
+ app:layout_constraintTop_toBottomOf="@id/homeworkDialogRecycler" />
-
+
-
-
-
-
-
+
+
diff --git a/app/src/main/res/layout/dialog_homework_add.xml b/app/src/main/res/layout/dialog_homework_add.xml
index 524f0db0..e0ff5b74 100644
--- a/app/src/main/res/layout/dialog_homework_add.xml
+++ b/app/src/main/res/layout/dialog_homework_add.xml
@@ -2,37 +2,35 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport="true"
- android:minWidth="300dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp">
+ android:layout_height="match_parent">
-
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp">
+ android:textAppearance="?attr/textAppearanceHeadlineSmall"
+ android:textColor="?attr/colorOnSurface"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+ android:hint="@string/all_subject"
+ app:layout_constraintTop_toBottomOf="@id/homeworkDialogDate">
+ android:hint="@string/all_teacher"
+ app:layout_constraintTop_toBottomOf="@id/homeworkDialogSubject">
+ android:hint="@string/all_content"
+ app:layout_constraintTop_toBottomOf="@id/homeworkDialogTeacher">
-
+
-
-
-
-
-
+
+
diff --git a/app/src/main/res/layout/dialog_lesson_completed.xml b/app/src/main/res/layout/dialog_lesson_completed.xml
index 500cdb6f..3a1d3fd0 100644
--- a/app/src/main/res/layout/dialog_lesson_completed.xml
+++ b/app/src/main/res/layout/dialog_lesson_completed.xml
@@ -1,4 +1,5 @@
+
-
-
-
-
+
-
-
+
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@id/timetableDialogLessonValue"
+ tools:visibility="visible" />
+ app:layout_constraintTop_toBottomOf="@id/timetableDialogTeacherValue"
+ tools:visibility="visible" />
+ app:layout_constraintTop_toBottomOf="@id/timetableDialogRoomValue"
+ tools:visibility="visible" />
@@ -106,7 +105,7 @@
diff --git a/app/src/main/res/layout/fragment_login_advanced.xml b/app/src/main/res/layout/fragment_login_advanced.xml
index c7acaa70..43016db4 100644
--- a/app/src/main/res/layout/fragment_login_advanced.xml
+++ b/app/src/main/res/layout/fragment_login_advanced.xml
@@ -97,7 +97,7 @@
+ app:layout_constraintTop_toBottomOf="@+id/loginFormUsernameLayout">
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_message.xml b/app/src/main/res/layout/fragment_message.xml
index 5269d95e..b3dac0dd 100644
--- a/app/src/main/res/layout/fragment_message.xml
+++ b/app/src/main/res/layout/fragment_message.xml
@@ -29,6 +29,7 @@
+ android:layout_height="0dp"
+ android:layout_weight="1">
@@ -173,4 +173,4 @@
app:srcCompat="@drawable/ic_chevron_right"
app:tint="?colorPrimary" />
-
+
diff --git a/app/src/main/res/layout/fragment_timetable_completed.xml b/app/src/main/res/layout/fragment_timetable_completed.xml
index e089275d..8d647ff6 100644
--- a/app/src/main/res/layout/fragment_timetable_completed.xml
+++ b/app/src/main/res/layout/fragment_timetable_completed.xml
@@ -93,7 +93,7 @@
+ android:layout_marginVertical="6dp">
+ android:layout_marginVertical="6dp">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_dashboard_announcements.xml b/app/src/main/res/layout/item_dashboard_announcements.xml
index 19f72088..b9ddb757 100644
--- a/app/src/main/res/layout/item_dashboard_announcements.xml
+++ b/app/src/main/res/layout/item_dashboard_announcements.xml
@@ -8,8 +8,7 @@
android:layout_marginVertical="6dp"
android:clickable="true"
android:focusable="true"
- android:foreground="?selectableItemBackground"
- app:cardElevation="4dp">
+ android:foreground="?selectableItemBackground">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_dashboard_conferences.xml b/app/src/main/res/layout/item_dashboard_conferences.xml
index 02d3edfc..b02b8e18 100644
--- a/app/src/main/res/layout/item_dashboard_conferences.xml
+++ b/app/src/main/res/layout/item_dashboard_conferences.xml
@@ -8,8 +8,7 @@
android:layout_marginVertical="6dp"
android:clickable="true"
android:focusable="true"
- android:foreground="?selectableItemBackground"
- app:cardElevation="4dp">
+ android:foreground="?selectableItemBackground">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_dashboard_exams.xml b/app/src/main/res/layout/item_dashboard_exams.xml
index 9cc98d79..84302403 100644
--- a/app/src/main/res/layout/item_dashboard_exams.xml
+++ b/app/src/main/res/layout/item_dashboard_exams.xml
@@ -8,8 +8,7 @@
android:layout_marginVertical="6dp"
android:clickable="true"
android:focusable="true"
- android:foreground="?selectableItemBackground"
- app:cardElevation="4dp">
+ android:foreground="?selectableItemBackground">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_dashboard_grades.xml b/app/src/main/res/layout/item_dashboard_grades.xml
index 5cc9ce30..345d8a5e 100644
--- a/app/src/main/res/layout/item_dashboard_grades.xml
+++ b/app/src/main/res/layout/item_dashboard_grades.xml
@@ -6,8 +6,7 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginVertical="6dp"
- android:foreground="?selectableItemBackground"
- app:cardElevation="4dp">
+ android:foreground="?selectableItemBackground">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_dashboard_homework.xml b/app/src/main/res/layout/item_dashboard_homework.xml
index 975d66ef..b36afc57 100644
--- a/app/src/main/res/layout/item_dashboard_homework.xml
+++ b/app/src/main/res/layout/item_dashboard_homework.xml
@@ -8,8 +8,7 @@
android:layout_marginVertical="6dp"
android:clickable="true"
android:focusable="true"
- android:foreground="?selectableItemBackground"
- app:cardElevation="4dp">
+ android:foreground="?selectableItemBackground">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_dashboard_horizontal_group.xml b/app/src/main/res/layout/item_dashboard_horizontal_group.xml
index 0c59d1eb..1c9246a1 100644
--- a/app/src/main/res/layout/item_dashboard_horizontal_group.xml
+++ b/app/src/main/res/layout/item_dashboard_horizontal_group.xml
@@ -13,7 +13,6 @@
android:layout_width="0dp"
android:layout_height="44dp"
android:layout_marginVertical="4dp"
- app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/dashboard_horizontal_group_item_message_container"
app:layout_constraintHorizontal_chainStyle="spread_inside"
@@ -81,7 +80,6 @@
android:layout_height="44dp"
android:layout_marginVertical="4dp"
android:layout_marginEnd="8dp"
- app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/dashboard_horizontal_group_item_attendance_container"
app:layout_constraintStart_toEndOf="@id/dashboard_horizontal_group_item_lucky_container"
@@ -154,7 +152,6 @@
android:layout_width="0dp"
android:layout_height="44dp"
android:layout_marginVertical="4dp"
- app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/dashboard_horizontal_group_item_message_container"
@@ -221,7 +218,6 @@
android:layout_height="44dp"
android:layout_marginVertical="4dp"
android:visibility="gone"
- app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/dashboard_horizontal_group_item_message_container"
diff --git a/app/src/main/res/layout/item_dashboard_lessons.xml b/app/src/main/res/layout/item_dashboard_lessons.xml
index 9156c1a2..a40f17f2 100644
--- a/app/src/main/res/layout/item_dashboard_lessons.xml
+++ b/app/src/main/res/layout/item_dashboard_lessons.xml
@@ -5,11 +5,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
- android:layout_marginVertical="6dp"
- android:clickable="true"
- android:focusable="true"
- android:foreground="?selectableItemBackground"
- app:cardElevation="4dp">
+ android:layout_marginVertical="6dp">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_grade_details.xml b/app/src/main/res/layout/item_grade_details.xml
index 2f3bd2de..6849e929 100644
--- a/app/src/main/res/layout/item_grade_details.xml
+++ b/app/src/main/res/layout/item_grade_details.xml
@@ -20,11 +20,12 @@
android:id="@+id/gradeItemValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@color/grade_material_default"
+ android:background="@drawable/background_grade_rounded"
+ android:backgroundTint="@color/grade_material_default"
android:gravity="center"
android:maxLength="5"
android:minWidth="45dp"
- android:minHeight="40dp"
+ android:minHeight="45dp"
android:textColor="@android:color/white"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/gradeDetailsContainer"
diff --git a/app/src/main/res/layout/item_grade_statistics_header.xml b/app/src/main/res/layout/item_grade_statistics_header.xml
index 92f522ba..cc35f606 100644
--- a/app/src/main/res/layout/item_grade_statistics_header.xml
+++ b/app/src/main/res/layout/item_grade_statistics_header.xml
@@ -1,8 +1,10 @@
+ android:layout_height="wrap_content"
+ tools:context=".ui.modules.grade.statistics.GradeStatisticsAdapter">
-
+ android:paddingEnd="16dp"
+ app:selectionRequired="true"
+ app:singleSelection="true">
-
-
-
-
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_homework_dialog_details.xml b/app/src/main/res/layout/item_homework_dialog_details.xml
index 9d560ba5..1b1c1d39 100644
--- a/app/src/main/res/layout/item_homework_dialog_details.xml
+++ b/app/src/main/res/layout/item_homework_dialog_details.xml
@@ -13,16 +13,17 @@
android:orientation="horizontal">
+ android:textAppearance="?attr/textAppearanceHeadlineSmall"
+ android:textColor="?attr/colorOnSurface"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
-
-
-
-
-
diff --git a/app/src/main/res/layout/item_menu_order.xml b/app/src/main/res/layout/item_menu_order.xml
new file mode 100644
index 00000000..84510d1e
--- /dev/null
+++ b/app/src/main/res/layout/item_menu_order.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_message_chips.xml b/app/src/main/res/layout/item_message_chips.xml
index da2e2031..c1f36c4d 100644
--- a/app/src/main/res/layout/item_message_chips.xml
+++ b/app/src/main/res/layout/item_message_chips.xml
@@ -21,29 +21,23 @@
+ android:text="@string/message_chip_only_unread" />
+ android:text="@string/message_chip_only_with_attachments" />
diff --git a/app/src/main/res/layout/item_notifications_center.xml b/app/src/main/res/layout/item_notifications_center.xml
index 16a7ae0c..a2a67748 100644
--- a/app/src/main/res/layout/item_notifications_center.xml
+++ b/app/src/main/res/layout/item_notifications_center.xml
@@ -5,8 +5,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
- android:layout_marginTop="12dp"
- app:cardElevation="4dp">
+ android:layout_marginTop="12dp">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/item_widget_timetable.xml b/app/src/main/res/layout/item_widget_timetable.xml
index 899d7503..27c9db66 100644
--- a/app/src/main/res/layout/item_widget_timetable.xml
+++ b/app/src/main/res/layout/item_widget_timetable.xml
@@ -1,129 +1,112 @@
-
+ android:textAppearance="?attr/textAppearanceHeadline6"
+ android:textSize="24sp"
+ tools:text="1"
+ tools:textColor="?attr/colorTimetableChange" />
-
-
-
+
+ android:textAppearance="?attr/textAppearanceBodySmall"
+ tools:text="08:00" />
+ android:layout_marginTop="4dp"
+ android:textAppearance="?attr/textAppearanceBodySmall"
+ tools:text="09:45" />
+
+
+
+
-
-
+ android:lines="1"
+ android:textAppearance="?attr/textAppearanceTitleMedium"
+ tools:text="Programowanie aplikacji mobilnych i desktopowych" />
+
+
+
+
+
+
+
+
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_widget_timetable_dark.xml b/app/src/main/res/layout/item_widget_timetable_dark.xml
deleted file mode 100644
index 06233244..00000000
--- a/app/src/main/res/layout/item_widget_timetable_dark.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/item_widget_timetable_footer.xml b/app/src/main/res/layout/item_widget_timetable_footer.xml
new file mode 100644
index 00000000..ef14da5d
--- /dev/null
+++ b/app/src/main/res/layout/item_widget_timetable_footer.xml
@@ -0,0 +1,12 @@
+
+
diff --git a/app/src/main/res/layout/item_widget_timetable_small.xml b/app/src/main/res/layout/item_widget_timetable_small.xml
deleted file mode 100644
index 1bf4072d..00000000
--- a/app/src/main/res/layout/item_widget_timetable_small.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/item_widget_timetable_small_dark.xml b/app/src/main/res/layout/item_widget_timetable_small_dark.xml
deleted file mode 100644
index 50bbbd03..00000000
--- a/app/src/main/res/layout/item_widget_timetable_small_dark.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/layout_preference_switch.xml b/app/src/main/res/layout/layout_preference_switch.xml
new file mode 100644
index 00000000..c4f8a6c2
--- /dev/null
+++ b/app/src/main/res/layout/layout_preference_switch.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/app/src/main/res/layout/subitem_dashboard_grades.xml b/app/src/main/res/layout/subitem_dashboard_grades.xml
index 9354be3d..c8165b95 100644
--- a/app/src/main/res/layout/subitem_dashboard_grades.xml
+++ b/app/src/main/res/layout/subitem_dashboard_grades.xml
@@ -23,7 +23,7 @@
android:id="@+id/dashboard_grades_subitem_grade_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
+ android:layout_marginStart="2dp"
android:layout_marginBottom="6dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
@@ -36,4 +36,4 @@
android:visibility="gone"
tools:visibility="visible" />
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/subitem_dashboard_small_grade.xml b/app/src/main/res/layout/subitem_dashboard_small_grade.xml
index 986d9602..6800b72e 100644
--- a/app/src/main/res/layout/subitem_dashboard_small_grade.xml
+++ b/app/src/main/res/layout/subitem_dashboard_small_grade.xml
@@ -5,7 +5,8 @@
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginStart="4dp"
- android:background="@color/grade_material_default"
+ android:background="@drawable/background_grade_small_rounded"
+ android:backgroundTint="@color/grade_material_default"
android:gravity="center"
android:maxLength="5"
android:minWidth="20dp"
diff --git a/app/src/main/res/layout/widget_luckynumber.xml b/app/src/main/res/layout/widget_luckynumber.xml
index 360a1970..fc8acc60 100644
--- a/app/src/main/res/layout/widget_luckynumber.xml
+++ b/app/src/main/res/layout/widget_luckynumber.xml
@@ -1,63 +1,46 @@
-
+ android:adjustViewBounds="true"
+ android:importantForAccessibility="no"
+ android:scaleType="fitCenter"
+ android:src="@drawable/shape_badge"
+ android:tint="?attr/colorSurface"
+ app:tint="?attr/colorSurface"
+ tools:ignore="UseAppTint" />
-
-
+ android:layout_gravity="center"
+ android:text="17"
+ android:textColor="?attr/colorPrimary"
+ android:textSize="72sp"
+ android:textStyle="bold"
+ tools:ignore="HardcodedText" />
-
-
+
+
diff --git a/app/src/main/res/layout/widget_luckynumber_dark.xml b/app/src/main/res/layout/widget_luckynumber_dark.xml
deleted file mode 100644
index def110de..00000000
--- a/app/src/main/res/layout/widget_luckynumber_dark.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/widget_timetable.xml b/app/src/main/res/layout/widget_timetable.xml
index 059bb741..3abc488e 100644
--- a/app/src/main/res/layout/widget_timetable.xml
+++ b/app/src/main/res/layout/widget_timetable.xml
@@ -1,99 +1,115 @@
-
-
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:paddingVertical="16dp">
-
-
-
-
-
-
-
-
+ android:layout_marginStart="8dp"
+ android:layout_weight="1"
+ android:lines="1"
+ android:textAppearance="?attr/textAppearanceHeadline5"
+ tools:text="Pon, 12.05" />
+ android:tint="?attr/colorPrimary"
+ app:tint="?attr/colorPrimary"
+ tools:ignore="UseAppTint" />
-
+ android:tint="?attr/colorPrimary"
+ app:tint="?attr/colorPrimary"
+ tools:ignore="UseAppTint" />
-
+
+
+
+
+
+
+
+
+ android:layout_height="match_parent">
-
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/widget_timetable_dark.xml b/app/src/main/res/layout/widget_timetable_dark.xml
deleted file mode 100644
index 9c8b8c56..00000000
--- a/app/src/main/res/layout/widget_timetable_dark.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/action_menu_dashboard.xml b/app/src/main/res/menu/action_menu_dashboard.xml
index 13565a19..71203d32 100644
--- a/app/src/main/res/menu/action_menu_dashboard.xml
+++ b/app/src/main/res/menu/action_menu_dashboard.xml
@@ -6,13 +6,13 @@
android:icon="@drawable/ic_settings_notifications"
android:orderInCategory="1"
android:title="@string/notifications_center_title"
- app:iconTint="@color/material_on_surface_emphasis_medium"
+ app:iconTint="?colorControlNormal"
app:showAsAction="ifRoom" />
-
\ No newline at end of file
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index da1bca12..a8699eec 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 4a91cc85..bedb491b 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -26,6 +26,7 @@
Informace o žáku
Domů
Centrum oznámení
+ Konfigurace menu
Semestr %1$d, %2$d/%3$d
@@ -678,8 +679,10 @@
Vrátit
Změnit
Přidat do kalendáře
+ Zrušit
Žádné lekce
+ Synchronizováno %1$s v %2$s
Vybrat motiv
Světlý
Tmavý
@@ -699,6 +702,8 @@
Známky barevné schéma
Třídění předmětů
Jazyk
+ Konfigurace menu
+ Nastavit pořadí funkcí v menu
Oznámení
Jiné
Zobrazit oznámení
@@ -800,6 +805,10 @@
Aktualizace byla stažena.
Restartovat
Aktualizace selhala! Wulkanowy nemusí fungovat správně. Zvažte aktualizaci
+
+ Restartování aplikace
+ Pro uložení změn je nutné aplikaci restartovat
+ Restartovat
Žádné internetové připojení
Vyskytla se chyba. Zkontrolujte hodiny svého zařízení
diff --git a/app/src/main/res/values-da-rDK/strings.xml b/app/src/main/res/values-da-rDK/strings.xml
index 66723127..ebec11b2 100644
--- a/app/src/main/res/values-da-rDK/strings.xml
+++ b/app/src/main/res/values-da-rDK/strings.xml
@@ -26,6 +26,7 @@
Student info
Dashboard
Notifications center
+ Menu configuartion
Semester %1$d, %2$d/%3$d
@@ -590,8 +591,10 @@
Undo
Change
Add to calendar
+ Cancel
No lessons
+ Synchronized on %1$s at %2$s
Choose theme
Light
Dark
@@ -611,6 +614,8 @@
Grades color scheme
Subjects sorting
Language
+ Menu configuration
+ Set the order of functions in the menu
Notifications
Other
Show notifications
@@ -712,6 +717,10 @@
An update has just been downloaded.
Restart
Update failed! Wulkanowy may not function properly. Consider updating
+
+ Application restart
+ The application must restart for the changes to be saved
+ Restart
No internet connection
An error occurred. Check your device clock
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index f7b8e7c4..1e1785bf 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -26,6 +26,7 @@
Schülerinfo
Übersicht
Benachrichtigungszentrum
+ Menu configuartion
Semester %1$d, %2$d/%3$d
@@ -590,8 +591,10 @@
lösen
Ändern
Zum Kalender hinzufügen
+ Cancel
Keine Lektionen
+ Synchronized on %1$s at %2$s
Thema wählen
Licht
Dunkel
@@ -611,6 +614,8 @@
Farbschema der Noten
Schulfachen sortieren
Sprache
+ Menu configuration
+ Set the order of functions in the menu
Benachrichtigungen
Sonstiges
Benachrichtigungen anzeigen
@@ -712,6 +717,10 @@
Ein Update wurde gerade heruntergeladen.
Neustart
Update fehlgeschlagen! Wulkanowy funktioniert möglicherweise nicht richtig. Überlegen Sie die Aktualisierung
+
+ Application restart
+ The application must restart for the changes to be saved
+ Restart
Keine Internetverbindung
Es ist ein Fehler aufgetreten. Überprüfen Sie Ihre Geräteuhr
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 66723127..ebec11b2 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -26,6 +26,7 @@
Student info
Dashboard
Notifications center
+ Menu configuartion
Semester %1$d, %2$d/%3$d
@@ -590,8 +591,10 @@
Undo
Change
Add to calendar
+ Cancel
No lessons
+ Synchronized on %1$s at %2$s
Choose theme
Light
Dark
@@ -611,6 +614,8 @@
Grades color scheme
Subjects sorting
Language
+ Menu configuration
+ Set the order of functions in the menu
Notifications
Other
Show notifications
@@ -712,6 +717,10 @@
An update has just been downloaded.
Restart
Update failed! Wulkanowy may not function properly. Consider updating
+
+ Application restart
+ The application must restart for the changes to be saved
+ Restart
No internet connection
An error occurred. Check your device clock
diff --git a/app/src/main/res/values-night-v31/styles.xml b/app/src/main/res/values-night-v31/styles.xml
new file mode 100644
index 00000000..067a4353
--- /dev/null
+++ b/app/src/main/res/values-night-v31/styles.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml
index 881d5bd4..7d2f0cfe 100644
--- a/app/src/main/res/values-night/styles.xml
+++ b/app/src/main/res/values-night/styles.xml
@@ -1,20 +1,41 @@
-
-
-
-
-
-
+
+
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 4891015d..f797a4dc 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -26,6 +26,7 @@
Informacje o uczniu
Start
Centrum powiadomień
+ Konfiguracja menu
Semestr %1$d, %2$d/%3$d
@@ -678,8 +679,10 @@
Cofnij
Zmień
Dodaj do kalendarza
+ Anuluj
Brak lekcji
+ Zsynchronizowano %1$s o %2$s
Wybierz motyw
Jasny
Ciemny
@@ -699,6 +702,8 @@
Schemat kolorów ocen
Sortowanie przedmiotów
Język
+ Konfiguracja menu
+ Ustaw kolejność funkcji w menu
Powiadomienia
Inne
Pokazuj powiadomienia
@@ -800,6 +805,10 @@
Aktualizacja została pobrana.
Uruchom ponownie
Aktualizacja nie powiodła się! Wulkanowy może nie działać prawidłowo. Rozważ aktualizację
+
+ Ponowne uruchomienie aplikacji
+ W celu zapisania zmian aplikacja musi zostać ponownie uruchomiona
+ Uruchom ponownie
Brak połączenia z internetem
Wystąpił błąd. Sprawdź poprawność daty w urządzeniu
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 01e43183..7a42e388 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -15,8 +15,8 @@
Отладка уведомлений
Разработчики
Лицензии
- Письма
- Написать
+ Сообщения
+ Новое сообщение
Новое домашнее задание
Замечания и свершения
Домашние задания
@@ -26,6 +26,7 @@
Информация о ученике
Главная
Центр уведомлений
+ Настройка меню
%1$d семестр, %2$d/%3$d
@@ -55,7 +56,7 @@
Неверный symbol
Ученик не найден. Проверьте symbol и выбранный тип дненика UONET+
Данный ученик уже авторизован
- The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Wygeneruj kod dostępu.\n\nMake sure that you have set the appropriate register variant in the UONET+ register variant field on the first login screen
+ Symbol можно найти на странице регистрации в Uczeń → Dostęp Mobilny → Wygeneruj kod dostępu.\n\nУбедитесь, что вы выбрали соответствующий тип дневника в поле Тип дневника UONET+ на первом экране входа
Выберите учеников для авторизации в приложении
Другие варианты
В этом режиме не работают: счастливый номер, статистика класса по оценкам, статистика посещаемости и уроков, информация о школе и список зарегистрированных устройств
@@ -72,14 +73,14 @@
Восстановить
Ученик уже авторизован
Стандартный
- Other search locations
- No active students found
- Enter a different symbol
+ Другие места поиска
+ Не найдено активных учеников
+ Введите другой symbol
- Enable notifications
- Enable notifications so you don\'t miss message from teacher or new grade
- Skip
- Enable
+ Включить уведомления
+ Включить уведомления, чтобы вы не пропустили сообщение от учителя или новую оценку
+ Пропустить
+ Включить
Менеджер аккаунтов
Войти
@@ -294,7 +295,7 @@
Отправленные
Корзина
(нет темы)
- Нет писем
+ Нет сообщений
От:
Кому:
Дата: %1$s
@@ -304,7 +305,7 @@
Отменить выбор
Перенести в корзину
Удалить навсегда
- Письмо успешно удалено
+ Сообщение успешно удалено
ученик
родитель
опекун
@@ -313,8 +314,8 @@
Печать
Тема
Содержание
- Письмо успешно отправлено
- Письма не существует
+ Сообщение успешно отправлено
+ Сообщения не существует
Вы должны выбрать как минимум одного получателя
Текст сообщения должен содержать как минимум 3 знака
Все почтовые ящики
@@ -334,8 +335,8 @@
- Новые сообщения
- Новые сообщения
- Вы хотите восстановить черновик письма?
- Вы хотите восстановить черновик письма с получателями: %s?
+ Вы хотите восстановить черновик сообщения?
+ Вы хотите восстановить черновик сообщения с получателями: %s?
- Вы получили %1$d новое сообщение
- Вы получили %1$d новых сообщения
@@ -493,8 +494,8 @@
Присутствует на встрече
Повестка дня
- Place
- Topic
+ Место
+ Тема
Объявления школы
Нет школьных объявлений
@@ -678,8 +679,10 @@
Отменить
Изменить
Добавить в календарь
+ Отменить
Нет уроков
+ Synchronized on %1$s at %2$s
Выбрать тему
Светлая
Тёмная
@@ -699,6 +702,8 @@
Цветовая схема оценок
Сортировка уроков
Язык
+ Настройка меню
+ Установить порядок функций в меню
Уведомления
Прочее
Показывать уведомления
@@ -780,7 +785,7 @@
Новые встречи
Новые тесты
Счастливый номер
- Новые письма
+ Новые сообщения
Новые замечания
Новые школьные объявления
Push-уведомления
@@ -800,6 +805,10 @@
Только что было скачано обновление.
Перезапустить
Не удалось обновить! Wulkanowy может работать некорректно. Рассмотрите возможность обновления
+
+ Перезапуск приложение
+ Для сохранения изменений необходимо перезапустить приложение
+ Перезапустить
Интернет-соединение отсутствует
Произошла ошибка. Проверьте время на вашем устройстве
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 4189c534..cde3178b 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -26,6 +26,7 @@
Informácie o žiakovi
Domov
Centrum oznámení
+ Konfigurácia menu
Semester %1$d, %2$d/%3$d
@@ -678,8 +679,10 @@
Vrátiť
Zmeniť
Pridať do kalendára
+ Zrušiť
Žiadne lekcie
+ Synchronizované %1$s v %2$s
Vybrať motív
Svetlý
Tmavý
@@ -699,6 +702,8 @@
Známky farebnú schému
Triedenie predmetov
Jazyk
+ Konfigurácia menu
+ Nastaviť poradie funkcií v menu
Oznámenia
Iné
Zobraziť oznámenia
@@ -800,6 +805,10 @@
Aktualizácia bola stiahnutá.
Reštartovať
Aktualizácia zlyhala! Wulkanowy nemusí fungovať správne. Zvážte aktualizáciu
+
+ Reštartovanie aplikácie
+ Pre uloženie zmien je nutné aplikáciu reštartovať
+ Reštartovať
Žiadne internetové pripojenie
Vyskytla sa chyba. Skontrolujte hodiny svojho zariadenia
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 99c34065..9602aabb 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -26,6 +26,7 @@
Інформація про учня
Головна
Центр сповіщень
+ Конфігурація меню
%1$d семестр, %2$d/%3$d
@@ -55,7 +56,7 @@
Неправильний symbol
Студента не знайдено. Перевірте symbol та обранний тип щоденника UONET+
Цього учня вже авторизовано
- The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Wygeneruj kod dostępu.\n\nMake sure that you have set the appropriate register variant in the UONET+ register variant field on the first login screen
+ Symbol можно знайти на сторінці щоденника у Uczeń → Dostęp Mobilny → Wygeneruj kod dostępu.\n\nПереконайтеся, що ви вказали відповідний щоденник у полі Тип щоденника UONET+ на першому екрані логування
Виберіть учнів для авторизації в додатку
Інші варіанти
У цьому режимі не працюють: щасливий номер, статистика класу по оцінкам, статистика відвідуваності та уроків, інформація про школу та список зареєстрованих пристроїв
@@ -72,14 +73,14 @@
Відновити
Учня вже авторизовано
Стандартний
- Other search locations
- No active students found
- Enter a different symbol
+ Інші розташування пошуку
+ Активних учнів не знайдено
+ Введіть інший symbol
- Enable notifications
- Enable notifications so you don\'t miss message from teacher or new grade
- Skip
- Enable
+ Увімкнути сповіщення
+ Увімкнути сповіщення, щоб не пропустити лист від вчителя або нову оцінку
+ Пропустити
+ Увімкнути
Змінити облікові записи
Увійти
@@ -493,8 +494,8 @@
Присутність на зустрічі
Порядок денний
- Place
- Topic
+ Місце
+ Тема
Оголошення школи
Немає шкільних оголошень
@@ -522,7 +523,7 @@
Ви впевнені, що хочете вийти з цього облікового запису?
Вийти з облікового запису учня
Обліковий запис учня
- Обліковий запис батька
+ Обліковий запис родителя
Змінити дані
Змінити облікові записи
Вибрати учня
@@ -678,8 +679,10 @@
Відмінити
Змінити
Додати у календар
+ Скасувати
Немаэ уроків
+ Synchronized on %1$s at %2$s
Увібрати тему
Яскрава
Темна
@@ -699,6 +702,8 @@
Схема кольорів оцінок
Сортування предметів
Мова
+ Конфігурація меню
+ Встановити порядок функцій в меню
Повідомлення
Інше
Показувати повідомлення
@@ -800,6 +805,10 @@
Щойно завантажено оновлення.
Перезавантажити
Помилка оновлення! Wulkanowy може не працювати належним чином. Подумайте про оновлення
+
+ Перезавантаження додатку
+ Додаток потрібно перезавантажити для збереження змін
+ Перезавантажити
Немає з\'єднання з інтернетом
Сталася помилка. Перевірте годинник пристрою
diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml
index 840f5357..95450ae1 100644
--- a/app/src/main/res/values-v23/styles.xml
+++ b/app/src/main/res/values-v23/styles.xml
@@ -3,6 +3,11 @@
-
\ No newline at end of file
+
+
+
diff --git a/app/src/main/res/values-v26/styles.xml b/app/src/main/res/values-v26/styles.xml
deleted file mode 100644
index 3fb0a5dd..00000000
--- a/app/src/main/res/values-v26/styles.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/values-v27/styles.xml b/app/src/main/res/values-v27/styles.xml
new file mode 100644
index 00000000..1cbe9791
--- /dev/null
+++ b/app/src/main/res/values-v27/styles.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/values-v28/styles.xml b/app/src/main/res/values-v28/styles.xml
deleted file mode 100644
index a936566f..00000000
--- a/app/src/main/res/values-v28/styles.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-v29/styles.xml b/app/src/main/res/values-v29/styles.xml
deleted file mode 100644
index a936566f..00000000
--- a/app/src/main/res/values-v29/styles.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-v31/styles.xml b/app/src/main/res/values-v31/styles.xml
new file mode 100644
index 00000000..cffb284e
--- /dev/null
+++ b/app/src/main/res/values-v31/styles.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index d4ed6e97..8986f357 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -2,6 +2,7 @@
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f3112b10..ac1b1c19 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,22 +1,51 @@
- #d32f2f
- #e57373
- #9a0007
+ #d32f2f
+ #B91B21
+ #FFFFFF
+ #FFDAD5
+ #410003
+ #FCFCFC
+ #201A1A
+ #F4DDDB
+ #534341
+ #857371
+ #352F2F
+ #FAEEEE
+ #FFB3AA
+
+ #FFB3AA
+ #680006
+ #93000E
+ #FFDAD5
+ #201A1A
+ #ECE0E0
+ #534341
+ #D7C1BF
+ #9F8C8A
+ #ECE0E0
+ #201A1A
+ #B91B21
+
+ #9a0007
#ff5722
#e84853
- #2D2D2D
- #1E1E1E
+ #f5e8e9
+ #312624
+ #150e0e
- #1C1C1C
- #0D0D0D
+ #f5e5e6
+ #342826
+ #181010
#FFD980
#ffd54f
- #ffd54f
- #ff8f00
+ #d32f2f
+ #e57373
+ #ff8f00
+ #ffd54f
#1f000000
#1fffffff
@@ -24,6 +53,12 @@
#ff383838
#ffffffff
+ #FF944841
+ #FFEDEA
+ #462527
+ #FFFBFF
+ #201A19
+
#a0c431
#d43f3f
diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml
index 9c092ec5..6c81100d 100644
--- a/app/src/main/res/values/preferences_defaults.xml
+++ b/app/src/main/res/values/preferences_defaults.xml
@@ -23,7 +23,6 @@
no
alphabetic
false
- false
false
false
0
diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml
index 80a71bc7..716639c0 100644
--- a/app/src/main/res/values/preferences_keys.xml
+++ b/app/src/main/res/values/preferences_keys.xml
@@ -28,7 +28,6 @@
show_whole_class_plan
show_groups_in_plan
timetable_show_timers
- homework_fullscreen
subjects_without_grades
optional_arithmetic_average
message_draft
@@ -40,4 +39,5 @@
ads_privacy_policy
ads_consent_data_processing
ads_over_eighteen
+ appearance_menu_order
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 38997054..4e53fd2f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,5 +1,6 @@
+
Login
Wulkanowy
@@ -26,8 +27,13 @@
Student info
Dashboard
Notifications center
+ Menu configuartion
+
+
Semester %1$d, %2$d/%3$d
+
+
Sign in with the student or parent account
Enter the symbol from the register page for account: <b>%1$s</b>
@@ -77,11 +83,15 @@
Other search locations
No active students found
Enter a different symbol
+
+
Enable notifications
Enable notifications so you don\'t miss message from teacher or new grade
Skip
Enable
+
+
Account manager
Log in
@@ -90,6 +100,8 @@
Application support
Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
Enable ads
+
+
Grade
Semester %d
@@ -152,6 +164,8 @@
- You received %1$d final grade
- You received %1$d final grades
+
+
Lesson
Room
@@ -187,6 +201,8 @@
- %d change
- %d changes
+
+
Completed lessons
Show completed lessons
@@ -194,6 +210,8 @@
Topic
Absence
Resources
+
+
Additional lessons
Show additional lessons
@@ -209,6 +227,8 @@
Start time
End time
End time must be greater than start time
+
+
Attendance summary
Absent for school reasons
@@ -241,8 +261,12 @@
- %d attendance
- %d attendance
+
+
Total
+
+
No exams this week
Type
@@ -259,6 +283,8 @@
- %d exam
- %d exams
+
+
Inbox
Sent
@@ -312,6 +338,8 @@
Messages deleted
Choose mailbox
+
+
No info about notes
Points
@@ -327,6 +355,8 @@
- You received %1$d note
- You received %1$d notes
+
+
- %d praise
@@ -340,6 +370,8 @@
- You received %1$d praise
- You received %1$d praises
+
+
- %d neutral note
@@ -353,6 +385,8 @@
- You received %1$d neutral note
- You received %1$d neutral notes
+
+
No info about homework
Mark as done
@@ -373,6 +407,8 @@
- %d homework
- %d homework
+
+
Lucky number
Today\'s lucky number is
@@ -380,9 +416,13 @@
Lucky number for today
Today\'s lucky number is: %s
Show history
+
+
Lucky number history
No info about lucky numbers
+
+
Mobile devices
No devices
@@ -392,8 +432,12 @@
Token
Symbol
PIN
+
+
School and teachers
+
+
School
No info about school
@@ -404,10 +448,14 @@
Name of pedagogue
Show on map
Call
+
+
Teachers
No info about teachers
No subject
+
+
Conferences
No info about conferences
@@ -427,6 +475,8 @@
Agenda
Place
Topic
+
+
School announcements
No school announcements
@@ -442,6 +492,8 @@
- You have %1$d new school announcement
- You have %1$d new school announcements
+
+
Add account
Logout
@@ -456,6 +508,8 @@
Contact
Residence details
Personal information
+
+
App version
Contributors
@@ -479,11 +533,17 @@
Licenses
Licenses of libraries used in the application
Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nIdentyfikator instalacji: %4$s\nTreść zgłoszenia:
+
+
License
+
+
Avatar
See more on GitHub
+
+
No info about student or student family
Name
@@ -506,13 +566,19 @@
Female
Last name
Guardian
+
+
Nick
Add nick
Choose avatar color
+
+
Share logs
Refresh
+
+
Lessons
(Tomorrow)
@@ -565,9 +631,13 @@
An error occurred while loading data
None
+
+
Check for updates
Before reporting a bug, check first if an update with the bug fix is available
+
+
Content
Retry
@@ -595,12 +665,18 @@
Undo
Change
Add to calendar
+ Cancel
+
+
No lessons
+ Synchronized on %1$s at %2$s
Choose theme
Light
Dark
System Theme
+
+
App
Default view
@@ -616,6 +692,8 @@
Grades color scheme
Subjects sorting
Language
+ Menu configuration
+ Set the order of functions in the menu
Notifications
Other
Show notifications
@@ -691,6 +769,8 @@
Advanced
App version, contributors, social portals
Displaying advertisements, project support
+
+
New grades
New homework
@@ -705,6 +785,8 @@
Debug
Timetable change
New attendance
+
+
Black
Red
@@ -712,11 +794,20 @@
Green
Purple
No color
+
+
Download of updates has started…
An update has just been downloaded.
Restart
Update failed! Wulkanowy may not function properly. Consider updating
+
+
+ Application restart
+ The application must restart for the changes to be saved
+ Restart
+
+
No internet connection
An error occurred. Check your device clock
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 7cd0f725..b0bf2819 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -2,19 +2,35 @@
-
-
-
-
-
+
+
diff --git a/app/src/main/res/xml/provider_widget_lucky_number.xml b/app/src/main/res/xml/provider_widget_lucky_number.xml
index 064f2057..330bd53f 100644
--- a/app/src/main/res/xml/provider_widget_lucky_number.xml
+++ b/app/src/main/res/xml/provider_widget_lucky_number.xml
@@ -4,11 +4,13 @@
android:configure="io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity"
android:initialLayout="@layout/widget_luckynumber"
android:minWidth="110dp"
- android:minHeight="40dp"
- android:minResizeWidth="40dp"
+ android:minHeight="110dp"
android:minResizeHeight="40dp"
android:previewImage="@drawable/img_luckynumber_widget_preview"
+ android:previewLayout="@layout/widget_luckynumber"
android:resizeMode="horizontal|vertical"
+ android:targetCellWidth="2"
+ android:targetCellHeight="2"
android:updatePeriodMillis="3600000"
android:widgetCategory="home_screen"
- tools:targetApi="jelly_bean_mr1" />
+ tools:targetApi="s" />
diff --git a/app/src/main/res/xml/provider_widget_timetable.xml b/app/src/main/res/xml/provider_widget_timetable.xml
index 5392dd50..3cdad0c8 100644
--- a/app/src/main/res/xml/provider_widget_timetable.xml
+++ b/app/src/main/res/xml/provider_widget_timetable.xml
@@ -8,7 +8,10 @@
android:minResizeWidth="250dp"
android:minResizeHeight="110dp"
android:previewImage="@drawable/img_timetable_widget_preview"
+ android:previewLayout="@layout/widget_timetable_preview"
android:resizeMode="horizontal|vertical"
+ android:targetCellWidth="3"
+ android:targetCellHeight="2"
android:updatePeriodMillis="3600000"
android:widgetCategory="home_screen"
- tools:targetApi="jelly_bean_mr1" />
+ tools:targetApi="s" />
diff --git a/app/src/main/res/xml/scheme_preferences_appearance.xml b/app/src/main/res/xml/scheme_preferences_appearance.xml
index b2da0287..62216c76 100644
--- a/app/src/main/res/xml/scheme_preferences_appearance.xml
+++ b/app/src/main/res/xml/scheme_preferences_appearance.xml
@@ -20,23 +20,21 @@
app:key="@string/pref_key_app_theme"
app:title="@string/pref_view_app_theme"
app:useSimpleSummaryProvider="true" />
-
+ app:key="@string/pref_key_menu_order"
+ app:summary="@string/pref_view_menu_order_summary"
+ app:title="@string/pref_view_menu_order_title" />
diff --git a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
index 84a0cb40..c8d95829 100644
--- a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
+++ b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
@@ -48,7 +48,7 @@ fun getSemesterPojo(diaryId: Int, semesterId: Int, start: LocalDate, end: LocalD
end = end,
)
-fun getStudentEntity(mode: Sdk.Mode = Sdk.Mode.API) = Student(
+fun getStudentEntity(mode: Sdk.Mode = Sdk.Mode.HEBE) = Student(
scrapperBaseUrl = "http://fakelog.cf",
email = "jan@fakelog.cf",
certificateKey = "",
diff --git a/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt b/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt
index a35e5d30..ac73becd 100644
--- a/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt
@@ -3,7 +3,7 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.sdk.pojo.Attendance
-import io.github.wulkanowy.sdk.scrapper.attendance.SentExcuse
+import io.github.wulkanowy.sdk.scrapper.attendance.SentExcuseStatus
import org.junit.Test
import java.time.Instant
import java.time.LocalDate
@@ -98,7 +98,7 @@ class AttendanceMapperTest {
timeId = 1,
categoryId = 1,
deleted = false,
- excuseStatus = SentExcuse.Status.WAITING,
+ excuseStatus = SentExcuseStatus.WAITING,
excusable = false,
absence = false,
excused = false,
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
index 896491ef..d0e500f1 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
@@ -63,7 +63,7 @@ class AttendanceRepositoryTest {
@Test
fun `force refresh without difference`() {
// prepare
- coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
+ coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester, emptyList())),
flowOf(remoteList.mapToEntities(semester, emptyList()))
@@ -77,7 +77,7 @@ class AttendanceRepositoryTest {
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
- coVerify { sdk.getAttendance(startDate, endDate, 1) }
+ coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) }
@@ -86,7 +86,7 @@ class AttendanceRepositoryTest {
@Test
fun `force refresh with more items in remote`() {
// prepare
- coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
+ coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())),
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())), // after fetch end before save result
@@ -101,7 +101,7 @@ class AttendanceRepositoryTest {
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
- coVerify { sdk.getAttendance(startDate, endDate, 1) }
+ coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify {
attendanceDb.insertAll(match {
@@ -114,7 +114,7 @@ class AttendanceRepositoryTest {
@Test
fun `force refresh with more items in local`() {
// prepare
- coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList.dropLast(1)
+ coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList.dropLast(1)
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester, emptyList())),
flowOf(remoteList.mapToEntities(semester, emptyList())), // after fetch end before save result
@@ -129,7 +129,7 @@ class AttendanceRepositoryTest {
// verify
assertEquals(null, res.errorOrNull)
assertEquals(1, res.dataOrNull?.size)
- coVerify { sdk.getAttendance(startDate, endDate, 1) }
+ coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
coVerify {
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
index e3790662..fb037a87 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
@@ -59,7 +59,7 @@ class ExamRemoteTest {
@Test
fun `force refresh without difference`() {
// prepare
- coEvery { sdk.getExams(startDate, realEndDate, 1) } returns remoteList
+ coEvery { sdk.getExams(startDate, realEndDate) } returns remoteList
coEvery { examDb.loadAll(1, 1, startDate, realEndDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
@@ -73,7 +73,7 @@ class ExamRemoteTest {
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
- coVerify { sdk.getExams(startDate, realEndDate, 1) }
+ coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify { examDb.insertAll(match { it.isEmpty() }) }
coVerify { examDb.deleteAll(match { it.isEmpty() }) }
@@ -82,7 +82,7 @@ class ExamRemoteTest {
@Test
fun `force refresh with more items in remote`() {
// prepare
- coEvery { sdk.getExams(startDate, realEndDate, 1) } returns remoteList
+ coEvery { sdk.getExams(startDate, realEndDate) } returns remoteList
coEvery { examDb.loadAll(1, 1, startDate, realEndDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
@@ -97,7 +97,7 @@ class ExamRemoteTest {
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
- coVerify { sdk.getExams(startDate, realEndDate, 1) }
+ coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify {
examDb.insertAll(match {
@@ -110,7 +110,7 @@ class ExamRemoteTest {
@Test
fun `force refresh with more items in local`() {
// prepare
- coEvery { sdk.getExams(startDate, realEndDate, 1) } returns remoteList.dropLast(1)
+ coEvery { sdk.getExams(startDate, realEndDate) } returns remoteList.dropLast(1)
coEvery { examDb.loadAll(1, 1, startDate, realEndDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
@@ -125,7 +125,7 @@ class ExamRemoteTest {
// verify
assertEquals(null, res.errorOrNull)
assertEquals(1, res.dataOrNull?.size)
- coVerify { sdk.getExams(startDate, realEndDate, 1) }
+ coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify { examDb.insertAll(match { it.isEmpty() }) }
coVerify {
@@ -137,7 +137,6 @@ class ExamRemoteTest {
private fun getExam(date: LocalDate) = SdkExam(
subject = "",
- group = "",
type = "",
description = "",
teacher = "",
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
index e8d0b6c8..1d6dfaff 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
@@ -9,13 +9,21 @@ import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
+import io.github.wulkanowy.sdk.pojo.Grades
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
+import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
-import org.junit.Assert.*
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import java.time.LocalDate
@@ -72,7 +80,7 @@ class GradeRepositoryTest {
createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"),
createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
)
- coEvery { sdk.getGrades(1) } returns (remoteList to emptyList())
+ coEvery { sdk.getGrades(1) } returns createGrades(remoteList)
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(listOf()), // empty because it is new user
@@ -122,7 +130,7 @@ class GradeRepositoryTest {
),
createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
)
- coEvery { sdk.getGrades(1) } returns (remoteList to emptyList())
+ coEvery { sdk.getGrades(1) } returns createGrades(remoteList)
val localList = listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Jedna ocena"),
@@ -169,7 +177,7 @@ class GradeRepositoryTest {
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)
- coEvery { sdk.getGrades(1) } returns (remoteList to emptyList())
+ coEvery { sdk.getGrades(1) } returns createGrades(remoteList)
val localList = listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
@@ -200,7 +208,7 @@ class GradeRepositoryTest {
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), // will be added...
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)
- coEvery { sdk.getGrades(1) } returns (remoteList to emptyList())
+ coEvery { sdk.getGrades(1) } returns createGrades(remoteList)
val localList = listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
@@ -230,7 +238,7 @@ class GradeRepositoryTest {
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)
- coEvery { sdk.getGrades(1) } returns (remoteList to emptyList())
+ coEvery { sdk.getGrades(1) } returns createGrades(remoteList)
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(listOf()),
@@ -250,7 +258,7 @@ class GradeRepositoryTest {
fun `force refresh when remote is empty`() {
// prepare
val remoteList = emptyList()
- coEvery { sdk.getGrades(semester.semesterId) } returns (remoteList to emptyList())
+ coEvery { sdk.getGrades(semester.semesterId) } returns createGrades(remoteList)
val localList = listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
@@ -284,4 +292,13 @@ class GradeRepositoryTest {
weight = weight.toString(),
weightValue = weight
)
+
+ private fun createGrades(grades: List): Grades = Grades(
+ details = grades,
+ summary = listOf(),
+ isAverage = false,
+ isPoints = false,
+ isForAdults = false,
+ type = 0,
+ )
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index 9a2c22fd..3a18ee97 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -224,7 +224,7 @@ class MessageRepositoryTest {
recipients = listOf(),
subject = "",
content = "Test",
- dateZoned = Instant.EPOCH.atZone(ZoneOffset.UTC),
+ date = Instant.EPOCH.atZone(ZoneOffset.UTC),
folderId = 1,
unread = true,
readBy = 1,
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
index 6865aa7d..1a3f9679 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
@@ -10,16 +10,21 @@ import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Device
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
+import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.Before
import org.junit.Test
-import java.time.LocalDateTime.of
-import java.time.ZoneId
+import java.time.ZoneOffset
+import java.time.ZonedDateTime.of
class MobileDeviceRepositoryTest {
@@ -134,9 +139,7 @@ class MobileDeviceRepositoryTest {
id = 0,
name = "",
deviceId = "",
- createDate = of(2019, 5, day, 0, 0, 0),
- modificationDate = of(2019, 5, day, 0, 0, 0),
- createDateZoned = of(2019, 5, day, 0, 0, 0).atZone(ZoneId.systemDefault()),
- modificationDateZoned = of(2019, 5, day, 0, 0, 0).atZone(ZoneId.systemDefault())
+ createDate = of(2019, 5, day, 0, 0, 0, 0, ZoneOffset.UTC),
+ modificationDate = of(2019, 5, day, 0, 0, 0, 0, ZoneOffset.UTC),
)
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/SemesterRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/SemesterRepositoryTest.kt
index 0ed00885..d8256869 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/SemesterRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/SemesterRepositoryTest.kt
@@ -75,7 +75,7 @@ class SemesterRepositoryTest {
coEvery { semesterDb.deleteAll(any()) } just Runs
coEvery { semesterDb.insertSemesters(any()) } returns listOf()
- val items = runBlocking { semesterRepository.getSemesters(student.copy(loginMode = Sdk.Mode.API.name)) }
+ val items = runBlocking { semesterRepository.getSemesters(student.copy(loginMode = Sdk.Mode.HEBE.name)) }
assertEquals(2, items.size)
assertEquals(0, items[0].diaryId)
}
@@ -215,6 +215,7 @@ class SemesterRepositoryTest {
@Test(expected = RuntimeException::class)
fun getCurrentSemester_emptyList() {
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns emptyList()
+ coEvery { sdk.getSemesters() } returns emptyList()
runBlocking { semesterRepository.getCurrentSemester(student) }
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt
deleted file mode 100644
index 9d3d7a2e..00000000
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-package io.github.wulkanowy.data.repositories
-
-import io.github.wulkanowy.TestDispatchersProvider
-import io.github.wulkanowy.data.db.dao.SemesterDao
-import io.github.wulkanowy.data.db.dao.StudentDao
-import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.sdk.pojo.Student
-import io.github.wulkanowy.utils.AppInfo
-import io.mockk.MockKAnnotations
-import io.mockk.coEvery
-import io.mockk.impl.annotations.MockK
-import io.mockk.mockk
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert.assertEquals
-import org.junit.Before
-import org.junit.Test
-
-class StudentTest {
-
- @MockK
- private lateinit var mockSdk: Sdk
-
- @MockK
- private lateinit var studentDb: StudentDao
-
- @MockK
- private lateinit var semesterDb: SemesterDao
-
- private lateinit var studentRepository: StudentRepository
-
- @Before
- fun initApi() {
- MockKAnnotations.init(this)
- studentRepository = StudentRepository(
- context = mockk(),
- dispatchers = TestDispatchersProvider(),
- studentDb = studentDb,
- semesterDb = semesterDb,
- sdk = mockSdk,
- appInfo = AppInfo(),
- appDatabase = mockk()
- )
- }
-
- @Test
- fun testRemoteAll() {
- coEvery { mockSdk.getStudentsFromScrapper(any(), any(), any(), any()) } returns listOf(
- getStudent("test")
- )
-
- val students = runBlocking { studentRepository.getStudentsScrapper("", "", "http://fakelog.cf", "") }
- assertEquals(1, students.size)
- assertEquals("test Kowalski", students.first().student.studentName)
- }
-
- private fun getStudent(name: String): Student {
- return Student(
- email = "",
- symbol = "",
- studentId = 0,
- userLoginId = 0,
- userLogin = "",
- userName = "",
- studentName = name,
- studentSurname = "Kowalski",
- schoolSymbol = "",
- schoolShortName = "",
- schoolName = "",
- className = "",
- classId = 0,
- certificateKey = "",
- privateKey = "",
- loginMode = Sdk.Mode.SCRAPPER,
- mobileBaseUrl = "",
- loginType = Sdk.ScrapperLoginType.STANDARD,
- scrapperBaseUrl = "",
- isParent = false,
- semesters = emptyList()
- )
- }
-}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
index e56aaa5d..92ad01b1 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
@@ -10,12 +10,17 @@ import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.sdk.pojo.TimetableFull
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
+import io.mockk.just
+import io.mockk.mockk
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -25,7 +30,7 @@ import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalDateTime.of
import java.time.ZoneId
-import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable
+import io.github.wulkanowy.sdk.pojo.Lesson as SdkLesson
class TimetableRepositoryTest {
@@ -62,18 +67,43 @@ class TimetableRepositoryTest {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
- timetableRepository = TimetableRepository(timetableDb, timetableAdditionalDao, timetableHeaderDao, sdk, timetableNotificationSchedulerHelper, refreshHelper)
+ timetableRepository = TimetableRepository(
+ timetableDb,
+ timetableAdditionalDao,
+ timetableHeaderDao,
+ sdk,
+ timetableNotificationSchedulerHelper,
+ refreshHelper
+ )
}
@Test
fun `force refresh without difference`() {
val remoteList = listOf(
- createTimetableRemote(of(2021, 1, 4, 8, 0), 1, "123", "Język polski", "Jan Kowalski", false),
- createTimetableRemote(of(2021, 1, 4, 8, 50), 2, "124", "Język niemiecki", "Joanna Czarniecka", true)
+ createTimetableRemote(
+ start = of(2021, 1, 4, 8, 0),
+ number = 1,
+ room = "123",
+ subject = "Język polski",
+ teacher = "Jan Kowalski",
+ changes = false
+ ),
+ createTimetableRemote(
+ start = of(2021, 1, 4, 8, 50),
+ number = 2,
+ room = "124",
+ subject = "Język niemiecki",
+ teacher = "Joanna Czarniecka",
+ changes = true
+ )
)
// prepare
- coEvery { sdk.getTimetableFull(startDate, endDate) } returns TimetableFull(emptyList(), remoteList, emptyList())
+ coEvery { sdk.getTimetable(startDate, endDate) } returns mockk {
+ every { headers } returns emptyList()
+ every { lessons } returns remoteList
+ every { additional } returns emptyList()
+ }
coEvery { timetableDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
@@ -81,7 +111,14 @@ class TimetableRepositoryTest {
coEvery { timetableDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { timetableDb.deleteAll(any()) } just Runs
- coEvery { timetableAdditionalDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
+ coEvery {
+ timetableAdditionalDao.loadAll(
+ diaryId = 1,
+ studentId = 1,
+ from = startDate,
+ end = endDate
+ )
+ } returns flowOf(listOf())
coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
@@ -90,23 +127,36 @@ class TimetableRepositoryTest {
coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
// execute
- val res = runBlocking { timetableRepository.getTimetable(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = runBlocking {
+ timetableRepository.getTimetable(
+ student = student,
+ semester = semester,
+ start = startDate,
+ end = endDate,
+ forceRefresh = true
+ ).toFirstResult()
+ }
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull!!.lessons.size)
- coVerify { sdk.getTimetableFull(startDate, endDate) }
+ coVerify { sdk.getTimetable(startDate, endDate) }
coVerify { timetableDb.loadAll(1, 1, startDate, endDate) }
coVerify { timetableDb.insertAll(match { it.isEmpty() }) }
coVerify { timetableDb.deleteAll(match { it.isEmpty() }) }
}
- private fun createTimetableRemote(start: LocalDateTime, number: Int = 1, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false) = SdkTimetable(
+ private fun createTimetableRemote(
+ start: LocalDateTime,
+ number: Int = 1,
+ room: String = "",
+ subject: String = "",
+ teacher: String = "",
+ changes: Boolean = false
+ ) = SdkLesson(
number = number,
- start = start,
- end = start.plusMinutes(45),
- startZoned = start.atZone(ZoneId.systemDefault()),
- endZoned = start.plusMinutes(45).atZone(ZoneId.systemDefault()),
+ start = start.atZone(ZoneId.systemDefault()),
+ end = start.plusMinutes(45).atZone(ZoneId.systemDefault()),
date = start.toLocalDate(),
subject = subject,
group = "",
diff --git a/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
index 6db16d2f..f58a5381 100644
--- a/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
@@ -207,7 +207,7 @@ class GetMailboxByStudentUseCaseTest {
className = "",
isCurrent = false,
isParent = false,
- loginMode = Sdk.Mode.API.name,
+ loginMode = Sdk.Mode.HEBE.name,
loginType = Sdk.ScrapperLoginType.STANDARD.name,
mobileBaseUrl = "",
password = "",
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
index a6ecdc26..b94002c0 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
@@ -1,14 +1,12 @@
package io.github.wulkanowy.ui.modules.grade
-import io.github.wulkanowy.data.Resource
-import io.github.wulkanowy.data.dataOrNull
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.Status
@@ -20,6 +18,7 @@ import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -31,7 +30,9 @@ import java.time.LocalDate.of
class GradeAverageProviderTest {
- private suspend fun Flow>.getResult() = toList()[1].dataOrNull!!
+ private suspend fun Flow>.getResult() = toFirstResult().let {
+ it.dataOrNull ?: throw it.errorOrNull ?: error("Unknown state")
+ }
@MockK
lateinit var preferencesRepository: PreferencesRepository
@@ -133,19 +134,23 @@ class GradeAverageProviderTest {
fun setUp() {
MockKAnnotations.init(this)
- every { preferencesRepository.gradeAverageForceCalc } returns false
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
coEvery { semesterRepository.getSemesters(student) } returns semesters
- every { preferencesRepository.gradeMinusModifier } returns .33
- every { preferencesRepository.gradePlusModifier } returns .33
+ every { preferencesRepository.gradeMinusModifierFlow } returns flowOf(.33)
+ every { preferencesRepository.gradePlusModifierFlow } returns flowOf(.33)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
- gradeAverageProvider = GradeAverageProvider(semesterRepository, gradeRepository, preferencesRepository)
+ gradeAverageProvider =
+ GradeAverageProvider(semesterRepository, gradeRepository, preferencesRepository)
}
@Test
fun `calc current semester standard average with no weights`() {
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery {
gradeRepository.getGrades(
student,
@@ -154,16 +159,26 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { noWeightGrades to noWeightGradesSummary }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(0.0, items.single { it.subject == "Matematyka" }.average, .0) // from summary: 0,0
+ assertEquals(
+ 0.0,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // from summary: 0,0
}
@Test
fun `calc current semester arithmetic average with no weights`() {
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns true
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(true)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery {
gradeRepository.getGrades(
student,
@@ -172,16 +187,26 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { noWeightGrades to noWeightGradesArithmeticSummary }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(4.0, items.single { it.subject == "Matematyka" }.average, .0) // from summary: 4,0
+ assertEquals(
+ 4.0,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // from summary: 4,0
}
@Test
fun `calc current semester average with load from cache sequence`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flow {
emit(Resource.Loading())
@@ -189,7 +214,13 @@ class GradeAverageProviderTest {
emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).toList() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).toList()
+ }
with(items[0]) {
assertEquals(Status.LOADING, status)
@@ -204,14 +235,18 @@ class GradeAverageProviderTest {
assertEquals(1, dataOrNull?.size)
}
- assertEquals(3.5, items[1].dataOrNull?.single { it.subject == "Język polski" }?.average ?: 0.0, .0) // from details and after set custom plus/minus
+ assertEquals(
+ 3.5,
+ items[1].dataOrNull?.single { it.subject == "Język polski" }?.average ?: 0.0,
+ .0
+ ) // from details and after set custom plus/minus
}
@Test
- fun `calc all year semester average with delayed emit`(){
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ fun `calc all year semester average with delayed emit`() {
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns flow {
@@ -224,7 +259,13 @@ class GradeAverageProviderTest {
emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, false).toList() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ false
+ ).toList()
+ }
with(items[0]) {
assertEquals(Status.LOADING, status)
@@ -235,14 +276,18 @@ class GradeAverageProviderTest {
assertEquals(1, dataOrNull?.size)
}
- assertEquals(3.5, items[1].dataOrNull?.single { it.subject == "Język polski" }?.average ?: 0.0, .0) // from details and after set custom plus/minus
+ assertEquals(
+ 3.5,
+ items[1].dataOrNull?.single { it.subject == "Język polski" }?.average ?: 0.0,
+ .0
+ ) // from details and after set custom plus/minus
}
@Test
fun `calc both semesters average with grade without grade in second semester`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
coEvery {
gradeRepository.getGrades(
@@ -251,7 +296,13 @@ class GradeAverageProviderTest {
false
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
- coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns resourceFlow {
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ false
+ )
+ } returns resourceFlow {
listOf(getGrade(semesters[2].semesterId, "Język polski", .0, .0, .0)) to listOf(
getSummary(semesters[2].semesterId, "Język polski", 2.5)
)
@@ -270,9 +321,9 @@ class GradeAverageProviderTest {
@Test
fun `calc both semesters average with no grade in second semester but with average in first semester`() {
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
coEvery {
gradeRepository.getGrades(
@@ -287,7 +338,15 @@ class GradeAverageProviderTest {
semesters[2],
false
)
- } returns resourceFlow { emptyList() to listOf(getSummary(24, "Język polski", .0)) }
+ } returns resourceFlow {
+ emptyList() to listOf(
+ getSummary(
+ 24,
+ "Język polski",
+ .0
+ )
+ )
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -302,9 +361,9 @@ class GradeAverageProviderTest {
@Test
fun `force calc average on no grades`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
coEvery {
gradeRepository.getGrades(
@@ -334,9 +393,9 @@ class GradeAverageProviderTest {
@Test
fun `force calc current semester average with default modifiers in scraper mode`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery {
gradeRepository.getGrades(
@@ -346,21 +405,31 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0) // from details and after set custom plus/minus
+ assertEquals(
+ 3.5,
+ items.single { it.subject == "Język polski" }.average,
+ .0
+ ) // from details and after set custom plus/minus
}
@Test
fun `force calc current semester average with custom modifiers in scraper mode`() {
val student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name)
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeMinusModifier } returns .33
- every { preferencesRepository.gradePlusModifier } returns .33
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeMinusModifierFlow } returns flowOf(.33)
+ every { preferencesRepository.gradePlusModifierFlow } returns flowOf(.33)
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery {
gradeRepository.getGrades(
@@ -370,21 +439,31 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0) // from details and after set custom plus/minus
+ assertEquals(
+ 3.5,
+ items.single { it.subject == "Język polski" }.average,
+ .0
+ ) // from details and after set custom plus/minus
}
@Test
fun `force calc current semester average with custom modifiers in api mode`() {
- val student = student.copy(loginMode = Sdk.Mode.API.name)
+ val student = student.copy(loginMode = Sdk.Mode.HEBE.name)
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeMinusModifier } returns .33 // useless in this mode
- every { preferencesRepository.gradePlusModifier } returns .33
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeMinusModifierFlow } returns flowOf(.33) // useless in this mode
+ every { preferencesRepository.gradePlusModifierFlow } returns flowOf(.33)
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery {
gradeRepository.getGrades(
@@ -394,21 +473,31 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(3.375, items.single { it.subject == "Język polski" }.average, .0) // (from details): 3.375
+ assertEquals(
+ 3.375,
+ items.single { it.subject == "Język polski" }.average,
+ .0
+ ) // (from details): 3.375
}
@Test
fun `force calc current semester average with custom modifiers in hybrid mode`() {
val student = student.copy(loginMode = Sdk.Mode.HYBRID.name)
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeMinusModifier } returns .33 // useless in this mode
- every { preferencesRepository.gradePlusModifier } returns .33
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeMinusModifierFlow } returns flowOf(.33) // useless in this mode
+ every { preferencesRepository.gradePlusModifierFlow } returns flowOf(.33)
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery {
gradeRepository.getGrades(
@@ -418,16 +507,26 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(3.375, items.single { it.subject == "Język polski" }.average, .0) // (from details): 3.375
+ assertEquals(
+ 3.375,
+ items.single { it.subject == "Język polski" }.average,
+ .0
+ ) // (from details): 3.375
}
@Test
fun `calc current semester average`() {
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery {
gradeRepository.getGrades(
student,
@@ -436,18 +535,28 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { secondGrades to secondSummaries }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(2.9, items.single { it.subject == "Matematyka" }.average, .0) // from summary: 2,9
+ assertEquals(
+ 2.9,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // from summary: 2,9
assertEquals(3.4, items.single { it.subject == "Fizyka" }.average, .0) // from details: 3,4
}
@Test
fun `force calc current semester average`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ONE_SEMESTER)
coEvery {
gradeRepository.getGrades(
student,
@@ -456,18 +565,28 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { secondGrades to secondSummaries }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(2.5, items.single { it.subject == "Matematyka" }.average, .0) // from details: 2,5
+ assertEquals(
+ 2.5,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // from details: 2,5
assertEquals(3.0, items.single { it.subject == "Fizyka" }.average, .0) // from details: 3,0
}
@Test
fun `force calc full year average when current is first`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery {
gradeRepository.getGrades(
student,
@@ -476,18 +595,32 @@ class GradeAverageProviderTest {
)
} returns resourceFlow { firstGrades to firstSummaries }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[1].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[1].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(3.5, items.single { it.subject == "Matematyka" }.average, .0) // (from summary): 3,5
- assertEquals(3.5, items.single { it.subject == "Fizyka" }.average, .0) // (from summary): 3,5
+ assertEquals(
+ 3.5,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // (from summary): 3,5
+ assertEquals(
+ 3.5,
+ items.single { it.subject == "Fizyka" }.average,
+ .0
+ ) // (from summary): 3,5
}
@Test
fun `calc full year average when current is first with load from cache sequence`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
emit(Resource.Loading())
@@ -495,7 +628,13 @@ class GradeAverageProviderTest {
emit(Resource.Success(firstGrades to firstSummaries))
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[1].semesterId, true).toList() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[1].semesterId,
+ true
+ ).toList()
+ }
with(items[0]) {
assertEquals(Status.LOADING, status)
@@ -511,40 +650,74 @@ class GradeAverageProviderTest {
}
assertEquals(2, items[2].dataOrNull?.size)
- assertEquals(3.5, items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0, .0) // (from summary): 3,5
- assertEquals(3.5, items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0, .0) // (from summary): 3,5
+ assertEquals(
+ 3.5,
+ items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0,
+ .0
+ ) // (from summary): 3,5
+ assertEquals(
+ 3.5,
+ items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0,
+ .0
+ ) // (from summary): 3,5
}
@Test
fun `calc both semesters average`() {
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow {
firstGrades to listOf(
getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.5)
)
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow {
secondGrades to listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
)
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(3.25, items.single { it.subject == "Matematyka" }.average, .0) // (from summaries ↑): 3,0 + 3,5 → 3,25
- assertEquals(3.75, items.single { it.subject == "Fizyka" }.average, .0) // (from summaries ↑): 3,5 + 4,0 → 3,75
+ assertEquals(
+ 3.25,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // (from summaries ↑): 3,0 + 3,5 → 3,25
+ assertEquals(
+ 3.75,
+ items.single { it.subject == "Fizyka" }.average,
+ .0
+ ) // (from summaries ↑): 3,5 + 4,0 → 3,75
}
@Test
fun `calc both semesters average when current is second with load from cache sequence`() {
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageForceCalc } returns false
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
emit(Resource.Loading())
emit(
@@ -584,7 +757,13 @@ class GradeAverageProviderTest {
)
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).toList() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).toList()
+ }
with(items[0]) {
assertEquals(Status.LOADING, status)
@@ -600,15 +779,23 @@ class GradeAverageProviderTest {
}
assertEquals(2, items[2].dataOrNull?.size)
- assertEquals(3.25, items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0, .0) // (from summaries ↑): 3,0 + 3,5 → 3,25
- assertEquals(3.75, items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0, .0) // (from summaries ↑): 3,5 + 4,0 → 3,75
+ assertEquals(
+ 3.25,
+ items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0,
+ .0
+ ) // (from summaries ↑): 3,0 + 3,5 → 3,25
+ assertEquals(
+ 3.75,
+ items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0,
+ .0
+ ) // (from summaries ↑): 3,5 + 4,0 → 3,75
}
@Test
fun `force calc full year average`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery {
gradeRepository.getGrades(
student,
@@ -616,7 +803,13 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow { firstGrades to firstSummaries }
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow {
secondGrades to listOf(
getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 7.26)
@@ -646,9 +839,9 @@ class GradeAverageProviderTest {
@Test
fun `calc all year average`() {
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
coEvery {
gradeRepository.getGrades(
student,
@@ -689,9 +882,9 @@ class GradeAverageProviderTest {
@Test
fun `force calc full year average when current is second with load from cache sequence`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
emit(Resource.Loading())
@@ -718,7 +911,13 @@ class GradeAverageProviderTest {
)
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).toList() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).toList()
+ }
with(items[0]) {
assertEquals(Status.LOADING, status)
@@ -734,15 +933,23 @@ class GradeAverageProviderTest {
}
assertEquals(2, items[2].dataOrNull?.size)
- assertEquals(3.0, items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0, .0) // (from details): 3,5 + 2,5 → 3,0
- assertEquals(3.25, items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0, .0) // (from details): 3,5 + 3,0 → 3,25
+ assertEquals(
+ 3.0,
+ items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0,
+ .0
+ ) // (from details): 3,5 + 2,5 → 3,0
+ assertEquals(
+ 3.25,
+ items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0,
+ .0
+ ) // (from details): 3,5 + 3,0 → 3,25
}
@Test
fun `force calc both semesters average when no summaries`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
coEvery {
gradeRepository.getGrades(
@@ -773,14 +980,18 @@ class GradeAverageProviderTest {
items.single { it.subject == "Matematyka" }.average,
.0
) // (from details): 3,5 + 2,5 → 3,0
- assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25
+ assertEquals(
+ 3.25,
+ items.single { it.subject == "Fizyka" }.average,
+ .0
+ ) // (from details): 3,5 + 3,0 → 3,25
}
@Test
fun `force calc full year average when no summaries`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery {
gradeRepository.getGrades(
@@ -820,33 +1031,59 @@ class GradeAverageProviderTest {
@Test
fun `calc both semesters average when missing summaries in both semesters`() {
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow {
firstGrades to listOf(
getSummary(22, "Matematyka", 4.0)
)
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow {
secondGrades to listOf(
getSummary(23, "Matematyka", 3.0)
)
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(3.5, items.single { it.subject == "Matematyka" }.average, .0) // (from summaries ↑): 4,0 + 3,0 → 3,5
- assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25
+ assertEquals(
+ 3.5,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // (from summaries ↑): 4,0 + 3,0 → 3,5
+ assertEquals(
+ 3.25,
+ items.single { it.subject == "Fizyka" }.average,
+ .0
+ ) // (from details): 3,5 + 3,0 → 3,25
}
@Test
fun `calc both semesters average when missing summary in second semester`() {
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
coEvery {
gradeRepository.getGrades(
@@ -886,9 +1123,9 @@ class GradeAverageProviderTest {
@Test
fun `calc both semesters average when missing summary in first semester`() {
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
coEvery {
gradeRepository.getGrades(
@@ -928,9 +1165,9 @@ class GradeAverageProviderTest {
@Test
fun `force calc full year average when missing summary in first semester`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery {
gradeRepository.getGrades(
@@ -970,11 +1207,17 @@ class GradeAverageProviderTest {
@Test
fun `force calc both semesters average with different average from all grades and from two semesters`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
@@ -986,7 +1229,13 @@ class GradeAverageProviderTest {
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
@@ -994,16 +1243,26 @@ class GradeAverageProviderTest {
) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(5.2296, items.single { it.subject == "Fizyka" }.average, .0001) // (from details): 5.72727272 + 4,732 → 5.229636363636364
+ assertEquals(
+ 5.2296,
+ items.single { it.subject == "Fizyka" }.average,
+ .0001
+ ) // (from details): 5.72727272 + 4,732 → 5.229636363636364
}
@Test
fun `force calc full year average with different average from all grades and from two semesters`() {
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
listOf(
@@ -1025,58 +1284,31 @@ class GradeAverageProviderTest {
) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(5.5429, items.single { it.subject == "Fizyka" }.average, .0001) // (from details): 5.72727272 + 4,732 → .average()
+ assertEquals(
+ 5.5429,
+ items.single { it.subject == "Fizyka" }.average,
+ .0001
+ ) // (from details): 5.72727272 + 4,732 → .average()
}
@Test
fun `force calc both semesters average with different average from all grades and from two semesters with custom modifiers`() {
val student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name)
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeMinusModifier } returns .33
- every { preferencesRepository.gradePlusModifier } returns .5
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeMinusModifierFlow } returns flowOf(.33)
+ every { preferencesRepository.gradePlusModifierFlow } returns flowOf(.5)
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- coEvery { semesterRepository.getSemesters(student) } returns semesters
-
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
- listOf(
- getGrade(22, "Fizyka", 5.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 5.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0)
- ) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
- }
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
- listOf(
- getGrade(23, "Fizyka", 5.0, weight = 1.0),
- getGrade(23, "Fizyka", 5.0, weight = 2.0),
- getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
- ) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
- }
-
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
-
- assertEquals(5.2636, items.single { it.subject == "Fizyka" }.average, .0001) // (from details): 5.72727272 + 4,8 → 5.26363636
- }
-
- @Test
- fun `force calc full year average with different average from all grades and from two semesters with custom modifiers`() {
- val student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name)
-
- every { preferencesRepository.gradeAverageForceCalc } returns true
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
- every { preferencesRepository.gradeMinusModifier } returns .33
- every { preferencesRepository.gradePlusModifier } returns .5
-
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
@@ -1107,6 +1339,65 @@ class GradeAverageProviderTest {
).getResult()
}
+ assertEquals(
+ 5.2636,
+ items.single { it.subject == "Fizyka" }.average,
+ .0001
+ ) // (from details): 5.72727272 + 4,8 → 5.26363636
+ }
+
+ @Test
+ fun `force calc full year average with different average from all grades and from two semesters with custom modifiers`() {
+ val student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name)
+
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
+ every { preferencesRepository.gradeMinusModifierFlow } returns flowOf(.33)
+ every { preferencesRepository.gradePlusModifierFlow } returns flowOf(.5)
+
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
+ coEvery { semesterRepository.getSemesters(student) } returns semesters
+
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow {
+ listOf(
+ getGrade(22, "Fizyka", 5.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 5.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0)
+ ) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
+ }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow {
+ listOf(
+ getGrade(23, "Fizyka", 5.0, weight = 1.0),
+ getGrade(23, "Fizyka", 5.0, weight = 2.0),
+ getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
+ ) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
+ }
+
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
+
assertEquals(
5.5555,
items.single { it.subject == "Fizyka" }.average,
@@ -1116,20 +1407,20 @@ class GradeAverageProviderTest {
@Test
fun `calc both semesters average when both summary have same average from vulcan and second semester has no grades`() {
- every { preferencesRepository.gradeAverageForceCalc } returns false
- every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- every { preferencesRepository.isOptionalArithmeticAverage } returns false
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns
- resourceFlow { firstGrades to firstSummaries }
+ resourceFlow { firstGrades to firstSummaries }
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns
resourceFlow { listOf() to firstSummaries }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
- student,
- semesters[2].semesterId,
- true
+ student = student,
+ semesterId = semesters[2].semesterId,
+ forceRefresh = true,
).getResult()
}
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt
index bf2d9f2c..eb1f5300 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt
@@ -3,11 +3,18 @@ package io.github.wulkanowy.ui.modules.login.form
import io.github.wulkanowy.MainCoroutineRule
import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.scrapper.Scrapper
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.mockk.*
+import io.github.wulkanowy.utils.AppInfo
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.every
import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import io.mockk.verify
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -30,13 +37,17 @@ class LoginFormPresenterTest {
@MockK(relaxed = true)
lateinit var analytics: AnalyticsHelper
+ @MockK
+ lateinit var appInfo: AppInfo
+
private lateinit var presenter: LoginFormPresenter
private val registerUser = RegisterUser(
email = "",
password = "",
login = "",
- baseUrl = "",
+ scrapperBaseUrl = "",
+ loginMode = Sdk.Mode.HEBE,
loginType = Scrapper.LoginType.AUTO,
symbols = listOf(),
)
@@ -54,8 +65,14 @@ class LoginFormPresenterTest {
every { loginFormView.setErrorPassInvalid(any()) } just Runs
every { loginFormView.setErrorPassRequired(any()) } just Runs
every { loginFormView.setErrorUsernameRequired() } just Runs
+ every { appInfo.isDebug } returns false
- presenter = LoginFormPresenter(repository, errorHandler, analytics)
+ presenter = LoginFormPresenter(
+ studentRepository = repository,
+ loginErrorHandler = errorHandler,
+ appInfo = appInfo,
+ analytics = analytics,
+ )
presenter.onAttachView(loginFormView)
}
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
index cf426a50..da292c51 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
@@ -6,14 +6,22 @@ import io.github.wulkanowy.data.pojos.RegisterSymbol
import io.github.wulkanowy.data.pojos.RegisterUnit
import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.scrapper.Scrapper
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.clearMocks
+import io.mockk.coEvery
+import io.mockk.every
import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import io.mockk.slot
+import io.mockk.verify
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -76,6 +84,9 @@ class LoginStudentSelectPresenterTest {
symbol = "",
error = null,
userName = "",
+ keyId = null,
+ privatePem = null,
+ hebeBaseUrl = null,
schools = listOf(school),
)
@@ -83,7 +94,8 @@ class LoginStudentSelectPresenterTest {
email = "",
password = "",
login = "",
- baseUrl = "",
+ scrapperBaseUrl = "",
+ loginMode = Sdk.Mode.SCRAPPER,
loginType = Scrapper.LoginType.AUTO,
symbols = listOf(symbol),
)
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
index 6cfab199..460c8385 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
@@ -46,7 +46,7 @@ class MainPresenterTest {
MockKAnnotations.init(this)
clearMocks(mainView)
- every { mainView.initView(any(), any()) } just Runs
+ every { mainView.initView(any(), any(), any()) } just Runs
presenter = MainPresenter(
errorHandler = errorHandler,
studentRepository = studentRepository,
diff --git a/build.gradle b/build.gradle
index 87e201ac..3c8552d2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,8 +1,8 @@
buildscript {
ext {
- kotlin_version = '1.7.21'
- about_libraries = '10.5.2'
- hilt_version = "2.44.2"
+ kotlin_version = '1.8.21'
+ about_libraries = '10.6.2'
+ hilt_version = "2.45"
}
repositories {
mavenCentral()
@@ -13,14 +13,14 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.3.1'
+ classpath 'com.android.tools.build:gradle:7.4.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
- classpath 'com.google.gms:google-services:4.3.14'
- classpath 'com.huawei.agconnect:agcp:1.7.3.302'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
+ classpath 'com.google.gms:google-services:4.3.15'
+ classpath 'com.huawei.agconnect:agcp:1.9.0.300'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
- classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
- classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.5.0.2730"
+ classpath "ru.cian:huawei-publish-gradle-plugin:1.3.5"
+ classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.0.0.2929"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 41d9927a..943f0cbf 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f42e62f3..e8be595e 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
+networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 1b6c7873..65dcd68d 100755
--- a/gradlew
+++ b/gradlew
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,10 +80,10 @@ do
esac
done
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
@@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -205,6 +209,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
diff --git a/gradlew.bat b/gradlew.bat
index ac1b06f9..6689b85b 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal