diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d744bdd1..806288a1 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,39 +10,14 @@ on:
workflow_dispatch:
jobs:
- build:
- name: Pre-build
- runs-on: ubuntu-latest
- timeout-minutes: 10
- steps:
- - uses: fkirc/skip-duplicate-actions@master
- - uses: actions/checkout@v2
- - uses: gradle/wrapper-validation-action@v1
- - uses: actions/setup-java@v1
- with:
- java-version: 11
- - uses: actions/cache@v2
- with:
- path: |
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
- - name: Build
- run: ./gradlew --build-cache compileFdroidDebugUnitTestKotlin preFdroidDebugAndroidTestBuild dexBuilderFdroidDebugAndroidTest packageFdroidDebug packageFdroidDebugAndroidTest
- - name: Prepare build cache
- run: tar -cf prebuild.tar .build-cache .gradle app/build
- - uses: actions/upload-artifact@v2
- with:
- name: prebuild.tar
- path: prebuild.tar
-
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
timeout-minutes: 10
- needs: [ build ]
steps:
+ - uses: fkirc/skip-duplicate-actions@master
- uses: actions/checkout@v2
+ - uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v1
with:
java-version: 11
@@ -52,11 +27,6 @@ jobs:
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
- - uses: actions/download-artifact@v2
- with:
- name: prebuild.tar
- - name: Extract build cache
- run: tar -xf prebuild.tar
- name: Unit tests
run: |
./gradlew --build-cache -Pcoverage testFdroidDebugUnitTest --stacktrace
@@ -65,49 +35,12 @@ jobs:
with:
flags: unit
- instrumentation-tests:
- name: Instrumentation tests
- runs-on: macOS-latest
- timeout-minutes: 15
- needs: [ build ]
- strategy:
- fail-fast: true
- matrix:
- api-level: [21, 29]
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v1
- with:
- java-version: 11
- - uses: actions/cache@v2
- with:
- path: |
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
- - uses: actions/download-artifact@v2
- with:
- name: prebuild.tar
- - name: Extract build cache
- run: tar -xf prebuild.tar
- - name: Instrumentation tests
- uses: reactivecircus/android-emulator-runner@v2
- with:
- api-level: ${{ matrix.api-level }}
- arch: x86
- script: |
- ./gradlew --build-cache -Pcoverage connectedFdroidDebugAndroidTest --stacktrace
- ./gradlew --build-cache -Pcoverage jacocoTestReport --stacktrace
- - uses: codecov/codecov-action@v1
- with:
- flags: instrumented,api-${{ matrix.api-level }}
-
deploy-google-play:
name: Deploy to google play
runs-on: ubuntu-latest
timeout-minutes: 10
environment: google-play
- needs: [ build, unit-tests, instrumentation-tests ]
+ needs: [ unit-tests ]
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v2
@@ -120,11 +53,6 @@ jobs:
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
- - uses: actions/download-artifact@v2
- with:
- name: prebuild.tar
- - name: Extract build cache
- run: tar -xf prebuild.tar
- name: Decrypt keys
env:
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
diff --git a/README.en.md b/README.en.md
index accc3608..0f2885cf 100644
--- a/README.en.md
+++ b/README.en.md
@@ -12,7 +12,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par
## Features
-* logging in using the email and password OR using token and pin
+* logging in using the email and password
* functions from the register website:
* grades
* grade statistics
@@ -25,15 +25,19 @@ Unofficial android VULCAN UONET+ register client for both students and their par
* homework
* notes
* lucky number
+ * additional lessons
+ * school conferences
+ * student and school information
* calculation of the average independently of school's preferences
* notifications, e.g. about a new grade
+* support for multiple accounts with the ability to rename students
* dark and black (AMOLED) theme
* offline mode
* no ads
## Download
-You can download the current beta version from the Google Play, F-Droid or Huawei AppGallery store
+You can download the current version from the Google Play, F-Droid or Huawei AppGallery store
[
{
- return arrayOf(
- Migration2(),
- Migration3(),
- Migration4(),
- Migration5(),
- Migration6(),
- Migration7(),
- Migration8(),
- Migration9(),
- Migration10(),
- Migration11(),
- Migration12(),
- Migration13(),
- Migration14(),
- Migration15(),
- Migration16(),
- Migration17(),
- Migration18(),
- Migration19(sharedPrefProvider),
- Migration20(),
- Migration21(),
- Migration22(),
- Migration23(),
- Migration24(),
- Migration25(),
- Migration26(),
- Migration27(),
- Migration28(),
- Migration29(),
- Migration30(),
- Migration31(),
- Migration32(),
- Migration33()
- )
- }
+ fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
+ Migration2(),
+ Migration3(),
+ Migration4(),
+ Migration5(),
+ Migration6(),
+ Migration7(),
+ Migration8(),
+ Migration9(),
+ Migration10(),
+ Migration11(),
+ Migration12(),
+ Migration13(),
+ Migration14(),
+ Migration15(),
+ Migration16(),
+ Migration17(),
+ Migration18(),
+ Migration19(sharedPrefProvider),
+ Migration20(),
+ Migration21(),
+ Migration22(),
+ Migration23(),
+ Migration24(),
+ Migration25(),
+ Migration26(),
+ Migration27(),
+ Migration28(),
+ Migration29(),
+ Migration30(),
+ Migration31(),
+ Migration32(),
+ Migration33(),
+ Migration34(),
+ Migration35(appInfo)
+ )
- fun newInstance(context: Context, sharedPrefProvider: SharedPrefProvider): AppDatabase {
- return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
- .setJournalMode(TRUNCATE)
- .fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
- .fallbackToDestructiveMigrationOnDowngrade()
- .addMigrations(*getMigrations(sharedPrefProvider))
- .build()
- }
+ fun newInstance(
+ context: Context,
+ sharedPrefProvider: SharedPrefProvider,
+ appInfo: AppInfo
+ ) = Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
+ .setJournalMode(TRUNCATE)
+ .fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
+ .fallbackToDestructiveMigrationOnDowngrade()
+ .addMigrations(*getMigrations(sharedPrefProvider, appInfo))
+ .build()
}
abstract val studentDao: StudentDao
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt
index 57f3005a..d9aa2436 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt
@@ -13,4 +13,7 @@ interface LuckyNumberDao : BaseDao {
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
fun load(studentId: Int, date: LocalDate): Flow
+
+ @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date >= :start AND date <= :end")
+ fun getAll(studentId: Int, start: LocalDate, end: LocalDate): Flow>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
index e9c5f157..0ad2ee59 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
@@ -8,7 +8,7 @@ import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.db.entities.StudentNick
+import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import javax.inject.Singleton
@@ -23,13 +23,13 @@ interface StudentDao {
suspend fun delete(student: Student)
@Update(entity = Student::class)
- suspend fun update(studentNick: StudentNick)
+ suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
@Query("SELECT * FROM Students WHERE is_current = 1")
suspend fun loadCurrent(): Student?
@Query("SELECT * FROM Students WHERE id = :id")
- suspend fun loadById(id: Int): Student?
+ suspend fun loadById(id: Long): Student?
@Query("SELECT * FROM Students")
suspend fun loadAll(): List
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt
index 1f10a164..7b6e0dbf 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt
@@ -10,7 +10,7 @@ import java.time.LocalDateTime
data class Message(
@ColumnInfo(name = "student_id")
- val studentId: Int,
+ val studentId: Long,
@ColumnInfo(name = "real_id")
val realId: Int,
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
index 6b60c814..af9fe831 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
@@ -81,4 +81,7 @@ data class Student(
var id: Long = 0
var nick = ""
+
+ @ColumnInfo(name = "avatar_color")
+ var avatarColor = 0L
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNick.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNickAndAvatar.kt
similarity index 57%
rename from app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNick.kt
rename to app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNickAndAvatar.kt
index 71f48f7a..546059ee 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNick.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentNickAndAvatar.kt
@@ -1,13 +1,17 @@
package io.github.wulkanowy.data.db.entities
+import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@Entity
-data class StudentNick(
+data class StudentNickAndAvatar(
- val nick: String
+ val nick: String,
+
+ @ColumnInfo(name = "avatar_color")
+ var avatarColor: Long
) : Serializable {
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt
new file mode 100644
index 00000000..2c57eb00
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt
@@ -0,0 +1,13 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration34 : Migration(33, 34) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("DELETE FROM ReportingUnits")
+ database.execSQL("DELETE FROM Recipients")
+ }
+}
+
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt
new file mode 100644
index 00000000..cc540388
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt
@@ -0,0 +1,24 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.core.database.getLongOrNull
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import io.github.wulkanowy.utils.AppInfo
+
+class Migration35(private val appInfo: AppInfo) : Migration(34, 35) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
+
+ val studentsCursor = database.query("SELECT * FROM Students")
+
+ while (studentsCursor.moveToNext()) {
+ val studentId = studentsCursor.getLongOrNull(0)
+ database.execSQL(
+ """UPDATE Students
+ SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
+ WHERE id = $studentId"""
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt
index fbd40433..b25802d2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt
@@ -5,10 +5,9 @@ import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
-import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
-import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject as SdkGradeStatisticsSubject
-import io.github.wulkanowy.sdk.pojo.GradeStatisticsSemester as SdkGradeStatisticsSemester
import io.github.wulkanowy.sdk.pojo.GradePointsStatistics as SdkGradePointsStatistics
+import io.github.wulkanowy.sdk.pojo.GradeStatisticsSemester as SdkGradeStatisticsSemester
+import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject as SdkGradeStatisticsSubject
@JvmName("mapToEntitiesSubject")
fun List.mapToEntities(semester: Semester) = map {
@@ -51,7 +50,7 @@ fun List.mapToEntities(semester: Semester) = map {
fun List.mapPartialToStatisticItems() = filterNot { it.classAmounts.isEmpty() }.map {
GradeStatisticsItem(
- type = ViewType.PARTIAL,
+ type = GradeStatisticsItem.DataType.PARTIAL,
average = it.classAverage,
partial = it,
points = null,
@@ -61,7 +60,7 @@ fun List.mapPartialToStatisticItems() = filterNot { it.c
fun List.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map {
GradeStatisticsItem(
- type = ViewType.SEMESTER,
+ type = GradeStatisticsItem.DataType.SEMESTER,
partial = null,
points = null,
average = "",
@@ -71,7 +70,7 @@ fun List.mapSemesterToStatisticItems() = filterNot { it
fun List.mapPointsToStatisticsItems() = map {
GradeStatisticsItem(
- type = ViewType.POINTS,
+ type = GradeStatisticsItem.DataType.POINTS,
partial = null,
semester = null,
average = "",
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 2c815b30..913e4d03 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
@@ -4,14 +4,14 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
-import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import java.time.LocalDateTime
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
+import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
+import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
fun List.mapToEntities(student: Student) = map {
Message(
- studentId = student.id.toInt(),
+ studentId = student.id,
realId = it.id ?: 0,
messageId = it.messageId ?: 0,
sender = it.sender?.name.orEmpty(),
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
index 67f56c62..c9332303 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt
@@ -5,7 +5,7 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import java.time.LocalDateTime
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
-fun List.mapToEntities(password: String = "") = map {
+fun List.mapToEntities(password: String = "", colors: List) = map {
StudentWithSemesters(
student = Student(
email = it.email,
@@ -28,8 +28,10 @@ fun List.mapToEntities(password: String = "") = map {
mobileBaseUrl = it.mobileBaseUrl,
privateKey = it.privateKey,
certificateKey = it.certificateKey,
- loginMode = it.loginMode.name
- ),
+ 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/pojos/GradeStatisticsItem.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt
index 88257470..bdcd049d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt
@@ -3,11 +3,10 @@ package io.github.wulkanowy.data.pojos
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
-import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
data class GradeStatisticsItem(
- val type: ViewType,
+ val type: DataType,
val average: String,
@@ -16,4 +15,11 @@ data class GradeStatisticsItem(
val semester: GradeSemesterStatistics?,
val points: GradePointsStatistics?
-)
+
+) {
+ enum class DataType {
+ SEMESTER,
+ PARTIAL,
+ POINTS,
+ }
+}
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 9a6528f3..ffccb059 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
@@ -14,6 +14,7 @@ import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
@@ -27,9 +28,12 @@ class AttendanceRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "attendance"
fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
index 4edb507b..cd4403c7 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
@@ -10,6 +10,7 @@ import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -20,9 +21,12 @@ class AttendanceSummaryRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "attendance_summary"
fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
index 59aabdd5..99ef56f4 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
@@ -12,6 +12,7 @@ import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@@ -23,9 +24,12 @@ class CompletedLessonsRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "completed"
fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = { completedLessonsDb.loadAll(semester.studentId, semester.diaryId, start.monday, end.sunday) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
index befcf9e6..0a839d27 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
@@ -10,6 +10,7 @@ import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -20,9 +21,12 @@ class ConferenceRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "conference"
fun getConferences(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
query = { conferenceDb.loadAll(semester.diaryId, student.studentId) },
fetch = {
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 bd6e7d2d..a8912f10 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
@@ -12,6 +12,7 @@ import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.startExamsDay
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@@ -23,9 +24,12 @@ class ExamRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "exam"
fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = { examDb.loadAll(semester.diaryId, semester.studentId, start.startExamsDay, start.endExamsDay) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
index bab290f3..9880e464 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
@@ -16,6 +16,7 @@ import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.sync.Mutex
import java.time.LocalDateTime
import javax.inject.Inject
import javax.inject.Singleton
@@ -28,14 +29,20 @@ class GradeRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "grade"
fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
- shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
+ mutex = saveFetchResultMutex,
+ shouldFetch = { (details, summaries) ->
+ val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester))
+ details.isEmpty() || summaries.isEmpty() || forceRefresh || isShouldBeRefreshed
+ },
query = {
- gradeDb.loadAll(semester.semesterId, semester.studentId).combine(gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)) { details, summaries ->
- details to summaries
- }
+ val detailsFlow = gradeDb.loadAll(semester.semesterId, semester.studentId)
+ val summaryFlow = gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
+ detailsFlow.combine(summaryFlow) { details, summaries -> details to summaries }
},
fetch = {
val (details, summary) = sdk.init(student)
@@ -92,19 +99,27 @@ class GradeRepository @Inject constructor(
}
fun getUnreadGrades(semester: Semester): Flow> {
- return gradeDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { grade -> !grade.isRead } }
+ return gradeDb.loadAll(semester.semesterId, semester.studentId).map {
+ it.filter { grade -> !grade.isRead }
+ }
}
fun getNotNotifiedGrades(semester: Semester): Flow> {
- return gradeDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { grade -> !grade.isNotified } }
+ return gradeDb.loadAll(semester.semesterId, semester.studentId).map {
+ it.filter { grade -> !grade.isNotified }
+ }
}
fun getNotNotifiedPredictedGrades(semester: Semester): Flow> {
- return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } }
+ return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map {
+ it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified }
+ }
}
fun getNotNotifiedFinalGrades(semester: Semester): Flow> {
- return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } }
+ return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map {
+ it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified }
+ }
}
suspend fun updateGrade(grade: Grade) {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
index ab65fb14..9cd8e711 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
@@ -17,6 +17,7 @@ import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import java.util.Locale
import javax.inject.Inject
import javax.inject.Singleton
@@ -30,11 +31,16 @@ class GradeStatisticsRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val partialMutex = Mutex()
+ private val semesterMutex = Mutex()
+ private val pointsMutex = Mutex()
+
private val partialCacheKey = "grade_stats_partial"
private val semesterCacheKey = "grade_stats_semester"
private val pointsCacheKey = "grade_stats_points"
fun getGradesPartialStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
+ mutex = partialMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(partialCacheKey, semester)) },
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
@@ -71,6 +77,7 @@ class GradeStatisticsRepository @Inject constructor(
)
fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
+ mutex = semesterMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(semesterCacheKey, semester)) },
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
@@ -112,6 +119,7 @@ class GradeStatisticsRepository @Inject constructor(
)
fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
+ mutex = pointsMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(pointsCacheKey, semester)) },
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
index 7625dbbc..068fd9a5 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
@@ -13,6 +13,7 @@ import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@@ -24,9 +25,12 @@ class HomeworkRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "homework"
fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = { homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
index 801292b4..b904b7db 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
@@ -9,6 +9,8 @@ import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.sync.Mutex
+import java.time.LocalDate
import java.time.LocalDate.now
import javax.inject.Inject
import javax.inject.Singleton
@@ -19,7 +21,10 @@ class LuckyNumberRepository @Inject constructor(
private val sdk: Sdk
) {
+ private val saveFetchResultMutex = Mutex()
+
fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it == null || forceRefresh },
query = { luckyNumberDb.load(student.studentId, now()) },
fetch = { sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) },
@@ -33,6 +38,9 @@ class LuckyNumberRepository @Inject constructor(
}
)
+ fun getLuckyNumberHistory(student: Student, start: LocalDate, end: LocalDate) =
+ luckyNumberDb.getAll(student.studentId, start, end)
+
suspend fun getNotNotifiedLuckyNumber(student: Student) = luckyNumberDb.load(student.studentId, now()).map {
if (it?.isNotified == false) it else null
}.first()
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index ea7b2b0e..5f555418 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -20,6 +20,7 @@ import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.sync.Mutex
import timber.log.Timber
import java.time.LocalDateTime.now
import javax.inject.Inject
@@ -33,10 +34,13 @@ class MessageRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "message"
@Suppress("UNUSED_PARAMETER")
fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student, folder)) },
query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
fetch = { sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()).mapToEntities(student) },
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
index 7e83ef7d..4b333bc6 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
@@ -13,6 +13,7 @@ import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -23,9 +24,12 @@ class MobileDeviceRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "devices"
fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student)) },
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
fetch = {
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 85789f09..85339dfa 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
@@ -13,6 +13,7 @@ import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -23,9 +24,12 @@ class NoteRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "note"
fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
query = { noteDb.loadAll(student.studentId) },
fetch = {
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 8cb815cc..11adbd0f 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
@@ -18,26 +18,43 @@ class PreferencesRepository @Inject constructor(
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)
+ 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))
+ get() = GradeAverageMode.getByValue(
+ getString(
+ R.string.pref_key_grade_average_mode,
+ R.string.pref_default_grade_average_mode
+ )
+ )
val gradeAverageForceCalc: Boolean
- get() = getBoolean(R.string.pref_key_grade_average_force_calc, R.bool.pref_default_grade_average_force_calc)
+ get() = getBoolean(
+ R.string.pref_key_grade_average_force_calc,
+ R.bool.pref_default_grade_average_force_calc
+ )
val isGradeExpandable: Boolean
get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade)
val showAllSubjectsOnStatisticsList: Boolean
- get() = getBoolean(R.string.pref_key_grade_statistics_list, R.bool.pref_default_grade_statistics_list)
+ get() = getBoolean(
+ R.string.pref_key_grade_statistics_list,
+ R.bool.pref_default_grade_statistics_list
+ )
val appThemeKey = context.getString(R.string.pref_key_app_theme)
val appTheme: String
get() = getString(appThemeKey, R.string.pref_default_app_theme)
val gradeColorTheme: String
- get() = getString(R.string.pref_key_grade_color_scheme, R.string.pref_default_grade_color_scheme)
+ get() = getString(
+ R.string.pref_key_grade_color_scheme,
+ R.string.pref_default_grade_color_scheme
+ )
val appLanguageKey = context.getString(R.string.pref_key_app_language)
val appLanguage
@@ -55,50 +72,86 @@ class PreferencesRepository @Inject constructor(
val isServicesOnlyWifi: Boolean
get() = getBoolean(servicesOnlyWifiKey, R.bool.pref_default_services_wifi_only)
+ val notificationsEnableKey = context.getString(R.string.pref_key_notifications_enable)
val isNotificationsEnable: Boolean
- get() = getBoolean(R.string.pref_key_notifications_enable, R.bool.pref_default_notifications_enable)
+ get() = getBoolean(notificationsEnableKey, R.bool.pref_default_notifications_enable)
- val isUpcomingLessonsNotificationsEnableKey = context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
+ val isUpcomingLessonsNotificationsEnableKey =
+ context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
val isUpcomingLessonsNotificationsEnable: Boolean
- get() = getBoolean(isUpcomingLessonsNotificationsEnableKey, R.bool.pref_default_notification_upcoming_lessons_enable)
+ get() = getBoolean(
+ isUpcomingLessonsNotificationsEnableKey,
+ R.bool.pref_default_notification_upcoming_lessons_enable
+ )
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnable: Boolean
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
val gradePlusModifier: Double
- get() = getString(R.string.pref_key_grade_modifier_plus, R.string.pref_default_grade_modifier_plus).toDouble()
+ get() = getString(
+ R.string.pref_key_grade_modifier_plus,
+ R.string.pref_default_grade_modifier_plus
+ ).toDouble()
val gradeMinusModifier: Double
- get() = getString(R.string.pref_key_grade_modifier_minus, R.string.pref_default_grade_modifier_minus).toDouble()
+ get() = getString(
+ R.string.pref_key_grade_modifier_minus,
+ R.string.pref_default_grade_modifier_minus
+ ).toDouble()
val fillMessageContent: Boolean
- get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
+ get() = getBoolean(
+ R.string.pref_key_fill_message_content,
+ R.bool.pref_default_fill_message_content
+ )
val showGroupsInPlan: Boolean
- get() = getBoolean(R.string.pref_key_timetable_show_groups, R.bool.pref_default_timetable_show_groups)
+ get() = getBoolean(
+ R.string.pref_key_timetable_show_groups,
+ R.bool.pref_default_timetable_show_groups
+ )
val showWholeClassPlan: String
- get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
+ get() = getString(
+ R.string.pref_key_timetable_show_whole_class,
+ R.string.pref_default_timetable_show_whole_class
+ )
val gradeSortingMode: GradeSortingMode
- get() = GradeSortingMode.getByValue(getString(R.string.pref_key_grade_sorting_mode, R.string.pref_default_grade_sorting_mode))
+ get() = GradeSortingMode.getByValue(
+ getString(
+ R.string.pref_key_grade_sorting_mode,
+ R.string.pref_default_grade_sorting_mode
+ )
+ )
val showTimetableTimers: Boolean
- get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers)
+ get() = getBoolean(
+ R.string.pref_key_timetable_show_timers,
+ R.bool.pref_default_timetable_show_timers
+ )
var isHomeworkFullscreen: Boolean
- get() = getBoolean(R.string.pref_key_homework_fullscreen, R.bool.pref_default_homework_fullscreen)
+ 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)
+ get() = getBoolean(
+ R.string.pref_key_subjects_without_grades,
+ R.bool.pref_default_subjects_without_grades
+ )
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
- private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
+ private fun getString(id: String, default: Int) =
+ sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
private fun getBoolean(id: Int, default: Int) = getBoolean(context.getString(id), default)
- private fun getBoolean(id: String, default: Int) = sharedPref.getBoolean(id, context.resources.getBoolean(default))
+ private fun getBoolean(id: String, default: Int) =
+ sharedPref.getBoolean(id, context.resources.getBoolean(default))
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
index 6b22b32c..8b59cb58 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
@@ -7,6 +7,7 @@ import io.github.wulkanowy.data.mappers.mapToEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
+import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -16,8 +17,11 @@ class SchoolRepository @Inject constructor(
private val sdk: Sdk
) {
+ private val saveFetchResultMutex = Mutex()
+
fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it == null || forceRefresh },
query = { schoolDb.load(semester.studentId, semester.classId) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
index e3deb447..de66ad20 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
@@ -7,6 +7,7 @@ import io.github.wulkanowy.data.mappers.mapToEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
+import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -16,8 +17,11 @@ class StudentInfoRepository @Inject constructor(
private val sdk: Sdk
) {
+ private val saveFetchResultMutex = Mutex()
+
fun getStudentInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it == null || forceRefresh },
query = { studentInfoDao.loadStudentInfo(student.studentId) },
fetch = {
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 55821479..c2f364b3 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
@@ -5,11 +5,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.db.entities.StudentNick
+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.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
@@ -23,7 +24,8 @@ class StudentRepository @Inject constructor(
private val dispatchers: DispatchersProvider,
private val studentDb: StudentDao,
private val semesterDb: SemesterDao,
- private val sdk: Sdk
+ private val sdk: Sdk,
+ private val appInfo: AppInfo
) {
suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty()
@@ -35,7 +37,8 @@ class StudentRepository @Inject constructor(
symbol: String,
token: String
): List =
- sdk.getStudentsFromMobileApi(token, pin, symbol, "").mapToEntities()
+ sdk.getStudentsFromMobileApi(token, pin, symbol, "")
+ .mapToEntities(colors = appInfo.defaultColorsForAvatar)
suspend fun getStudentsScrapper(
email: String,
@@ -44,7 +47,7 @@ class StudentRepository @Inject constructor(
symbol: String
): List =
sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol)
- .mapToEntities(password)
+ .mapToEntities(password, appInfo.defaultColorsForAvatar)
suspend fun getStudentsHybrid(
email: String,
@@ -52,46 +55,58 @@ class StudentRepository @Inject constructor(
scrapperBaseUrl: String,
symbol: String
): List =
- sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol).mapToEntities(password)
+ sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
+ .mapToEntities(password, appInfo.defaultColorsForAvatar)
suspend fun getSavedStudents(decryptPass: Boolean = true) =
- withContext(dispatchers.backgroundThread) {
- studentDb.loadStudentsWithSemesters().map {
+ studentDb.loadStudentsWithSemesters()
+ .map {
it.apply {
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
- student.password = decrypt(student.password)
+ student.password = withContext(dispatchers.backgroundThread) {
+ decrypt(student.password)
+ }
}
}
}
- }
- suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) {
- studentDb.loadById(id)?.apply {
- if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) {
- password = decrypt(password)
+ 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) {
+ student.password = withContext(dispatchers.backgroundThread) {
+ decrypt(student.password)
}
}
- } ?: throw NoCurrentStudentException()
+ return student
+ }
- suspend fun getCurrentStudent(decryptPass: Boolean = true) =
- withContext(dispatchers.backgroundThread) {
- studentDb.loadCurrent()?.apply {
- if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) {
- password = decrypt(password)
- }
+ suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
+ val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
+
+ if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
+ student.password = withContext(dispatchers.backgroundThread) {
+ decrypt(student.password)
}
- } ?: throw NoCurrentStudentException()
+ }
+ return student
+ }
suspend fun saveStudents(studentsWithSemesters: List): List {
- semesterDb.insertSemesters(studentsWithSemesters.flatMap { it.semesters })
+ val semesters = studentsWithSemesters.flatMap { it.semesters }
+ val students = studentsWithSemesters.map { it.student }
+ .map {
+ it.apply {
+ if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) {
+ password = withContext(dispatchers.backgroundThread) {
+ encrypt(password, context)
+ }
+ }
+ }
+ }
- return withContext(dispatchers.backgroundThread) {
- studentDb.insertAll(studentsWithSemesters.map { it.student }.map {
- if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) {
- it.copy(password = encrypt(it.password, context))
- } else it
- })
- }
+ semesterDb.insertSemesters(semesters)
+ return studentDb.insertAll(students)
}
suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) {
@@ -103,5 +118,6 @@ class StudentRepository @Inject constructor(
suspend fun logoutStudent(student: Student) = studentDb.delete(student)
- suspend fun updateStudentNick(studentNick: StudentNick) = studentDb.update(studentNick)
+ suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) =
+ studentDb.update(studentNickAndAvatar)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
index ef07a1d4..b4bfef18 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
@@ -8,6 +8,7 @@ import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -17,7 +18,10 @@ class SubjectRepository @Inject constructor(
private val sdk: Sdk
) {
+ private val saveFetchResultMutex = Mutex()
+
fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh },
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
fetch = {
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 25da718c..7135edbe 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
@@ -8,6 +8,7 @@ import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
+import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -17,7 +18,10 @@ class TeacherRepository @Inject constructor(
private val sdk: Sdk
) {
+ private val saveFetchResultMutex = Mutex()
+
fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { it.isEmpty() || forceRefresh },
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
fetch = {
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 fa1898f5..927565b5 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
@@ -18,6 +18,7 @@ import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@@ -31,9 +32,12 @@ class TimetableRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
) {
+ private val saveFetchResultMutex = Mutex()
+
private val cacheKey = "timetable"
fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean, refreshAdditional: Boolean = false) = networkBoundResource(
+ mutex = saveFetchResultMutex,
shouldFetch = { (timetable, additional) -> timetable.isEmpty() || (additional.isEmpty() && refreshAdditional) || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = {
timetableDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt
deleted file mode 100644
index e69de29b..00000000
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 86b6701b..9b93953d 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
@@ -37,6 +37,7 @@ abstract class BaseActivity, VB : ViewBinding> :
abstract var presenter: T
override fun onCreate(savedInstanceState: Bundle?) {
+ inject()
themeManager.applyActivityTheme(this)
super.onCreate(savedInstanceState)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
@@ -44,7 +45,9 @@ abstract class BaseActivity, VB : ViewBinding> :
if (SDK_INT >= LOLLIPOP) {
@Suppress("DEPRECATION")
- setTaskDescription(ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface)))
+ setTaskDescription(
+ ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface))
+ )
}
}
@@ -84,4 +87,9 @@ abstract class BaseActivity, VB : ViewBinding> :
invalidateOptionsMenu()
presenter.onDetachView()
}
+
+ //https://github.com/google/dagger/releases/tag/dagger-2.33
+ protected open fun inject() {
+ throw UnsupportedOperationException()
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt
index 5f8bf417..bd735535 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt
@@ -4,6 +4,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
+//TODO Use ViewPager2
class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) :
FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
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 f76614e1..4ce97770 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
@@ -42,10 +42,8 @@ class ErrorDialog : BaseDialogFragment() {
companion object {
private const val ARGUMENT_KEY = "Data"
- fun newInstance(error: Throwable): ErrorDialog {
- return ErrorDialog().apply {
- arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) }
- }
+ fun newInstance(error: Throwable) = ErrorDialog().apply {
+ arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) }
}
}
@@ -57,12 +55,14 @@ class ErrorDialog : BaseDialogFragment() {
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- return DialogErrorBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogErrorBinding.inflate(inflater).apply { binding = this }.root
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
val stringWriter = StringWriter().apply {
error.printStackTrace(PrintWriter(this))
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 a2379c3e..b560ed2e 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
@@ -8,6 +8,9 @@ import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.PreferencesRepository
+import io.github.wulkanowy.ui.modules.login.LoginActivity
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import javax.inject.Inject
import javax.inject.Singleton
@@ -17,7 +20,13 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
fun applyActivityTheme(activity: AppCompatActivity) {
if (isThemeApplicable(activity)) {
applyDefaultTheme()
- if (preferencesRepository.appTheme == "black") activity.setTheme(R.style.WulkanowyTheme_Black)
+ if (preferencesRepository.appTheme == "black") {
+ 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)
+ }
+ }
}
}
@@ -33,8 +42,13 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
}
private fun isThemeApplicable(activity: AppCompatActivity): Boolean {
- return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
- .activities.singleOrNull { it.name == activity::class.java.canonicalName }?.theme
- .let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar }
+ return activity.packageManager
+ .getPackageInfo(activity.packageName, GET_ACTIVITIES)
+ .activities.singleOrNull { it.name == activity::class.java.canonicalName }
+ ?.theme.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
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt
index cdd29b41..4c3b608a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseFragment.kt
@@ -4,9 +4,9 @@ import android.os.Bundle
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
-import androidx.appcompat.app.AlertDialog
import androidx.core.text.parseAsHtml
import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.entity.Library
import dagger.hilt.android.AndroidEntryPoint
@@ -26,14 +26,9 @@ class LicenseFragment : BaseFragment(R.layout.fragment_l
@Inject
lateinit var licenseAdapter: LicenseAdapter
- private val libs by lazy { Libs(requireContext()) }
-
override val titleStringId get() = R.string.license_title
- override val appLibraries: ArrayList?
- get() = context?.let {
- libs.prepareLibraries(it, emptyArray(), emptyArray(), autoDetect = true, checkCachedDetection = true, sort = true)
- }
+ override val appLibraries by lazy { Libs(requireContext()).libraries }
companion object {
fun newInstance() = LicenseFragment()
@@ -63,7 +58,7 @@ class LicenseFragment : BaseFragment(R.layout.fragment_l
override fun openLicense(licenseHtml: String) {
context?.let {
- AlertDialog.Builder(it).apply {
+ MaterialAlertDialogBuilder(it).apply {
setTitle(R.string.license_dialog_title)
setMessage(licenseHtml.parseAsHtml())
setPositiveButton(android.R.string.ok) { _, _ -> }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt
index 0680dbb7..6c97d875 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseView.kt
@@ -5,7 +5,7 @@ import io.github.wulkanowy.ui.base.BaseView
interface LicenseView : BaseView {
- val appLibraries: ArrayList?
+ val appLibraries: List
fun initView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt
index 342fd2d4..227f0661 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt
@@ -1,16 +1,17 @@
package io.github.wulkanowy.ui.modules.account
import android.annotation.SuppressLint
-import android.graphics.PorterDuff
import android.view.LayoutInflater
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
+import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.HeaderAccountBinding
import io.github.wulkanowy.databinding.ItemAccountBinding
+import io.github.wulkanowy.utils.createNameInitialsDrawable
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.nickOrName
import javax.inject.Inject
@@ -72,9 +73,13 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragment_a
override val titleStringId = R.string.account_title
- override var subtitleString = ""
-
- override val isViewEmpty get() = accountAdapter.items.isEmpty()
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
+ @Suppress("UNCHECKED_CAST")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+
binding = FragmentAccountBinding.bind(view)
presenter.onAttachView(this)
}
override fun initView() {
- binding.accountErrorRetry.setOnClickListener { presenter.onRetry() }
- binding.accountErrorDetails.setOnClickListener { presenter.onDetailsClick() }
-
binding.accountRecycler.apply {
layoutManager = LinearLayoutManager(context)
adapter = accountAdapter
@@ -60,9 +55,7 @@ class AccountFragment : BaseFragment(R.layout.fragment_a
accountAdapter.onClickListener = presenter::onItemSelected
- with(binding) {
- accountAdd.setOnClickListener { presenter.onAddSelected() }
- }
+ binding.accountAdd.setOnClickListener { presenter.onAddSelected() }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -84,28 +77,7 @@ class AccountFragment : BaseFragment(R.layout.fragment_a
override fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) {
(activity as? MainActivity)?.pushView(
- AccountDetailsFragment.newInstance(
- studentWithSemesters
- )
+ AccountDetailsFragment.newInstance(studentWithSemesters)
)
}
-
- override fun showErrorView(show: Boolean) {
- binding.accountError.visibility = if (show) View.VISIBLE else View.GONE
- }
-
- override fun setErrorDetails(message: String) {
- binding.accountErrorMessage.text = message
- }
-
- override fun showProgress(show: Boolean) {
- binding.accountProgress.visibility = if (show) View.VISIBLE else View.GONE
- }
-
- override fun showContent(show: Boolean) {
- with(binding) {
- accountRecycler.visibility = if (show) View.VISIBLE else View.GONE
- accountAdd.visibility = if (show) View.VISIBLE else View.GONE
- }
- }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
index 366793b6..8d165139 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
@@ -5,7 +5,6 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
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.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
@@ -16,28 +15,13 @@ class AccountPresenter @Inject constructor(
studentRepository: StudentRepository,
) : BasePresenter(errorHandler, studentRepository) {
- private lateinit var lastError: Throwable
-
override fun onAttachView(view: AccountView) {
super.onAttachView(view)
view.initView()
Timber.i("Account view was initialized")
- errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
- fun onRetry() {
- view?.run {
- showErrorView(false)
- showProgress(true)
- }
- loadData()
- }
-
- fun onDetailsClick() {
- view?.showErrorDetailsDialog(lastError)
- }
-
fun onAddSelected() {
Timber.i("Select add account")
view?.openLoginView()
@@ -47,6 +31,24 @@ class AccountPresenter @Inject constructor(
view?.openAccountDetailsView(studentWithSemesters)
}
+ private fun loadData() {
+ flowWithResource { studentRepository.getSavedStudents(false) }
+ .onEach {
+ when (it.status) {
+ Status.LOADING -> Timber.i("Loading account data started")
+ Status.SUCCESS -> {
+ Timber.i("Loading account result: Success")
+ view?.updateData(createAccountItems(it.data!!))
+ }
+ Status.ERROR -> {
+ Timber.i("Loading account result: An exception occurred")
+ errorHandler.dispatch(it.error!!)
+ }
+ }
+ }
+ .launch("load")
+ }
+
private fun createAccountItems(items: List): List> {
return items.groupBy {
Account("${it.student.userName} (${it.student.email})", it.student.isParent)
@@ -60,45 +62,4 @@ class AccountPresenter @Inject constructor(
}
.flatten()
}
-
- private fun loadData() {
- flowWithResource { studentRepository.getSavedStudents(false) }
- .onEach {
- when (it.status) {
- Status.LOADING -> {
- Timber.i("Loading account data started")
- view?.run {
- showProgress(true)
- showContent(false)
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading account result: Success")
- view?.updateData(createAccountItems(it.data!!))
- view?.run {
- showContent(true)
- showErrorView(false)
- }
- }
- Status.ERROR -> {
- Timber.i("Loading account result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }
- .afterLoading { view?.showProgress(false) }
- .launch()
- }
-
- private fun showErrorViewOnError(message: String, error: Throwable) {
- view?.run {
- if (isViewEmpty) {
- lastError = error
- setErrorDetails(message)
- showErrorView(true)
- showContent(false)
- showProgress(false)
- } else showError(message, error)
- }
- }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
index 3453909d..d7deefaf 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
@@ -5,8 +5,6 @@ import io.github.wulkanowy.ui.base.BaseView
interface AccountView : BaseView {
- val isViewEmpty: Boolean
-
fun initView()
fun updateData(data: List>)
@@ -14,13 +12,4 @@ interface AccountView : BaseView {
fun openLoginView()
fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters)
-
- fun showErrorView(show: Boolean)
-
- fun setErrorDetails(message: String)
-
- fun showProgress(show: Boolean)
-
- fun showContent(show: Boolean)
}
-
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 f4b2c833..f1c7f7bd 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
@@ -7,6 +7,7 @@ import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.view.get
+import androidx.core.view.isVisible
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
@@ -18,6 +19,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
+import io.github.wulkanowy.utils.createNameInitialsDrawable
import io.github.wulkanowy.utils.nickOrName
import javax.inject.Inject
@@ -31,8 +33,6 @@ class AccountDetailsFragment :
override val titleStringId = R.string.account_details_title
- override var subtitleString = ""
-
companion object {
private const val ARGUMENT_KEY = "Data"
@@ -88,8 +88,15 @@ class AccountDetailsFragment :
override fun showAccountData(student: Student) {
with(binding) {
+ accountDetailsCheck.isVisible = student.isCurrent
accountDetailsName.text = student.nickOrName
accountDetailsSchool.text = student.schoolName
+ accountDetailsAvatar.setImageDrawable(
+ requireContext().createNameInitialsDrawable(
+ student.nickOrName,
+ student.avatarColor
+ )
+ )
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt
index 7b93d3d8..cc53c9b6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt
@@ -21,7 +21,7 @@ class AccountDetailsPresenter @Inject constructor(
private val syncManager: SyncManager
) : BasePresenter(errorHandler, studentRepository) {
- private lateinit var studentWithSemesters: StudentWithSemesters
+ private var studentWithSemesters: StudentWithSemesters? = null
private lateinit var lastError: Throwable
@@ -69,10 +69,10 @@ class AccountDetailsPresenter @Inject constructor(
}
Status.SUCCESS -> {
Timber.i("Loading account details view result: Success")
- studentWithSemesters = it.data!!
+ studentWithSemesters = it.data
view?.run {
- showAccountData(studentWithSemesters.student)
- enableSelectStudentButton(!studentWithSemesters.student.isCurrent)
+ showAccountData(studentWithSemesters!!.student)
+ enableSelectStudentButton(!studentWithSemesters!!.student.isCurrent)
showContent(true)
showErrorView(false)
}
@@ -88,17 +88,23 @@ class AccountDetailsPresenter @Inject constructor(
}
fun onAccountEditSelected() {
- view?.showAccountEditDetailsDialog(studentWithSemesters.student)
+ studentWithSemesters?.let {
+ view?.showAccountEditDetailsDialog(it.student)
+ }
}
fun onStudentInfoSelected(infoType: StudentInfoView.Type) {
- view?.openStudentInfoView(infoType, studentWithSemesters)
+ studentWithSemesters?.let {
+ view?.openStudentInfoView(infoType, it)
+ }
}
fun onStudentSelect() {
- Timber.i("Select student ${studentWithSemesters.student.id}")
+ if (studentWithSemesters == null) return
- flowWithResource { studentRepository.switchStudent(studentWithSemesters) }
+ Timber.i("Select student ${studentWithSemesters!!.student.id}")
+
+ flowWithResource { studentRepository.switchStudent(studentWithSemesters!!) }
.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to change a student")
@@ -122,8 +128,10 @@ class AccountDetailsPresenter @Inject constructor(
}
fun onLogoutConfirm() {
+ if (studentWithSemesters == null) return
+
flowWithResource {
- val studentToLogout = studentWithSemesters.student
+ val studentToLogout = studentWithSemesters!!.student
studentRepository.logoutStudent(studentToLogout)
val students = studentRepository.getSavedStudents(false)
@@ -143,7 +151,7 @@ class AccountDetailsPresenter @Inject constructor(
syncManager.stopSyncWorker()
openClearLoginView()
}
- studentWithSemesters.student.isCurrent -> {
+ studentWithSemesters!!.student.isCurrent -> {
Timber.i("Logout result: Logout student and switch to another")
recreateMainView()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt
new file mode 100644
index 00000000..ab6eec41
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt
@@ -0,0 +1,90 @@
+package io.github.wulkanowy.ui.modules.account.accountedit
+
+import android.annotation.SuppressLint
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.RippleDrawable
+import android.graphics.drawable.StateListDrawable
+import android.os.Build
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import io.github.wulkanowy.databinding.ItemAccountEditColorBinding
+import javax.inject.Inject
+
+class AccountEditColorAdapter @Inject constructor() :
+ RecyclerView.Adapter() {
+
+ var items = listOf()
+
+ var selectedColor = 0
+
+ override fun getItemCount() = items.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
+ ItemAccountEditColorBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ )
+
+ @SuppressLint("RestrictedApi", "NewApi")
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = items[position]
+
+ with(holder.binding) {
+ accountEditItemColor.setImageDrawable(GradientDrawable().apply {
+ shape = GradientDrawable.OVAL
+ setColor(item)
+ })
+
+ accountEditItemColorContainer.foreground = item.createForegroundDrawable()
+ accountEditCheck.isVisible = selectedColor == item
+
+ root.setOnClickListener {
+ val oldSelectedPosition = items.indexOf(selectedColor)
+ selectedColor = item
+
+ notifyItemChanged(oldSelectedPosition)
+ notifyItemChanged(position)
+ }
+ }
+ }
+
+ private fun Int.createForegroundDrawable(): Drawable =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ val mask = GradientDrawable().apply {
+ shape = GradientDrawable.OVAL
+ setColor(Color.BLACK)
+ }
+ RippleDrawable(ColorStateList.valueOf(this.rippleColor), null, mask)
+ } else {
+ val foreground = StateListDrawable().apply {
+ alpha = 80
+ setEnterFadeDuration(250)
+ setExitFadeDuration(250)
+ }
+
+ val mask = GradientDrawable().apply {
+ shape = GradientDrawable.OVAL
+ setColor(this@createForegroundDrawable.rippleColor)
+ }
+
+ foreground.apply {
+ addState(intArrayOf(android.R.attr.state_pressed), mask)
+ addState(intArrayOf(), ColorDrawable(Color.TRANSPARENT))
+ }
+ }
+
+ private inline val Int.rippleColor: Int
+ get() {
+ val hsv = FloatArray(3)
+ Color.colorToHSV(this, hsv)
+ hsv[2] = hsv[2] * 0.5f
+ return Color.HSVToColor(hsv)
+ }
+
+ class ViewHolder(val binding: ItemAccountEditColorBinding) :
+ RecyclerView.ViewHolder(binding.root)
+}
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 89f23e29..21a7a492 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
@@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.recyclerview.widget.GridLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.databinding.DialogAccountEditBinding
@@ -16,6 +17,9 @@ class AccountEditDialog : BaseDialogFragment(), Accoun
@Inject
lateinit var presenter: AccountEditPresenter
+ @Inject
+ lateinit var accountEditColorAdapter: AccountEditColorAdapter
+
companion object {
private const val ARGUMENT_KEY = "student_with_semesters"
@@ -48,8 +52,30 @@ class AccountEditDialog : BaseDialogFragment(), Accoun
with(binding) {
accountEditDetailsCancel.setOnClickListener { dismiss() }
accountEditDetailsSave.setOnClickListener {
- presenter.changeStudentNick(binding.accountEditDetailsNickText.text.toString())
+ presenter.changeStudentNickAndAvatar(
+ binding.accountEditDetailsNickText.text.toString(),
+ accountEditColorAdapter.selectedColor
+ )
}
+
+ with(binding.accountEditColors) {
+ layoutManager = GridLayoutManager(context, 4)
+ adapter = accountEditColorAdapter
+ }
+ }
+ }
+
+ override fun updateSelectedColorData(color: Int) {
+ with(accountEditColorAdapter) {
+ selectedColor = color
+ notifyDataSetChanged()
+ }
+ }
+
+ override fun updateColorsData(colors: List) {
+ with(accountEditColorAdapter) {
+ items = colors
+ notifyDataSetChanged()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt
index 7830605c..62dd70ab 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt
@@ -2,10 +2,11 @@ package io.github.wulkanowy.ui.modules.account.accountedit
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.db.entities.StudentNick
+import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
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.utils.AppInfo
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
@@ -13,12 +14,15 @@ import timber.log.Timber
import javax.inject.Inject
class AccountEditPresenter @Inject constructor(
+ private val appInfo: AppInfo,
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter(errorHandler, studentRepository) {
lateinit var student: Student
+ private val colors = appInfo.defaultColorsForAvatar.map { it.toInt() }
+
fun onAttachView(view: AccountEditView, student: Student) {
super.onAttachView(view)
this.student = student
@@ -28,27 +32,49 @@ class AccountEditPresenter @Inject constructor(
showCurrentNick(student.nick.trim())
}
Timber.i("Account edit dialog view was initialized")
+ loadData()
+
+ view.updateColorsData(colors)
}
- fun changeStudentNick(nick: String) {
+ private fun loadData() {
+ flowWithResource {
+ studentRepository.getStudentById(student.id, false).avatarColor
+ }.onEach { resource ->
+ when (resource.status) {
+ Status.LOADING -> Timber.i("Attempt to load student")
+ Status.SUCCESS -> {
+ view?.updateSelectedColorData(resource.data?.toInt()!!)
+ Timber.i("Attempt to load student: Success")
+ }
+ Status.ERROR -> {
+ Timber.i("Attempt to load student: An exception occurred")
+ errorHandler.dispatch(resource.error!!)
+ }
+ }
+ }.launch("load_data")
+ }
+
+ fun changeStudentNickAndAvatar(nick: String, avatarColor: Int) {
flowWithResource {
val studentNick =
- StudentNick(nick = nick.trim()).apply { id = student.id }
- studentRepository.updateStudentNick(studentNick)
+ StudentNickAndAvatar(nick = nick.trim(), avatarColor = avatarColor.toLong())
+ .apply { id = student.id }
+ studentRepository.updateStudentNickAndAvatar(studentNick)
}.onEach {
when (it.status) {
- Status.LOADING -> Timber.i("Attempt to change a student nick")
+ Status.LOADING -> Timber.i("Attempt to change a student nick and avatar")
Status.SUCCESS -> {
- Timber.i("Change a student nick result: Success")
+ Timber.i("Change a student nick and avatar result: Success")
view?.recreateMainView()
}
Status.ERROR -> {
- Timber.i("Change a student result: An exception occurred")
+ Timber.i("Change a student nick and avatar result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}
.afterLoading { view?.popView() }
- .launch()
+ .launch("update_student")
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditView.kt
index b25eec6c..517492de 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditView.kt
@@ -11,4 +11,8 @@ interface AccountEditView : BaseView {
fun recreateMainView()
fun showCurrentNick(nick: String)
+
+ fun updateSelectedColorData(color: Int)
+
+ fun updateColorsData(colors: List)
}
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 cb64a8fd..4279102e 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
@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.DialogAccountQuickBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.ui.modules.account.AccountAdapter
@@ -24,7 +25,15 @@ class AccountQuickDialog : BaseDialogFragment(), Acco
lateinit var presenter: AccountQuickPresenter
companion object {
- fun newInstance() = AccountQuickDialog()
+
+ private const val STUDENTS_ARGUMENT_KEY = "students"
+
+ fun newInstance(studentsWithSemesters: List) =
+ AccountQuickDialog().apply {
+ arguments = Bundle().apply {
+ putSerializable(STUDENTS_ARGUMENT_KEY, studentsWithSemesters.toTypedArray())
+ }
+ }
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -38,8 +47,12 @@ class AccountQuickDialog : BaseDialogFragment(), Acco
savedInstanceState: Bundle?
) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root
+ @Suppress("UNCHECKED_CAST")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- presenter.onAttachView(this)
+ val studentsWithSemesters =
+ (requireArguments()[STUDENTS_ARGUMENT_KEY] as Array).toList()
+
+ presenter.onAttachView(this, studentsWithSemesters)
}
override fun initView() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt
index 43cc8bc7..39d8fce2 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt
@@ -17,11 +17,15 @@ class AccountQuickPresenter @Inject constructor(
studentRepository: StudentRepository
) : BasePresenter(errorHandler, studentRepository) {
- override fun onAttachView(view: AccountQuickView) {
+ private lateinit var studentsWithSemesters: List
+
+ fun onAttachView(view: AccountQuickView, studentsWithSemesters: List) {
super.onAttachView(view)
+ this.studentsWithSemesters = studentsWithSemesters
+
view.initView()
Timber.i("Account quick dialog view was initialized")
- loadData()
+ view.updateData(createAccountItems(studentsWithSemesters))
}
fun onManagerSelected() {
@@ -57,22 +61,6 @@ class AccountQuickPresenter @Inject constructor(
.launch("switch")
}
- private fun loadData() {
- flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading account data started")
- Status.SUCCESS -> {
- Timber.i("Loading account result: Success")
- view?.updateData(createAccountItems(it.data!!))
- }
- Status.ERROR -> {
- Timber.i("Loading account result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }.launch()
- }
-
private fun createAccountItems(items: List) = items.map {
AccountItem(it, AccountItem.ViewType.ITEM)
}
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 d5e2fe12..8b6526cd 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
@@ -18,12 +18,11 @@ class AttendanceDialog : DialogFragment() {
private lateinit var attendance: Attendance
companion object {
+
private const val ARGUMENT_KEY = "Item"
- fun newInstance(exam: Attendance): AttendanceDialog {
- return AttendanceDialog().apply {
- arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
- }
+ fun newInstance(exam: Attendance) = AttendanceDialog().apply {
+ arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
}
}
@@ -35,12 +34,14 @@ class AttendanceDialog : DialogFragment() {
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
with(binding) {
attendanceDialogSubject.text = attendance.subject
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
index c1a6f1f0..3f815a2c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
@@ -17,12 +17,11 @@ class ExamDialog : DialogFragment() {
private lateinit var exam: Exam
companion object {
+
private const val ARGUMENT_KEY = "Item"
- fun newInstance(exam: Exam): ExamDialog {
- return ExamDialog().apply {
- arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
- }
+ fun newInstance(exam: Exam) = ExamDialog().apply {
+ arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
}
}
@@ -34,12 +33,14 @@ class ExamDialog : DialogFragment() {
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogExamBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogExamBinding.inflate(inflater).apply { binding = this }.root
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
with(binding) {
examDialogSubjectValue.text = exam.subject
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 36c3c4f8..7e9b56b9 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
@@ -86,7 +86,11 @@ class GradeAverageProvider @Inject constructor(
return@combine firstSemesterGradeSubject
}
- val isAnyAverage = secondSemesterGradeSubject.data.orEmpty().any { it.average != .0 }
+ val isAnyVulcanAverageInFirstSemester =
+ firstSemesterGradeSubject.data.orEmpty().any { it.average != .0 }
+ val isAnyVulcanAverageInSecondSemester =
+ secondSemesterGradeSubject.data.orEmpty().any { it.average != .0 }
+
val updatedData = secondSemesterGradeSubject.data?.map { secondSemesterSubject ->
val firstSemesterSubject = firstSemesterGradeSubject.data.orEmpty()
.singleOrNull { it.subject == secondSemesterSubject.subject }
@@ -94,7 +98,7 @@ class GradeAverageProvider @Inject constructor(
val updatedAverage = if (averageMode == ALL_YEAR) {
calculateAllYearAverage(
student = student,
- isAnyAverage = isAnyAverage,
+ isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
gradeAverageForceCalc = gradeAverageForceCalc,
secondSemesterSubject = secondSemesterSubject,
firstSemesterSubject = firstSemesterSubject
@@ -102,7 +106,7 @@ class GradeAverageProvider @Inject constructor(
} else {
calculateBothSemestersAverage(
student = student,
- isAnyAverage = isAnyAverage,
+ isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
gradeAverageForceCalc = gradeAverageForceCalc,
secondSemesterSubject = secondSemesterSubject,
firstSemesterSubject = firstSemesterSubject
@@ -116,11 +120,11 @@ class GradeAverageProvider @Inject constructor(
private fun calculateAllYearAverage(
student: Student,
- isAnyAverage: Boolean,
+ isAnyVulcanAverage: Boolean,
gradeAverageForceCalc: Boolean,
secondSemesterSubject: GradeSubject,
firstSemesterSubject: GradeSubject?
- ) = if (!isAnyAverage || gradeAverageForceCalc) {
+ ) = if (!isAnyVulcanAverage || gradeAverageForceCalc) {
val updatedSecondSemesterGrades =
secondSemesterSubject.grades.updateModifiers(student)
val updatedFirstSemesterGrades =
@@ -133,20 +137,23 @@ class GradeAverageProvider @Inject constructor(
private fun calculateBothSemestersAverage(
student: Student,
- isAnyAverage: Boolean,
+ isAnyVulcanAverage: Boolean,
gradeAverageForceCalc: Boolean,
secondSemesterSubject: GradeSubject,
firstSemesterSubject: GradeSubject?
- ) = if (!isAnyAverage || gradeAverageForceCalc) {
- val secondSemesterAverage =
- secondSemesterSubject.grades.updateModifiers(student).calcAverage()
- val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
- ?.calcAverage() ?: secondSemesterAverage
+ ): Double {
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
- (secondSemesterAverage + firstSemesterAverage) / divider
- } else {
- (secondSemesterSubject.average + (firstSemesterSubject?.average ?: secondSemesterSubject.average)) / 2
+ return if (!isAnyVulcanAverage || gradeAverageForceCalc) {
+ val secondSemesterAverage =
+ secondSemesterSubject.grades.updateModifiers(student).calcAverage()
+ val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
+ ?.calcAverage() ?: secondSemesterAverage
+
+ (secondSemesterAverage + firstSemesterAverage) / divider
+ } else {
+ (secondSemesterSubject.average + (firstSemesterSubject?.average ?: secondSemesterSubject.average)) / divider
+ }
}
private fun getGradeSubjects(
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 91e39e06..06b3e931 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
@@ -63,11 +63,13 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade
override fun initView() {
with(pagerAdapter) {
containerId = binding.gradeViewPager.id
- addFragmentsWithTitle(mapOf(
- GradeDetailsFragment.newInstance() to getString(R.string.all_details),
- GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary),
- GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics)
- ))
+ addFragmentsWithTitle(
+ mapOf(
+ GradeDetailsFragment.newInstance() to getString(R.string.all_details),
+ GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary),
+ GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics)
+ )
+ )
}
with(binding.gradeViewPager) {
@@ -137,7 +139,10 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade
override fun setCurrentSemesterName(semester: Int, schoolYear: Int) {
subtitleString = getString(R.string.grade_subtitle, semester, schoolYear, schoolYear + 1)
- (activity as MainView).setViewSubTitle(subtitleString)
+
+ if (isVisible) {
+ (activity as MainView?)?.setViewSubTitle(subtitleString)
+ }
}
fun onChildRefresh() {
@@ -149,7 +154,8 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade
}
override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) {
- (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentLoadData(semesterId, forceRefresh)
+ (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)
+ ?.onParentLoadData(semesterId, forceRefresh)
}
override fun notifyChildParentReselected(index: Int) {
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 698aff3e..28619446 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
@@ -24,17 +24,18 @@ class GradeDetailsDialog : DialogFragment() {
private lateinit var colorScheme: String
companion object {
+
private const val ARGUMENT_KEY = "Item"
+
private const val COLOR_SCHEME_KEY = "Scheme"
- fun newInstance(grade: Grade, colorScheme: String): GradeDetailsDialog {
- return GradeDetailsDialog().apply {
+ fun newInstance(grade: Grade, colorScheme: String) =
+ GradeDetailsDialog().apply {
arguments = Bundle().apply {
putSerializable(ARGUMENT_KEY, grade)
putString(COLOR_SCHEME_KEY, colorScheme)
}
}
- }
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -46,12 +47,14 @@ class GradeDetailsDialog : DialogFragment() {
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogGradeBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogGradeBinding.inflate(inflater).apply { binding = this }.root
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
with(binding) {
gradeDialogSubject.text = grade.subject
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 0ffb4225..bf0b2014 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
@@ -22,6 +22,7 @@ import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding
+import io.github.wulkanowy.databinding.ItemGradeStatisticsHeaderBinding
import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
@@ -29,12 +30,16 @@ import javax.inject.Inject
class GradeStatisticsAdapter @Inject constructor() :
RecyclerView.Adapter() {
+ var currentDataType = GradeStatisticsItem.DataType.PARTIAL
+
var items = emptyList()
var theme: String = "vulcan"
var showAllSubjectsOnList: Boolean = false
+ var onDataTypeChangeListener: () -> Unit = {}
+
private val vulcanGradeColors = listOf(
6 to R.color.grade_vulcan_six,
5 to R.color.grade_vulcan_five,
@@ -62,37 +67,90 @@ class GradeStatisticsAdapter @Inject constructor() :
"6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+"
)
- override fun getItemCount() = if (showAllSubjectsOnList) items.size else (if (items.isEmpty()) 0 else 1)
+ override fun getItemCount() =
+ (if (showAllSubjectsOnList) items.size else (if (items.isEmpty()) 0 else 1)) + 1
- override fun getItemViewType(position: Int) = items[position].type.id
+ override fun getItemViewType(position: Int) =
+ if (position == 0) {
+ ViewType.HEADER.id
+ } else {
+ when (items[position - 1].type) {
+ GradeStatisticsItem.DataType.PARTIAL -> ViewType.PARTIAL.id
+ GradeStatisticsItem.DataType.POINTS -> ViewType.POINTS.id
+ GradeStatisticsItem.DataType.SEMESTER -> ViewType.SEMESTER.id
+ }
+ }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
- ViewType.PARTIAL.id -> PartialViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
- ViewType.SEMESTER.id -> SemesterViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
- ViewType.POINTS.id -> PointsViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false))
+ ViewType.PARTIAL.id -> PartialViewHolder(
+ ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)
+ )
+ ViewType.SEMESTER.id -> SemesterViewHolder(
+ ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)
+ )
+ ViewType.POINTS.id -> PointsViewHolder(
+ ItemGradeStatisticsBarBinding.inflate(inflater, parent, false)
+ )
+ ViewType.HEADER.id -> HeaderViewHolder(
+ ItemGradeStatisticsHeaderBinding.inflate(inflater, parent, false)
+ )
else -> throw IllegalStateException()
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ val index = position - 1
+
when (holder) {
- is PartialViewHolder -> bindPartialChart(holder.binding, items[position].partial!!)
- is SemesterViewHolder -> bindSemesterChart(holder.binding, items[position].semester!!)
- is PointsViewHolder -> bindBarChart(holder.binding, items[position].points!!)
+ is PartialViewHolder -> bindPartialChart(holder.binding, items[index].partial!!)
+ is SemesterViewHolder -> bindSemesterChart(holder.binding, items[index].semester!!)
+ is PointsViewHolder -> bindBarChart(holder.binding, items[index].points!!)
+ is HeaderViewHolder -> bindHeader(holder.binding)
}
}
- private fun bindPartialChart(binding: ItemGradeStatisticsPieBinding, partials: GradePartialStatistics) {
+ private fun bindHeader(binding: ItemGradeStatisticsHeaderBinding) {
+ binding.gradeStatisticsTypeSwitch.check(
+ when (currentDataType) {
+ GradeStatisticsItem.DataType.PARTIAL -> R.id.gradeStatisticsTypePartial
+ GradeStatisticsItem.DataType.SEMESTER -> R.id.gradeStatisticsTypeSemester
+ GradeStatisticsItem.DataType.POINTS -> R.id.gradeStatisticsTypePoints
+ }
+ )
+
+ binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
+ currentDataType = when (checkedId) {
+ R.id.gradeStatisticsTypePartial -> GradeStatisticsItem.DataType.PARTIAL
+ R.id.gradeStatisticsTypeSemester -> GradeStatisticsItem.DataType.SEMESTER
+ R.id.gradeStatisticsTypePoints -> GradeStatisticsItem.DataType.POINTS
+ else -> GradeStatisticsItem.DataType.PARTIAL
+ }
+ onDataTypeChangeListener()
+ }
+ }
+
+ private fun bindPartialChart(
+ binding: ItemGradeStatisticsPieBinding,
+ partials: GradePartialStatistics
+ ) {
bindPieChart(binding, partials.subject, partials.classAverage, partials.classAmounts)
}
- private fun bindSemesterChart(binding: ItemGradeStatisticsPieBinding, semester: GradeSemesterStatistics) {
+ private fun bindSemesterChart(
+ binding: ItemGradeStatisticsPieBinding,
+ semester: GradeSemesterStatistics
+ ) {
bindPieChart(binding, semester.subject, semester.average, semester.amounts)
}
- private fun bindPieChart(binding: ItemGradeStatisticsPieBinding, subject: String, average: String, amounts: List) {
+ private fun bindPieChart(
+ binding: ItemGradeStatisticsPieBinding,
+ subject: String,
+ average: String,
+ amounts: List
+ ) {
with(binding.gradeStatisticsPieTitle) {
text = subject
visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE
@@ -114,7 +172,8 @@ class GradeStatisticsAdapter @Inject constructor() :
valueTextSize = 12f
sliceSpace = 1f
valueTextColor = Color.WHITE
- val grades = amounts.mapIndexed { grade, amount -> (grade + 1) to amount }.filterNot { it.second == 0 }
+ val grades = amounts.mapIndexed { grade, amount -> (grade + 1) to amount }
+ .filterNot { it.second == 0 }
setColors(grades.reversed().map { (grade, _) ->
gradeColors.single { color -> color.first == grade }.second
}.toIntArray(), binding.root.context)
@@ -126,7 +185,11 @@ class GradeStatisticsAdapter @Inject constructor() :
data = PieData(dataset).apply {
setValueFormatter(object : ValueFormatter() {
override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
- return resources.getQuantityString(R.plurals.grade_number_item, value.toInt(), value.toInt())
+ return resources.getQuantityString(
+ R.plurals.grade_number_item,
+ value.toInt(),
+ value.toInt()
+ )
}
})
}
@@ -143,11 +206,14 @@ class GradeStatisticsAdapter @Inject constructor() :
val numberOfGradesString = amounts.fold(0) { acc, it -> acc + it }
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
- val averageString = binding.root.context.getString(R.string.grade_statistics_average, average)
+ val averageString =
+ binding.root.context.getString(R.string.grade_statistics_average, average)
minAngleForSlices = 25f
description.isEnabled = false
- centerText = numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() }.orEmpty()
+ centerText =
+ numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() }
+ .orEmpty()
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
@@ -155,16 +221,21 @@ class GradeStatisticsAdapter @Inject constructor() :
}
}
- private fun bindBarChart(binding: ItemGradeStatisticsBarBinding, points: GradePointsStatistics) {
+ private fun bindBarChart(
+ binding: ItemGradeStatisticsBarBinding,
+ points: GradePointsStatistics
+ ) {
with(binding.gradeStatisticsBarTitle) {
text = points.subject
visibility = if (items.size == 1) GONE else VISIBLE
}
- val dataset = BarDataSet(listOf(
- BarEntry(1f, points.others.toFloat()),
- BarEntry(2f, points.student.toFloat())
- ), binding.root.context.getString(R.string.grade_statistics_legend))
+ val dataset = BarDataSet(
+ listOf(
+ BarEntry(1f, points.others.toFloat()),
+ BarEntry(2f, points.student.toFloat())
+ ), binding.root.context.getString(R.string.grade_statistics_legend)
+ )
with(dataset) {
valueTextSize = 12f
@@ -189,7 +260,8 @@ class GradeStatisticsAdapter @Inject constructor() :
form = Legend.LegendForm.SQUARE
},
LegendEntry().apply {
- label = binding.root.context.getString(R.string.grade_statistics_average_student)
+ label =
+ binding.root.context.getString(R.string.grade_statistics_average_student)
formColor = gradePointsColors[1]
form = Legend.LegendForm.SQUARE
}
@@ -226,4 +298,7 @@ class GradeStatisticsAdapter @Inject constructor() :
private class PointsViewHolder(val binding: ItemGradeStatisticsBarBinding) :
RecyclerView.ViewHolder(binding.root)
+
+ private class HeaderViewHolder(val binding: ItemGradeStatisticsHeaderBinding) :
+ RecyclerView.ViewHolder(binding.root)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt
index 1ce7a202..0adac300 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt
@@ -38,27 +38,28 @@ class GradeStatisticsFragment :
override val isViewEmpty get() = statisticsAdapter.items.isEmpty()
- override val currentType
- get() = when (binding.gradeStatisticsTypeSwitch.checkedRadioButtonId) {
- R.id.gradeStatisticsTypeSemester -> ViewType.SEMESTER
- R.id.gradeStatisticsTypePartial -> ViewType.PARTIAL
- else -> ViewType.POINTS
- }
+ override val currentType get() = statisticsAdapter.currentDataType
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentGradeStatisticsBinding.bind(view)
messageContainer = binding.gradeStatisticsSwipe
- presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? ViewType)
+ presenter.onAttachView(
+ this,
+ savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType
+ )
}
override fun initView() {
+ statisticsAdapter.onDataTypeChangeListener = presenter::onTypeChange
+
with(binding.gradeStatisticsRecycler) {
layoutManager = LinearLayoutManager(requireContext())
adapter = statisticsAdapter
}
- subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
+ subjectsAdapter =
+ ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject)
with(binding.gradeStatisticsSubjects) {
@@ -71,7 +72,9 @@ class GradeStatisticsFragment :
gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
gradeStatisticsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
- gradeStatisticsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
+ gradeStatisticsSwipe.setProgressBackgroundColorSchemeColor(
+ requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)
+ )
gradeStatisticsErrorRetry.setOnClickListener { presenter.onRetry() }
gradeStatisticsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
@@ -85,11 +88,15 @@ class GradeStatisticsFragment :
}
}
- override fun updateData(items: List, theme: String, showAllSubjectsOnStatisticsList: Boolean) {
+ override fun updateData(
+ newItems: List,
+ newTheme: String,
+ showAllSubjectsOnStatisticsList: Boolean
+ ) {
with(statisticsAdapter) {
- this.showAllSubjectsOnList = showAllSubjectsOnStatisticsList
- this.theme = theme
- this.items = items
+ showAllSubjectsOnList = showAllSubjectsOnStatisticsList
+ theme = newTheme
+ items = newItems
notifyDataSetChanged()
}
}
@@ -103,11 +110,7 @@ class GradeStatisticsFragment :
}
override fun resetView() {
- binding.gradeStatisticsScroll.scrollTo(0, 0)
- }
-
- override fun showContent(show: Boolean) {
- binding.gradeStatisticsRecycler.visibility = if (show) View.VISIBLE else View.GONE
+ binding.gradeStatisticsRecycler.scrollToPosition(0)
}
override fun showEmpty(show: Boolean) {
@@ -154,11 +157,6 @@ class GradeStatisticsFragment :
(parentFragment as? GradeFragment)?.onChildRefresh()
}
- override fun onResume() {
- super.onResume()
- binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, _ -> presenter.onTypeChange() }
- }
-
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt
index 47ea52d3..53eccad6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt
@@ -35,12 +35,12 @@ class GradeStatisticsPresenter @Inject constructor(
private lateinit var lastError: Throwable
- var currentType: ViewType = ViewType.PARTIAL
+ var currentType: GradeStatisticsItem.DataType = GradeStatisticsItem.DataType.PARTIAL
private set
- fun onAttachView(view: GradeStatisticsView, type: ViewType?) {
+ fun onAttachView(view: GradeStatisticsView, type: GradeStatisticsItem.DataType?) {
super.onAttachView(view)
- currentType = type ?: ViewType.PARTIAL
+ currentType = type ?: GradeStatisticsItem.DataType.PARTIAL
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
}
@@ -59,11 +59,11 @@ class GradeStatisticsPresenter @Inject constructor(
}
fun onParentViewChangeSemester() {
+ clearDataInView()
view?.run {
showProgress(true)
enableSwipe(false)
showRefresh(false)
- showContent(false)
showErrorView(false)
showEmpty(false)
clearView()
@@ -90,8 +90,8 @@ class GradeStatisticsPresenter @Inject constructor(
fun onSubjectSelected(name: String?) {
Timber.i("Select grade stats subject $name")
+ clearDataInView()
view?.run {
- showContent(false)
showProgress(true)
enableSwipe(false)
showEmpty(false)
@@ -104,11 +104,11 @@ class GradeStatisticsPresenter @Inject constructor(
}
fun onTypeChange() {
- val type = view?.currentType ?: ViewType.POINTS
+ val type = view?.currentType ?: GradeStatisticsItem.DataType.POINTS
Timber.i("Select grade stats semester: $type")
cancelJobs("load")
+ clearDataInView()
view?.run {
- showContent(false)
showProgress(true)
enableSwipe(false)
showEmpty(false)
@@ -143,10 +143,16 @@ class GradeStatisticsPresenter @Inject constructor(
}.launch("subjects")
}
- private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) {
+ private fun loadDataByType(
+ semesterId: Int,
+ subjectName: String,
+ type: GradeStatisticsItem.DataType,
+ forceRefresh: Boolean = false
+ ) {
Timber.i("Loading grade stats data started")
- currentSubjectName = if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName
+ currentSubjectName =
+ if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName
currentType = type
flowWithResourceIn {
@@ -156,9 +162,30 @@ class GradeStatisticsPresenter @Inject constructor(
with(gradeStatisticsRepository) {
when (type) {
- ViewType.PARTIAL -> getGradesPartialStatistics(student, semester, currentSubjectName, forceRefresh)
- ViewType.SEMESTER -> getGradesSemesterStatistics(student, semester, currentSubjectName, forceRefresh)
- ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh)
+ GradeStatisticsItem.DataType.PARTIAL -> {
+ getGradesPartialStatistics(
+ student = student,
+ semester = semester,
+ subjectName = currentSubjectName,
+ forceRefresh = forceRefresh
+ )
+ }
+ GradeStatisticsItem.DataType.SEMESTER -> {
+ getGradesSemesterStatistics(
+ student = student,
+ semester = semester,
+ subjectName = currentSubjectName,
+ forceRefresh = forceRefresh
+ )
+ }
+ GradeStatisticsItem.DataType.POINTS -> {
+ getGradesPointsStatistics(
+ student = student,
+ semester = semester,
+ subjectName = currentSubjectName,
+ forceRefresh = forceRefresh
+ )
+ }
}
}
}.onEach {
@@ -168,12 +195,15 @@ class GradeStatisticsPresenter @Inject constructor(
if (!isNoContent) {
view?.run {
showEmpty(isNoContent)
- showContent(!isNoContent)
showErrorView(false)
enableSwipe(true)
showRefresh(true)
showProgress(false)
- updateData(it.data!!, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
+ updateData(
+ if (isNoContent) emptyList() else it.data!!,
+ preferencesRepository.gradeColorTheme,
+ preferencesRepository.showAllSubjectsOnStatisticsList
+ )
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
}
@@ -183,9 +213,12 @@ class GradeStatisticsPresenter @Inject constructor(
view?.run {
val isNoContent = checkIsNoContent(it.data!!, type)
showEmpty(isNoContent)
- showContent(!isNoContent)
showErrorView(false)
- updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
+ updateData(
+ if (isNoContent) emptyList() else it.data,
+ preferencesRepository.gradeColorTheme,
+ preferencesRepository.showAllSubjectsOnStatisticsList
+ )
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
analytics.logEvent(
@@ -209,16 +242,31 @@ class GradeStatisticsPresenter @Inject constructor(
}.launch("load")
}
- private fun checkIsNoContent(items: List, type: ViewType): Boolean {
+ private fun checkIsNoContent(
+ items: List,
+ type: GradeStatisticsItem.DataType
+ ): Boolean {
return items.isEmpty() || when (type) {
- ViewType.SEMESTER -> items.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0
- ViewType.PARTIAL -> items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0
- ViewType.POINTS -> items.firstOrNull()?.points?.let { points ->
- points.student == .0 && points.others == .0
- } ?: false
+ GradeStatisticsItem.DataType.SEMESTER -> {
+ items.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0
+ }
+ GradeStatisticsItem.DataType.PARTIAL -> {
+ items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0
+ }
+ GradeStatisticsItem.DataType.POINTS -> {
+ items.firstOrNull()?.points?.let { points -> points.student == .0 && points.others == .0 } ?: false
+ }
}
}
+ private fun clearDataInView() {
+ view?.updateData(
+ emptyList(),
+ preferencesRepository.gradeColorTheme,
+ preferencesRepository.showAllSubjectsOnStatisticsList
+ )
+ }
+
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt
index 26b4a119..40511817 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt
@@ -7,13 +7,17 @@ interface GradeStatisticsView : BaseView {
val isViewEmpty: Boolean
- val currentType: ViewType
+ val currentType: GradeStatisticsItem.DataType
fun initView()
fun updateSubjects(data: ArrayList)
- fun updateData(items: List, theme: String, showAllSubjectsOnStatisticsList: Boolean)
+ fun updateData(
+ newItems: List,
+ newTheme: String,
+ showAllSubjectsOnStatisticsList: Boolean
+ )
fun showSubjects(show: Boolean)
@@ -25,8 +29,6 @@ interface GradeStatisticsView : BaseView {
fun resetView()
- fun showContent(show: Boolean)
-
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/ViewType.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/ViewType.kt
index 02e95b0e..f695eaf9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/ViewType.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/ViewType.kt
@@ -3,5 +3,6 @@ package io.github.wulkanowy.ui.modules.grade.statistics
enum class ViewType(val id: Int) {
SEMESTER(1),
PARTIAL(2),
- POINTS(3)
+ POINTS(3),
+ HEADER(4)
}
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 aecaa394..93045a48 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
@@ -28,12 +28,11 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew
private lateinit var homework: Homework
companion object {
+
private const val ARGUMENT_KEY = "Item"
- fun newInstance(homework: Homework): HomeworkDetailsDialog {
- return HomeworkDetailsDialog().apply {
- arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) }
- }
+ fun newInstance(homework: Homework) = HomeworkDetailsDialog().apply {
+ arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) }
}
}
@@ -45,19 +44,22 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogHomeworkBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogHomeworkBinding.inflate(inflater).apply { binding = this }.root
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
}
@SuppressLint("SetTextI18n")
override fun initView() {
with(binding) {
- homeworkDialogRead.text = view?.context?.getString(if (homework.isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done)
+ homeworkDialogRead.text =
+ view?.context?.getString(if (homework.isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done)
homeworkDialogRead.setOnClickListener { presenter.toggleDone(homework) }
homeworkDialogClose.setOnClickListener { dismiss() }
}
@@ -87,7 +89,8 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew
}
override fun updateMarkAsDoneLabel(isDone: Boolean) {
- binding.homeworkDialogRead.text = view?.context?.getString(if (isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done)
+ binding.homeworkDialogRead.text =
+ view?.context?.getString(if (isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done)
}
override fun onDestroyView() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt
index aff1c84c..8d96a498 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt
@@ -52,6 +52,8 @@ class LoginActivity : BaseActivity(), Logi
updateHelper.onResume(this)
}
+ //https://developer.android.com/guide/playcore/in-app-updates#status_callback
+ @Suppress("DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
updateHelper.onActivityResult(requestCode, resultCode)
@@ -65,13 +67,15 @@ class LoginActivity : BaseActivity(), Logi
with(loginAdapter) {
containerId = binding.loginViewpager.id
- addFragments(listOf(
- LoginFormFragment.newInstance(),
- LoginSymbolFragment.newInstance(),
- LoginStudentSelectFragment.newInstance(),
- LoginAdvancedFragment.newInstance(),
- LoginRecoverFragment.newInstance()
- ))
+ addFragments(
+ listOf(
+ LoginFormFragment.newInstance(),
+ LoginSymbolFragment.newInstance(),
+ LoginStudentSelectFragment.newInstance(),
+ LoginAdvancedFragment.newInstance(),
+ LoginRecoverFragment.newInstance()
+ )
+ )
}
with(binding.loginViewpager) {
@@ -99,14 +103,20 @@ class LoginActivity : BaseActivity(), Logi
}
override fun notifyInitSymbolFragment(loginData: Triple) {
- (loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)?.onParentInitSymbolFragment(loginData)
+ (loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)?.onParentInitSymbolFragment(
+ loginData
+ )
}
override fun notifyInitStudentSelectFragment(studentsWithSemesters: List) {
- (loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)?.onParentInitStudentSelectFragment(studentsWithSemesters)
+ (loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)
+ ?.onParentInitStudentSelectFragment(studentsWithSemesters)
}
- fun onFormFragmentAccountLogged(studentsWithSemesters: List, loginData: Triple) {
+ fun onFormFragmentAccountLogged(
+ studentsWithSemesters: List,
+ loginData: Triple
+ ) {
presenter.onFormViewAccountLogged(studentsWithSemesters, 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 c6d0209d..4e09bd4d 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
@@ -150,6 +150,13 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
}
}
+ override fun setErrorEmailInvalid(domain: String) {
+ with(binding.loginFormUsernameLayout) {
+ requestFocus()
+ error = getString(R.string.login_invalid_custom_email,domain)
+ }
+ }
+
override fun clearUsernameError() {
binding.loginFormUsernameLayout.error = null
}
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 f6a528ae..70b46899 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
@@ -11,6 +11,7 @@ import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
+import java.net.URL
import javax.inject.Inject
class LoginFormPresenter @Inject constructor(
@@ -87,7 +88,14 @@ class LoginFormPresenter @Inject constructor(
if (!validateCredentials(email, password, host)) return
- flowWithResource { studentRepository.getStudentsScrapper(email, password, host, symbol) }.onEach {
+ flowWithResource {
+ studentRepository.getStudentsScrapper(
+ email,
+ password,
+ host,
+ symbol
+ )
+ }.onEach {
when (it.status) {
Status.LOADING -> view?.run {
Timber.i("Login started")
@@ -150,11 +158,18 @@ class LoginFormPresenter @Inject constructor(
view?.setErrorLoginRequired()
isCorrect = false
}
-
if ("@" !in login && "email" in host) {
view?.setErrorEmailRequired()
isCorrect = false
}
+ if ("@" in login && "login" !in host && "email" !in host) {
+ val emailHost = login.substringAfter("@")
+ val emailDomain = URL(host).host
+ if (emailHost != emailDomain) {
+ view?.setErrorEmailInvalid(domain = emailDomain)
+ isCorrect = false
+ }
+ }
}
if (password.isEmpty()) {
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 31f8a621..079629ef 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
@@ -39,6 +39,8 @@ interface LoginFormView : BaseView {
fun setErrorPassIncorrect()
+ fun setErrorEmailInvalid(domain: String)
+
fun clearUsernameError()
fun clearPassError()
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 3de23585..0a73fe15 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
@@ -9,6 +9,8 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.databinding.FragmentLuckyNumberBinding
import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.luckynumber.history.LuckyNumberHistoryFragment
+import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
@@ -42,6 +44,7 @@ class LuckyNumberFragment :
luckyNumberSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
luckyNumberSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
luckyNumberSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
+ luckyNumberHistoryButton.setOnClickListener { openLuckyNumberHistory() }
luckyNumberErrorRetry.setOnClickListener { presenter.onRetry() }
luckyNumberErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
@@ -79,6 +82,10 @@ class LuckyNumberFragment :
binding.luckyNumberContent.visibility = if (show) VISIBLE else GONE
}
+ override fun openLuckyNumberHistory() {
+ (activity as? MainActivity)?.pushView(LuckyNumberHistoryFragment.newInstance())
+ }
+
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
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 a680c83e..0c05a156 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
@@ -24,4 +24,6 @@ interface LuckyNumberView : BaseView {
fun enableSwipe(enable: Boolean)
fun showContent(show: Boolean)
+
+ fun openLuckyNumberHistory()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryAdapter.kt
new file mode 100644
index 00000000..7c09c96f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryAdapter.kt
@@ -0,0 +1,36 @@
+package io.github.wulkanowy.ui.modules.luckynumber.history
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.databinding.ItemLuckyNumberHistoryBinding
+import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.weekDayName
+import java.util.Locale
+import javax.inject.Inject
+
+class LuckyNumberHistoryAdapter @Inject constructor() :
+ RecyclerView.Adapter() {
+
+ var items = emptyList()
+
+ override fun getItemCount() = items.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
+ ItemLuckyNumberHistoryBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ )
+
+ @SuppressLint("DefaultLocale")
+ override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
+ val item = items[position]
+ with(holder.binding) {
+ luckyNumberHistoryWeekName.text = item.date.weekDayName.capitalize()
+ luckyNumberHistoryDate.text = item.date.toFormattedString()
+ luckyNumberHistory.text = item.luckyNumber.toString()
+ }
+ }
+
+ class ItemViewHolder(val binding: ItemLuckyNumberHistoryBinding) : RecyclerView.ViewHolder(binding.root)
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..6e991ba1
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
@@ -0,0 +1,134 @@
+package io.github.wulkanowy.ui.modules.luckynumber.history
+
+import android.os.Bundle
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.databinding.FragmentLuckyNumberHistoryBinding
+import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.ui.widgets.DividerItemDecoration
+import io.github.wulkanowy.utils.SchooldaysRangeLimiter
+import io.github.wulkanowy.utils.dpToPx
+import io.github.wulkanowy.utils.getThemeAttrColor
+import java.time.LocalDate
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class LuckyNumberHistoryFragment :
+ BaseFragment(R.layout.fragment_lucky_number_history), LuckyNumberHistoryView,
+ MainView.TitledView {
+
+ @Inject
+ lateinit var presenter: LuckyNumberHistoryPresenter
+
+ @Inject
+ lateinit var luckyNumberHistoryAdapter: LuckyNumberHistoryAdapter
+
+ companion object {
+ fun newInstance() = LuckyNumberHistoryFragment()
+ }
+
+ override val titleStringId: Int
+ get() = R.string.lucky_number_history_title
+
+ override val isViewEmpty get() = luckyNumberHistoryAdapter.items.isEmpty()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding = FragmentLuckyNumberHistoryBinding.bind(view)
+ messageContainer = binding.luckyNumberHistoryRecycler
+ presenter.onAttachView(this)
+ }
+
+ override fun initView() {
+ with(binding.luckyNumberHistoryRecycler) {
+ layoutManager = LinearLayoutManager(context)
+ adapter = luckyNumberHistoryAdapter
+ addItemDecoration(DividerItemDecoration(context))
+ }
+
+ with(binding) {
+ luckyNumberHistoryNavDate.setOnClickListener { presenter.onPickDate() }
+ luckyNumberHistoryErrorRetry.setOnClickListener { presenter.onRetry() }
+ luckyNumberHistoryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
+
+ luckyNumberHistoryPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
+ luckyNumberHistoryNextButton.setOnClickListener { presenter.onNextWeek() }
+
+ luckyNumberHistoryNavContainer.setElevationCompat(requireContext().dpToPx(8f))
+ }
+ }
+
+ override fun updateData(data: List) {
+ with(luckyNumberHistoryAdapter) {
+ items = data
+ notifyDataSetChanged()
+ }
+ }
+
+ override fun clearData() {
+ with(luckyNumberHistoryAdapter) {
+ items = emptyList()
+ notifyDataSetChanged()
+ }
+ }
+
+ override fun showEmpty(show: Boolean) {
+ binding.luckyNumberHistoryEmpty.visibility = if (show) VISIBLE else GONE
+ }
+
+ override fun showErrorView(show: Boolean) {
+ binding.luckyNumberHistoryError.visibility = if (show) VISIBLE else GONE
+ }
+
+ override fun setErrorDetails(message: String) {
+ binding.luckyNumberHistoryErrorMessage.text = message
+ }
+
+ override fun updateNavigationWeek(date: String) {
+ binding.luckyNumberHistoryNavDate.text = date
+ }
+
+ override fun showProgress(show: Boolean) {
+ binding.luckyNumberHistoryProgress.visibility = if (show) VISIBLE else GONE
+ }
+
+ override fun showPreButton(show: Boolean) {
+ binding.luckyNumberHistoryPreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE
+ }
+
+ override fun showNextButton(show: Boolean) {
+ binding.luckyNumberHistoryNextButton.visibility = if (show) VISIBLE else View.INVISIBLE
+ }
+
+ override fun showDatePickerDialog(currentDate: LocalDate) {
+ val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
+ presenter.onDateSet(year, month + 1, dayOfMonth)
+ }
+ val datePickerDialog = DatePickerDialog.newInstance(dateSetListener,
+ currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth)
+
+ with(datePickerDialog) {
+ setDateRangeLimiter(SchooldaysRangeLimiter())
+ version = DatePickerDialog.Version.VERSION_2
+ scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
+ vibrate(false)
+ show(this@LuckyNumberHistoryFragment.parentFragmentManager, null)
+ }
+ }
+
+ override fun showContent(show: Boolean) {
+ binding.luckyNumberHistoryRecycler.visibility = if (show) VISIBLE else GONE
+ }
+
+ override fun onDestroyView() {
+ presenter.onDetachView()
+ super.onDestroyView()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt
new file mode 100644
index 00000000..556dda75
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt
@@ -0,0 +1,151 @@
+package io.github.wulkanowy.ui.modules.luckynumber.history
+
+import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.repositories.LuckyNumberRepository
+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.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.afterLoading
+import io.github.wulkanowy.utils.flowWithResource
+import io.github.wulkanowy.utils.isHolidays
+import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.previousOrSameSchoolDay
+import io.github.wulkanowy.utils.sunday
+import io.github.wulkanowy.utils.toFormattedString
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.onEach
+import timber.log.Timber
+import java.time.LocalDate
+import javax.inject.Inject
+
+class LuckyNumberHistoryPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository,
+ private val luckyNumberRepository: LuckyNumberRepository,
+ private val analytics: AnalyticsHelper
+) : BasePresenter(errorHandler, studentRepository) {
+
+ private lateinit var lastError: Throwable
+
+ var currentDate: LocalDate = LocalDate.now().previousOrSameSchoolDay
+
+ override fun onAttachView(view: LuckyNumberHistoryView) {
+ super.onAttachView(view)
+ view.run {
+ initView()
+ reloadNavigation()
+ showContent(false)
+ }
+ Timber.i("Lucky number history view was initialized")
+ errorHandler.showErrorMessage = ::showErrorViewOnError
+ loadData()
+ }
+
+ private fun loadData() {
+ flowWithResource {
+ val student = studentRepository.getCurrentStudent()
+ luckyNumberRepository.getLuckyNumberHistory(student, currentDate.monday, currentDate.sunday)
+ }.onEach {
+ when (it.status) {
+ Status.LOADING -> Timber.i("Loading lucky number history started")
+ Status.SUCCESS -> {
+ if (!it.data?.first().isNullOrEmpty()) {
+ Timber.i("Loading lucky number result: Success")
+ view?.apply {
+ updateData(it.data!!.first())
+ showContent(true)
+ showEmpty(false)
+ showErrorView(false)
+ showProgress(false)
+ }
+ analytics.logEvent(
+ "load_items",
+ "type" to "lucky_number_history",
+ "numbers" to it.data
+ )
+ } else {
+ Timber.i("Loading lucky number history result: No lucky numbers found")
+ view?.run {
+ showContent(false)
+ showEmpty(true)
+ showErrorView(false)
+ }
+ }
+ }
+ Status.ERROR -> {
+ Timber.i("Loading lucky number history result: An exception occurred")
+ errorHandler.dispatch(it.error!!)
+ }
+ }
+ }.afterLoading {
+ view?.run {
+ showProgress(false)
+ }
+ }.launch()
+ }
+
+ private fun showErrorViewOnError(message: String, error: Throwable) {
+ view?.run {
+ if (isViewEmpty) {
+ lastError = error
+ setErrorDetails(message)
+ showErrorView(true)
+ showEmpty(false)
+ } else showError(message, error)
+ }
+ }
+
+ private fun reloadView(date: LocalDate) {
+ currentDate = date
+ Timber.i("Reload lucky number history view with the date ${currentDate.toFormattedString()}")
+ view?.apply {
+ showProgress(true)
+ showContent(false)
+ showEmpty(false)
+ showErrorView(false)
+ clearData()
+ reloadNavigation()
+ }
+ }
+
+ fun onRetry() {
+ view?.run {
+ showErrorView(false)
+ showProgress(true)
+ }
+ loadData()
+ }
+
+ fun onDetailsClick() {
+ view?.showErrorDetailsDialog(lastError)
+ }
+
+ private fun reloadNavigation() {
+ view?.apply {
+ showPreButton(!currentDate.minusDays(7).isHolidays)
+ showNextButton(!currentDate.plusDays(7).isHolidays)
+ updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " +
+ currentDate.sunday.toFormattedString("dd.MM"))
+ }
+ }
+
+ fun onDateSet(year: Int, month: Int, day: Int) {
+ reloadView(LocalDate.of(year, month, day))
+ loadData()
+ }
+
+ fun onPickDate() {
+ view?.showDatePickerDialog(currentDate)
+ }
+
+ fun onPreviousWeek() {
+ reloadView(currentDate.minusDays(7))
+ loadData()
+ }
+
+ fun onNextWeek() {
+ reloadView(currentDate.plusDays(7))
+ loadData()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt
new file mode 100644
index 00000000..331e4ff8
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt
@@ -0,0 +1,36 @@
+package io.github.wulkanowy.ui.modules.luckynumber.history
+
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.ui.base.BaseView
+import java.time.LocalDate
+
+interface LuckyNumberHistoryView : BaseView {
+
+ val isViewEmpty: Boolean
+
+ fun initView()
+
+ fun updateData(data: List)
+
+ fun clearData()
+
+ fun showEmpty(show: Boolean)
+
+ fun showErrorView(show: Boolean)
+
+ fun setErrorDetails(message: String)
+
+ fun updateNavigationWeek(date: String)
+
+ fun showProgress(show: Boolean)
+
+ fun showPreButton(show: Boolean)
+
+ fun showNextButton(show: Boolean)
+
+ fun showDatePickerDialog(currentDate: LocalDate)
+
+ fun showContent(show: Boolean)
+
+ fun onDestroyView()
+}
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 5d93c594..2b207933 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
@@ -11,15 +11,21 @@ import android.graphics.drawable.Icon
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.LOLLIPOP
+import android.os.Build.VERSION_CODES.P
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
-import android.view.View
+import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import androidx.core.view.ViewCompat
+import androidx.core.view.isVisible
+import androidx.core.view.updateLayoutParams
+import androidx.core.view.updateMargins
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState.ALWAYS_SHOW
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem
import com.google.android.material.elevation.ElevationOverlayProvider
@@ -27,6 +33,8 @@ import com.ncapdevi.fragnav.FragNavController
import com.ncapdevi.fragnav.FragNavController.Companion.HIDE
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.ActivityMainBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
@@ -42,15 +50,18 @@ import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.UpdateHelper
+import io.github.wulkanowy.utils.createNameInitialsDrawable
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.getThemeAttrColor
+import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.safelyPopFragments
import io.github.wulkanowy.utils.setOnViewChangeListener
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
-class MainActivity : BaseActivity(), MainView {
+class MainActivity : BaseActivity(), MainView,
+ PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
@Inject
override lateinit var presenter: MainPresenter
@@ -64,6 +75,8 @@ class MainActivity : BaseActivity(), MainVie
@Inject
lateinit var appInfo: AppInfo
+ private var accountMenu: MenuItem? = null
+
private val overlayProvider by lazy { ElevationOverlayProvider(this) }
private val navController =
@@ -121,6 +134,11 @@ class MainActivity : BaseActivity(), MainVie
initialize(startMenuIndex, savedInstanceState)
pushFragment(moreMenuFragments[startMenuMoreIndex])
}
+
+ if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) {
+ initShortcuts()
+ }
+
updateHelper.checkAndInstallUpdates(this)
}
@@ -129,11 +147,11 @@ class MainActivity : BaseActivity(), MainVie
updateHelper.onResume(this)
}
- @SuppressLint("NewApi")
+ //https://developer.android.com/guide/playcore/in-app-updates#status_callback
+ @Suppress("DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
updateHelper.onActivityResult(requestCode, resultCode)
- if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) initShortcuts()
}
@RequiresApi(Build.VERSION_CODES.N_MR1)
@@ -160,11 +178,6 @@ class MainActivity : BaseActivity(), MainVie
getString(R.string.timetable_title),
R.drawable.ic_shortcut_timetable,
MainView.Section.TIMETABLE
- ),
- Triple(
- getString(R.string.message_title),
- R.drawable.ic_shortcut_message,
- MainView.Section.MESSAGE
)
).forEach { (title, icon, enum) ->
shortcutsList.add(
@@ -191,9 +204,13 @@ class MainActivity : BaseActivity(), MainVie
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.action_menu_main, menu)
+ accountMenu = menu?.findItem(R.id.mainMenuAccount)
+
+ presenter.onActionMenuCreated()
return true
}
+ @SuppressLint("NewApi")
override fun initView() {
with(binding.mainToolbar) {
if (SDK_INT >= LOLLIPOP) stateListAnimator = null
@@ -233,13 +250,27 @@ class MainActivity : BaseActivity(), MainVie
with(navController) {
setOnViewChangeListener { section, name ->
- binding.mainBottomNav.visibility =
- if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) {
- View.GONE
- } else {
- View.VISIBLE
+ if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) {
+ binding.mainBottomNav.isVisible = false
+ binding.mainFragmentContainer.updateLayoutParams {
+ updateMargins(bottom = 0)
}
+ if (appInfo.systemVersion >= P) {
+ window.navigationBarColor = getThemeAttrColor(R.attr.colorSurface)
+ }
+ } else {
+ binding.mainBottomNav.isVisible = true
+ binding.mainFragmentContainer.updateLayoutParams {
+ updateMargins(bottom = dpToPx(56f).toInt())
+ }
+
+ if (appInfo.systemVersion >= P) {
+ window.navigationBarColor =
+ getThemeAttrColor(android.R.attr.navigationBarColor)
+ }
+ }
+
analytics.setCurrentScreen(this@MainActivity, name)
presenter.onViewChange(section)
}
@@ -254,6 +285,16 @@ class MainActivity : BaseActivity(), MainVie
}
}
+ override fun onPreferenceStartFragment(
+ caller: PreferenceFragmentCompat,
+ pref: Preference
+ ): Boolean {
+ val fragment =
+ supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment)
+ navController.pushFragment(fragment)
+ return true
+ }
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected()
else false
@@ -280,8 +321,8 @@ class MainActivity : BaseActivity(), MainVie
supportActionBar?.setDisplayHomeAsUpEnabled(show)
}
- override fun showAccountPicker() {
- navController.showDialogFragment(AccountQuickDialog.newInstance())
+ override fun showAccountPicker(studentWithSemesters: List) {
+ navController.showDialogFragment(AccountQuickDialog.newInstance(studentWithSemesters))
}
override fun showActionBarElevation(show: Boolean) {
@@ -315,6 +356,13 @@ class MainActivity : BaseActivity(), MainVie
presenter.onBackPressed { super.onBackPressed() }
}
+ override fun showStudentAvatar(student: Student) {
+ accountMenu?.run {
+ icon = createNameInitialsDrawable(student.nickOrName, student.avatarColor, 0.44f)
+ title = getString(R.string.main_account_picker)
+ }
+ }
+
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
navController.onSaveInstanceState(outState)
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 59937b33..85777983 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
@@ -1,5 +1,7 @@
package io.github.wulkanowy.ui.modules.main
+import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
@@ -9,6 +11,8 @@ import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE
import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE
import io.github.wulkanowy.ui.modules.main.MainView.Section.SCHOOL
import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.flowWithResource
+import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -17,9 +21,11 @@ class MainPresenter @Inject constructor(
studentRepository: StudentRepository,
private val prefRepository: PreferencesRepository,
private val syncManager: SyncManager,
- private val analytics: AnalyticsHelper
+ private val analytics: AnalyticsHelper,
) : BasePresenter(errorHandler, studentRepository) {
+ var studentsWitSemesters: List? = null
+
fun onAttachView(view: MainView, initMenu: MainView.Section?) {
super.onAttachView(view)
view.apply {
@@ -35,6 +41,28 @@ class MainPresenter @Inject constructor(
analytics.logEvent("app_open", "destination" to initMenu?.name)
}
+ fun onActionMenuCreated() {
+ if (!studentsWitSemesters.isNullOrEmpty()) {
+ showCurrentStudentAvatar()
+ return
+ }
+
+ flowWithResource { studentRepository.getSavedStudents(false) }
+ .onEach { resource ->
+ when (resource.status) {
+ Status.LOADING -> Timber.i("Loading student avatar data started")
+ Status.SUCCESS -> {
+ studentsWitSemesters = resource.data
+ showCurrentStudentAvatar()
+ }
+ Status.ERROR -> {
+ Timber.i("Loading student avatar result: An exception occurred")
+ errorHandler.dispatch(resource.error!!)
+ }
+ }
+ }.launch("avatar")
+ }
+
fun onViewChange(section: MainView.Section?) {
view?.apply {
showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL)
@@ -48,8 +76,10 @@ class MainPresenter @Inject constructor(
}
fun onAccountManagerSelected(): Boolean {
+ if (studentsWitSemesters.isNullOrEmpty()) return true
+
Timber.i("Select account manager")
- view?.showAccountPicker()
+ view?.showAccountPicker(studentsWitSemesters!!)
return true
}
@@ -81,6 +111,13 @@ class MainPresenter @Inject constructor(
} == true
}
+ private fun showCurrentStudentAvatar() {
+ val currentStudent =
+ studentsWitSemesters!!.single { it.student.isCurrent }.student
+
+ view?.showStudentAvatar(currentStudent)
+ }
+
private fun getProperViewIndexes(initMenu: MainView.Section?): Pair {
return when (initMenu?.id) {
in 0..3 -> initMenu!!.id to -1
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 7f409814..a4b7d094 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
@@ -1,5 +1,7 @@
package io.github.wulkanowy.ui.modules.main
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
interface MainView : BaseView {
@@ -22,7 +24,7 @@ interface MainView : BaseView {
fun showHomeArrow(show: Boolean)
- fun showAccountPicker()
+ fun showAccountPicker(studentWithSemesters: List)
fun showActionBarElevation(show: Boolean)
@@ -36,6 +38,8 @@ interface MainView : BaseView {
fun popView(depth: Int = 1)
+ fun showStudentAvatar(student: Student)
+
interface MainChildView {
fun onFragmentReselected()
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 48150d8d..6357c495 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
@@ -20,13 +20,15 @@ import io.github.wulkanowy.ui.base.BaseDialogFragment
import javax.inject.Inject
@AndroidEntryPoint
-class MobileDeviceTokenDialog : BaseDialogFragment(), MobileDeviceTokenVIew {
+class MobileDeviceTokenDialog : BaseDialogFragment(),
+ MobileDeviceTokenVIew {
@Inject
lateinit var presenter: MobileDeviceTokenPresenter
companion object {
- fun newInstance(): MobileDeviceTokenDialog = MobileDeviceTokenDialog()
+
+ fun newInstance() = MobileDeviceTokenDialog()
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -34,12 +36,14 @@ class MobileDeviceTokenDialog : BaseDialogFragment(),
setStyle(STYLE_NO_TITLE, 0)
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogMobileDeviceBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogMobileDeviceBinding.inflate(inflater).apply { binding = this }.root
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ 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/MoreFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt
index bf8918fc..c4a14a52 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt
@@ -63,9 +63,6 @@ class MoreFragment : BaseFragment(R.layout.fragment_more),
override val settingsRes: Pair?
get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) }
- override val aboutRes: Pair?
- get() = context?.run { getString(R.string.about_title) to getCompatDrawable(R.drawable.ic_all_about) }
-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentMoreBinding.bind(view)
@@ -124,10 +121,6 @@ class MoreFragment : BaseFragment(R.layout.fragment_more),
(activity as? MainActivity)?.pushView(SettingsFragment.newInstance())
}
- override fun openAboutView() {
- (activity as? MainActivity)?.pushView(AboutFragment.newInstance())
- }
-
override fun popView(depth: Int) {
(activity as? MainActivity)?.popView(depth)
}
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 d119000d..6079141f 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
@@ -30,7 +30,6 @@ class MorePresenter @Inject constructor(
conferencesRes?.first -> openConferencesView()
schoolAndTeachersRes?.first -> openSchoolAndTeachersView()
settingsRes?.first -> openSettingsView()
- aboutRes?.first -> openAboutView()
}
}
}
@@ -51,8 +50,7 @@ class MorePresenter @Inject constructor(
mobileDevicesRes,
conferencesRes,
schoolAndTeachersRes,
- settingsRes,
- aboutRes
+ settingsRes
))
}
}
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 bb1faeda..0543742e 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
@@ -21,16 +21,12 @@ interface MoreView : BaseView {
val settingsRes: Pair?
- val aboutRes: Pair?
-
fun initView()
fun updateData(data: List>)
fun openSettingsView()
- fun openAboutView()
-
fun popView(depth: Int)
fun openMessagesView()
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 e016fe0b..4cb1cda0 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
@@ -22,12 +22,11 @@ class NoteDialog : DialogFragment() {
private lateinit var note: Note
companion object {
+
private const val ARGUMENT_KEY = "Item"
- fun newInstance(exam: Note): NoteDialog {
- return NoteDialog().apply {
- arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
- }
+ fun newInstance(exam: Note) = NoteDialog().apply {
+ arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
}
}
@@ -39,13 +38,15 @@ class NoteDialog : DialogFragment() {
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogNoteBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogNoteBinding.inflate(inflater).apply { binding = this }.root
@SuppressLint("SetTextI18n")
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
with(binding) {
noteDialogDate.text = note.date.toFormattedString()
@@ -57,11 +58,19 @@ class NoteDialog : DialogFragment() {
if (note.isPointsShow) {
with(binding.noteDialogPoints) {
text = "${if (note.points > 0) "+" else ""}${note.points}"
- setTextColor(when (NoteCategory.getByValue(note.categoryType)) {
- NoteCategory.POSITIVE -> ContextCompat.getColor(requireContext(), R.color.note_positive)
- NoteCategory.NEGATIVE -> ContextCompat.getColor(requireContext(), R.color.note_negative)
- else -> requireContext().getThemeAttrColor(android.R.attr.textColorPrimary)
- })
+ setTextColor(
+ when (NoteCategory.getByValue(note.categoryType)) {
+ NoteCategory.POSITIVE -> ContextCompat.getColor(
+ requireContext(),
+ R.color.note_positive
+ )
+ NoteCategory.NEGATIVE -> ContextCompat.getColor(
+ requireContext(),
+ R.color.note_negative
+ )
+ else -> requireContext().getThemeAttrColor(android.R.attr.textColorPrimary)
+ }
+ )
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt
index ad4692b9..2612fab3 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt
@@ -1,148 +1,22 @@
package io.github.wulkanowy.ui.modules.settings
-import android.content.SharedPreferences
import android.os.Bundle
-import androidx.appcompat.app.AlertDialog
-import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
-import com.thelittlefireman.appkillermanager.AppKillerManager
-import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException
-import com.yariksoffice.lingver.Lingver
-import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
-import io.github.wulkanowy.ui.base.BaseActivity
-import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.main.MainView
-import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.openInternetBrowser
-import javax.inject.Inject
+import timber.log.Timber
-@AndroidEntryPoint
-class SettingsFragment : PreferenceFragmentCompat(),
- SharedPreferences.OnSharedPreferenceChangeListener,
- MainView.TitledView, SettingsView {
-
- @Inject
- lateinit var presenter: SettingsPresenter
-
- @Inject
- lateinit var appInfo: AppInfo
-
- @Inject
- lateinit var lingver: Lingver
+class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView {
companion object {
+
fun newInstance() = SettingsFragment()
}
override val titleStringId get() = R.string.settings_title
- override val syncSuccessString get() = getString(R.string.pref_services_message_sync_success)
-
- override val syncFailedString get() = getString(R.string.pref_services_message_sync_failed)
-
- override fun initView() {
- findPreference(getString(R.string.pref_key_services_force_sync))?.run {
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- presenter.onSyncNowClicked()
- true
- }
- }
- findPreference(getString(R.string.pref_key_notifications_fix_issues))?.run {
- isVisible = AppKillerManager.isDeviceSupported() && AppKillerManager.isAnyActionAvailable(requireContext())
- setOnPreferenceClickListener {
- presenter.onFixSyncIssuesClicked()
- true
- }
- }
- }
-
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
- presenter.onAttachView(this)
- }
-
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.scheme_preferences, rootKey)
- findPreference(getString(R.string.pref_key_notification_debug))?.isVisible = appInfo.isDebug
- }
-
- override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
- presenter.onSharedPreferenceChanged(key)
- }
-
- override fun recreateView() {
- activity?.recreate()
- }
-
- override fun updateLanguage(langCode: String) {
- lingver.setLocale(requireContext(), langCode)
- }
-
- override fun updateLanguageToFollowSystem() {
- lingver.setFollowSystemLocale(requireContext())
- }
-
- override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) {
- findPreference(serviceEnablesKey)?.run {
- summary = if (isHolidays) getString(R.string.pref_services_suspended) else ""
- isEnabled = !isHolidays
- }
- }
-
- override fun setSyncInProgress(inProgress: Boolean) {
- if (activity == null || !isAdded) return
-
- findPreference(getString(R.string.pref_key_services_force_sync))?.run {
- isEnabled = !inProgress
- summary = if (inProgress) getString(R.string.pref_services_sync_in_progress) else ""
- }
- }
-
- override fun showError(text: String, error: Throwable) {
- (activity as? BaseActivity<*, *>)?.showError(text, error)
- }
-
- override fun showMessage(text: String) {
- (activity as? BaseActivity<*, *>)?.showMessage(text)
- }
-
- override fun showExpiredDialog() {
- (activity as? BaseActivity<*, *>)?.showExpiredDialog()
- }
-
- override fun openClearLoginView() {
- (activity as? BaseActivity<*, *>)?.openClearLoginView()
- }
-
- override fun showErrorDetailsDialog(error: Throwable) {
- ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
- }
-
- override fun showFixSyncDialog() {
- AlertDialog.Builder(requireContext())
- .setTitle(R.string.pref_notify_fix_sync_issues)
- .setMessage(R.string.pref_notify_fix_sync_issues_message)
- .setNegativeButton(android.R.string.cancel) { _, _ -> }
- .setPositiveButton(R.string.pref_notify_fix_sync_issues_settings_button) { _, _ ->
- try {
- AppKillerManager.doActionPowerSaving(requireContext())
- AppKillerManager.doActionAutoStart(requireContext())
- AppKillerManager.doActionNotification(requireContext())
- } catch (e: NoActionFoundException) {
- requireContext().openInternetBrowser("https://dontkillmyapp.com/${AppKillerManager.getDevice()?.manufacturer}", ::showMessage)
- }
- }
- .show()
- }
-
- override fun onResume() {
- super.onResume()
- preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
- }
-
- override fun onPause() {
- super.onPause()
- preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ Timber.i("Settings view was initialized")
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt
new file mode 100644
index 00000000..524d7ba6
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt
@@ -0,0 +1,78 @@
+package io.github.wulkanowy.ui.modules.settings.advanced
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.View
+import androidx.preference.PreferenceFragmentCompat
+import com.yariksoffice.lingver.Lingver
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.ui.base.BaseActivity
+import io.github.wulkanowy.ui.base.ErrorDialog
+import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.utils.AppInfo
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AdvancedFragment : PreferenceFragmentCompat(),
+ SharedPreferences.OnSharedPreferenceChangeListener,
+ MainView.TitledView, AdvancedView {
+
+ @Inject
+ lateinit var presenter: AdvancedPresenter
+
+ @Inject
+ lateinit var appInfo: AppInfo
+
+ @Inject
+ lateinit var lingver: Lingver
+
+ companion object {
+ fun newInstance() = AdvancedFragment()
+ }
+
+ override val titleStringId get() = R.string.pref_settings_advanced_title
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ presenter.onAttachView(this)
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ setPreferencesFromResource(R.xml.scheme_preferences_advanced, rootKey)
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
+ presenter.onSharedPreferenceChanged(key)
+ }
+
+ override fun showError(text: String, error: Throwable) {
+ (activity as? BaseActivity<*, *>)?.showError(text, error)
+ }
+
+ override fun showMessage(text: String) {
+ (activity as? BaseActivity<*, *>)?.showMessage(text)
+ }
+
+ override fun showExpiredDialog() {
+ (activity as? BaseActivity<*, *>)?.showExpiredDialog()
+ }
+
+ override fun openClearLoginView() {
+ (activity as? BaseActivity<*, *>)?.openClearLoginView()
+ }
+
+ override fun showErrorDetailsDialog(error: Throwable) {
+ ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
+ }
+
+ override fun onResume() {
+ super.onResume()
+ preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt
new file mode 100644
index 00000000..d38f841f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt
@@ -0,0 +1,25 @@
+package io.github.wulkanowy.ui.modules.settings.advanced
+
+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.utils.AnalyticsHelper
+import timber.log.Timber
+import javax.inject.Inject
+
+class AdvancedPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository,
+ private val analytics: AnalyticsHelper,
+) : BasePresenter(errorHandler, studentRepository) {
+
+ override fun onAttachView(view: AdvancedView) {
+ super.onAttachView(view)
+ Timber.i("Settings advanced view was initialized")
+ }
+
+ fun onSharedPreferenceChanged(key: String) {
+ Timber.i("Change settings $key")
+ analytics.logEvent("setting_changed", "name" to key)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedView.kt
new file mode 100644
index 00000000..c2e8e52d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedView.kt
@@ -0,0 +1,5 @@
+package io.github.wulkanowy.ui.modules.settings.advanced
+
+import io.github.wulkanowy.ui.base.BaseView
+
+interface AdvancedView : BaseView {}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt
new file mode 100644
index 00000000..5ac801dc
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt
@@ -0,0 +1,90 @@
+package io.github.wulkanowy.ui.modules.settings.appearance
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.View
+import androidx.preference.PreferenceFragmentCompat
+import com.yariksoffice.lingver.Lingver
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.ui.base.BaseActivity
+import io.github.wulkanowy.ui.base.ErrorDialog
+import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.utils.AppInfo
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AppearanceFragment : PreferenceFragmentCompat(),
+ SharedPreferences.OnSharedPreferenceChangeListener,
+ MainView.TitledView, AppearanceView {
+
+ @Inject
+ lateinit var presenter: AppearancePresenter
+
+ @Inject
+ lateinit var appInfo: AppInfo
+
+ @Inject
+ lateinit var lingver: Lingver
+
+ companion object {
+ fun newInstance() = AppearanceFragment()
+ }
+
+ override val titleStringId get() = R.string.pref_settings_appearance_title
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ presenter.onAttachView(this)
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ setPreferencesFromResource(R.xml.scheme_preferences_appearance, rootKey)
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
+ presenter.onSharedPreferenceChanged(key)
+ }
+
+ override fun recreateView() {
+ activity?.recreate()
+ }
+
+ override fun updateLanguage(langCode: String) {
+ lingver.setLocale(requireContext(), langCode)
+ }
+
+ override fun updateLanguageToFollowSystem() {
+ lingver.setFollowSystemLocale(requireContext())
+ }
+
+ override fun showError(text: String, error: Throwable) {
+ (activity as? BaseActivity<*, *>)?.showError(text, error)
+ }
+
+ override fun showMessage(text: String) {
+ (activity as? BaseActivity<*, *>)?.showMessage(text)
+ }
+
+ override fun showExpiredDialog() {
+ (activity as? BaseActivity<*, *>)?.showExpiredDialog()
+ }
+
+ override fun openClearLoginView() {
+ (activity as? BaseActivity<*, *>)?.openClearLoginView()
+ }
+
+ override fun showErrorDetailsDialog(error: Throwable) {
+ ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
+ }
+
+ override fun onResume() {
+ super.onResume()
+ preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt
new file mode 100644
index 00000000..14592a6c
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt
@@ -0,0 +1,45 @@
+package io.github.wulkanowy.ui.modules.settings.appearance
+
+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.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.AppInfo
+import timber.log.Timber
+import javax.inject.Inject
+
+class AppearancePresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository,
+ private val preferencesRepository: PreferencesRepository,
+ private val analytics: AnalyticsHelper,
+ private val appInfo: AppInfo
+) : BasePresenter(errorHandler, studentRepository) {
+
+ override fun onAttachView(view: AppearanceView) {
+ super.onAttachView(view)
+ Timber.i("Settings appearance view was initialized")
+ }
+
+ fun onSharedPreferenceChanged(key: String) {
+ Timber.i("Change settings $key")
+
+ preferencesRepository.apply {
+ when (key) {
+ appThemeKey -> view?.recreateView()
+ appLanguageKey -> view?.run {
+ if (appLanguage == "system") {
+ updateLanguageToFollowSystem()
+ analytics.logEvent("language", "setting_changed" to appInfo.systemLanguage)
+ } else {
+ updateLanguage(appLanguage)
+ analytics.logEvent("language", "setting_changed" to appLanguage)
+ }
+ recreateView()
+ }
+ }
+ }
+ analytics.logEvent("setting_changed", "name" to key)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceView.kt
new file mode 100644
index 00000000..ecee4f42
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceView.kt
@@ -0,0 +1,12 @@
+package io.github.wulkanowy.ui.modules.settings.appearance
+
+import io.github.wulkanowy.ui.base.BaseView
+
+interface AppearanceView : BaseView {
+
+ fun recreateView()
+
+ fun updateLanguage(langCode: String)
+
+ fun updateLanguageToFollowSystem()
+}
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
new file mode 100644
index 00000000..a9641d88
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt
@@ -0,0 +1,130 @@
+package io.github.wulkanowy.ui.modules.settings.notifications
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AlertDialog
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
+import androidx.recyclerview.widget.RecyclerView
+import com.thelittlefireman.appkillermanager.AppKillerManager
+import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.ui.base.BaseActivity
+import io.github.wulkanowy.ui.base.ErrorDialog
+import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.utils.openInternetBrowser
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class NotificationsFragment : PreferenceFragmentCompat(),
+ SharedPreferences.OnSharedPreferenceChangeListener,
+ MainView.TitledView, NotificationsView {
+
+ @Inject
+ lateinit var presenter: NotificationsPresenter
+
+ companion object {
+ fun newInstance() = NotificationsFragment()
+ }
+
+ override val titleStringId get() = R.string.pref_settings_notifications_title
+
+ override fun initView(showDebugNotificationSwitch: Boolean) {
+ findPreference(getString(R.string.pref_key_notification_debug))?.isVisible =
+ showDebugNotificationSwitch
+
+ findPreference(getString(R.string.pref_key_notifications_fix_issues))?.run {
+ isVisible = AppKillerManager.isDeviceSupported()
+ && AppKillerManager.isAnyActionAvailable(requireContext())
+
+ setOnPreferenceClickListener {
+ presenter.onFixSyncIssuesClicked()
+ true
+ }
+ }
+ }
+
+ override fun onCreateRecyclerView(
+ inflater: LayoutInflater?,
+ parent: ViewGroup?,
+ state: Bundle?
+ ): RecyclerView? = super.onCreateRecyclerView(inflater, parent, state)
+ .also {
+ it.itemAnimator = null
+ it.layoutAnimation = null
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ presenter.onAttachView(this)
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ setPreferencesFromResource(R.xml.scheme_preferences_notifications, rootKey)
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
+ presenter.onSharedPreferenceChanged(key)
+ }
+
+ override fun enableNotification(notificationKey: String, enable: Boolean) {
+ findPreference(notificationKey)?.run {
+ isEnabled = enable
+ summary = if (enable) null else getString(R.string.pref_notify_disabled_summary)
+ }
+ }
+
+ override fun showError(text: String, error: Throwable) {
+ (activity as? BaseActivity<*, *>)?.showError(text, error)
+ }
+
+ override fun showMessage(text: String) {
+ (activity as? BaseActivity<*, *>)?.showMessage(text)
+ }
+
+ override fun showExpiredDialog() {
+ (activity as? BaseActivity<*, *>)?.showExpiredDialog()
+ }
+
+ override fun openClearLoginView() {
+ (activity as? BaseActivity<*, *>)?.openClearLoginView()
+ }
+
+ override fun showErrorDetailsDialog(error: Throwable) {
+ ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
+ }
+
+ override fun showFixSyncDialog() {
+ AlertDialog.Builder(requireContext())
+ .setTitle(R.string.pref_notify_fix_sync_issues)
+ .setMessage(R.string.pref_notify_fix_sync_issues_message)
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
+ .setPositiveButton(R.string.pref_notify_fix_sync_issues_settings_button) { _, _ ->
+ try {
+ AppKillerManager.doActionPowerSaving(requireContext())
+ AppKillerManager.doActionAutoStart(requireContext())
+ AppKillerManager.doActionNotification(requireContext())
+ } catch (e: NoActionFoundException) {
+ requireContext().openInternetBrowser(
+ "https://dontkillmyapp.com/${AppKillerManager.getDevice()?.manufacturer}",
+ ::showMessage
+ )
+ }
+ }
+ .show()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt
new file mode 100644
index 00000000..981af17d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt
@@ -0,0 +1,58 @@
+package io.github.wulkanowy.ui.modules.settings.notifications
+
+import com.chuckerteam.chucker.api.ChuckerCollector
+import io.github.wulkanowy.data.repositories.PreferencesRepository
+import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.AppInfo
+import timber.log.Timber
+import javax.inject.Inject
+
+class NotificationsPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository,
+ private val preferencesRepository: PreferencesRepository,
+ private val timetableNotificationHelper: TimetableNotificationSchedulerHelper,
+ private val appInfo: AppInfo,
+ private val analytics: AnalyticsHelper,
+ private val chuckerCollector: ChuckerCollector
+) : BasePresenter(errorHandler, studentRepository) {
+
+ override fun onAttachView(view: NotificationsView) {
+ super.onAttachView(view)
+
+ with(view) {
+ enableNotification(
+ preferencesRepository.notificationsEnableKey,
+ preferencesRepository.isServiceEnabled
+ )
+ initView(appInfo.isDebug)
+ }
+ Timber.i("Settings notifications view was initialized")
+ }
+
+ fun onSharedPreferenceChanged(key: String) {
+ Timber.i("Change settings $key")
+
+ preferencesRepository.apply {
+ when (key) {
+ isUpcomingLessonsNotificationsEnableKey -> {
+ if (!isUpcomingLessonsNotificationsEnable) {
+ timetableNotificationHelper.cancelNotification()
+ }
+ }
+ isDebugNotificationEnableKey -> {
+ chuckerCollector.showNotification = isDebugNotificationEnable
+ }
+ }
+ }
+ analytics.logEvent("setting_changed", "name" to key)
+ }
+
+ fun onFixSyncIssuesClicked() {
+ view?.showFixSyncDialog()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt
new file mode 100644
index 00000000..2618cde1
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt
@@ -0,0 +1,12 @@
+package io.github.wulkanowy.ui.modules.settings.notifications
+
+import io.github.wulkanowy.ui.base.BaseView
+
+interface NotificationsView : BaseView {
+
+ fun initView(showDebugNotificationSwitch: Boolean)
+
+ fun showFixSyncDialog()
+
+ fun enableNotification(notificationKey: String, enable: Boolean)
+}
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
new file mode 100644
index 00000000..207fe2ff
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
@@ -0,0 +1,100 @@
+package io.github.wulkanowy.ui.modules.settings.sync
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.View
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.ui.base.BaseActivity
+import io.github.wulkanowy.ui.base.ErrorDialog
+import io.github.wulkanowy.ui.modules.main.MainView
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class SyncFragment : PreferenceFragmentCompat(),
+ SharedPreferences.OnSharedPreferenceChangeListener,
+ MainView.TitledView, SyncView {
+
+ @Inject
+ lateinit var presenter: SyncPresenter
+
+ companion object {
+ fun newInstance() = SyncFragment()
+ }
+
+ override val titleStringId get() = R.string.pref_settings_sync_title
+
+ override val syncSuccessString get() = getString(R.string.pref_services_message_sync_success)
+
+ override val syncFailedString get() = getString(R.string.pref_services_message_sync_failed)
+
+ override fun initView() {
+ findPreference(getString(R.string.pref_key_services_force_sync))?.run {
+ onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ presenter.onSyncNowClicked()
+ true
+ }
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ presenter.onAttachView(this)
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ setPreferencesFromResource(R.xml.scheme_preferences_sync, rootKey)
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
+ presenter.onSharedPreferenceChanged(key)
+ }
+
+ override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) {
+ findPreference(serviceEnablesKey)?.run {
+ summary = if (isHolidays) getString(R.string.pref_services_suspended) else ""
+ isEnabled = !isHolidays
+ }
+ }
+
+ override fun setSyncInProgress(inProgress: Boolean) {
+ if (activity == null || !isAdded) return
+
+ findPreference(getString(R.string.pref_key_services_force_sync))?.run {
+ isEnabled = !inProgress
+ summary = if (inProgress) getString(R.string.pref_services_sync_in_progress) else ""
+ }
+ }
+
+ override fun showError(text: String, error: Throwable) {
+ (activity as? BaseActivity<*, *>)?.showError(text, error)
+ }
+
+ override fun showMessage(text: String) {
+ (activity as? BaseActivity<*, *>)?.showMessage(text)
+ }
+
+ override fun showExpiredDialog() {
+ (activity as? BaseActivity<*, *>)?.showExpiredDialog()
+ }
+
+ override fun openClearLoginView() {
+ (activity as? BaseActivity<*, *>)?.openClearLoginView()
+ }
+
+ override fun showErrorDetailsDialog(error: Throwable) {
+ ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
+ }
+
+ override fun onResume() {
+ super.onResume()
+ preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
similarity index 63%
rename from app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt
rename to app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
index e3b2e232..36b8d792 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
@@ -1,15 +1,12 @@
-package io.github.wulkanowy.ui.modules.settings
+package io.github.wulkanowy.ui.modules.settings.sync
import androidx.work.WorkInfo
-import com.chuckerteam.chucker.api.ChuckerCollector
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.isHolidays
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.onEach
@@ -17,20 +14,17 @@ import timber.log.Timber
import java.time.LocalDate.now
import javax.inject.Inject
-class SettingsPresenter @Inject constructor(
+class SyncPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val preferencesRepository: PreferencesRepository,
- private val timetableNotificationHelper: TimetableNotificationSchedulerHelper,
private val analytics: AnalyticsHelper,
private val syncManager: SyncManager,
- private val chuckerCollector: ChuckerCollector,
- private val appInfo: AppInfo
-) : BasePresenter(errorHandler, studentRepository) {
+) : BasePresenter(errorHandler, studentRepository) {
- override fun onAttachView(view: SettingsView) {
+ override fun onAttachView(view: SyncView) {
super.onAttachView(view)
- Timber.i("Settings view was initialized")
+ Timber.i("Settings sync view was initialized")
view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays)
view.initView()
}
@@ -42,20 +36,6 @@ class SettingsPresenter @Inject constructor(
when (key) {
serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startPeriodicSyncWorker() else stopSyncWorker() }
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startPeriodicSyncWorker(true)
- isDebugNotificationEnableKey -> chuckerCollector.showNotification =
- isDebugNotificationEnable
- appThemeKey -> view?.recreateView()
- isUpcomingLessonsNotificationsEnableKey -> if (!isUpcomingLessonsNotificationsEnable) timetableNotificationHelper.cancelNotification()
- appLanguageKey -> view?.run {
- if (appLanguage == "system") {
- updateLanguageToFollowSystem()
- analytics.logEvent("language", "setting_changed" to appInfo.systemLanguage)
- } else {
- updateLanguage(appLanguage)
- analytics.logEvent("language", "setting_changed" to appLanguage)
- }
- recreateView()
- }
}
}
analytics.logEvent("setting_changed", "name" to key)
@@ -89,8 +69,4 @@ class SettingsPresenter @Inject constructor(
}.launch("sync")
}
}
-
- fun onFixSyncIssuesClicked() {
- view?.showFixSyncDialog()
- }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncView.kt
similarity index 54%
rename from app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt
rename to app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncView.kt
index 80271741..9da473ba 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncView.kt
@@ -1,8 +1,8 @@
-package io.github.wulkanowy.ui.modules.settings
+package io.github.wulkanowy.ui.modules.settings.sync
import io.github.wulkanowy.ui.base.BaseView
-interface SettingsView : BaseView {
+interface SyncView : BaseView {
val syncSuccessString: String
@@ -10,15 +10,7 @@ interface SettingsView : BaseView {
fun initView()
- fun recreateView()
-
- fun updateLanguage(langCode: String)
-
- fun updateLanguageToFollowSystem()
-
fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean)
fun setSyncInProgress(inProgress: Boolean)
-
- fun showFixSyncDialog()
}
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 f7d5b1ed..7744515d 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
@@ -24,12 +24,11 @@ class TimetableDialog : DialogFragment() {
private lateinit var lesson: Timetable
companion object {
+
private const val ARGUMENT_KEY = "Item"
- fun newInstance(exam: Timetable): TimetableDialog {
- return TimetableDialog().apply {
- arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
- }
+ fun newInstance(exam: Timetable) = TimetableDialog().apply {
+ arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
}
}
@@ -41,12 +40,14 @@ class TimetableDialog : DialogFragment() {
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogTimetableBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogTimetableBinding.inflate(inflater).apply { binding = this }.root
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
with(lesson) {
setInfo(info, teacher, canceled, changes)
@@ -76,15 +77,24 @@ class TimetableDialog : DialogFragment() {
}
}
+ @SuppressLint("DefaultLocale")
private fun setInfo(info: String, teacher: String, canceled: Boolean, changes: Boolean) {
with(binding) {
when {
info.isNotBlank() -> {
if (canceled) {
- timetableDialogChangesTitle.setTextColor(requireContext().getThemeAttrColor(R.attr.colorPrimary))
+ timetableDialogChangesTitle.setTextColor(
+ requireContext().getThemeAttrColor(
+ R.attr.colorPrimary
+ )
+ )
timetableDialogChanges.setTextColor(requireContext().getThemeAttrColor(R.attr.colorPrimary))
} else {
- timetableDialogChangesTitle.setTextColor(requireContext().getThemeAttrColor(R.attr.colorTimetableChange))
+ timetableDialogChangesTitle.setTextColor(
+ requireContext().getThemeAttrColor(
+ R.attr.colorTimetableChange
+ )
+ )
timetableDialogChanges.setTextColor(requireContext().getThemeAttrColor(R.attr.colorTimetableChange))
}
@@ -167,6 +177,7 @@ class TimetableDialog : DialogFragment() {
@SuppressLint("SetTextI18n")
private fun setTime(start: LocalDateTime, end: LocalDateTime) {
- binding.timetableDialogTime.text = "${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}"
+ binding.timetableDialogTime.text =
+ "${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}"
}
}
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 56ea16cf..258fc59d 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,6 +1,5 @@
package io.github.wulkanowy.ui.modules.timetable.completed
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -17,12 +16,11 @@ class CompletedLessonDialog : DialogFragment() {
private lateinit var completedLesson: CompletedLesson
companion object {
+
private const val ARGUMENT_KEY = "Item"
- fun newInstance(exam: CompletedLesson): CompletedLessonDialog {
- return CompletedLessonDialog().apply {
- arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
- }
+ fun newInstance(exam: CompletedLesson) = CompletedLessonDialog().apply {
+ arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
}
}
@@ -34,13 +32,14 @@ class CompletedLessonDialog : DialogFragment() {
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogLessonCompletedBinding.inflate(inflater).apply { binding = this }.root
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogLessonCompletedBinding.inflate(inflater).apply { binding = this }.root
- @SuppressLint("SetTextI18n")
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
with(binding) {
completedLessonDialogSubject.text = completedLesson.subject
diff --git a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt
index 82671a7f..a3961aed 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt
@@ -35,8 +35,8 @@ open class AppInfo @Inject constructor() {
open val systemLanguage: String
get() = Resources.getSystem().configuration.locale.language
- open val defaultColorsForAvatar = listOf(
- 0xe57373, 0xf06292, 0xba68c8, 0x9575cd, 0x7986cb, 0x64b5f6, 0x4fc3f7, 0x4dd0e1, 0x4db6ac,
- 0x81c784, 0xaed581, 0xff8a65, 0xd4e157, 0xffd54f, 0xffb74d, 0xa1887f, 0x90a4ae
- )
+ val defaultColorsForAvatar = listOf(
+ 0xd32f2f, 0xE64A19, 0xFFA000, 0xAFB42B, 0x689F38, 0x388E3C, 0x00796B, 0x0097A7,
+ 0x1976D2, 0x3647b5, 0x6236c9, 0x9225c1, 0xC2185B, 0x616161, 0x455A64, 0x7a5348
+ ).map { (it and 0x00ffffff or (255 shl 24)).toLong() }
}
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 cf715e65..92c0ddfc 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
@@ -1,8 +1,15 @@
package io.github.wulkanowy.utils
+import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.Typeface
import android.net.Uri
+import android.text.TextPaint
import android.util.DisplayMetrics.DENSITY_DEFAULT
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
@@ -10,6 +17,9 @@ import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
+import androidx.core.graphics.applyCanvas
+import androidx.core.graphics.drawable.RoundedBitmapDrawable
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import io.github.wulkanowy.BuildConfig.APPLICATION_ID
@ColorInt
@@ -30,7 +40,8 @@ fun Context.getThemeAttrColor(@AttrRes colorAttr: Int, alpha: Int): Int {
@ColorInt
fun Context.getCompatColor(@ColorRes colorRes: Int) = ContextCompat.getColor(this, colorRes)
-fun Context.getCompatDrawable(@DrawableRes drawableRes: Int) = ContextCompat.getDrawable(this, drawableRes)
+fun Context.getCompatDrawable(@DrawableRes drawableRes: Int) =
+ ContextCompat.getDrawable(this, drawableRes)
fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit) {
Intent.parseUri(uri, 0).let {
@@ -45,7 +56,13 @@ fun Context.openAppInMarket(onActivityNotFound: (uri: String) -> Unit) {
}
}
-fun Context.openEmailClient(chooserTitle: String, email: String, subject: String, body: String, onActivityNotFound: () -> Unit = {}) {
+fun Context.openEmailClient(
+ chooserTitle: String,
+ email: String,
+ subject: String,
+ body: String,
+ onActivityNotFound: () -> Unit = {}
+) {
val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")).apply {
putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
putExtra(Intent.EXTRA_SUBJECT, subject)
@@ -85,3 +102,39 @@ fun Context.shareText(text: String, subject: String?) {
}
fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT
+
+@SuppressLint("DefaultLocale")
+fun Context.createNameInitialsDrawable(
+ text: String,
+ backgroundColor: Long,
+ scaleFactory: Float = 1f
+): RoundedBitmapDrawable {
+ val words = text.split(" ")
+ val firstCharFirstWord = words.getOrNull(0)?.firstOrNull() ?: ""
+ val firstCharSecondWord = words.getOrNull(1)?.firstOrNull() ?: ""
+
+ val initials = "$firstCharFirstWord$firstCharSecondWord".toUpperCase()
+
+ val bounds = Rect()
+ val dimension = this.dpToPx(64f * scaleFactory).toInt()
+ val textPaint = TextPaint().apply {
+ typeface = Typeface.SANS_SERIF
+ color = Color.WHITE
+ textAlign = Paint.Align.CENTER
+ isAntiAlias = true
+ textSize = this@createNameInitialsDrawable.dpToPx(30f * scaleFactory)
+ getTextBounds(initials, 0, initials.length, bounds)
+ }
+
+ val xCoordinate = (dimension / 2).toFloat()
+ val yCoordinate = (dimension / 2 + (bounds.bottom - bounds.top) / 2).toFloat()
+
+ val bitmap = Bitmap.createBitmap(dimension, dimension, Bitmap.Config.ARGB_8888)
+ .applyCanvas {
+ drawColor(backgroundColor.toInt())
+ drawText(initials, 0, initials.length, xCoordinate, yCoordinate, textPaint)
+ }
+
+ return RoundedBitmapDrawableFactory.create(this.resources, bitmap)
+ .apply { isCircular = true }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
index 049e1d42..5dd28967 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
@@ -13,8 +13,11 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
inline fun networkBoundResource(
+ mutex: Mutex = Mutex(),
showSavedOnLoading: Boolean = true,
crossinline query: () -> Flow,
crossinline fetch: suspend (ResultType) -> RequestType,
@@ -31,7 +34,7 @@ inline fun networkBoundResource(
try {
val newData = fetch(data)
- saveFetchResult(data, newData)
+ mutex.withLock { saveFetchResult(query().first(), newData) }
query().map { Resource.success(filterResult(it)) }
} catch (throwable: Throwable) {
onFetchFailed(throwable)
@@ -44,11 +47,12 @@ inline fun networkBoundResource(
@JvmName("networkBoundResourceWithMap")
inline fun networkBoundResource(
+ mutex: Mutex = Mutex(),
showSavedOnLoading: Boolean = true,
crossinline query: () -> Flow,
crossinline fetch: suspend (ResultType) -> RequestType,
crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
- crossinline onFetchFailed: (Throwable) -> Unit = { Unit },
+ crossinline onFetchFailed: (Throwable) -> Unit = { },
crossinline shouldFetch: (ResultType) -> Boolean = { true },
crossinline mapResult: (ResultType) -> T
) = flow {
@@ -59,7 +63,8 @@ inline fun networkBoundResource(
if (showSavedOnLoading) emit(Resource.loading(mapResult(data)))
try {
- saveFetchResult(data, fetch(data))
+ val newData = fetch(data)
+ mutex.withLock { saveFetchResult(query().first(), newData) }
query().map { Resource.success(mapResult(it)) }
} catch (throwable: Throwable) {
onFetchFailed(throwable)
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 b96faeb2..d2a8908c 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt
@@ -1,5 +1,7 @@
package io.github.wulkanowy.utils
+import android.os.Handler
+import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
@@ -28,7 +30,8 @@ class LifecycleAwareVariable : ReadWriteProperty, Lifecycl
}
}
-class LifecycleAwareVariableActivity : ReadWriteProperty, LifecycleObserver {
+class LifecycleAwareVariableActivity : ReadWriteProperty,
+ LifecycleObserver {
private var _value: T? = null
@@ -44,11 +47,12 @@ class LifecycleAwareVariableActivity : ReadWriteProperty Fragment.lifecycleAwareVariable() = LifecycleAwareVariable()
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 1d18f575..35320c1e 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,10 +1,11 @@
-Wersja 1.0.0
+Wersja 1.1.0
-Pierwsza produkcyjna wersja Wulkanowego 🎉
-
-Dziękujemy wszystkim użytkownikom za testowanie dotychczasowej wersji będącej we wczesnym dostępie
-jak i programistom, którzy w wolnych chwilach pomagali rozwijać aplikację i naprawiać znalezione błędy.
-
-Bez was wszystkich niemożliwe byłoby to dzieło!
+- dodaliśmy wyświetlanie inicjałów imienia ucznia jako awatar widoczny w aplikacji
+- dodaliśmy historię szczęśliwego numerka
+- dodaliśmy język słowacki
+- zmieniliśmy kolor górnego i dolnego paska systemowego lepiej dostosowując je do aplikacji
+- zmieniliśmy wygląd ustawień dzieląc je na sekcje
+- naprawiliśmy problem dublujących się czasem ocen
+- naprawiliśmy kilka innych błędów i poprawiliśmy stabilność aplikacji
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
diff --git a/app/src/main/res/drawable/ic_all_about.xml b/app/src/main/res/drawable/ic_all_about.xml
index 3dfbbda1..3868f85c 100644
--- a/app/src/main/res/drawable/ic_all_about.xml
+++ b/app/src/main/res/drawable/ic_all_about.xml
@@ -2,6 +2,7 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
+ android:tint="?colorOnSurface"
android:viewportHeight="24">
+
+
diff --git a/app/src/main/res/drawable/ic_settings_advanced.xml b/app/src/main/res/drawable/ic_settings_advanced.xml
new file mode 100644
index 00000000..2fd7e590
--- /dev/null
+++ b/app/src/main/res/drawable/ic_settings_advanced.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_settings_appearance.xml b/app/src/main/res/drawable/ic_settings_appearance.xml
new file mode 100644
index 00000000..afea27f2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_settings_appearance.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_settings_notifications.xml b/app/src/main/res/drawable/ic_settings_notifications.xml
new file mode 100644
index 00000000..f4ff247f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_settings_notifications.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_settings_sync.xml b/app/src/main/res/drawable/ic_settings_sync.xml
new file mode 100644
index 00000000..3697ac0b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_settings_sync.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 2f88ecc8..2ea0a4d3 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,5 +1,5 @@
-
+ app:contentInsetStartWithNavigation="0dp" />
+ android:layout_height="match_parent"
+ android:layout_marginTop="?actionBarSize"
+ android:layout_marginBottom="@dimen/bottom_navigation_height" />
-
+ android:layout_gravity="bottom" />
+
diff --git a/app/src/main/res/layout/dialog_account_edit.xml b/app/src/main/res/layout/dialog_account_edit.xml
index b65f85ac..9f617e44 100644
--- a/app/src/main/res/layout/dialog_account_edit.xml
+++ b/app/src/main/res/layout/dialog_account_edit.xml
@@ -1,87 +1,125 @@
-
-
-
-
+ android:layout_height="wrap_content">
-
+
-
+
-
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_account_quick.xml b/app/src/main/res/layout/dialog_account_quick.xml
index da31d31d..4095c91a 100644
--- a/app/src/main/res/layout/dialog_account_quick.xml
+++ b/app/src/main/res/layout/dialog_account_quick.xml
@@ -2,7 +2,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_account_details.xml b/app/src/main/res/layout/fragment_account_details.xml
index 1a0de147..af9564b5 100644
--- a/app/src/main/res/layout/fragment_account_details.xml
+++ b/app/src/main/res/layout/fragment_account_details.xml
@@ -21,9 +21,9 @@
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
- android:visibility="invisible"
+ android:visibility="gone"
tools:ignore="UseCompoundDrawables"
- tools:visibility="visible">
+ tools:visibility="gone">
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ tools:visibility="visible">
+ tools:ignore="ContentDescription"
+ tools:src="@tools:sample/avatars" />
+
+
+ android:orientation="vertical">
@@ -34,153 +34,101 @@
+ android:layout_height="match_parent">
-
+ android:layout_height="match_parent">
+
+
+
+
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="invisible"
+ tools:ignore="UseCompoundDrawables">
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_marginTop="20dp"
+ android:gravity="center"
+ android:text="@string/grade_no_items"
+ android:textSize="20sp" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_lucky_number.xml b/app/src/main/res/layout/fragment_lucky_number.xml
index f6de01e6..b2d4f40a 100644
--- a/app/src/main/res/layout/fragment_lucky_number.xml
+++ b/app/src/main/res/layout/fragment_lucky_number.xml
@@ -67,6 +67,26 @@
android:textSize="20sp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_account.xml b/app/src/main/res/layout/item_account.xml
index 95fdb0e8..563e6b1e 100644
--- a/app/src/main/res/layout/item_account.xml
+++ b/app/src/main/res/layout/item_account.xml
@@ -18,15 +18,27 @@
android:layout_height="40dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- app:srcCompat="@drawable/ic_all_account"
tools:ignore="ContentDescription"
- tools:tint="@color/colorPrimary" />
+ tools:src="@tools:sample/avatars" />
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_grade_statistics_header.xml b/app/src/main/res/layout/item_grade_statistics_header.xml
new file mode 100644
index 00000000..92f522ba
--- /dev/null
+++ b/app/src/main/res/layout/item_grade_statistics_header.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_grade_statistics_pie.xml b/app/src/main/res/layout/item_grade_statistics_pie.xml
index 15992a37..2ea91ecf 100644
--- a/app/src/main/res/layout/item_grade_statistics_pie.xml
+++ b/app/src/main/res/layout/item_grade_statistics_pie.xml
@@ -23,5 +23,4 @@
android:layout_margin="10dp"
android:background="?android:windowBackground"
tools:context=".ui.modules.grade.statistics.GradeStatisticsAdapter" />
-
diff --git a/app/src/main/res/layout/item_lucky_number_history.xml b/app/src/main/res/layout/item_lucky_number_history.xml
new file mode 100644
index 00000000..79a0fcb2
--- /dev/null
+++ b/app/src/main/res/layout/item_lucky_number_history.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/action_menu_main.xml b/app/src/main/res/menu/action_menu_main.xml
index 72bea25a..21905939 100644
--- a/app/src/main/res/menu/action_menu_main.xml
+++ b/app/src/main/res/menu/action_menu_main.xml
@@ -1,11 +1,11 @@
diff --git a/app/src/main/res/values-cs-v29/preferences_values.xml b/app/src/main/res/values-cs-v29/preferences_values.xml
new file mode 100644
index 00000000..25125a30
--- /dev/null
+++ b/app/src/main/res/values-cs-v29/preferences_values.xml
@@ -0,0 +1,9 @@
+
+
+
+ - Motiv systému
+ - Světlý
+ - Tmavý
+ - Černý (AMOLED)
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-cs-rCZ/preferences_values.xml b/app/src/main/res/values-cs/preferences_values.xml
similarity index 92%
rename from app/src/main/res/values-cs-rCZ/preferences_values.xml
rename to app/src/main/res/values-cs/preferences_values.xml
index bcb690e5..7abbb10b 100644
--- a/app/src/main/res/values-cs-rCZ/preferences_values.xml
+++ b/app/src/main/res/values-cs/preferences_values.xml
@@ -13,6 +13,7 @@
- Українська
- Deutsch
- Čeština
+ - Slovenčina
- 15 minut
@@ -45,8 +46,8 @@
- Průměr známek z celého roku
- - Neukaž
- - Ukázat vše
- - Ukázat menší
+ - Nezobrazovat
+ - Zobrazit vše
+ - Zobrazit menší
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs/strings.xml
similarity index 77%
rename from app/src/main/res/values-cs-rCZ/strings.xml
rename to app/src/main/res/values-cs/strings.xml
index cadf4145..4745e171 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -19,7 +19,7 @@
Domácí úkoly
Manažer účtů
Vyberte účet
- Manažer účtů
+ Podrobnosti účtu
Informace o žáku
Semestr %1$d, %2$d/%3$d
@@ -43,17 +43,18 @@
Přihlašovací údaje jsou nesprávné. Ujistěte se, že je v poli níže vybrána správná variace deníku UONET+
Neplatný PIN
Neplatný token
- Platnost tokenu vypršela
+ Token vypršel
Neplatný e-mail
Místo e-mailu použijte přiřazené přihlašovací údaje
+ Použijte přiřazené přihlašovací nebo e-mail v @%1$s
Neplatný symbol
Žák nebyl nalezen. Zkontrolujte správnost symbolu a vybrané varianty deníku UONET+
Toto pole je povinné
- Vybraný žák je již přihlášen
- Symbol najdete na stránce deníku v Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUjistěte se, že jste na předchozí obrazovce nastavili správnou variantu deníku do pole Variace deníku UONET+. Wulkanowy v tuto chvíli nezjistí předškolní studenty
+ Vybraný žák je už přihlášen
+ Symbol najdete na stránce deníku v Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUjistěte se, že jste na předchozí obrazovce nastavili správnou variantu deníku do pole Variace deníku UONET+. Wulkanowy v tuto chvíli nezjistí předškolní żaków
Vyberte žáky, kteří se mají do aplikace přihlásit
Jiné možnosti
- V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí docházky, ospravedlnění nepřítomnosti, absolvované lekce, informace o škole a prohlížení seznamu registrovaných zařízení
+ V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí docházky, ospravedlnění nepřítomnosti, dokončené lekce, informace o škole a prohlížení seznamu registrovaných zařízení
Tento režim zobrazuje stejná data, která se zobrazují na webových stránkách deníka
Kombinace nejlepších vlastností ostatních dvou režimů. Funguje rychleji než scraper a poskytuje funkce, které nejsou k dispozici v režimu Mobile API. Je to v experimentální fázi
Zásady ochrany osobních údajů
@@ -66,12 +67,12 @@
Zapomněl jsem své heslo
Obnovte svůj účet
Obnovit
- Žák je již přihlášen
+ Žák je už přihlášen
Manažer účtů
Přihlásit se
- Platnost relace vypršela
- Vaše relace vypršela, přihlaste se prosím znovu
+ Relace vypršela
+ Relace vypršela. Přihlaste se prosím znovu
Známka
Semestr %d
@@ -85,7 +86,7 @@
Průměr: %1$.2f
Body: %s
Bez průměru
- Předpovězeno: %1$s
+ Předpokládaná: %1$s
Konečná: %1$s
Součet bodů
Konečná známka
@@ -129,20 +130,20 @@
- Máte %1$d novou známku
- Máte %1$d nové známky
- - Máte %1$d nové známky
- - Máte %1$d nové známky
+ - Máte %1$d nových známek
+ - Máte %1$d nových známek
- Máte %1$d novou předpokládanou známku
- Máte %1$d nové předpokládané známky
- - Máte %1$d nové předpokládané známky
- - Máte %1$d nové předpokládané známky
+ - Máte %1$d nových předpokládaných známek
+ - Máte %1$d nových předpokládaných známek
- Máte %1$d novou konečnou známku
- Máte %1$d nové konečné známky
- - Máte %1$d nové konečné známky
- - Máte %1$d nové konečné známky
+ - Máte %1$d nových konečných známek
+ - Máte %1$d nových konečných známek
Lekce
@@ -153,11 +154,11 @@
Žádné lekce tento den
%s min
%s sek
- dosud %1$s
+ ještě %1$s
za %1$s
- Lekce skončila
- Nyní: %s
- Okamžik: %s
+ Ukončila
+ Teď: %s
+ Za chvíli: %s
Později: %s
Dokončené lekce
@@ -172,14 +173,14 @@
Žádné informace o dalších lekcích
Shrnutí docházky
- Nepřítomen ze školních důvodů
+ Neprítomnosť zo školských dôvodov
Omluvená nepřítomnost
Neomluvená nepřítomnost
Osvobození
Omluvené zpoždění
Neomluvené zpoždění
Přítomnost
- Smazáno
+ Odstraněno
Neznámý
Číslo lekce
Žádné položky
@@ -195,7 +196,7 @@
Musíte vybrat alespoň jednu nepřítomnost!
Ospravedlnit
- Účast
+ Docházka
Společně
Tento týden žádné testy
@@ -205,7 +206,7 @@
Doručená pošta
Odesláno
Koš
- (žádné téma)
+ (žádný předmět)
Žádné zprávy
Při stahování obsahu zprávy došlo k chybě
Od:
@@ -213,13 +214,13 @@
Datum: %s
Odpověď
Poslat dále
- Vymazat
+ Odstranit
Přesunout do koše
- Trvale smazat
- Zpráva byla úspěšně smazána
- Podíl
+ Odstranit natrvalo
+ Zpráva byla úspěšně odstraněna
+ Sdílet
Vytisknout
- Téma
+ Předmět
Obsah
Zpráva úspěšně odeslána
Zpráva neexistuje
@@ -228,8 +229,8 @@
- %d zpráva
- %d zprávy
- - %d zprávy
- - %d zprávy
+ - %d zpráv
+ - %d zpráv
- Nová zpráva
@@ -240,8 +241,8 @@
- Máte %1$d novou zprávu
- Máte %1$d nové zprávy
- - Máte %1$d nové zprávy
- - Máte %1$d nové zprávy
+ - Máte %1$d nových zpráv
+ - Máte %1$d nových zpráv
Žádné informace o poznámkách
@@ -249,8 +250,8 @@
- %d poznámka
- %d poznámky
- - %d poznámky
- - %d poznámky
+ - %d poznámek
+ - %d poznámek
- Nová poznámka
@@ -261,15 +262,15 @@
- Máte %1$d novou poznámku
- Máte %1$d nové poznámky
- - Máte %1$d nové poznámky
- - Máte %1$d nové poznámky
+ - Máte %1$d nových poznámek
+ - Máte %1$d nových poznámek
- %d chvála
- %d chvály
- - %d chvály
- - %d chvály
+ - %d chvál
+ - %d chvál
- Nová chvála
@@ -280,8 +281,8 @@
- Máte %1$d novou chválu
- Máte %1$d nové chvály
- - Máte %1$d nové chvály
- - Máte %1$d nové chvály
+ - Máte %1$d nových chvál
+ - Máte %1$d nových chvál
@@ -297,10 +298,10 @@
- Nové neutrální poznámky
- - Máte %1$d novou neutrální pozornost
- - Máte %1$d nové neutrální pozornosti
- - Máte %1$d nové neutrální pozornosti
- - Máte %1$d nové neutrální pozornosti
+ - Máte %1$d novou neutrální poznámku
+ - Máte %1$d nové neutrální poznámky
+ - Máte %1$d nových neutrální poznámek
+ - Máte %1$d nových neutrální poznámek
Žádné informace o domácích úkolech
@@ -313,11 +314,15 @@
Žádné informace o šťastném čísle
Šťastné číslo pro dnešek
Dnes je šťastným číslem: %d
+ Zobrazit historii
+
+ Historie šťastných čísel
+ Žádné informace o šťastných číslech
Mobilní přístup
Žádná zařízení
Zrušit registraci
- Zařízení odstraněno
+ Zařízení odstranění
QR kód
Token
Symbol
@@ -333,17 +338,17 @@
Jméno ředitele
Jméno pedagoga
Zobrazit na mapě
- Volání
+ Volat
Učitelé
Žádné informace o učitelích
Žádný předmět
Setkání
- Žádné informace o setkání
+ Žádné informace o setkáních
Přidat účet
- Odhlásit se
+ Odhlásit
Chcete se odhlásit z aktivního žáka?
Odhlášení žáků
Žákův účet
@@ -360,19 +365,19 @@
Verze aplikace
Tvůrci
- Seznam vývojářů Wulkanowy
+ Seznam vývojářů Wulkanového
Nahlásit chybu
Odeslat zprávu o chybě e-mailem
FAQ
Přečtěte si často kladené otázky
Server Discord
- Připojte se ke komunitě Wulkanowy
+ Připojte se ke komunitě Wulkanového
Facebooková fanpage
Stejně jako naše facebooková fanpage
Zásady ochrany osobních údajů
Pravidla pro shromažďování osobních údajů
Domovská stránka
- Navštivte web a pomozte s vývojem aplikace
+ Navštivte stránku a pomozte s vývojem aplikace
Licence
Licence knihoven použitých v aplikaci
@@ -404,22 +409,23 @@
Přezdívka
Přidat přezdívku
+ Vybrat barvu avataru
- Sdílejte protokoly
+ Sdílet protokoly
Obnovit
Zkontrolovat aktualizace
Před hlášením chyby zkontrolujte, zda je k dispozici aktualizace s opravou chyb
Obsah
- Zkuste to znovu
+ Zkusit znovu
Popis
- Bez popisu
+ Žádný popis
Učitel
Datum
Datum vstupu
Barva
- Detaily
+ Podrobnosti
Kategorie
Zavřít
Žádná data
@@ -438,60 +444,74 @@
Tmavý
Motiv systému
- Vzhled
+ Vzhled a chování aplikací
Výchozí zobrazení
Výpočet koncoročního průměru
Vynutit průměrný výpočet podle aplikace
- Zobrazit přítomnost v účasti
- Motiv aplikace
+ Zobrazit přítomnost
+ Motiv
Rozbalit známky
- Označte aktuální lekci v plánu lekce
- Ukázat skupiny vedle předmětů v plánu lekce
- Ukázat seznam grafů ve třídních známkách
- Ukázat lekce pro celou třídu
- Ukázat předměty bez známek v \"Známky\"
+ Označit aktuální lekci
+ Zobrazit skupiny vedle předmětů
+ Zobrazit seznam grafů v známkách třídy
+ Zobrazit lekce pro celou třídu
+ Zobrazit předměty bez známek
Známky barevné schéma
- Předměty seřazené v \"Známky\"
- Jazyk aplikace
- Oznámení
- Ukázat notifikace
- Ukázat nadcházející oznámení o lekci
+ Třídění předmětů
+ Jazyk
+ Upozornění
+ Zobrazit upozornění
+ Zobrazit upozornění o nadcházející lekci
Opravte problémy se synchronizací a upozorněním
- Ve vašem zařízení mohou nastat problémy se synchronizací dat a oznámenímii.\n\nChcete-li je opravit, přidejte Wulkanowy do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu.
+ Vaše zařízení může mít problémy se synchronizací dat as upozorněními.\n\nChcete-li je opravit, přidejte Wulkanového do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu.
Přejít do nastavení
- Ukázat oznámení o ladění
+ Zobrazit upozornění o ladění
+ Synchronizace je vypnutá
Synchronizace
Automatická aktualizace
Pozastaveno na dovolené
Interval aktualizací
Pouze Wi-Fi
- Synchronizovat nyní
+ Synchronizovat teď
Synchronizováno!
Synchronizace selhala
Probíhá synchronizace
- Synchronizace
- Ruční synchronizace neobnoví zobrazení aplikace.
- \nChcete-li zobrazit synchronizovaná data, restartujte aplikaci po synchronizaci.
-
- Jiné
Hodnota plusu
Hodnota mínusu
Odpovědět s historií zpráv
+ Pokročilé
+ Vzhled a chování
+ Upozornění
+ Synchronizace
+ Známky
+ Docházka
+ Plán lekce
+ Známky
+ Zprávy
+ Vzhled a chování
+ Jazyky, motivy, třídění předmětů
+ Upozornění aplikace, oprava problémů
+ Upozornění
+ Synchronizace
+ Automatická aktualizace, interval aktualizací
+ Hodnota plusu a mínusu, výpočet průměru
+ Pokročilé
+ Verze aplikace, tvůrci, sociální portály, licence
- Nové položky v deník
+ Nové položky v deníku
Nové známky
Šťastné číslo
Nové zprávy
Nové poznámky
- Push oznámení
+ Push upozornění
Nadcházející lekce
- Debug
+ Ladění
Černý
Červený
Modrý
Zelený
- Nachový
+ Fialový
Žádná barva
Zkopírováno
@@ -509,7 +529,7 @@
Probíhá údržba UONET+ deník. Zkuste to později znovu
Neznámá chyba denika UONET+. Prosím zkuste to znovu později
Neznámá chyba aplikace. Prosím zkuste to znovu později
- Došlo k neočekávané chybě
- Funkce deaktivována vaší školou
+ Vyskytla se neočekávaná chyba
+ Funkce je deaktivována přes vaší školou
Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API
diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml
index 7226ff2d..f578ff11 100644
--- a/app/src/main/res/values-de/preferences_values.xml
+++ b/app/src/main/res/values-de/preferences_values.xml
@@ -13,6 +13,7 @@
- Українська
- Deutsch
- Čeština
+ - Slovenčina
- 15 Minuten
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 1acbdacb..a0994b42 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -46,6 +46,7 @@
Token ist nicht mehr gültig
Ungültige email
Den zugewiesenen Login anstelle von email verwenden
+ Benutze den zugewiesenen Login oder E-Mail in @%1$s
Ungültige symbol
Schüler nicht gefunden. Überprüfen Sie das Symbol und die gewählte Variation des UONET+ Registers
Dieses Datenfeld ist erforderlich
@@ -273,6 +274,10 @@
Keine Information über die Glücksnummer.
Glücksnummer für heute
Die heutige Glücksnummer ist: %d
+ Verlauf anzeigen
+
+ Glücksnummerverlauf
+ Keine Information über die Glücksnummer
Mobile Geräte
Keine Geräte
@@ -364,6 +369,7 @@
Nick
Nick hinzufügen
+ Avatar-Farbe wählen
Logs teilen
Aktualisieren
@@ -383,7 +389,7 @@
Kategorie
Schließ
Keine Daten
- Thema
+ Schulfach
Zurück
Nächste
Suchen
@@ -398,21 +404,21 @@
Dunkel
Systemthema
- Erscheinungsbild
+ Aussehen & Verhalten
Standard Ansicht
Berechnung des Jahresenddurchschnitts
Mittelwertberechnung durch App erzwingen
- Anwesenheit in Schulbesuch zeigen
- Thema der Anwendung
+ Anwesendheit zeigen
+ Thema
Noten erweitern
- Aktuelle Lektion im Stundenplan markieren
- Zeige Gruppen neben Themen im Zeitplan
+ Aktuelle Lektion markieren
+ Gruppen neben Schulfächen anzeigen
Liste der Diagramme in Klassenbewertungen anzeigen
Unterricht der ganzen Klasse anzeigen
- Zeigen Sie Themen ohne Noten in Noten
+ Schulfächer ohne Noten anzeigen
Farbschema der Noten
- Themen sortieren in \"Noten\"
- App Sprache
+ Schulfachen sortieren
+ Sprache
Benachrichtigungen
Benachrichtigungen anzeigen
Benachrichtigungen über bevorstehende Lektionen anzeigen
@@ -420,6 +426,7 @@
Ihr Gerät hat möglicherweise Probleme mit der Datensynchronisierung und Benachrichtigungen.\n\nUm diese zu reparieren, fügen Sie Wulkanowy zum Autostart hinzu und deaktivieren Sie die Batterieoptimierung in den Systemeinstellungen des Geräts.
Gehe zu den Einstellungen
Debug-Benachrichtigungen anzeigen
+ Synchronisierung ist deaktiviert
Synchronisierung
Automatische Aktualisierung
An Feiertagen suspendiert
@@ -429,14 +436,27 @@
Synchronisiert!
Synchronisierung fehlgeschlagen
Synchronisierung läuft
- Synchronisation
- Die manuelle Synchronisierung aktualisiert die App-Ansichten nicht.
- \nUm die synchronisierten Daten anzuzeigen, starten Sie die App nach der Synchronisierung neu.
-
- Andere
Wert des Plus
Wert des Minus
Antwort mit Nachrichtenhistorie
+ Erweitert
+ Aussehen & Verhalten
+ Benachrichtigungen
+ Synchronisierung
+ Noten
+ Schulbesuch
+ Zeitplan
+ Noten
+ Nachrichten
+ Aussehen & Verhalten
+ Sprachen, Themen, Schulfachen sortieren
+ App-Benachrichtigungen, Probleme beheben
+ Benachrichtigungen
+ Synchronisierung
+ Automatisches Update, Synchronisierungsintervall
+ Plus und Minus Werte, Durchschnittsberechnung
+ Erweitert
+ App-Version, Mitarbeiter, soziale Portale, Lizenzen
Neue Einträge im Klassenbuch
Neue Noten
diff --git a/app/src/main/res/values-lt-v29/preferences_values.xml b/app/src/main/res/values-lt-v29/preferences_values.xml
new file mode 100644
index 00000000..18cbd4cf
--- /dev/null
+++ b/app/src/main/res/values-lt-v29/preferences_values.xml
@@ -0,0 +1,9 @@
+
+
+
+ - Sistemos tema
+ - Šviesi
+ - Tamsi
+ - Juoda (AMOLED)
+
+
diff --git a/app/src/main/res/values-lt/preferences_values.xml b/app/src/main/res/values-lt/preferences_values.xml
new file mode 100644
index 00000000..fcb637d2
--- /dev/null
+++ b/app/src/main/res/values-lt/preferences_values.xml
@@ -0,0 +1,53 @@
+
+
+
+ - Šviesi
+ - Tamsi
+ - Juoda (AMOLED)
+
+
+ - Sistemos kalba
+ - Polski
+ - English
+ - Pусский
+ - Українська
+ - Deutsch
+ - Čeština
+ - Slovenčina
+
+
+ - 15 minučių
+ - 30 minučių
+ - 1 valandą
+ - 2 valandas
+ - 6 valandas
+ - 12 valandas
+ - 24 valandas
+
+
+ - 0,00
+ - 0,25
+ - 0,33
+ - 0,5
+ - 0,75
+
+
+ - Pagal abėcėlę
+ - Pagal datą
+
+
+ - Dzienniczek+
+ - Wulkanowy
+ - Laipsnio spalvos registre
+
+
+ - Antrojo semestro laipsnių vidurkis
+ - Abiejų semestrų laipsnių vidurkis
+ - Visų metų laipsnių vidurkis
+
+
+ - Nerodyti
+ - Rodyti viską
+ - Rodyti mažesnį
+
+
diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-lt/strings.xml
similarity index 69%
rename from app/src/main/res/values-sk-rSK/strings.xml
rename to app/src/main/res/values-lt/strings.xml
index 1e85cfed..6542a5bc 100644
--- a/app/src/main/res/values-sk-rSK/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -1,161 +1,162 @@
- Prihlásenie
+ Login
Wulkanowy
- Známky
- Dochádzka
- Skúšky
- Plán lekcie
- Nastavenia
- Viac
- O aplikácii
- Prehliadač protokolov
- Tvorcovia
- Licencie
- Správy
- Nová správa
- Poznámky a úspechy
- Domáce úlohy
- Accounts manager
- Select account
- Account details
- Student info
+ Laipsnis
+ Attendance
+ Exams
+ Timetable
+ Nustatymai
+ Daugiau
+ Apie
+ Peržiūrėti žurnalą
+ Prisidėję
+ Licencijos
+ Žinutės
+ Nauja žinutė
+ Pastabos ir pasiekimai
+ Namų darbai
+ Paskyros valdymas
+ Pasirinkite paskyrą
+ Paskyros informacija
+ Studentų informacija
- Semester %1$d, %2$d/%3$d
+ Semestras %1$d, %2$d/%3$d
- Prihláste sa pomocou študentského alebo rodičovského konta
- Zadajte symbol zo stránky denníka
- Užívateľské meno
- Email
- Prihlásenie, číslo PESEL alebo e-mail
- Heslo
- Variácie denníka UONET+
+ Sign in with the student or parent account
+ Enter the symbol from the register page
+ Vartotojo vardas
+ El. paštas
+ Prisijunkite, PESEL arba el. paštas
+ Slaptažodis
+ UONET+ register variant
Mobile API
Scraper
- Hybridné
+ Hibridas
Token
PIN
- Kľúč API
+ API raktas
Symbol
- Prihlásiť
- Toto heslo je príliš krátke
- Prihlasovacie údaje sú nesprávne. Uistite sa, že je v poli nižšie vybraná správna variácie denníka UONET+
- Neplatný PIN
- Neplatný token
- Platnosť tokenu vypršala
- Neplatný e-mail
- Namiesto e-mailu použite priradené prihlasovacie údaje
- Neplatný symbol
- Žiak nebol nájdený. Skontrolujte správnosť symbolu a vybrané varianty denníka UONET+
- Toto pole je povinné
- Vybraný žiak už je prihlásený
+ Prisijungti
+ Slaptažodis per trumpas
+ Login details are incorrect. Make sure the correct UONET+ register variation is selected in the field below
+ Netinkamas PIN kodas
+ Invalid token
+ Token expired
+ Neteisingas el. paštas
+ Use the assigned login instead of email
+ Use the assigned login or email in @%1$s
+ Netinkamas simbolis
+ Student not found. Validate the symbol and the chosen variation of the UONET+ register
+ This field is required
+ Selected student is already logged in
The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nMake sure that you have set the appropriate register variant in the UONET+ register variant field on the previous screen. Wulkanowy does not detect pre-school students at the moment
- Vyberte žiakov, ktorí sa majú do aplikácie prihlásiť
- Iné možnosti
+ Select students to log in to the application
+ Papildomi nustatymai
In this mode, a lucky number does not work, a class grade stats, summary of attendance, excuse for absence, completed lessons, school information and preview of the list of registered devices
This mode displays the same data as it appears on the register website
The combination of the best features of the other two modes. It works faster than scraper and provides features not available in the Mobile API mode. It is in the experimental phase
- Zásady ochrany osobných údajov
- Problémy s prihlásením? Napíšte nám!
- Email
+ Privatumo politika
+ Trouble signing in? Contact us!
+ El. paštas
Discord
- Poslať e-mail
- Opíšte podrobnosti problému:
- Uistite sa, že ste vybrali správny variant denníka UONET+!
- Zabudol som heslo
- Obnovte svoj účet
- Obnoviť
- Študent je už prihlásený
+ Send email
+ Describe details of problem:
+ Make sure you select the correct UONET+ register variation!
+ I forgot my password
+ Recover your account
+ Atkurti
+ Student is already signed in
- Manažér účtov
- Prihlásiť sa
- Platnosť relácie vypršala
- Vaša relácia vypršala, prihláste sa prosím znovu
+ Paskyros valdymas
+ Prisijungti
+ Sesijos laikas baigėsi
+ Sesijos laikas baigėsi, prašome prisijungti iš naujo
- Známka
- Semester %d
- Zmeniť semester
- Žiadne známky
- Váha
- Váha: %s
- Komentár
- Žiadne nové známky
+ Laipsnis
+ Semestras %d
+ Keisti semestrą
+ Jokių laipsnių
+ Svoris
+ Svoris: %s
+ Pastabos
+ Jokių naujų laipsnių
Number of new ratings: %1$d
- Priemer: %1$.2f
- Body: %s
- Bez priemeru
- Predpovedané: %1$s
- Konečná: %1$s
- Súčet bodov
- Konečná známka
- Predpokladaná známka
- Vypočítaný priemer
- Konečný priemer
- Zhrnutie
- Trieda
- Označiť ako prečítané
- Čiastočné
+ Vidurkis: %1$.2f
+ Puantai: %s
+ Jokiu vidurkis
+ Numatomas: %1$s
+ Galutinis: %1$s
+ Bendras punktų skaičius
+ Galutinis laipsnis
+ Numatomas laipsnis
+ Calculated average
+ Final average
+ Summary
+ Class
+ Mark as read
+ Partial
Semester
- Body
- Legenda
+ Points
+ Legend
Average: %1$s
- Trieda
- Žiák
+ Class
+ Student
- - %d známka
- - %d známky
- - %d známok
- - %d známok
+ - %d grade
+ - %d grades
+ - %d grades
+ - %d grades
- - Nová známka
- - Nové známky
- - Nové známky
- - Nové známky
+ - New grade
+ - New grades
+ - New grades
+ - New grades
- - Nová predpokladaná známka
- - Nové predpokladané známky
- - Nové predpokladané známky
- - Nové predpokladané známky
+ - New predicted grade
+ - New predicted grades
+ - New predicted grades
+ - New predicted grades
- - Nová konečná známka
- - Nové konečné známky
- - Nové konečné známky
- - Nové konečné známky
+ - New final grade
+ - New final grades
+ - New final grades
+ - New final grades
- - Máte %1$d novú známku
- - Máte %1$d nové známky
- - Máte %1$d nových známok
- - Máte %1$d nových známok
+ - You received %1$d grade
+ - You received %1$d grades
+ - You received %1$d grades
+ - You received %1$d grades
- - Máte %1$d novú predpokladanú známku
- - Máte %1$d nové predpokladané známky
- - Máte %1$d nových predpokladaných známok
- - Máte %1$d nových predpokladaných známok
+ - You received %1$d predicted grade
+ - You received %1$d predicted grades
+ - You received %1$d predicted grades
+ - You received %1$d predicted grades
- - Máte %1$d novú konečnú známku
+ - You received %1$d final grade
- You received %1$d final grades
- You received %1$d final grades
- You received %1$d final grades
- Lekcia
- Učebňa
- Skupina
- Hodiny
- Zmeny
- Žiadne lekcie tento deň
+ Lesson
+ Room
+ Group
+ Hours
+ Changes
+ No lessons this day
%s min
%s sec
%1$s left
in %1$s
- Lekcia skončila
+ Finished
Now: %s
Next: %s
Later: %s
@@ -163,11 +164,11 @@
Completed lessons
Show completed lessons
No info about completed lessons
- Téma
- Neprítomnosť
- Zdroje
+ Topic
+ Absence
+ Resources
- Ďalší lekcie
+ Additional lessons
Show additional lessons
No info about additional lessons
@@ -178,11 +179,11 @@
Exemption
Excused lateness
Unexcused lateness
- Prítomnosť
+ Present
Deleted
- Neznámy
+ Unknown
Number of lesson
- Žiadne položky
+ No entries
- %1$d absence
- %1$d absences
@@ -190,7 +191,7 @@
- %1$d absences
Absence reason (optional)
- Poslať
+ Send
Absence excused successfully!
You must select at least one absence!
Excuse
@@ -313,6 +314,10 @@
No info about the lucky number
Lucky number for today
Today\'s lucky number is: %d
+ Show history
+
+ Lucky number history
+ No info about lucky numbers
Mobile devices
No devices
@@ -404,6 +409,7 @@
Nick
Add nick
+ Choose avatar color
Share logs
Refresh
@@ -438,21 +444,21 @@
Dark
System Theme
- Appearance
+ App appearance & behavior
Default view
Calculation of the end-of-year average
Force average calculation by app
- Show presence in attendance
- Application theme
+ Show presence
+ Theme
Expand grades
- Mark current lesson in timetable
- Show groups next to subjects in timetable
+ Mark current lesson
+ Show groups next to subjects
Show chart list in class grades
Show whole class lessons
- Show subjects without grades in Grades
+ Show subjects without grades
Grades color scheme
- Subjects sorting in \"Grades\"
- App language
+ Subjects sorting
+ Language
Notifications
Show notifications
Show upcoming lesson notifications
@@ -460,6 +466,7 @@
Your device may have data synchronization issues and with notifications.\n\nTo fix them, you need to add Wulkanowy to the autostart and turn off battery optimization/saving in the phone settings.
Go to settings
Show debug notifications
+ Synchronization is disabled
Synchronization
Automatic update
Suspended on holidays
@@ -469,14 +476,27 @@
Synced!
Sync failed
Sync in progress
- Synchronization
- Manual sync doesn\'t refresh app views.
- \nTo see the synced data relaunch the app after syncing.
-
- Other
Value of the plus
Value of the minus
Reply with message history
+ Advanced
+ Appearance & Behavior
+ Notifications
+ Synchronization
+ Grades
+ Attendance
+ Timetable
+ Grades
+ Messages
+ Appearance & Behavior
+ Languages, themes, subjects sorting
+ App notifications, fix problems
+ Notifications
+ Synchronization
+ Automatic update, synchronization interval
+ Plus and minus values, average calculation
+ Advanced
+ App version, contributors, social portals, licenses
New entries in register
New grades
diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml
index bf5cd769..b218fbc8 100644
--- a/app/src/main/res/values-night/styles.xml
+++ b/app/src/main/res/values-night/styles.xml
@@ -10,14 +10,43 @@
- @color/colorSwipeRefreshDark
- ?colorSurface
- ?android:textColorPrimary
- - @android:color/black
+
-
+ @color/colorNavigationBarLight
+
+ -
+ @color/colorStatusBarLight
- - @android:color/black
- false
- true
+ - false
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-pl/preferences_values.xml b/app/src/main/res/values-pl/preferences_values.xml
index 0634f657..b25dc3e5 100644
--- a/app/src/main/res/values-pl/preferences_values.xml
+++ b/app/src/main/res/values-pl/preferences_values.xml
@@ -13,6 +13,7 @@
- Українська
- Deutsch
- Čeština
+ - Slovenčina
- 15 minut
@@ -24,7 +25,7 @@
- 24 godziny
- - 0,0
+ - 0,00
- 0,25
- 0,33
- 0,5
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 52134613..3f69d2a8 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -46,6 +46,7 @@
Token stracił ważność
Niepoprawny adres email
Użyj przydzielonego loginu zamiast emaila
+ Użyj przypisanego loginu lub adresu e-mail w @%1$s
Niepoprawny symbol
Nie znaleziono ucznia. Sprawdź poprawność symbolu i wybranej odmiany dziennika UONET+
To pole jest wymagane
@@ -313,6 +314,10 @@
Brak informacji o szczęśliwym numerku
Szczęśliwy numerek na dzisiaj
Dziś szczęśliwym numerkiem jest: %d
+ Pokaż historię
+
+ Historia numerków
+ Brak informacji o szczęśliwych numerach
Dostęp mobilny
Brak urządzeń
@@ -404,6 +409,7 @@
Pseudonim
Dodaj pseudonim
+ Wybierz kolor awatara
Udostępnij logi
Odśwież
@@ -438,21 +444,21 @@
Ciemny
Motyw systemu
- Wygląd
+ Wygląd i zachowanie aplikacji
Domyślny widok
Obliczanie średniej końcoworocznej
Wymuś obliczanie średniej przez aplikację
- Pokazuj obecność we frekwencji
- Motyw aplikacji
+ Pokazuj obecność
+ Motyw
Rozwiń oceny
- Oznaczaj bieżącą lekcję na planie
- Pokazuj grupę obok przedmiotu na planie
+ Oznaczaj bieżącą lekcję
+ Pokazuj grupę obok przedmiotu
Pokazuj listę wykresów w ocenach klasy
Pokazuj lekcje całej klasy
- Pokazuj przedmioty bez ocen w Ocenach
+ Pokazuj przedmioty bez ocen
Schemat kolorów ocen
- Sortowanie przedmiotów w Ocenach
- Język aplikacji
+ Sortowanie przedmiotów
+ Język
Powiadomienia
Pokazuj powiadomienia
Pokazuj powiadomienia o nadchodzących lekcjach
@@ -460,6 +466,7 @@
Na twoim urządzeniu mogą występować problemy z synchronizacją danych i powiadomieniami.\n\nBy je naprawić, dodaj Wulkanowego do autostartu i wyłącz optymalizację/oszczędzanie baterii w ustawieniach systemowych telefonu.
Przejdź do ustawień
Pokazuj powiadomienia debugowania
+ Synchronizacja jest wyłączona
Synchronizacja
Automatyczna aktualizacja
Zawieszona na wakacjach
@@ -469,14 +476,27 @@
Zsynchronizowano!
Synchronizacja nie powiodła się
Synchronizacja w trakcie
- Synchronizacja
- Ręczna synchronizacja nie odświeża widoków w aplikacji.
- \nAby zobaczyć zsynchronizowane informacje, uruchom ponownie aplikację po zsynchronizowaniu.
-
- Inne
Wartość plusa
Wartość minusa
Odpowiadaj z historią wiadomości
+ Zaawansowane
+ Wygląd i zachowanie
+ Powiadomienia
+ Synchronizacja
+ Oceny
+ Frekwencja
+ Plan lekcji
+ Oceny
+ Wiadomości
+ Wygląd i zachowanie
+ Języki, motywy, sortowanie przedmiotów
+ Powiadomienia aplikacji, naprawianie problemów
+ Powiadomienia
+ Synchronizacja
+ Automatyczna aktualizacja, interwał synchronizacji
+ Wartości plusa i minusa, obliczanie średniej
+ Zaawansowane
+ Wersja aplikacji, twórcy, media społecznościowe, licencje
Nowe wpisy w dzienniku
Nowe oceny
diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml
index 304d678f..64b777fb 100644
--- a/app/src/main/res/values-ru/preferences_values.xml
+++ b/app/src/main/res/values-ru/preferences_values.xml
@@ -13,6 +13,7 @@
- Українська
- Deutsch
- Čeština
+ - Slovenčina
- 15 минут
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 0de20ea9..02407bbd 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -46,6 +46,7 @@
Token просрочен
Неверный адрес электронной почты
Используйте назначенный логин вместо электронной почты
+ Использовать назначенный логин или email в @%1$s
Неправильный символ
Студент не найден. Подтвердите символ и выбранный вариант регистра UONET+
Это обязательное поле
@@ -313,6 +314,10 @@
Нет данных о счастливом номере
Сегодняшний счастливый номер
Сегодняшний счастливый номер это: %d
+ Показать историю
+
+ История удачных чисел
+ Нет информации о номерах удачи
Мобильные устройства
Нет устройств
@@ -404,6 +409,7 @@
Ник
Добавить ник
+ Выберите цвет аватара
Поделиться логами
Обновить
@@ -438,21 +444,21 @@
Тёмная
Тема системы
- Вид
+ Внешний вид приложения & поведение
Окно по умолчанию
Способ определения средней годовой оценки
Принудительно высчитать среднюю оценку через приложение
- Показывать присутствия в посещаемости
- Тема приложения
+ Показать присутствие
+ Тема
Разворачивать оценки
- Отмечать текущий урок в расписании
- Показать группу возле предмета в расписании
+ Отметить текущий урок
+ Показать группы рядом с темами
Показывать диаграммы в оценках класса
Показать уроки всего класса
- Показывать предметы без оценок в \"Оценках\"
+ Показать предметы без оценок
Схема цветов оценок
- Сортировка предметов в \"Оценках\"
- Язык приложения
+ Сортировка уроков
+ Язык
Уведомления
Показывать уведомления
Показывать уведомления о будущих уроках
@@ -460,6 +466,7 @@
На вашем устройстве могут быть проблемы с синхронизацией данных и уведомлениями.\n\nЧтобы их исправить, вам необходимо добавить Wulkanowy в авто-старт и выключить оптимизацию/экономию батареи в настройках устройства.
Перейти в настройски
Показывать дебаг-уведомления
+ Синхронизация отключена
Синхронизация
Автоматическая синхронизация
Приостановить синхронизации во время каникул
@@ -469,14 +476,27 @@
Синхронизировано!
Синхронизация не удалась
Идёт синхронизация
- Синхронизация
- Ручная синхронизация не обновляет данные в приложении.
- \nЧтобы увидеть обновлённые данные, перезапустите приложение.
-
- Другие
Стоимость плюса
Стоимость минуса
Отвечать с историей сообщений
+ Расширенные
+ Внешний вид & Поведение
+ Уведомления
+ Синхронизация
+ Оценки
+ Посещаемость
+ Расписание
+ Оценки
+ Сообщения
+ Внешний вид & Поведение
+ Языки, темы, темы сортировки темы
+ Уведомления приложений, проблемы с устранением
+ Уведомления
+ Синхронизация
+ Автоматическое обновление, интервал синхронизации
+ Значения плюс и минус, средний расчет
+ Расширенные
+ Версия приложения, участники, социальные порталы, лицензии
Новые данные в дневнике
Новые оценки
diff --git a/app/src/main/res/values-sk-v29/preferences_values.xml b/app/src/main/res/values-sk-v29/preferences_values.xml
new file mode 100644
index 00000000..8c5306bb
--- /dev/null
+++ b/app/src/main/res/values-sk-v29/preferences_values.xml
@@ -0,0 +1,9 @@
+
+
+
+ - Motív systému
+ - Svetlý
+ - Tmavý
+ - Čierny (AMOLED)
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sk-rSK/preferences_values.xml b/app/src/main/res/values-sk/preferences_values.xml
similarity index 92%
rename from app/src/main/res/values-sk-rSK/preferences_values.xml
rename to app/src/main/res/values-sk/preferences_values.xml
index d57e603b..b0ba9839 100644
--- a/app/src/main/res/values-sk-rSK/preferences_values.xml
+++ b/app/src/main/res/values-sk/preferences_values.xml
@@ -13,6 +13,7 @@
- Українська
- Deutsch
- Čeština
+ - Slovenčina
- 15 minút
@@ -45,8 +46,8 @@
- Priemer známok z celého roka
- - Neukaz
- - Zobrazit vše
- - Zobrazit menší
+ - Nezobrazovať
+ - Zobraziť všetko
+ - Zobraziť menšie
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
new file mode 100644
index 00000000..32bd8067
--- /dev/null
+++ b/app/src/main/res/values-sk/strings.xml
@@ -0,0 +1,535 @@
+
+
+
+ Prihlásenie
+ Wulkanowy
+ Známky
+ Dochádzka
+ Skúšky
+ Plán lekcie
+ Nastavenia
+ Viac
+ O aplikácii
+ Prehliadač protokolov
+ Prispievatelia
+ Licencie
+ Správy
+ Nová správa
+ Poznámky a úspechy
+ Domáce úlohy
+ Manažér účtov
+ Vyberte účet
+ Podrobnosti účtu
+ Informácie o žiakovi
+
+ Semester %1$d, %2$d/%3$d
+
+ Prihláste sa pomocou študentského alebo rodičovského konta
+ Zadajte symbol zo stránky denníka
+ Užívateľské meno
+ Email
+ Prihlásenie, číslo PESEL alebo e-mail
+ Heslo
+ Variácie denníka UONET+
+ Mobile API
+ Scraper
+ Hybridné
+ Token
+ PIN
+ Kľúč API
+ Symbol
+ Prihlásiť
+ Toto heslo je príliš krátke
+ Prihlasovacie údaje sú nesprávne. Uistite sa, že je v poli nižšie vybraná správna variácie denníka UONET+
+ Neplatný PIN
+ Neplatný token
+ Platnosť tokenu vypršala
+ Neplatný e-mail
+ Namiesto e-mailu použite priradené prihlasovacie údaje
+ Použite priradené prihlasovacie alebo e-mail v @%1$s
+ Neplatný symbol
+ Žiak nebol nájdený. Skontrolujte správnosť symbolu a vybrané varianty denníka UONET+
+ Toto pole je povinné
+ Vybraný žiak už je prihlásený
+ Symbol nájdete na stránke denníka v Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUistite sa, že ste na predchádzajúcu obrazovke nastaviť správny variant denníka do poľa Variácie denníka UONET+. Wulkanowy v túto chvíľu nezistí predškolské żaków
+ Vyberte žiakov, ktorí sa majú do aplikácie prihlásiť
+ Iné možnosti
+ V tomto režime nefungujú nasledovné: šťastné číslo, štatistiky triedy, zhrnutie dochádzky, ospravedlnenie neprítomnosti, dokončené lekcie, informácie o škole a prezeranie zoznamu registrovaných zariadení
+ Tento režim zobrazuje rovnaké dáta, ktoré sa zobrazujú na webových stránkach denníka
+ Kombinácia najlepších vlastností ostatných dvoch režimov. Funguje rýchlejšie ako scraper a poskytuje funkcie, ktoré nie sú k dispozícii v režime Mobilne API. Je to v experimentálnej fáze
+ Zásady ochrany osobných údajov
+ Problémy s prihlásením? Napíšte nám!
+ Email
+ Discord
+ Poslať e-mail
+ Opíšte podrobnosti problému:
+ Uistite sa, že ste vybrali správny variant denníka UONET+!
+ Zabudol som heslo
+ Obnovte svoj účet
+ Obnoviť
+ Žiak je už prihlásený
+
+ Manažér účtov
+ Prihlásiť sa
+ Relácia vypršala
+ Relácia vypršala. Prihláste sa prosím znovu
+
+ Známka
+ Semester %d
+ Zmeniť semester
+ Žiadne známky
+ Váha
+ Váha: %s
+ Komentár
+ Žiadne nové známky
+ Počet nových známok %1$d
+ Priemer: %1$.2f
+ Body: %s
+ Bez priemeru
+ Predpokladaná: %1$s
+ Konečná: %1$s
+ Súčet bodov
+ Konečná známka
+ Predpokladaná známka
+ Vypočítaný priemer
+ Konečný priemer
+ Zhrnutie
+ Trieda
+ Označiť ako prečítané
+ Čiastočné
+ Semester
+ Body
+ Vysvetlivky
+ Priemer: %1$s
+ Trieda
+ Žiák
+
+ - %d známka
+ - %d známky
+ - %d známok
+ - %d známok
+
+
+ - Nová známka
+ - Nové známky
+ - Nové známky
+ - Nové známky
+
+
+ - Nová predpokladaná známka
+ - Nové predpokladané známky
+ - Nové predpokladané známky
+ - Nové predpokladané známky
+
+
+ - Nová konečná známka
+ - Nové konečné známky
+ - Nové konečné známky
+ - Nové konečné známky
+
+
+ - Máte %1$d novú známku
+ - Máte %1$d nové známky
+ - Máte %1$d nových známok
+ - Máte %1$d nových známok
+
+
+ - Máte %1$d novú predpokladanú známku
+ - Máte %1$d nové predpokladané známky
+ - Máte %1$d nových predpokladaných známok
+ - Máte %1$d nových predpokladaných známok
+
+
+ - Máte %1$d novú konečnú známku
+ - Máte %1$d nové konečnej známky
+ - Máte %1$d nových konečných známok
+ - Máte %1$d nových konečných známok
+
+
+ Lekcia
+ Učebňa
+ Skupina
+ Hodiny
+ Zmeny
+ Žiadne lekcie tento deň
+ %s min
+ %s sek
+ ešte %1$s
+ za %1$s
+ Ukončila
+ Teraz: %s
+ Za chvíľu: %s
+ Neskôr: %s
+
+ Dokončené lekcie
+ Zobraziť dokončené lekcie
+ Žiadne informácie o dokončených lekciách
+ Téma
+ Neprítomnosť
+ Zdroje
+
+ Ďalší lekcie
+ Zobraziť ďalšie lekcie
+ Žiadne informácie o ďalších lekciách
+
+ Zhrnutie dochádzky
+ Neprítomnosť zo školských dôvodov
+ Ospravedlnená neprítomnosť
+ Neospravedlnená neprítomnosť
+ Oslobodenie
+ Ospravedlnenie meškanie
+ Neospravedlnenie meškanie
+ Prítomnosť
+ Odstránené
+ Neznámy
+ Číslo lekcie
+ Žiadne položky
+
+ - %1$d neprítomnosť
+ - %1$d neprítomnosti
+ - %1$d neprítomnosti
+ - %1$d neprítomnosti
+
+ Dôvod neprítomnosti (voliteľný)
+ Poslať
+ Neprítomnosť úspešne ospravedlnená!
+ Musíte vybrať aspoň jednu neprítomnosť!
+ Ospravedlniť
+
+ Dochádzka
+ Spoločne
+
+ Tento týždeň žiadne testy
+ Typ
+ Dátum vstupu
+
+ Doručená pošta
+ Odoslané
+ Kôš
+ (žiadny predmet)
+ Žiadne správy
+ Pri sťahovaní obsahu správy došlo k chybe
+ Od:
+ Komu:
+ Dátum: %s
+ Odpoveď
+ Poslať ďalej
+ Odstrániť
+ Presunúť do koša
+ Odstrániť natrvalo
+ Správa bola úspešne odstránená
+ Zdieľať
+ Vytlačiť
+ Predmet
+ Obsah
+ Správa úspešne odoslaná
+ Správa neexistuje
+ Musíte vybrať aspoň 1 príjemca
+ Obsah správy musí mať aspoň 3 znaky
+
+ - %d správa
+ - %d správy
+ - %d správ
+ - %d správ
+
+
+ - Nová správa
+ - Nové správy
+ - Nové správy
+ - Nové správy
+
+
+ - Máte %1$d novú správu
+ - Máte %1$d nové správy
+ - Máte %1$d nových správ
+ - Máte %1$d nových správ
+
+
+ Žiadne informácie o poznámkach
+ Body
+
+ - %d poznámka
+ - %d poznámky
+ - %d poznámok
+ - %d poznámok
+
+
+ - Nová poznámka
+ - Nové poznámky
+ - Nové poznámky
+ - Nové poznámky
+
+
+ - Máte %1$d novú poznámku
+ - Máte %1$d nové poznámky
+ - Máte %1$d nových poznámok
+ - Máte %1$d nových poznámok
+
+
+
+ - %d chvála
+ - %d chvály
+ - %d chvál
+ - %d chvál
+
+
+ - Nová chvála
+ - Nové chvály
+ - Nové chvály
+ - Nové chvály
+
+
+ - Máte %1$d novú chválu
+ - Máte %1$d nové chvály
+ - Máte %1$d nových chvál
+ - Máte %1$d nových chvál
+
+
+
+ - %d neutrálny poznámka
+ - %d neutrálne poznámky
+ - %d neutrálne poznámky
+ - %d neutrálne poznámky
+
+
+ - Nová neutrálny poznámka
+ - Nové neutrálne poznámky
+ - Nové neutrálne poznámky
+ - Nové neutrálne poznámky
+
+
+ - Máte %1$d novú neutrálny poznámku
+ - Máte %1$d nové neutrálne poznámky
+ - Máte %1$d nových neutrálne poznámok
+ - Máte %1$d nových neutrálne poznámok
+
+
+ Žiadne informácie o domácich úlohách
+ Označiť ako hotové
+ Nevyrobené
+ Prílohy
+
+ Šťastné číslo
+ Dnešné šťastné číslo je
+ Žiadne informácie o šťastnom čísle
+ Šťastné číslo pre dnešok
+ Dnes je šťastným číslom: %d
+ Zobraziť históriu
+
+ História šťastných čísel
+ Žiadne informácie o šťastných číslach
+
+ Mobilný prístup
+ Žiadne zariadenia
+ Zrušiť registráciu
+ Zariadenie odstránenie
+ QR kód
+ Token
+ Symbol
+ PIN
+
+ Škola a učitelia
+
+ Škola
+ Žiadne informácie o škole
+ Názov školy
+ Adresa školy
+ Telefón
+ Meno riaditeľa
+ Meno pedagóga
+ Zobraziť na mape
+ Volať
+
+ Učitelia
+ Žiadne informácie o učiteľoch
+ Žiadny predmet
+
+ Stretnutie
+ Žiadne informácie o stretnutiach
+
+ Pridať účet
+ Odhlásiť
+ Chcete sa odhlásiť z aktívneho žiaka?
+ Odhlásenie žiakov
+ Zakov účet
+ Rodičovský účet
+ Režim Mobilného API
+ Hybridný režim
+ Upraviť dáta
+ Manažér účtov
+ Vyberte žiaka
+ Rodina
+ Kontakt
+ Údaje o adresách
+ Osobné údaje
+
+ Verzia aplikácie
+ Prispievatelia
+ Zoznam vývojárov Wulkanového
+ Nahlásiť chybu
+ Odoslať správu o chybe e-mailom
+ FAQ
+ Prečítajte si často kladené otázky
+ Server Discord
+ Pripojte sa ku komunite Wulkanového
+ Facebooková fanpage
+ Rovnako ako naše facebooková fanpage
+ Zásady ochrany osobných údajov
+ Pravidlá pre zhromažďovanie osobných údajov
+ Domovská stránka
+ Navštívte stránku a pomôžte s vývojom aplikácie
+ Licencie
+ Licencia knižníc použitých v aplikácii
+
+ Licencia
+
+ Avatar
+ Zobraziť viac na GitHub
+
+ Žiadne informácie o žiakov alebo rodine žiaka
+ Meno
+ Druhé meno
+ Pohlavie
+ Poľské občianstvo
+ Rodinné meno
+ Mená matky a otca
+ Telefón
+ Mobilný telefón
+ E-mail
+ Adresa bydliska
+ Registrovaná adresa
+ Korešpondenčná adresa
+ Priezvisko a meno
+ Stupeň príbuznosti
+ Adresa
+ Telefóny
+ Muž
+ Žena
+ Priezvisko
+
+ Prezývka
+ Pridať prezývku
+ Vybrať farbu avataru
+
+ Zdieľať protokoly
+ Obnoviť
+
+ Skontrolovať aktualizácie
+ Pred hlásením chyby skontrolujte, či je k dispozícii aktualizácia s opravou chýb
+
+ Obsah
+ Skúsiť znova
+ Popis
+ Žiadny popis
+ Učiteľ
+ Dátum
+ Dátum vstupu
+ Farba
+ Podrobnosti
+ Kategória
+ Zavrieť
+ Žiadne údaje
+ Predmet
+ Vráť
+ Ďalej
+ Hľadať
+ Hledať…
+ Áno
+ Nie
+ Uložiť
+
+ Žiadne lekcie
+ Vybrať motív
+ Svetlý
+ Tmavý
+ Motív systému
+
+ Vzhľad a správanie aplikácií
+ Predvolené zobrazenie
+ Výpočet koncoročního priemeru
+ Vynútiť priemerný výpočet podľa aplikácie
+ Zobraziť prítomnosť
+ Motív
+ Rozbaliť známky
+ Označiť aktuálne lekciu
+ Zobraziť skupiny vedľa predmetov
+ Zobraziť zoznam grafov v známkach triedy
+ Zobraziť lekcie pre celú triedu
+ Zobraziť predmety bez známok
+ Známky farebnú schému
+ Triedenie predmetov
+ Jazyk
+ Upozornenia
+ Zobraziť upozornenia
+ Zobraziť upozornenia o nadchádzajúcej lekciu
+ Opravte problémy so synchronizáciou a upozornením
+ Vaše zariadenie môže mať problémy so synchronizáciou dát as upozorneniami.\n\nAk ich chcete opraviť, pridajte Wulkanového do funkcie Autostart a vypnite optimalizáciu/úsporu batérie v nastavení systému telefóne.
+ Prejsť do nastavení
+ Zobraziť upozornenia o ladení
+ Synchronizácia je vypnutá
+ Synchronizácia
+ Automatická aktualizácia
+ Pozastavený počas dovolenky
+ Interval aktualizácií
+ Iba Wi-Fi
+ Synchronizovať teraz
+ Synchronizovano!
+ Synchronizácia zlyhala
+ Prebieha synchronizácia
+ Hodnota plusu
+ Hodnota mínusu
+ Odpovedať s históriou správ
+ Pokročilé
+ Vzhľad a správanie
+ Upozornenia
+ Synchronizácia
+ Známky
+ Dochádzka
+ Plán lekcie
+ Známky
+ Správy
+ Vzhľad a správanie
+ Jazyky, motívy, triedenie predmetov
+ Upozornenia aplikácie, oprava problémov
+ Upozornenia
+ Synchronizácia
+ Automatická aktualizácia, interval aktualizácií
+ Hodnota plusu a mínusu, výpočet priemeru
+ Pokročilé
+ Verzia aplikácie, prispievatelia, sociálne portály, licencie
+
+ Nové položky v denníku
+ Nové známky
+ Šťastné číslo
+ Nové správy
+ Nové poznámky
+ Push upozornenia
+ Nadchádzajúce lekcie
+ Ladenie
+
+ Čierny
+ Červený
+ Modrý
+ Zelený
+ Fialový
+ Žiadna farba
+
+ Skopírované
+ Vrátiť
+
+ Sťahovanie aktualizácií začalo…
+ Aktualizácia bola stiahnutá.
+ Reštartovať
+ Aktualizácia zlyhala! Wulkanowy nemusí fungovať správne. Zvážte aktualizáciu
+
+ Žiadne internetové pripojenie
+ Nedá sa pripojiť ku denníku. Servery môžu byť preťažené. Prosím skúste to znova neskôr
+ Načítanie údajov zlyhalo. Skúste neskôr prosím
+ Je vyžadovaná zmena hesla pre denník
+ Prebieha údržba UONET+ denník. Skúste to neskôr znova
+ Neznáma chyba dennika UONET+. Prosím skúste to znova neskôr
+ Neznáma chyba aplikácie. Prosím skúste to znova neskôr
+ Vyskytla sa neočakávaná chyba
+ Funkcia je deaktivovaná cez vašou školou
+ Funkcia nie je k dispozícii. Prihláste sa v inom režime než Mobile API
+
diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml
index 02b6ccf8..d0891925 100644
--- a/app/src/main/res/values-uk/preferences_values.xml
+++ b/app/src/main/res/values-uk/preferences_values.xml
@@ -13,6 +13,7 @@
- Українська
- Deutsch
- Čeština
+ - Slovenčina
- 15 хвилин
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 843c3616..64e147e7 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -46,6 +46,7 @@
Минув термін дії токену
Недійсна адреса електронної пошти
Використовуйте призначений логін замість електронної пошти
+ Використовуйте призначений логін або електронну адресу в @% 1 $ s
Неправильний симбвол
Студента не знайдено Перевірте символ та обраний варіант реєстру UONET+
Обов\'язкове поле
@@ -313,6 +314,10 @@
Брак інформації о щасливому номері
Сьогоднішній щасливий номер
Сьогоднішнім щасливим номером є %d
+ Показати історію
+
+ Історія щасливих чисел
+ Немає інформації про щасливі номери
Мобільні пристрої
Брак пристроїв
@@ -404,6 +409,7 @@
Псевдонім
Додати псевдонім
+ Оберіть колір аватара
Поділитися логами
Оновити
@@ -438,21 +444,21 @@
Темна
Тема системи
- Вигляд
+ Поява додатка & amp; поведінки
Вікно за замовчуванням
Спосіб облічування оцінки на кінець року
Примусово розрахувати середню оцінку через додаток
- Показувати присутність у відвідуваності
- Тема додатку
+ Показати присутність
+ Тема
Більше оцінок
- Позначити поточний урок у розкладі
- Покажіть групи поруч із предметами в розкладі
+ Позначити поточний урок
+ Показувати групи поруч з темами
Показувати діаграми в оцінках класу
Показати уроки всього класу
- Показуйте предмети без оцінок у оцінках
+ Показати предмети без оцінок
Схема кольорів оцінок
- Сортування предметів за \"Оцінками\"
- Мова додатку
+ Сортування предметів
+ Мова
Повідомлення
Показувати повідомлення
Показувати повідомлення о наступних уроках
@@ -460,6 +466,7 @@
На вашому пристрої можуть бути помилки з синхронізацією і повідомленнями\n\nЩоб виправити іх, вам необхідно додати Wulkanowy в авто-старт и вимкнути оптимізацію/экономію батареї в налаштуваннях пристрою.
Перейти до налаштувань
Показувати дебаг-повідомлення
+ Синхронізація вимкнена
Синхронізація
Автоматична синхронізація
Призупинено на час канікул
@@ -469,14 +476,27 @@
Синхронізовано!
Синхронізація не вдалася
Триває синхронізація
- Синхронізація
- Ручна синхронізація не оновлює дані в додатку.
- \nЩоб побачити оновлені дані, перезавантажте додаток.
-
- Інші
Вартість плюсу
Вага мінуса
Відповісти з історією повідомлень
+ Додатково
+ Вигляд & Поведінка
+ Повідомлення
+ Синхронізація
+ Оцінки
+ Відвідуваність
+ Розклад
+ Класи
+ Повідомлення
+ Вигляд & Поведінка
+ Мови, теми, тема сортування
+ Сповіщення додатку, виправляти проблеми
+ Повідомлення
+ Синхронізація
+ Автоматичне оновлення, інтервал синхронізації
+ Плюс і мінус значення, середні обчислення
+ Додатково
+ Версія програми, учасники, соціальні портали, ліцензії
Нові дані в щоденнику
Нові оцінки
diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml
new file mode 100644
index 00000000..574e8488
--- /dev/null
+++ b/app/src/main/res/values-v23/styles.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ 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
new file mode 100644
index 00000000..55413c05
--- /dev/null
+++ b/app/src/main/res/values-v26/styles.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/values-v27/styles.xml b/app/src/main/res/values-v28/styles.xml
similarity index 75%
rename from app/src/main/res/values-v27/styles.xml
rename to app/src/main/res/values-v28/styles.xml
index d33f6422..ee77091d 100644
--- a/app/src/main/res/values-v27/styles.xml
+++ b/app/src/main/res/values-v28/styles.xml
@@ -1,9 +1,10 @@
-
+
\ 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
new file mode 100644
index 00000000..ee77091d
--- /dev/null
+++ b/app/src/main/res/values-v29/styles.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml
index 0a43612d..15849047 100644
--- a/app/src/main/res/values/api_hosts.xml
+++ b/app/src/main/res/values/api_hosts.xml
@@ -14,7 +14,7 @@
- Skarżysko-Kamienna - e-Skarżysko
- Łask - Platforma vEdukacja
- Powiat łaski - Platforma edukacyjna
- - Powiat Krasnostawski - Platforma oświatowa
+ - Powiat krasnostawski - Platforma oświatowa
- Powiat kętrzyński - Platforma e-Usług
- Gmina Ulan-Majorat - Platforma oświatowa
- Gmina Ozorków - Platforma edukacyjna
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 99456744..b2e0dcef 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -6,6 +6,12 @@
#ff5722
#e84853
+ #2D2D2D
+ #1E1E1E
+
+ #1C1C1C
+ #0D0D0D
+
#ffd54f
#ff8f00
diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml
index 5f2aec4c..44f54c17 100644
--- a/app/src/main/res/values/preferences_values.xml
+++ b/app/src/main/res/values/preferences_values.xml
@@ -32,6 +32,7 @@
- Українська
- Deutsch
- Čeština
+ - Slovenčina
- system
@@ -41,6 +42,7 @@
- uk
- de
- cs
+ - sk
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c21146c9..574c21b0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -50,6 +50,7 @@
Token expired
Invalid email
Use the assigned login instead of email
+ Use the assigned login or email in @%1$s
Invalid symbol
Student not found. Validate the symbol and the chosen variation of the UONET+ register
This field is required
@@ -305,6 +306,11 @@
No info about the lucky number
Lucky number for today
Today\'s lucky number is: %d
+ Show history
+
+
+ Lucky number history
+ No info about lucky numbers
Mobile devices
@@ -418,6 +424,7 @@
Nick
Add nick
+ Choose avatar color
@@ -462,21 +469,21 @@
- Appearance
+ App appearance & behavior
Default view
Calculation of the end-of-year average
Force average calculation by app
- Show presence in attendance
- Application theme
+ Show presence
+ Theme
Expand grades
- Mark current lesson in timetable
- Show groups next to subjects in timetable
+ Mark current lesson
+ Show groups next to subjects
Show chart list in class grades
Show whole class lessons
- Show subjects without grades in Grades
+ Show subjects without grades
Grades color scheme
- Subjects sorting in "Grades"
- App language
+ Subjects sorting
+ Language
Notifications
Show notifications
@@ -485,6 +492,7 @@
Your device may have data synchronization issues and with notifications.\n\nTo fix them, you need to add Wulkanowy to the autostart and turn off battery optimization/saving in the phone settings.
Go to settings
Show debug notifications
+ Synchronization is disabled
Synchronization
Automatic update
@@ -495,17 +503,32 @@
Synced!
Sync failed
Sync in progress
- Synchronization
-
- Manual sync doesn\'t refresh app views.
- \nTo see the synced data relaunch the app after syncing.
-
- Other
Value of the plus
Value of the minus
Reply with message history
+ Advanced
+ Appearance & Behavior
+ Notifications
+ Synchronization
+
+ Grades
+ Attendance
+ Timetable
+ Grades
+ Messages
+
+ Appearance & Behavior
+ Languages, themes, subjects sorting
+ App notifications, fix problems
+ Notifications
+ Synchronization
+ Automatic update, synchronization interval
+ Plus and minus values, average calculation
+ Advanced
+ App version, contributors, social portals, licenses
+
New entries in register
@@ -549,5 +572,4 @@
An unexpected error occurred
Feature disabled by your school
Feature not available. Login in a mode other than Mobile API
-
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index cf587cbf..fc7ffb77 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -13,6 +13,7 @@
- @color/colorSwipeRefresh
- ?android:textColorPrimary
- @style/PreferenceThemeOverlay
+ - false
+
+
+
+
diff --git a/app/src/main/res/xml/scheme_preferences.xml b/app/src/main/res/xml/scheme_preferences.xml
index 3f24c787..08621492 100644
--- a/app/src/main/res/xml/scheme_preferences.xml
+++ b/app/src/main/res/xml/scheme_preferences.xml
@@ -1,182 +1,33 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/app/src/main/res/xml/scheme_preferences_advanced.xml b/app/src/main/res/xml/scheme_preferences_advanced.xml
new file mode 100644
index 00000000..1d7e9b83
--- /dev/null
+++ b/app/src/main/res/xml/scheme_preferences_advanced.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/scheme_preferences_appearance.xml b/app/src/main/res/xml/scheme_preferences_appearance.xml
new file mode 100644
index 00000000..095c5070
--- /dev/null
+++ b/app/src/main/res/xml/scheme_preferences_appearance.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/scheme_preferences_notifications.xml b/app/src/main/res/xml/scheme_preferences_notifications.xml
new file mode 100644
index 00000000..3d435ca8
--- /dev/null
+++ b/app/src/main/res/xml/scheme_preferences_notifications.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/scheme_preferences_sync.xml b/app/src/main/res/xml/scheme_preferences_sync.xml
new file mode 100644
index 00000000..b6c3c2a8
--- /dev/null
+++ b/app/src/main/res/xml/scheme_preferences_sync.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/play/java/io/github/wulkanowy/utils/UpdateHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/UpdateHelper.kt
index 1dd3bc68..6772237e 100644
--- a/app/src/play/java/io/github/wulkanowy/utils/UpdateHelper.kt
+++ b/app/src/play/java/io/github/wulkanowy/utils/UpdateHelper.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.utils
import android.app.Activity
import android.app.Activity.RESULT_OK
import android.content.Context
+import android.content.IntentSender
import android.view.View
import android.widget.Toast
import com.google.android.material.snackbar.Snackbar
@@ -87,9 +88,13 @@ class UpdateHelper @Inject constructor(
private fun startUpdate(activity: Activity, appUpdateInfo: AppUpdateInfo, updateType: Int) {
Timber.d("Start update ($updateType): $appUpdateInfo")
- appUpdateManager.startUpdateFlowForResult(
- appUpdateInfo, updateType, activity, IN_APP_UPDATE_REQUEST_CODE
- )
+ try {
+ appUpdateManager.startUpdateFlowForResult(
+ appUpdateInfo, updateType, activity, IN_APP_UPDATE_REQUEST_CODE
+ )
+ } catch (e: IntentSender.SendIntentException) {
+ Timber.i("Update failed! Duplicated PendingIntent")
+ }
}
fun onActivityResult(requestCode: Int, resultCode: Int) {
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt
similarity index 73%
rename from app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt
rename to app/src/test/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt
index aca28732..f9fc7631 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt
@@ -9,6 +9,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider
+import io.github.wulkanowy.utils.AppInfo
import org.junit.Rule
abstract class AbstractMigrationTest {
@@ -24,12 +25,16 @@ abstract class AbstractMigrationTest {
fun getMigratedRoomDatabase(): AppDatabase {
val context = ApplicationProvider.getApplicationContext()
- val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
- AppDatabase::class.java, dbName)
- .addMigrations(*AppDatabase.getMigrations(SharedPrefProvider(PreferenceManager
- .getDefaultSharedPreferences(context)))
+ val database = Room.databaseBuilder(
+ ApplicationProvider.getApplicationContext(),
+ AppDatabase::class.java,
+ dbName
+ ).addMigrations(
+ *AppDatabase.getMigrations(
+ SharedPrefProvider(PreferenceManager.getDefaultSharedPreferences(context)),
+ AppInfo()
)
- .build()
+ ).build()
// close the database and release any stream resources when the test finishes
helper.closeWhenFinished(database)
return database
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt
similarity index 92%
rename from app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt
rename to app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt
index b312048d..a0290473 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt
@@ -2,14 +2,20 @@ package io.github.wulkanowy.data.db.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL
+import android.os.Build
import androidx.sqlite.db.SupportSQLiteDatabase
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.HiltTestApplication
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
import kotlin.test.assertEquals
-@RunWith(AndroidJUnit4::class)
+@HiltAndroidTest
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
class Migration12Test : AbstractMigrationTest() {
@Test
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
similarity index 95%
rename from app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
rename to app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
index 15e99f5f..2350da45 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
@@ -2,17 +2,26 @@ package io.github.wulkanowy.data.db.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
+import android.os.Build
import androidx.sqlite.db.SupportSQLiteDatabase
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.HiltTestApplication
import io.github.wulkanowy.data.db.Converters
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
import java.time.LocalDate.of
import kotlin.test.assertFalse
import kotlin.test.assertTrue
+@HiltAndroidTest
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
class Migration13Test : AbstractMigrationTest() {
@Test
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt
similarity index 91%
rename from app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt
rename to app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt
index 75955258..8e744f27 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration27Test.kt
@@ -2,12 +2,21 @@ package io.github.wulkanowy.data.db.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
+import android.os.Build
import androidx.sqlite.db.SupportSQLiteDatabase
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.HiltTestApplication
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
import kotlin.random.Random
+@HiltAndroidTest
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
class Migration27Test : AbstractMigrationTest() {
@Test
diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration35Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration35Test.kt
new file mode 100644
index 00000000..883cdb81
--- /dev/null
+++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration35Test.kt
@@ -0,0 +1,70 @@
+package io.github.wulkanowy.data.db.migrations
+
+import android.content.ContentValues
+import android.database.sqlite.SQLiteDatabase
+import android.os.Build
+import androidx.sqlite.db.SupportSQLiteDatabase
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.HiltTestApplication
+import io.github.wulkanowy.utils.AppInfo
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import kotlin.random.Random
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+@HiltAndroidTest
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
+class Migration35Test : AbstractMigrationTest() {
+
+ @Test
+ fun addRandomAvatarColorsForStudents() {
+ with(helper.createDatabase(dbName, 34)) {
+ createStudent(this, 1)
+ createStudent(this, 2)
+ close()
+ }
+
+ helper.runMigrationsAndValidate(dbName, 35, true, Migration35(AppInfo()))
+
+ val db = getMigratedRoomDatabase()
+ val students = runBlocking { db.studentDao.loadAll() }
+
+ assertEquals(2, students.size)
+
+ assertTrue { students[0].avatarColor in AppInfo().defaultColorsForAvatar }
+ assertTrue { students[1].avatarColor in AppInfo().defaultColorsForAvatar }
+ }
+
+ private fun createStudent(db: SupportSQLiteDatabase, id: Long) {
+ db.insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
+ put("id", id)
+ put("scrapper_base_url", "https://fakelog.cf")
+ put("mobile_base_url", "")
+ put("login_mode", "SCRAPPER")
+ put("login_type", "STANDARD")
+ put("certificate_key", "")
+ put("private_key", "")
+ put("is_parent", false)
+ put("email", "jan@fakelog.cf")
+ put("password", "******")
+ put("symbol", "Default")
+ put("school_short", "")
+ put("class_name", "")
+ put("student_id", Random.nextInt())
+ put("class_id", Random.nextInt())
+ put("school_id", "123")
+ put("school_name", "Wulkan first class school")
+ put("is_current", false)
+ put("registration_date", "0")
+ put("user_login_id", Random.nextInt())
+ put("student_name", "")
+ put("user_name", "")
+ put("nick", "")
+ })
+ }
+}
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 4c6a1172..1c592c09 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
@@ -87,6 +87,7 @@ class AttendanceRepositoryTest {
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
+ flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
@@ -114,6 +115,7 @@ class AttendanceRepositoryTest {
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList.dropLast(1)
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
+ flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
index 461e1809..b116a623 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
@@ -87,6 +87,7 @@ class CompletedLessonsRepositoryTest {
coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList
coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
+ flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
coEvery { completedLessonDb.insertAll(any()) } returns listOf(1, 2, 3)
@@ -114,6 +115,7 @@ class CompletedLessonsRepositoryTest {
coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList.dropLast(1)
coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
+ flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester))
)
coEvery { completedLessonDb.insertAll(any()) } returns listOf(1, 2, 3)
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 42a89707..ead6dc5d 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
@@ -88,6 +88,7 @@ class ExamRemoteTest {
coEvery { sdk.getExams(startDate, realEndDate, 1) } 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
flowOf(remoteList.mapToEntities(semester))
)
coEvery { examDb.insertAll(any()) } returns listOf(1, 2, 3)
@@ -115,6 +116,7 @@ class ExamRemoteTest {
coEvery { sdk.getExams(startDate, realEndDate, 1) } 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
flowOf(remoteList.dropLast(1).mapToEntities(semester))
)
coEvery { examDb.insertAll(any()) } returns listOf(1, 2, 3)
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 002c7ad7..8a19d633 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
@@ -57,7 +57,7 @@ class GradeRepositoryTest {
coEvery { gradeDb.deleteAll(any()) } just Runs
coEvery { gradeDb.insertAll(any()) } returns listOf()
- coEvery { gradeSummaryDb.loadAll(1, 1) } returnsMany listOf(flowOf(listOf()), flowOf(listOf()))
+ coEvery { gradeSummaryDb.loadAll(1, 1) } returnsMany listOf(flowOf(listOf()), flowOf(listOf()), flowOf(listOf()))
coEvery { gradeSummaryDb.deleteAll(any()) } just Runs
coEvery { gradeSummaryDb.insertAll(any()) } returns listOf()
}
@@ -76,7 +76,8 @@ class GradeRepositoryTest {
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(listOf()), // empty because it is new user
- flowOf(remoteList.mapToEntities(semester))
+ flowOf(listOf()), // empty again, after fetch end before save result
+ flowOf(remoteList.mapToEntities(semester)),
)
// execute
@@ -114,6 +115,7 @@ class GradeRepositoryTest {
)
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(localList.mapToEntities(semester)),
+ flowOf(localList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
@@ -155,6 +157,7 @@ class GradeRepositoryTest {
)
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(localList.mapToEntities(semester)),
+ flowOf(localList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
@@ -184,6 +187,7 @@ class GradeRepositoryTest {
)
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(localList.mapToEntities(semester)),
+ flowOf(localList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
@@ -209,6 +213,7 @@ class GradeRepositoryTest {
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(listOf()),
+ flowOf(listOf()), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
index 9cbad8ac..a89aad35 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
@@ -72,6 +72,7 @@ class LuckyNumberRemoteTest {
coEvery { sdk.getLuckyNumber(student.schoolShortName) } returns luckyNumber
coEvery { luckyNumberDb.load(1, date) } returnsMany listOf(
flowOf(luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)),
+ flowOf(luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)), // after fetch end before save result
flowOf(luckyNumber.mapToEntity(student))
)
coEvery { luckyNumberDb.insertAll(any()) } returns listOf(1, 2, 3)
@@ -101,6 +102,7 @@ class LuckyNumberRemoteTest {
coEvery { sdk.getLuckyNumber(student.schoolShortName) } returns luckyNumber
coEvery { luckyNumberDb.load(1, date) } returnsMany listOf(
flowOf(null),
+ flowOf(null), // after fetch end before save result
flowOf(luckyNumber.mapToEntity(student))
)
coEvery { luckyNumberDb.insertAll(any()) } returns listOf(1, 2, 3)
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 4a4f2c76..e5b3d101 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
@@ -82,6 +82,7 @@ class MobileDeviceRepositoryTest {
coEvery { sdk.getRegisteredDevices() } returns remoteList
coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
+ flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
@@ -109,6 +110,7 @@ class MobileDeviceRepositoryTest {
coEvery { sdk.getRegisteredDevices() } returns remoteList.dropLast(1)
coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
+ flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester))
)
coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
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
index 415f6e0a..402b2272 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt
@@ -5,6 +5,7 @@ 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
@@ -30,7 +31,14 @@ class StudentTest {
@Before
fun initApi() {
MockKAnnotations.init(this)
- studentRepository = StudentRepository(mockk(), TestDispatchersProvider(), studentDb, semesterDb, mockSdk)
+ studentRepository = StudentRepository(
+ mockk(),
+ TestDispatchersProvider(),
+ studentDb,
+ semesterDb,
+ mockSdk,
+ AppInfo()
+ )
}
@Test
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 0da02cf5..f3d92d2e 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
@@ -198,6 +198,19 @@ class GradeAverageProviderTest {
assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0)
}
+ @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.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+
+ coEvery { gradeRepository.getGrades(student, semesters[1], false) } returns flowWithResource { secondGradeWithModifier to secondSummariesWithModifier }
+ coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns flowWithResource { emptyList() to listOf(getSummary(24, "Język polski", .0))}
+
+ val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, false).getResult() }
+
+ assertEquals(3.49, items.single { it.subject == "Język polski" }.average, .0)
+ }
+
@Test
fun `force calc average on no grades`() {
every { preferencesRepository.gradeAverageForceCalc } returns true
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 31e3e198..67d3e626 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
@@ -65,7 +65,7 @@ class LoginFormPresenterTest {
fun emptyNicknameLoginTest() {
every { loginFormView.formUsernameValue } returns ""
every { loginFormView.formPassValue } returns "test123"
- every { loginFormView.formHostValue } returns "https://fakelog.cf/?standard"
+ every { loginFormView.formHostValue } returns "https://fakelog.cf/?email"
presenter.onSignInClick()
verify { loginFormView.setErrorUsernameRequired() }
@@ -73,11 +73,21 @@ class LoginFormPresenterTest {
verify(exactly = 0) { loginFormView.setErrorPassInvalid(false) }
}
+ @Test
+ fun invalidEmailLoginTest() {
+ every { loginFormView.formUsernameValue } returns "@"
+ every { loginFormView.formPassValue } returns "123456"
+ every { loginFormView.formHostValue } returns "https://fakelog.cf/"
+ presenter.onSignInClick()
+
+ verify { loginFormView.setErrorEmailInvalid("fakelog.cf") }
+ }
+
@Test
fun emptyPassLoginTest() {
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns ""
- every { loginFormView.formHostValue } returns "https://fakelog.cf/?standard"
+ every { loginFormView.formHostValue } returns "https://fakelog.cf/?email"
presenter.onSignInClick()
verify(exactly = 0) { loginFormView.setErrorUsernameRequired() }
@@ -89,7 +99,7 @@ class LoginFormPresenterTest {
fun invalidPassLoginTest() {
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123"
- every { loginFormView.formHostValue } returns "https://fakelog.cf/?standard"
+ every { loginFormView.formHostValue } returns "https://fakelog.cf/?email"
presenter.onSignInClick()
verify(exactly = 0) { loginFormView.setErrorUsernameRequired() }
@@ -102,7 +112,7 @@ class LoginFormPresenterTest {
val studentTest = Student(
email = "test@",
password = "123",
- scrapperBaseUrl = "https://fakelog.cf/",
+ scrapperBaseUrl = "https://fakelog.cf/?email",
loginType = "AUTO",
studentName = "",
schoolSymbol = "",
@@ -128,7 +138,7 @@ class LoginFormPresenterTest {
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"
- every { loginFormView.formHostValue } returns "https://fakelog.cf/?standard"
+ every { loginFormView.formHostValue } returns "https://fakelog.cf/?email"
every { loginFormView.formHostSymbol } returns "Default"
presenter.onSignInClick()
@@ -144,7 +154,7 @@ class LoginFormPresenterTest {
coEvery { repository.getStudentsScrapper(any(), any(), any(), any()) } returns listOf()
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"
- every { loginFormView.formHostValue } returns "https://fakelog.cf/?standard"
+ every { loginFormView.formHostValue } returns "https://fakelog.cf/?email"
every { loginFormView.formHostSymbol } returns "Default"
presenter.onSignInClick()
@@ -160,7 +170,7 @@ class LoginFormPresenterTest {
coEvery { repository.getStudentsScrapper(any(), any(), any(), any()) } returns listOf()
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"
- every { loginFormView.formHostValue } returns "https://fakelog.cf/?standard"
+ every { loginFormView.formHostValue } returns "https://fakelog.cf/?email"
every { loginFormView.formHostSymbol } returns "Default"
presenter.onSignInClick()
presenter.onSignInClick()
@@ -178,7 +188,7 @@ class LoginFormPresenterTest {
coEvery { repository.getStudentsScrapper(any(), any(), any(), any()) } throws testException
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"
- every { loginFormView.formHostValue } returns "https://fakelog.cf/?standard"
+ every { loginFormView.formHostValue } returns "https://fakelog.cf/?email"
every { loginFormView.formHostSymbol } returns "Default"
every { loginFormView.showProgress(any()) } just Runs
every { loginFormView.showProgress(any()) } just Runs
diff --git a/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt b/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt
new file mode 100644
index 00000000..375a2403
--- /dev/null
+++ b/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt
@@ -0,0 +1,192 @@
+package io.github.wulkanowy.utils
+
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerifyOrder
+import io.mockk.just
+import io.mockk.mockk
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.test.TestCoroutineScope
+import org.junit.Test
+import kotlin.test.assertEquals
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class FlowUtilsKtTest {
+
+ private val testScope = TestCoroutineScope()
+
+ @Test
+ fun `fetch from two places with same remote data`() {
+ val repo = mockk()
+ coEvery { repo.query() } returnsMany listOf(
+ // initial data
+ flowOf(listOf(1, 2, 3)),
+ flowOf(listOf(1, 2, 3)),
+
+ // for first
+ flowOf(listOf(1, 2, 3)), // before save
+ flowOf(listOf(2, 3, 4)), // after save
+
+ // for second
+ flowOf(listOf(2, 3, 4)), // before save
+ flowOf(listOf(2, 3, 4)), // after save
+ )
+ coEvery { repo.fetch() } returnsMany listOf(
+ listOf(2, 3, 4),
+ listOf(2, 3, 4),
+ )
+ coEvery { repo.save(any(), any()) } just Runs
+
+ // first
+ networkBoundResource(
+ showSavedOnLoading = false,
+ query = { repo.query() },
+ fetch = {
+ val data = repo.fetch()
+ delay(2_000)
+ data
+ },
+ saveFetchResult = { old, new -> repo.save(old, new) }
+ ).launchIn(testScope)
+
+ testScope.advanceTimeBy(1_000)
+
+ // second
+ networkBoundResource(
+ showSavedOnLoading = false,
+ query = { repo.query() },
+ fetch = {
+ val data = repo.fetch()
+ delay(2_000)
+ data
+ },
+ saveFetchResult = { old, new -> repo.save(old, new) }
+ ).launchIn(testScope)
+
+ testScope.advanceTimeBy(3_000)
+
+ coVerifyOrder {
+ // from first
+ repo.query()
+ repo.fetch() // hang for 2 sec
+
+ // wait 1 sec
+
+ // from second
+ repo.query()
+ repo.fetch() // hang for 2 sec
+
+ // from first
+ repo.query()
+ repo.save(withArg {
+ assertEquals(listOf(1, 2, 3), it)
+ }, any())
+ repo.query()
+
+ // from second
+ repo.query()
+ repo.save(withArg {
+ assertEquals(listOf(2, 3, 4), it)
+ }, any())
+ repo.query()
+ }
+ }
+
+ @Test
+ fun `fetch from two places with same remote data and save at the same moment`() {
+ val repo = mockk()
+ coEvery { repo.query() } returnsMany listOf(
+ // initial data
+ flowOf(listOf(1, 2, 3)),
+ flowOf(listOf(1, 2, 3)),
+
+ // for first
+ flowOf(listOf(1, 2, 3)), // before save
+ flowOf(listOf(2, 3, 4)), // after save
+
+ // for second
+ flowOf(listOf(2, 3, 4)), // before save
+ flowOf(listOf(2, 3, 4)), // after save
+ )
+ coEvery { repo.fetch() } returnsMany listOf(
+ listOf(2, 3, 4),
+ listOf(2, 3, 4),
+ )
+ coEvery { repo.save(any(), any()) } just Runs
+
+ val saveResultMutex = Mutex()
+
+ // first
+ networkBoundResource(
+ mutex = saveResultMutex,
+ showSavedOnLoading = false,
+ query = { repo.query() },
+ fetch = {
+ val data = repo.fetch()
+ delay(2_000)
+ data
+ },
+ saveFetchResult = { old, new ->
+ delay(1_500)
+ repo.save(old, new)
+ }
+ ).launchIn(testScope)
+
+ testScope.advanceTimeBy(1_000)
+
+ // second
+ networkBoundResource(
+ mutex = saveResultMutex,
+ showSavedOnLoading = false,
+ query = { repo.query() },
+ fetch = {
+ val data = repo.fetch()
+ delay(2_000)
+ data
+ },
+ saveFetchResult = { old, new ->
+ repo.save(old, new)
+ }
+ ).launchIn(testScope)
+
+ testScope.advanceTimeBy(3_000)
+
+ coVerifyOrder {
+ // from first
+ repo.query()
+ repo.fetch() // hang for 2 sec
+
+ // wait 1 sec
+
+ // from second
+ repo.query()
+ repo.fetch() // hang for 2 sec
+
+ // from first
+ repo.query()
+ repo.save(withArg {
+ assertEquals(listOf(1, 2, 3), it)
+ }, any())
+
+ // from second
+ repo.query()
+ repo.save(withArg {
+ assertEquals(listOf(2, 3, 4), it)
+ }, any())
+
+ repo.query()
+ repo.query()
+ }
+ }
+
+ @Suppress("UNUSED_PARAMETER", "RedundantSuspendModifier")
+ private class TestRepo {
+ fun query() = flowOf>()
+ suspend fun fetch() = listOf()
+ suspend fun save(old: List, new: List) {}
+ }
+}
diff --git a/build.gradle b/build.gradle
index 5ab0182b..8a10f65b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,8 +1,8 @@
buildscript {
ext {
- kotlin_version = '1.4.21'
- about_libraries = '8.7.0'
- hilt_version = "2.31.2-alpha"
+ kotlin_version = '1.4.31'
+ about_libraries = '8.8.4'
+ hilt_version = "2.33-beta"
}
repositories {
mavenCentral()
@@ -16,8 +16,8 @@ buildscript {
classpath 'com.android.tools.build:gradle:4.1.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.5'
- classpath 'com.huawei.agconnect:agcp:1.5.0.200'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
+ classpath 'com.huawei.agconnect:agcp:1.5.0.300'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.1'
classpath "com.github.triplet.gradle:play-publisher:2.8.0"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.1.1"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1c4bcc29..8cf6eb5a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists