Merge branch 'release/1.5.0'

This commit is contained in:
Mikołaj Pich 2022-01-01 17:51:11 +01:00
commit 6dc16b288d
181 changed files with 9510 additions and 1622 deletions

View File

@ -2,14 +2,6 @@
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" /> <option name="LINE_SEPARATOR" value="&#10;" />
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">
@ -126,13 +118,6 @@
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="kotlin"> <codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings> </codeStyleSettings>
</code_scheme> </code_scheme>
</component> </component>

View File

@ -22,8 +22,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 31
versionCode 102 versionCode 103
versionName "1.4.4" versionName "1.5.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy" resValue "string", "app_name", "Wulkanowy"
@ -149,8 +149,10 @@ kapt {
play { play {
defaultToAppBundles = false defaultToAppBundles = false
track = 'beta' track = 'production'
updatePriority = 4 releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
userFraction = 0.25d
updatePriority = 1
enabled.set(false) enabled.set(false)
} }
@ -167,18 +169,18 @@ huaweiPublish {
ext { ext {
work_manager = "2.7.1" work_manager = "2.7.1"
android_hilt = "1.0.0" android_hilt = "1.0.0"
room = "2.3.0" room = "2.4.0"
chucker = "3.5.2" chucker = "3.5.2"
mockk = "1.12.1" mockk = "1.12.1"
coroutines = "1.5.2" coroutines = "1.6.0"
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:sdk:1.4.4" implementation "io.github.wulkanowy:sdk:1.5.0"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.7.0" implementation "androidx.core:core-ktx:1.7.0"
@ -229,7 +231,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.3.1' implementation 'me.xdrop:fuzzywuzzy:1.3.1'
implementation 'com.fredporciuncula:flow-preferences:1.6.0' implementation 'com.fredporciuncula:flow-preferences:1.6.0'
playImplementation platform('com.google.firebase:firebase-bom:29.0.2') playImplementation platform('com.google.firebase:firebase-bom:29.0.3')
playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:' playImplementation 'com.google.firebase:firebase-crashlytics:'
@ -238,7 +240,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:20.5.0' playImplementation 'com.google.android.gms:play-services-ads:20.5.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.3.2.300' hmsImplementation 'com.huawei.hms:hianalytics:6.3.2.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.2.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.3.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -114,7 +114,6 @@
</intent-filter> </intent-filter>
</service> </service>
<receiver <receiver
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider" android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
android:exported="true" android:exported="true"

View File

@ -1,10 +1,7 @@
package io.github.wulkanowy package io.github.wulkanowy
import android.app.Application import android.app.Application
import android.util.Log.DEBUG import android.util.Log.*
import android.util.Log.INFO
import android.util.Log.VERBOSE
import android.webkit.WebView
import androidx.hilt.work.HiltWorkerFactory import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration import androidx.work.Configuration
import com.yariksoffice.lingver.Lingver import com.yariksoffice.lingver.Lingver
@ -12,12 +9,7 @@ import dagger.hilt.android.HiltAndroidApp
import fr.bipi.tressence.file.FileLoggerTree import fr.bipi.tressence.file.FileLoggerTree
import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.ui.base.ThemeManager import io.github.wulkanowy.ui.base.ThemeManager
import io.github.wulkanowy.utils.ActivityLifecycleLogger import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.CrashLogExceptionTree
import io.github.wulkanowy.utils.CrashLogTree
import io.github.wulkanowy.utils.DebugLogTree
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -44,7 +36,6 @@ class WulkanowyApp : Application(), Configuration.Provider {
initializeAppLanguage() initializeAppLanguage()
themeManager.applyDefaultTheme() themeManager.applyDefaultTheme()
initLogging() initLogging()
fixWebViewLocale()
} }
private fun initLogging() { private fun initLogging() {
@ -76,15 +67,6 @@ class WulkanowyApp : Application(), Configuration.Provider {
} }
} }
private fun fixWebViewLocale() {
//https://stackoverflow.com/questions/40398528/android-webview-language-changes-abruptly-on-android-7-0-and-above
try {
WebView(this).destroy()
} catch (e: Throwable) {
//Ignore exceptions
}
}
override fun getWorkManagerConfiguration() = Configuration.Builder() override fun getWorkManagerConfiguration() = Configuration.Builder()
.setWorkerFactory(workerFactory) .setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO) .setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.data.db package io.github.wulkanowy.data.db
import android.content.Context import android.content.Context
import androidx.room.AutoMigration
import androidx.room.Database import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
@ -66,49 +67,7 @@ import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.db.migrations.Migration10 import io.github.wulkanowy.data.db.migrations.*
import io.github.wulkanowy.data.db.migrations.Migration11
import io.github.wulkanowy.data.db.migrations.Migration12
import io.github.wulkanowy.data.db.migrations.Migration13
import io.github.wulkanowy.data.db.migrations.Migration14
import io.github.wulkanowy.data.db.migrations.Migration15
import io.github.wulkanowy.data.db.migrations.Migration16
import io.github.wulkanowy.data.db.migrations.Migration17
import io.github.wulkanowy.data.db.migrations.Migration18
import io.github.wulkanowy.data.db.migrations.Migration19
import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration20
import io.github.wulkanowy.data.db.migrations.Migration21
import io.github.wulkanowy.data.db.migrations.Migration22
import io.github.wulkanowy.data.db.migrations.Migration23
import io.github.wulkanowy.data.db.migrations.Migration24
import io.github.wulkanowy.data.db.migrations.Migration25
import io.github.wulkanowy.data.db.migrations.Migration26
import io.github.wulkanowy.data.db.migrations.Migration27
import io.github.wulkanowy.data.db.migrations.Migration28
import io.github.wulkanowy.data.db.migrations.Migration29
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration30
import io.github.wulkanowy.data.db.migrations.Migration31
import io.github.wulkanowy.data.db.migrations.Migration32
import io.github.wulkanowy.data.db.migrations.Migration33
import io.github.wulkanowy.data.db.migrations.Migration34
import io.github.wulkanowy.data.db.migrations.Migration35
import io.github.wulkanowy.data.db.migrations.Migration36
import io.github.wulkanowy.data.db.migrations.Migration37
import io.github.wulkanowy.data.db.migrations.Migration38
import io.github.wulkanowy.data.db.migrations.Migration39
import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration40
import io.github.wulkanowy.data.db.migrations.Migration41
import io.github.wulkanowy.data.db.migrations.Migration42
import io.github.wulkanowy.data.db.migrations.Migration43
import io.github.wulkanowy.data.db.migrations.Migration44
import io.github.wulkanowy.data.db.migrations.Migration5
import io.github.wulkanowy.data.db.migrations.Migration6
import io.github.wulkanowy.data.db.migrations.Migration7
import io.github.wulkanowy.data.db.migrations.Migration8
import io.github.wulkanowy.data.db.migrations.Migration9
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import javax.inject.Singleton import javax.inject.Singleton
@ -146,6 +105,10 @@ import javax.inject.Singleton
Notification::class, Notification::class,
AdminMessage::class AdminMessage::class
], ],
autoMigrations = [
AutoMigration(from = 44, to = 45),
AutoMigration(from = 46, to = 47),
],
version = AppDatabase.VERSION_SCHEMA, version = AppDatabase.VERSION_SCHEMA,
exportSchema = true exportSchema = true
) )
@ -153,7 +116,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 44 const val VERSION_SCHEMA = 47
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(), Migration2(),
@ -198,7 +161,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration41(sharedPrefProvider), Migration41(sharedPrefProvider),
Migration42(), Migration42(),
Migration43(), Migration43(),
Migration44() Migration44(),
Migration46(),
) )
fun newInstance( fun newInstance(

View File

@ -1,40 +1,33 @@
package io.github.wulkanowy.data.db package io.github.wulkanowy.data.db
import androidx.room.TypeConverter import androidx.room.TypeConverter
import io.github.wulkanowy.utils.toTimestamp
import kotlinx.serialization.SerializationException import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
import java.time.Month import java.time.Month
import java.time.ZoneOffset import java.time.ZoneOffset
import java.util.Date import java.util.*
class Converters { class Converters {
private val json = Json private val json = Json
@TypeConverter @TypeConverter
fun timestampToDate(value: Long?): LocalDate? = value?.run { fun timestampToLocalDate(value: Long?): LocalDate? =
Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate() value?.let(::Date)?.toInstant()?.atZone(ZoneOffset.UTC)?.toLocalDate()
}
@TypeConverter @TypeConverter
fun dateToTimestamp(date: LocalDate?): Long? { fun dateToTimestamp(date: LocalDate?): Long? = date?.toTimestamp()
return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli()
}
@TypeConverter @TypeConverter
fun timestampToTime(value: Long?): LocalDateTime? = value?.let { fun instantToTimestamp(instant: Instant?): Long? = instant?.toEpochMilli()
LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)
}
@TypeConverter @TypeConverter
fun timeToTimestamp(date: LocalDateTime?): Long? { fun timestampToInstant(timestamp: Long?): Instant? = timestamp?.let(Instant::ofEpochMilli)
return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
}
@TypeConverter @TypeConverter
fun monthToInt(month: Month?) = month?.value fun monthToInt(month: Month?) = month?.value

View File

@ -4,7 +4,7 @@ import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.data.db.entities.Conference
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import java.time.LocalDateTime import java.time.Instant
import javax.inject.Singleton import javax.inject.Singleton
@Dao @Dao
@ -12,5 +12,5 @@ import javax.inject.Singleton
interface ConferenceDao : BaseDao<Conference> { interface ConferenceDao : BaseDao<Conference> {
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate") @Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate")
fun loadAll(diaryId: Int, studentId: Int, startDate: LocalDateTime): Flow<List<Conference>> fun loadAll(diaryId: Int, studentId: Int, startDate: Instant): Flow<List<Conference>>
} }

View File

@ -1,12 +1,7 @@
package io.github.wulkanowy.data.db.dao package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.*
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy.ABORT import androidx.room.OnConflictStrategy.ABORT
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.Student
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.db.entities.StudentWithSemesters

View File

@ -5,6 +5,7 @@ import androidx.room.Query
import io.github.wulkanowy.data.db.entities.TimetableAdditional import io.github.wulkanowy.data.db.entities.TimetableAdditional
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import java.time.LocalDate import java.time.LocalDate
import java.util.UUID
import javax.inject.Singleton import javax.inject.Singleton
@Dao @Dao
@ -12,5 +13,13 @@ import javax.inject.Singleton
interface TimetableAdditionalDao : BaseDao<TimetableAdditional> { interface TimetableAdditionalDao : BaseDao<TimetableAdditional> {
@Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<TimetableAdditional>> fun loadAll(
diaryId: Int,
studentId: Int,
from: LocalDate,
end: LocalDate
): Flow<List<TimetableAdditional>>
@Query("DELETE FROM TimetableAdditional WHERE repeat_id = :repeatId")
suspend fun deleteAllByRepeatId(repeatId: UUID)
} }

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.io.Serializable import java.io.Serializable
import java.time.LocalDateTime import java.time.Instant
@Entity(tableName = "Conferences") @Entity(tableName = "Conferences")
data class Conference( data class Conference(
@ -27,7 +27,7 @@ data class Conference(
@ColumnInfo(name = "conference_id") @ColumnInfo(name = "conference_id")
val conferenceId: Int, val conferenceId: Int,
val date: LocalDateTime val date: Instant,
) : Serializable { ) : Serializable {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.time.LocalDateTime import java.time.Instant
@Entity(tableName = "GradesSummary") @Entity(tableName = "GradesSummary")
data class GradeSummary( data class GradeSummary(
@ -45,8 +45,8 @@ data class GradeSummary(
var isFinalGradeNotified: Boolean = true var isFinalGradeNotified: Boolean = true
@ColumnInfo(name = "predicted_grade_last_change") @ColumnInfo(name = "predicted_grade_last_change")
var predictedGradeLastChange: LocalDateTime = LocalDateTime.now() var predictedGradeLastChange: Instant = Instant.now()
@ColumnInfo(name = "final_grade_last_change") @ColumnInfo(name = "final_grade_last_change")
var finalGradeLastChange: LocalDateTime = LocalDateTime.now() var finalGradeLastChange: Instant = Instant.now()
} }

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.io.Serializable import java.io.Serializable
import java.time.LocalDateTime import java.time.Instant
@Entity(tableName = "Messages") @Entity(tableName = "Messages")
data class Message( data class Message(
@ -29,7 +29,7 @@ data class Message(
val subject: String, val subject: String,
val date: LocalDateTime, val date: Instant,
@ColumnInfo(name = "folder_id") @ColumnInfo(name = "folder_id")
val folderId: Int, val folderId: Int,

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.io.Serializable import java.io.Serializable
import java.time.LocalDateTime import java.time.Instant
@Entity(tableName = "MobileDevices") @Entity(tableName = "MobileDevices")
data class MobileDevice( data class MobileDevice(
@ -17,7 +17,7 @@ data class MobileDevice(
val name: String, val name: String,
val date: LocalDateTime val date: Instant,
) : Serializable { ) : Serializable {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import io.github.wulkanowy.services.sync.notifications.NotificationType import io.github.wulkanowy.services.sync.notifications.NotificationType
import java.time.LocalDateTime import java.time.Instant
@Entity(tableName = "Notifications") @Entity(tableName = "Notifications")
data class Notification( data class Notification(
@ -18,7 +18,7 @@ data class Notification(
val type: NotificationType, val type: NotificationType,
val date: LocalDateTime, val date: Instant,
val data: String? = null val data: String? = null
) { ) {

View File

@ -7,7 +7,12 @@ import androidx.room.PrimaryKey
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate import java.time.LocalDate
@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)]) @Entity(
tableName = "Semesters", indices = [Index(
value = ["student_id", "diary_id", "kindergarten_diary_id", "semester_id"],
unique = true
)]
)
data class Semester( data class Semester(
@ColumnInfo(name = "student_id") @ColumnInfo(name = "student_id")
@ -16,6 +21,9 @@ data class Semester(
@ColumnInfo(name = "diary_id") @ColumnInfo(name = "diary_id")
val diaryId: Int, val diaryId: Int,
@ColumnInfo(name = "kindergarten_diary_id", defaultValue = "0")
val kindergartenDiaryId: Int,
@ColumnInfo(name = "diary_name") @ColumnInfo(name = "diary_name")
val diaryName: String, val diaryName: String,
@ -42,7 +50,6 @@ data class Semester(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0 var id: Long = 0
@ColumnInfo(name = "is_current") @ColumnInfo(name = "is_current")
var current: Boolean = false var current: Boolean = false
} }

View File

@ -5,7 +5,7 @@ import androidx.room.Entity
import androidx.room.Index import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.io.Serializable import java.io.Serializable
import java.time.LocalDateTime import java.time.Instant
@Entity( @Entity(
tableName = "Students", tableName = "Students",
@ -74,7 +74,7 @@ data class Student(
val isCurrent: Boolean, val isCurrent: Boolean,
@ColumnInfo(name = "registration_date") @ColumnInfo(name = "registration_date")
val registrationDate: LocalDateTime val registrationDate: Instant,
) : Serializable { ) : Serializable {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)

View File

@ -4,8 +4,8 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.io.Serializable import java.io.Serializable
import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
@Entity(tableName = "Timetable") @Entity(tableName = "Timetable")
data class Timetable( data class Timetable(
@ -18,9 +18,9 @@ data class Timetable(
val number: Int, val number: Int,
val start: LocalDateTime, val start: Instant,
val end: LocalDateTime, val end: Instant,
val date: LocalDate, val date: LocalDate,

View File

@ -4,8 +4,9 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.io.Serializable import java.io.Serializable
import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.util.*
@Entity(tableName = "TimetableAdditional") @Entity(tableName = "TimetableAdditional")
data class TimetableAdditional( data class TimetableAdditional(
@ -16,9 +17,9 @@ data class TimetableAdditional(
@ColumnInfo(name = "diary_id") @ColumnInfo(name = "diary_id")
val diaryId: Int, val diaryId: Int,
val start: LocalDateTime, val start: Instant,
val end: LocalDateTime, val end: Instant,
val date: LocalDate, val date: LocalDate,
@ -27,4 +28,10 @@ data class TimetableAdditional(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0 var id: Long = 0
@ColumnInfo(name = "repeat_id", defaultValue = "NULL")
var repeatId: UUID? = null
@ColumnInfo(name = "is_added_by_user", defaultValue = "0")
var isAddedByUser: Boolean = false
} }

View File

@ -43,12 +43,14 @@ class Migration12 : Migration(11, 12) {
private fun getStudentsIds(database: SupportSQLiteDatabase): List<Int> { private fun getStudentsIds(database: SupportSQLiteDatabase): List<Int> {
val students = mutableListOf<Int>() val students = mutableListOf<Int>()
val studentsCursor = database.query("SELECT student_id FROM Students") database.query("SELECT student_id FROM Students").use {
if (studentsCursor.moveToFirst()) { if (it.moveToFirst()) {
do { do {
students.add(studentsCursor.getInt(0)) students.add(it.getInt(0))
} while (studentsCursor.moveToNext()) } while (it.moveToNext())
} }
}
return students return students
} }

View File

@ -25,12 +25,14 @@ class Migration13 : Migration(12, 13) {
private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> { private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
val students = mutableListOf<Pair<Int, String>>() val students = mutableListOf<Pair<Int, String>>()
val studentsCursor = database.query("SELECT id, school_name FROM Students") database.query("SELECT id, school_name FROM Students").use {
if (studentsCursor.moveToFirst()) { if (it.moveToFirst()) {
do { do {
students.add(studentsCursor.getInt(0) to studentsCursor.getString(1)) students.add(it.getInt(0) to it.getString(1))
} while (studentsCursor.moveToNext()) } while (it.moveToNext())
} }
}
return students return students
} }
@ -42,12 +44,14 @@ class Migration13 : Migration(12, 13) {
private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List<Pair<Int, Int>> { private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List<Pair<Int, Int>> {
val students = mutableListOf<Pair<Int, Int>>() val students = mutableListOf<Pair<Int, Int>>()
val studentsCursor = database.query("SELECT student_id, class_id FROM Students") database.query("SELECT student_id, class_id FROM Students").use {
if (studentsCursor.moveToFirst()) { if (it.moveToFirst()) {
do { do {
students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1)) students.add(it.getInt(0) to it.getInt(1))
} while (studentsCursor.moveToNext()) } while (it.moveToNext())
} }
}
return students return students
} }

View File

@ -22,23 +22,27 @@ class Migration27 : Migration(26, 27) {
private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> { private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> {
val students = mutableListOf<Triple<Long, Int, String>>() val students = mutableListOf<Triple<Long, Int, String>>()
val studentsCursor = database.query("SELECT id, user_login_id, student_name FROM Students") database.query("SELECT id, user_login_id, student_name FROM Students").use {
if (studentsCursor.moveToFirst()) { if (it.moveToFirst()) {
do { do {
students.add(Triple(studentsCursor.getLong(0), studentsCursor.getInt(1), studentsCursor.getString(2))) students.add(Triple(it.getLong(0), it.getInt(1), it.getString(2)))
} while (studentsCursor.moveToNext()) } while (it.moveToNext())
} }
}
return students return students
} }
private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> { private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
val units = mutableListOf<Pair<Int, String>>() val units = mutableListOf<Pair<Int, String>>()
val unitsCursor = database.query("SELECT sender_id, sender_name FROM ReportingUnits") database.query("SELECT sender_id, sender_name FROM ReportingUnits").use {
if (unitsCursor.moveToFirst()) { if (it.moveToFirst()) {
do { do {
units.add(unitsCursor.getInt(0) to unitsCursor.getString(1)) units.add(it.getInt(0) to it.getString(1))
} while (unitsCursor.moveToNext()) } while (it.moveToNext())
} }
}
return units return units
} }

View File

@ -10,15 +10,17 @@ class Migration35(private val appInfo: AppInfo) : Migration(34, 35) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0") database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
val studentsCursor = database.query("SELECT * FROM Students") database.query("SELECT * FROM Students").use {
while (it.moveToNext()) {
while (studentsCursor.moveToNext()) { val studentId = it.getLongOrNull(0)
val studentId = studentsCursor.getLongOrNull(0)
database.execSQL( database.execSQL(
"""UPDATE Students """
UPDATE Students
SET avatar_color = ${appInfo.defaultColorsForAvatar.random()} SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
WHERE id = $studentId""" WHERE id = $studentId
"""
) )
} }
} }
} }
}

View File

@ -0,0 +1,102 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import java.time.Instant
import java.time.ZoneId
import java.time.ZoneOffset
class Migration46 : Migration(45, 46) {
override fun migrate(database: SupportSQLiteDatabase) {
migrateConferences(database)
migrateMessages(database)
migrateMobileDevices(database)
migrateNotifications(database)
migrateTimetable(database)
migrateTimetableAdditional(database)
}
private fun migrateConferences(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Conferences").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateMessages(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Messages").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateMobileDevices(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM MobileDevices").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateNotifications(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Notifications").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateTimetable(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Timetable").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end"))
val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
database.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
}
}
}
private fun migrateTimetableAdditional(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM TimetableAdditional").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end"))
val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
database.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
}
}
}
private fun Long.timestampLocalToUTC(): Long = Instant.ofEpochMilli(this)
.atZone(ZoneOffset.UTC)
.withZoneSameLocal(ZoneId.of("Europe/Warsaw"))
.withZoneSameInstant(ZoneId.systemDefault())
.toInstant()
.toEpochMilli()
}

View File

@ -10,7 +10,7 @@ fun List<SdkConference>.mapToEntities(semester: Semester) = map {
diaryId = semester.diaryId, diaryId = semester.diaryId,
agenda = it.agenda, agenda = it.agenda,
conferenceId = it.id, conferenceId = it.id,
date = it.date, date = it.dateZoned.toInstant(),
presentOnConference = it.presentOnConference, presentOnConference = it.presentOnConference,
subject = it.subject, subject = it.subject,
title = it.title title = it.title

View File

@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map { fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
SchoolAnnouncement( SchoolAnnouncement(
studentId = student.studentId, studentId = student.userLoginId,
date = it.date, date = it.date,
subject = it.subject, subject = it.subject,
content = it.content, content = it.content,

View File

@ -4,7 +4,7 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import java.time.LocalDateTime import java.time.Instant
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
@ -18,7 +18,7 @@ fun List<SdkMessage>.mapToEntities(student: Student) = map {
senderId = it.sender?.loginId ?: 0, senderId = it.sender?.loginId ?: 0,
recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów", recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
subject = it.subject.trim(), subject = it.subject.trim(),
date = it.date ?: LocalDateTime.now(), date = it.dateZoned?.toInstant() ?: Instant.now(),
folderId = it.folderId, folderId = it.folderId,
unread = it.unread ?: false, unread = it.unread ?: false,
removed = it.removed, removed = it.removed,

View File

@ -3,13 +3,13 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.pojos.MobileDeviceToken import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
fun List<SdkDevice>.mapToEntities(semester: Semester) = map { fun List<SdkDevice>.mapToEntities(semester: Semester) = map {
MobileDevice( MobileDevice(
userLoginId = semester.studentId, userLoginId = semester.studentId,
date = it.createDate, date = it.createDateZoned.toInstant(),
deviceId = it.id, deviceId = it.id,
name = it.name name = it.name
) )

View File

@ -7,6 +7,7 @@ fun List<SdkSemester>.mapToEntities(studentId: Int) = map {
Semester( Semester(
studentId = studentId, studentId = studentId,
diaryId = it.diaryId, diaryId = it.diaryId,
kindergartenDiaryId = it.kindergartenDiaryId,
diaryName = it.diaryName, diaryName = it.diaryName,
schoolYear = it.schoolYear, schoolYear = it.schoolYear,
semesterId = it.semesterId, semesterId = it.semesterId,

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import java.time.LocalDateTime import java.time.Instant
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) = map { fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) = map {
@ -24,7 +24,7 @@ fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) =
scrapperBaseUrl = it.scrapperBaseUrl, scrapperBaseUrl = it.scrapperBaseUrl,
loginType = it.loginType.name, loginType = it.loginType.name,
isCurrent = false, isCurrent = false,
registrationDate = LocalDateTime.now(), registrationDate = Instant.now(),
mobileBaseUrl = it.mobileBaseUrl, mobileBaseUrl = it.mobileBaseUrl,
privateKey = it.privateKey, privateKey = it.privateKey,
certificateKey = it.certificateKey, certificateKey = it.certificateKey,

View File

@ -21,8 +21,8 @@ fun List<SdkTimetable>.mapToEntities(semester: Semester) = map {
studentId = semester.studentId, studentId = semester.studentId,
diaryId = semester.diaryId, diaryId = semester.diaryId,
number = it.number, number = it.number,
start = it.start, start = it.startZoned.toInstant(),
end = it.end, end = it.endZoned.toInstant(),
date = it.date, date = it.date,
subject = it.subject, subject = it.subject,
subjectOld = it.subjectOld, subjectOld = it.subjectOld,
@ -45,8 +45,8 @@ fun List<SdkTimetableAdditional>.mapToEntities(semester: Semester) = map {
diaryId = semester.diaryId, diaryId = semester.diaryId,
subject = it.subject, subject = it.subject,
date = it.date, date = it.date,
start = it.start, start = it.startZoned.toInstant(),
end = it.end end = it.endZoned.toInstant(),
) )
} }

View File

@ -4,7 +4,6 @@ import io.github.wulkanowy.data.api.AdminMessageService
import io.github.wulkanowy.data.db.dao.AdminMessageDao import io.github.wulkanowy.data.db.dao.AdminMessageDao
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject import javax.inject.Inject
@ -14,23 +13,17 @@ import javax.inject.Singleton
class AdminMessageRepository @Inject constructor( class AdminMessageRepository @Inject constructor(
private val adminMessageService: AdminMessageService, private val adminMessageService: AdminMessageService,
private val adminMessageDao: AdminMessageDao, private val adminMessageDao: AdminMessageDao,
private val appInfo: AppInfo, private val appInfo: AppInfo
private val refreshHelper: AutoRefreshHelper,
) { ) {
private val saveFetchResultMutex = Mutex() private val saveFetchResultMutex = Mutex()
private val cacheKey = "admin_messages" suspend fun getAdminMessages(student: Student) = networkBoundResource(
suspend fun getAdminMessages(student: Student, forceRefresh: Boolean) = networkBoundResource(
mutex = saveFetchResultMutex, mutex = saveFetchResultMutex,
query = { adminMessageDao.loadAll() }, query = { adminMessageDao.loadAll() },
fetch = { adminMessageService.getAdminMessages() }, fetch = { adminMessageService.getAdminMessages() },
shouldFetch = { shouldFetch = { true },
refreshHelper.shouldBeRefreshed(cacheKey) || forceRefresh
},
saveFetchResult = { oldItems, newItems -> saveFetchResult = { oldItems, newItems ->
adminMessageDao.removeOldAndSaveNew(oldItems, newItems) adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
refreshHelper.updateLastRefreshTimestamp(cacheKey)
}, },
showSavedOnLoading = false, showSavedOnLoading = false,
mapResult = { adminMessages -> mapResult = { adminMessages ->

View File

@ -7,13 +7,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Absent import io.github.wulkanowy.sdk.pojo.Absent
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
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.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate import java.time.LocalDate
@ -52,7 +46,8 @@ class AttendanceRepository @Inject constructor(
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
}, },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getAttendance(start.monday, end.sunday, semester.semesterId) .getAttendance(start.monday, end.sunday, semester.semesterId)
.mapToEntities(semester) .mapToEntities(semester)
}, },
@ -90,7 +85,8 @@ class AttendanceRepository @Inject constructor(
timeId = attendance.timeId timeId = attendance.timeId
) )
} }
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.excuseForAbsence(items, reason) .excuseForAbsence(items, reason)
} }
} }

View File

@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
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 kotlinx.coroutines.sync.Mutex
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -38,7 +34,8 @@ class AttendanceSummaryRepository @Inject constructor(
}, },
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) }, query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getAttendanceSummary(subjectId) .getAttendanceSummary(subjectId)
.mapToEntities(semester, subjectId) .mapToEntities(semester, subjectId)
}, },

View File

@ -5,13 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
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 kotlinx.coroutines.sync.Mutex
import java.time.LocalDate import java.time.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -51,7 +45,8 @@ class CompletedLessonsRepository @Inject constructor(
) )
}, },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getCompletedLessons(start.monday, end.sunday) .getCompletedLessons(start.monday, end.sunday)
.mapToEntities(semester) .mapToEntities(semester)
}, },

View File

@ -6,16 +6,10 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
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.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import java.time.Instant import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneOffset
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -35,7 +29,7 @@ class ConferenceRepository @Inject constructor(
semester: Semester, semester: Semester,
forceRefresh: Boolean, forceRefresh: Boolean,
notify: Boolean = false, notify: Boolean = false,
startDate: LocalDateTime = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC), startDate: Instant = Instant.EPOCH,
) = networkBoundResource( ) = networkBoundResource(
mutex = saveFetchResultMutex, mutex = saveFetchResultMutex,
shouldFetch = { shouldFetch = {
@ -46,7 +40,8 @@ class ConferenceRepository @Inject constructor(
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate) conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
}, },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getConferences() .getConferences()
.mapToEntities(semester) .mapToEntities(semester)
.filter { it.date >= startDate } .filter { it.date >= startDate }
@ -66,7 +61,7 @@ class ConferenceRepository @Inject constructor(
conferenceDb.loadAll( conferenceDb.loadAll(
diaryId = semester.diaryId, diaryId = semester.diaryId,
studentId = semester.studentId, studentId = semester.studentId,
startDate = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC) startDate = Instant.EPOCH,
) )
suspend fun updateConference(conference: List<Conference>) = conferenceDb.updateAll(conference) suspend fun updateConference(conference: List<Conference>) = conferenceDb.updateAll(conference)

View File

@ -6,13 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.endExamsDay
import io.github.wulkanowy.utils.getRefreshKey
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.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate import java.time.LocalDate
@ -54,7 +48,8 @@ class ExamRepository @Inject constructor(
) )
}, },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getExams(start.startExamsDay, start.endExamsDay, semester.semesterId) .getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
.mapToEntities(semester) .mapToEntities(semester)
}, },

View File

@ -8,16 +8,12 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
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.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import java.time.LocalDateTime import java.time.Instant
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -51,7 +47,7 @@ class GradeRepository @Inject constructor(
}, },
fetch = { fetch = {
val (details, summary) = sdk.init(student) val (details, summary) = sdk.init(student)
.switchDiary(semester.diaryId, semester.schoolYear) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getGrades(semester.semesterId) .getGrades(semester.semesterId)
details.mapToEntities(semester) to summary.mapToEntities(semester) details.mapToEntities(semester) to summary.mapToEntities(semester)
@ -70,8 +66,8 @@ class GradeRepository @Inject constructor(
newDetails: List<Grade>, newDetails: List<Grade>,
notify: Boolean notify: Boolean
) { ) {
val notifyBreakDate = oldGrades.maxByOrNull {it.date } val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
?.date ?: student.registrationDate.toLocalDate() ?: student.registrationDate.toLocalDate()
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails) gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach { gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply { if (it.date >= notifyBreakDate) it.apply {
@ -101,13 +97,13 @@ class GradeRepository @Inject constructor(
} }
summary.predictedGradeLastChange = when { summary.predictedGradeLastChange = when {
oldSummary == null -> LocalDateTime.now() oldSummary == null -> Instant.now()
summary.predictedGrade != oldSummary.predictedGrade -> LocalDateTime.now() summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
else -> oldSummary.predictedGradeLastChange else -> oldSummary.predictedGradeLastChange
} }
summary.finalGradeLastChange = when { summary.finalGradeLastChange = when {
oldSummary == null -> LocalDateTime.now() oldSummary == null -> Instant.now()
summary.finalGrade != oldSummary.finalGrade -> LocalDateTime.now() summary.finalGrade != oldSummary.finalGrade -> Instant.now()
else -> oldSummary.finalGradeLastChange else -> oldSummary.finalGradeLastChange
} }
}) })

View File

@ -12,13 +12,9 @@ import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
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 kotlinx.coroutines.sync.Mutex
import java.util.Locale import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -54,7 +50,8 @@ class GradeStatisticsRepository @Inject constructor(
}, },
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getGradesPartialStatistics(semester.semesterId) .getGradesPartialStatistics(semester.semesterId)
.mapToEntities(semester) .mapToEntities(semester)
}, },
@ -101,7 +98,8 @@ class GradeStatisticsRepository @Inject constructor(
}, },
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getGradesSemesterStatistics(semester.semesterId) .getGradesSemesterStatistics(semester.semesterId)
.mapToEntities(semester) .mapToEntities(semester)
}, },
@ -155,7 +153,8 @@ class GradeStatisticsRepository @Inject constructor(
}, },
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getGradesPointsStatistics(semester.semesterId) .getGradesPointsStatistics(semester.semesterId)
.mapToEntities(semester) .mapToEntities(semester)
}, },

View File

@ -6,13 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
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 kotlinx.coroutines.sync.Mutex
import java.time.LocalDate import java.time.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -53,7 +47,8 @@ class HomeworkRepository @Inject constructor(
) )
}, },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getHomework(start.monday, end.sunday) .getHomework(start.monday, end.sunday)
.mapToEntities(semester) .mapToEntities(semester)
}, },

View File

@ -8,11 +8,7 @@ import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
import io.github.wulkanowy.data.pojos.MobileDeviceToken import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
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 kotlinx.coroutines.sync.Mutex
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -40,7 +36,8 @@ class MobileDeviceRepository @Inject constructor(
}, },
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) }, query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getRegisteredDevices() .getRegisteredDevices()
.mapToEntities(semester) .mapToEntities(semester)
}, },
@ -53,14 +50,16 @@ class MobileDeviceRepository @Inject constructor(
) )
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) { suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.unregisterDevice(device.deviceId) .unregisterDevice(device.deviceId)
mobileDb.deleteAll(listOf(device)) mobileDb.deleteAll(listOf(device))
} }
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken { suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) return sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getToken() .getToken()
.mapToMobileDeviceToken() .mapToMobileDeviceToken()
} }

View File

@ -6,11 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
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.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject import javax.inject.Inject
@ -42,7 +38,8 @@ class NoteRepository @Inject constructor(
}, },
query = { noteDb.loadAll(student.studentId) }, query = { noteDb.loadAll(student.studentId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getNotes(semester.semesterId) .getNotes(semester.semesterId)
.mapToEntities(semester) .mapToEntities(semester)
}, },

View File

@ -7,24 +7,16 @@ import com.fredporciuncula.flow.preferences.FlowSharedPreferences
import com.fredporciuncula.flow.preferences.Preference import com.fredporciuncula.flow.preferences.Preference
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.enums.AppTheme import io.github.wulkanowy.data.enums.*
import io.github.wulkanowy.data.enums.GradeColorTheme
import io.github.wulkanowy.data.enums.GradeExpandMode
import io.github.wulkanowy.data.enums.GradeSortingMode
import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.sdk.toLocalDate
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.time.LocalDate import java.time.Instant
import java.time.LocalDateTime
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -133,6 +125,12 @@ class PreferencesRepository @Inject constructor(
R.bool.pref_default_notification_piggyback R.bool.pref_default_notification_piggyback
) )
val isNotificationPiggybackRemoveOriginalEnabled: Boolean
get() = getBoolean(
R.string.pref_key_notifications_piggyback_cancel_original,
R.bool.pref_default_notification_piggyback_cancel_original
)
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug) val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnable: Boolean val isDebugNotificationEnable: Boolean
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug) get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
@ -202,10 +200,10 @@ class PreferencesRepository @Inject constructor(
R.bool.pref_default_optional_arithmetic_average R.bool.pref_default_optional_arithmetic_average
) )
var lasSyncDate: LocalDateTime var lasSyncDate: Instant?
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date) get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
.toLocalDateTime() .takeIf { it != 0L }?.let(Instant::ofEpochMilli)
set(value) = sharedPref.edit().putLong("last_sync_date", value.toTimestamp()).apply() set(value) = sharedPref.edit().putLong("last_sync_date", value?.toEpochMilli() ?: 0).apply()
var dashboardItemsPosition: Map<DashboardItem.Type, Int>? var dashboardItemsPosition: Map<DashboardItem.Type, Int>?
get() { get() {
@ -264,11 +262,12 @@ class PreferencesRepository @Inject constructor(
get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0) get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0)
set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply() set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply()
var inAppReviewDate: LocalDate? var inAppReviewDate: Instant?
get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L } get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L }
?.toLocalDate() ?.let(Instant::ofEpochMilli)
set(value) = sharedPref.edit().putLong(PREF_KEY_IN_APP_REVIEW_DATE, value!!.toTimestamp()) set(value) = sharedPref.edit {
.apply() putLong(PREF_KEY_IN_APP_REVIEW_DATE, value?.toEpochMilli() ?: 0)
}
var isAppReviewDone: Boolean var isAppReviewDone: Boolean
get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false) get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false)

View File

@ -38,7 +38,9 @@ class SchoolRepository @Inject constructor(
}, },
query = { schoolDb.load(semester.studentId, semester.classId) }, query = { schoolDb.load(semester.studentId, semester.classId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool() sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getSchool()
.mapToEntity(semester) .mapToEntity(semester)
}, },
saveFetchResult = { old, new -> saveFetchResult = { old, new ->

View File

@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.getCurrentOrLast
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.isCurrent
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -43,10 +39,14 @@ class SemesterRepository @Inject constructor(
): Boolean { ): Boolean {
val isNoSemesters = semesters.isEmpty() val isNoSemesters = semesters.isEmpty()
val isRefreshOnModeChangeRequired = val isRefreshOnModeChangeRequired = when {
if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> {
semesters.firstOrNull { it.isCurrent }?.diaryId == 0 semesters.firstOrNull { it.isCurrent }?.let {
} else false 0 == it.diaryId && 0 == it.kindergartenDiaryId
} == true
}
else -> false
}
val isRefreshOnNoCurrentAppropriate = val isRefreshOnNoCurrentAppropriate =
refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent } refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }

View File

@ -28,7 +28,8 @@ class StudentInfoRepository @Inject constructor(
shouldFetch = { it == null || forceRefresh }, shouldFetch = { it == null || forceRefresh },
query = { studentInfoDao.loadStudentInfo(student.studentId) }, query = { studentInfoDao.loadStudentInfo(student.studentId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getStudentInfo().mapToEntity(semester) .getStudentInfo().mapToEntity(semester)
}, },
saveFetchResult = { old, new -> saveFetchResult = { old, new ->

View File

@ -128,4 +128,7 @@ class StudentRepository @Inject constructor(
suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) = suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) =
studentDb.update(studentNickAndAvatar) studentDb.update(studentNickAndAvatar)
suspend fun isOneUniqueStudent() = getSavedStudents(false)
.distinctBy { it.student.studentName }.size == 1
} }

View File

@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
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 kotlinx.coroutines.sync.Mutex
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -37,7 +33,8 @@ class SubjectRepository @Inject constructor(
}, },
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) }, query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getSubjects().mapToEntities(semester) .getSubjects().mapToEntities(semester)
}, },
saveFetchResult = { old, new -> saveFetchResult = { old, new ->

View File

@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
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 kotlinx.coroutines.sync.Mutex
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -37,7 +33,8 @@ class TeacherRepository @Inject constructor(
}, },
query = { teacherDb.loadAll(semester.studentId, semester.classId) }, query = { teacherDb.loadAll(semester.studentId, semester.classId) },
fetch = { fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getTeachers(semester.semesterId) .getTeachers(semester.semesterId)
.mapToEntities(semester) .mapToEntities(semester)
}, },

View File

@ -3,22 +3,12 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
import io.github.wulkanowy.data.db.dao.TimetableDao import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.pojos.TimetableFull import io.github.wulkanowy.data.pojos.TimetableFull
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
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.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
@ -62,7 +52,7 @@ class TimetableRepository @Inject constructor(
query = { getFullTimetableFromDatabase(student, semester, start, end) }, query = { getFullTimetableFromDatabase(student, semester, start, end) },
fetch = { fetch = {
val timetableFull = sdk.init(student) val timetableFull = sdk.init(student)
.switchDiary(semester.diaryId, semester.schoolYear) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getTimetableFull(start.monday, end.sunday) .getTimetableFull(start.monday, end.sunday)
timetableFull.mapToEntities(semester) timetableFull.mapToEntities(semester)
@ -152,7 +142,8 @@ class TimetableRepository @Inject constructor(
old: List<TimetableAdditional>, old: List<TimetableAdditional>,
new: List<TimetableAdditional> new: List<TimetableAdditional>
) { ) {
timetableAdditionalDb.deleteAll(old uniqueSubtract new) val oldFiltered = old.filter { !it.isAddedByUser }
timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new)
timetableAdditionalDb.insertAll(new uniqueSubtract old) timetableAdditionalDb.insertAll(new uniqueSubtract old)
} }
@ -160,4 +151,14 @@ class TimetableRepository @Inject constructor(
timetableHeaderDb.deleteAll(old uniqueSubtract new) timetableHeaderDb.deleteAll(old uniqueSubtract new)
timetableHeaderDb.insertAll(new uniqueSubtract old) timetableHeaderDb.insertAll(new uniqueSubtract old)
} }
suspend fun saveAdditionalList(additionalList: List<TimetableAdditional>) =
timetableAdditionalDb.insertAll(additionalList)
suspend fun deleteAdditional(additional: TimetableAdditional, deleteSeries: Boolean) =
if (deleteSeries) {
timetableAdditionalDb.deleteAllByRepeatId(additional.repeatId!!)
} else {
timetableAdditionalDb.deleteAll(listOf(additional))
}
} }

View File

@ -1,9 +0,0 @@
package io.github.wulkanowy.services
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
abstract class HiltBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {}
}

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.services.alarm package io.github.wulkanowy.services.alarm
import android.app.PendingIntent import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
@ -12,14 +13,12 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.HiltBroadcastReceiver
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID
import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.PendingIntentCompat import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.toLocalDateTime
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -28,7 +27,7 @@ import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class TimetableNotificationReceiver : HiltBroadcastReceiver() { class TimetableNotificationReceiver : BroadcastReceiver() {
@Inject @Inject
lateinit var studentRepository: StudentRepository lateinit var studentRepository: StudentRepository
@ -41,6 +40,8 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
const val NOTIFICATION_TYPE_UPCOMING = 2 const val NOTIFICATION_TYPE_UPCOMING = 2
const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3 const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3
// FIXME only shows one notification even if there are multiple students.
// Probably want to fix after #721 is merged.
const val NOTIFICATION_ID = 2137 const val NOTIFICATION_ID = 2137
const val STUDENT_NAME = "student_name" const val STUDENT_NAME = "student_name"
@ -56,20 +57,24 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
Timber.d("Receiving intent... ${intent.toUri(0)}") Timber.d("Receiving intent... ${intent.toUri(0)}")
flowWithResource { flowWithResource {
val showStudentName = !studentRepository.isOneUniqueStudent()
val student = studentRepository.getCurrentStudent(false) val student = studentRepository.getCurrentStudent(false)
val studentId = intent.getIntExtra(STUDENT_ID, 0) val studentId = intent.getIntExtra(STUDENT_ID, 0)
if (student.studentId == studentId) prepareNotification(context, intent)
else Timber.d("Notification studentId($studentId) differs from current(${student.studentId})") if (student.studentId == studentId) {
prepareNotification(context, intent, showStudentName)
} else {
Timber.d("Notification studentId($studentId) differs from current(${student.studentId})")
}
}.onEach { }.onEach {
if (it.status == Status.ERROR) Timber.e(it.error!!) if (it.status == Status.ERROR) Timber.e(it.error!!)
}.launchIn(GlobalScope) }.launchIn(GlobalScope)
} }
private fun prepareNotification(context: Context, intent: Intent) { private fun prepareNotification(context: Context, intent: Intent, showStudentName: Boolean) {
val type = intent.getIntExtra(LESSON_TYPE, 0) val type = intent.getIntExtra(LESSON_TYPE, 0)
val isPersistent = preferencesRepository.isUpcomingLessonsNotificationsPersistent val isPersistent = preferencesRepository.isUpcomingLessonsNotificationsPersistent
@ -78,7 +83,7 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
} }
val studentId = intent.getIntExtra(STUDENT_ID, 0) val studentId = intent.getIntExtra(STUDENT_ID, 0)
val studentName = intent.getStringExtra(STUDENT_NAME) val studentName = intent.getStringExtra(STUDENT_NAME).takeIf { showStudentName }
val subject = intent.getStringExtra(LESSON_TITLE) val subject = intent.getStringExtra(LESSON_TITLE)
val room = intent.getStringExtra(LESSON_ROOM) val room = intent.getStringExtra(LESSON_ROOM)
@ -89,21 +94,28 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE) val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE)
val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM) val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM)
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId") Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: $start, student: $studentId")
showNotification( val notificationTitleResId =
context, isPersistent, studentName, if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next
if (type == NOTIFICATION_TYPE_CURRENT) end else start, end - start, val notificationTitle =
context.getString( context.getString(notificationTitleResId, "($room) $subject".removePrefix("()"))
if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next,
"($room) $subject".removePrefix("()") val nextLessonText = nextSubject?.let {
),
nextSubject?.let {
context.getString( context.getString(
R.string.timetable_later, R.string.timetable_later,
"($nextRoom) $nextSubject".removePrefix("()") "($nextRoom) $nextSubject".removePrefix("()")
) )
} }
showNotification(
context = context,
isPersistent = isPersistent,
studentName = studentName,
countDown = if (type == NOTIFICATION_TYPE_CURRENT) end else start,
timeout = end - start,
title = notificationTitle,
next = nextLessonText
) )
} }
@ -130,9 +142,10 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
.setTimeoutAfter(timeout) .setTimeoutAfter(timeout)
.setSmallIcon(R.drawable.ic_stat_timetable) .setSmallIcon(R.drawable.ic_stat_timetable)
.setColor(context.getCompatColor(R.color.colorPrimary)) .setColor(context.getCompatColor(R.color.colorPrimary))
.setStyle(NotificationCompat.InboxStyle().also { .setStyle(NotificationCompat.InboxStyle()
it.setSummaryText(studentName) .addLine(next)
it.addLine(next) .also { inboxStyle ->
studentName?.let { inboxStyle.setSummaryText(it) }
}) })
.setContentIntent( .setContentIntent(
PendingIntent.getActivity( PendingIntent.getActivity(

View File

@ -28,12 +28,12 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio
import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.PendingIntentCompat import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.nickOrName import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.toTimestamp
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import java.time.Duration.ofMinutes
import java.time.Instant
import java.time.Instant.now
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalDateTime.now
import javax.inject.Inject import javax.inject.Inject
class TimetableNotificationSchedulerHelper @Inject constructor( class TimetableNotificationSchedulerHelper @Inject constructor(
@ -43,14 +43,14 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
private val dispatchersProvider: DispatchersProvider, private val dispatchersProvider: DispatchersProvider,
) { ) {
private fun getRequestCode(time: LocalDateTime, studentId: Int) = private fun getRequestCode(time: Instant, studentId: Int): Int =
(time.toTimestamp() * studentId).toInt() (time.toEpochMilli() * studentId).toInt()
private fun getUpcomingLessonTime( private fun getUpcomingLessonTime(
index: Int, index: Int,
day: List<Timetable>, day: List<Timetable>,
lesson: Timetable lesson: Timetable
) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30) ): Instant = day.getOrNull(index - 1)?.end ?: lesson.start.minus(ofMinutes(30))
suspend fun cancelScheduled(lessons: List<Timetable>, student: Student) { suspend fun cancelScheduled(lessons: List<Timetable>, student: Student) {
val studentId = student.studentId val studentId = student.studentId
@ -71,7 +71,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
} }
} }
private fun cancelScheduledTo(range: ClosedRange<LocalDateTime>, requestCode: Int) { private fun cancelScheduledTo(range: ClosedRange<Instant>, requestCode: Int) {
if (now() in range) cancelNotification() if (now() in range) cancelNotification()
alarmManager.cancel( alarmManager.cancel(
@ -150,8 +150,8 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
putExtra(STUDENT_ID, student.studentId) putExtra(STUDENT_ID, student.studentId)
putExtra(STUDENT_NAME, student.nickOrName) putExtra(STUDENT_NAME, student.nickOrName)
putExtra(LESSON_ROOM, lesson.room) putExtra(LESSON_ROOM, lesson.room)
putExtra(LESSON_START, lesson.start.toTimestamp()) putExtra(LESSON_START, lesson.start.toEpochMilli())
putExtra(LESSON_END, lesson.end.toTimestamp()) putExtra(LESSON_END, lesson.end.toEpochMilli())
putExtra(LESSON_TITLE, lesson.subject) putExtra(LESSON_TITLE, lesson.subject)
putExtra(LESSON_NEXT_TITLE, nextLesson?.subject) putExtra(LESSON_NEXT_TITLE, nextLesson?.subject)
putExtra(LESSON_NEXT_ROOM, nextLesson?.room) putExtra(LESSON_NEXT_ROOM, nextLesson?.room)
@ -162,11 +162,11 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
intent: Intent, intent: Intent,
studentId: Int, studentId: Int,
notificationType: Int, notificationType: Int,
time: LocalDateTime time: Instant
) { ) {
try { try {
AlarmManagerCompat.setExactAndAllowWhileIdle( AlarmManagerCompat.setExactAndAllowWhileIdle(
alarmManager, RTC_WAKEUP, time.toTimestamp(), alarmManager, RTC_WAKEUP, time.toEpochMilli(),
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also { PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
it.putExtra(LESSON_TYPE, notificationType) it.putExtra(LESSON_TYPE, notificationType)
}, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)

View File

@ -19,6 +19,9 @@ class VulcanNotificationListenerService : NotificationListenerService() {
override fun onNotificationPosted(statusBarNotification: StatusBarNotification?) { override fun onNotificationPosted(statusBarNotification: StatusBarNotification?) {
if (statusBarNotification?.packageName == "pl.edu.vulcan.hebe" && preferenceRepository.isNotificationPiggybackEnabled) { if (statusBarNotification?.packageName == "pl.edu.vulcan.hebe" && preferenceRepository.isNotificationPiggybackEnabled) {
syncManager.startOneTimeSyncWorker() syncManager.startOneTimeSyncWorker()
if (preferenceRepository.isNotificationPiggybackRemoveOriginalEnabled) {
cancelNotification(statusBarNotification.key)
}
} }
} }
} }

View File

@ -74,10 +74,12 @@ class SyncManager @Inject constructor(
} }
} }
fun startOneTimeSyncWorker(): Flow<WorkInfo?> { // if quiet, no notifications will be sent
fun startOneTimeSyncWorker(quiet: Boolean = false): Flow<WorkInfo?> {
val work = OneTimeWorkRequestBuilder<SyncWorker>() val work = OneTimeWorkRequestBuilder<SyncWorker>()
.setInputData( .setInputData(
Data.Builder() Data.Builder()
.putBoolean("quiet", quiet)
.putBoolean("one_time", true) .putBoolean("one_time", true)
.build() .build()
) )

View File

@ -23,7 +23,7 @@ import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import java.time.LocalDateTime import java.time.Instant
import kotlin.random.Random import kotlin.random.Random
@HiltWorker @HiltWorker
@ -38,18 +38,23 @@ class SyncWorker @AssistedInject constructor(
private val dispatchersProvider: DispatchersProvider private val dispatchersProvider: DispatchersProvider
) : CoroutineWorker(appContext, workerParameters) { ) : CoroutineWorker(appContext, workerParameters) {
override suspend fun doWork() = withContext(dispatchersProvider.io) { override suspend fun doWork(): Result = withContext(dispatchersProvider.io) {
Timber.i("SyncWorker is starting") Timber.i("SyncWorker is starting")
if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure() if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure()
val (student, semester) = try {
val student = studentRepository.getCurrentStudent() val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student, true) val semester = semesterRepository.getCurrentSemester(student, true)
student to semester
} catch (e: Throwable) {
return@withContext getResultFromErrors(listOf(e))
}
val exceptions = works.mapNotNull { work -> val exceptions = works.mapNotNull { work ->
try { try {
Timber.i("${work::class.java.simpleName} is starting") Timber.i("${work::class.java.simpleName} is starting")
work.doWork(student, semester) work.doWork(student, semester, isNotificationsEnabled())
Timber.i("${work::class.java.simpleName} result: Success") Timber.i("${work::class.java.simpleName} result: Success")
null null
} catch (e: Throwable) { } catch (e: Throwable) {
@ -62,20 +67,7 @@ class SyncWorker @AssistedInject constructor(
} }
} }
} }
val result = when { val result = getResultFromErrors(exceptions)
exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
Result.failure(
Data.Builder()
.putString("error", exceptions.map { it.stackTraceToString() }.toString())
.build()
)
}
exceptions.isNotEmpty() -> Result.retry()
else -> {
preferencesRepository.lasSyncDate = LocalDateTime.now()
Result.success()
}
}
if (preferencesRepository.isDebugNotificationEnable) notify(result) if (preferencesRepository.isDebugNotificationEnable) notify(result)
Timber.i("SyncWorker result: $result") Timber.i("SyncWorker result: $result")
@ -83,6 +75,27 @@ class SyncWorker @AssistedInject constructor(
return@withContext result return@withContext result
} }
private fun isNotificationsEnabled(): Boolean {
val quiet = inputData.getBoolean("quiet", false)
return preferencesRepository.isNotificationsEnable && !quiet
}
private fun getResultFromErrors(errors: List<Throwable>): Result = when {
errors.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
Result.failure(
Data.Builder()
.putString("error_message", errors.joinToString { it.message.toString() })
.putString("error_stack", errors.map { it.stackTraceToString() }.toString())
.build()
)
}
errors.isNotEmpty() -> Result.retry()
else -> {
preferencesRepository.lasSyncDate = Instant.now()
Result.success()
}
}
private fun notify(result: Result) { private fun notify(result: Result) {
notificationManager.notify( notificationManager.notify(
Random.nextInt(Int.MAX_VALUE), Random.nextInt(Int.MAX_VALUE),

View File

@ -18,7 +18,7 @@ import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.getCompatBitmap import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.nickOrName import io.github.wulkanowy.utils.nickOrName
import java.time.LocalDateTime import java.time.Instant
import javax.inject.Inject import javax.inject.Inject
import kotlin.random.Random import kotlin.random.Random
@ -57,7 +57,7 @@ class AppNotificationManager @Inject constructor(
NotificationCompat.BigTextStyle() NotificationCompat.BigTextStyle()
.bigText(notificationData.content) .bigText(notificationData.content)
.also { builder -> .also { builder ->
if (shouldShowStudentName()) { if (!studentRepository.isOneUniqueStudent()) {
builder.setSummaryText(student.nickOrName) builder.setSummaryText(student.nickOrName)
} }
} }
@ -102,7 +102,7 @@ class AppNotificationManager @Inject constructor(
NotificationCompat.BigTextStyle() NotificationCompat.BigTextStyle()
.bigText(notificationData.content) .bigText(notificationData.content)
.also { builder -> .also { builder ->
if (shouldShowStudentName()) { if (!studentRepository.isOneUniqueStudent()) {
builder.setSummaryText(student.nickOrName) builder.setSummaryText(student.nickOrName)
} }
} }
@ -134,7 +134,7 @@ class AppNotificationManager @Inject constructor(
.setStyle( .setStyle(
NotificationCompat.InboxStyle() NotificationCompat.InboxStyle()
.also { builder -> .also { builder ->
if (shouldShowStudentName()) { if (!studentRepository.isOneUniqueStudent()) {
builder.setSummaryText(student.nickOrName) builder.setSummaryText(student.nickOrName)
} }
groupNotificationData.notificationDataList.forEach { groupNotificationData.notificationDataList.forEach {
@ -169,12 +169,9 @@ class AppNotificationManager @Inject constructor(
title = notificationData.title, title = notificationData.title,
content = notificationData.content, content = notificationData.content,
type = notificationType, type = notificationType,
date = LocalDateTime.now() date = Instant.now(),
) )
notificationRepository.saveNotification(notificationEntity) notificationRepository.saveNotification(notificationEntity)
} }
private suspend fun shouldShowStudentName(): Boolean =
studentRepository.getSavedStudents(decryptPass = false).size > 1
} }

View File

@ -11,8 +11,8 @@ import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
import javax.inject.Inject import javax.inject.Inject
class ChangeTimetableNotification @Inject constructor( class ChangeTimetableNotification @Inject constructor(
@ -21,7 +21,7 @@ class ChangeTimetableNotification @Inject constructor(
) { ) {
suspend fun notify(items: List<Timetable>, student: Student) { suspend fun notify(items: List<Timetable>, student: Student) {
val currentTime = LocalDateTime.now() val currentTime = Instant.now()
val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime } val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime }
val notificationDataList = changedLessons.groupBy { it.date } val notificationDataList = changedLessons.groupBy { it.date }
.map { (date, lessons) -> .map { (date, lessons) ->

View File

@ -11,7 +11,7 @@ import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import java.time.LocalDateTime import java.time.Instant
import javax.inject.Inject import javax.inject.Inject
class NewConferenceNotification @Inject constructor( class NewConferenceNotification @Inject constructor(
@ -20,7 +20,7 @@ class NewConferenceNotification @Inject constructor(
) { ) {
suspend fun notify(items: List<Conference>, student: Student) { suspend fun notify(items: List<Conference>, student: Student) {
val today = LocalDateTime.now() val today = Instant.now()
val lines = items.filter { !it.date.isBefore(today) } val lines = items.filter { !it.date.isBefore(today) }
.map { .map {
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}" "${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"

View File

@ -22,7 +22,10 @@ class NewGradeNotification @Inject constructor(
val notificationDataList = items.map { val notificationDataList = items.map {
NotificationData( NotificationData(
title = context.getPlural(R.plurals.grade_new_items, 1), title = context.getPlural(R.plurals.grade_new_items, 1),
content = "${it.subject}: ${it.entry}", content = buildString {
append("${it.subject}: ${it.entry}")
if (it.comment.isNotBlank()) append(" (${it.comment})")
},
intentToStart = SplashActivity.getStartIntent(context, Destination.Grade), intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
) )
} }

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.services.sync.notifications package io.github.wulkanowy.services.sync.notifications
import android.content.Context import android.content.Context
import androidx.core.text.parseAsHtml
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
@ -28,7 +29,7 @@ class NewSchoolAnnouncementNotification @Inject constructor(
R.plurals.school_announcement_notify_new_item_title, R.plurals.school_announcement_notify_new_item_title,
1 1
), ),
content = "${it.subject}: ${it.content}" content = "${it.subject}: ${it.content.parseAsHtml()}"
) )
} }
val groupNotificationData = GroupNotificationData( val groupNotificationData = GroupNotificationData(

View File

@ -10,7 +10,12 @@ class AttendanceSummaryWork @Inject constructor(
private val attendanceSummaryRepository: AttendanceSummaryRepository private val attendanceSummaryRepository: AttendanceSummaryRepository
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true).waitForResult() attendanceSummaryRepository.getAttendanceSummary(
student = student,
semester = semester,
subjectId = -1,
forceRefresh = true,
).waitForResult()
} }
} }

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.AttendanceRepository import io.github.wulkanowy.data.repositories.AttendanceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification
import io.github.wulkanowy.utils.previousOrSameSchoolDay import io.github.wulkanowy.utils.previousOrSameSchoolDay
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
@ -14,17 +13,16 @@ import javax.inject.Inject
class AttendanceWork @Inject constructor( class AttendanceWork @Inject constructor(
private val attendanceRepository: AttendanceRepository, private val attendanceRepository: AttendanceRepository,
private val newAttendanceNotification: NewAttendanceNotification, private val newAttendanceNotification: NewAttendanceNotification,
private val preferencesRepository: PreferencesRepository
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
attendanceRepository.getAttendance( attendanceRepository.getAttendance(
student = student, student = student,
semester = semester, semester = semester,
start = now().previousOrSameSchoolDay, start = now().previousOrSameSchoolDay,
end = now().previousOrSameSchoolDay, end = now().previousOrSameSchoolDay,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify,
) )
.waitForResult() .waitForResult()

View File

@ -13,7 +13,13 @@ class CompletedLessonWork @Inject constructor(
private val completedLessonsRepository: CompletedLessonsRepository private val completedLessonsRepository: CompletedLessonsRepository
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().sunday, true).waitForResult() completedLessonsRepository.getCompletedLessons(
student = student,
semester = semester,
start = now().monday,
end = now().sunday,
forceRefresh = true,
).waitForResult()
} }
} }

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.ConferenceRepository import io.github.wulkanowy.data.repositories.ConferenceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -11,16 +10,15 @@ import javax.inject.Inject
class ConferenceWork @Inject constructor( class ConferenceWork @Inject constructor(
private val conferenceRepository: ConferenceRepository, private val conferenceRepository: ConferenceRepository,
private val preferencesRepository: PreferencesRepository,
private val newConferenceNotification: NewConferenceNotification, private val newConferenceNotification: NewConferenceNotification,
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
conferenceRepository.getConferences( conferenceRepository.getConferences(
student = student, student = student,
semester = semester, semester = semester,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify
).waitForResult() ).waitForResult()
conferenceRepository.getConferenceFromDatabase(semester).first() conferenceRepository.getConferenceFromDatabase(semester).first()

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.ExamRepository import io.github.wulkanowy.data.repositories.ExamRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewExamNotification import io.github.wulkanowy.services.sync.notifications.NewExamNotification
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -12,18 +11,17 @@ import javax.inject.Inject
class ExamWork @Inject constructor( class ExamWork @Inject constructor(
private val examRepository: ExamRepository, private val examRepository: ExamRepository,
private val preferencesRepository: PreferencesRepository,
private val newExamNotification: NewExamNotification, private val newExamNotification: NewExamNotification,
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
examRepository.getExams( examRepository.getExams(
student = student, student = student,
semester = semester, semester = semester,
start = now(), start = now(),
end = now(), end = now(),
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify,
).waitForResult() ).waitForResult()
examRepository.getExamsFromDatabase(semester, now()).first() examRepository.getExamsFromDatabase(semester, now()).first()

View File

@ -10,7 +10,7 @@ class GradeStatisticsWork @Inject constructor(
private val gradeStatisticsRepository: GradeStatisticsRepository private val gradeStatisticsRepository: GradeStatisticsRepository
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
with(gradeStatisticsRepository) { with(gradeStatisticsRepository) {
getGradesPartialStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() getGradesPartialStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
getGradesSemesterStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() getGradesSemesterStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.GradeRepository import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewGradeNotification import io.github.wulkanowy.services.sync.notifications.NewGradeNotification
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -11,16 +10,15 @@ import javax.inject.Inject
class GradeWork @Inject constructor( class GradeWork @Inject constructor(
private val gradeRepository: GradeRepository, private val gradeRepository: GradeRepository,
private val preferencesRepository: PreferencesRepository,
private val newGradeNotification: NewGradeNotification, private val newGradeNotification: NewGradeNotification,
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
gradeRepository.getGrades( gradeRepository.getGrades(
student = student, student = student,
semester = semester, semester = semester,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify,
).waitForResult() ).waitForResult()
gradeRepository.getGradesFromDatabase(semester).first() gradeRepository.getGradesFromDatabase(semester).first()

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.HomeworkRepository import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification
import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
@ -13,18 +12,17 @@ import javax.inject.Inject
class HomeworkWork @Inject constructor( class HomeworkWork @Inject constructor(
private val homeworkRepository: HomeworkRepository, private val homeworkRepository: HomeworkRepository,
private val preferencesRepository: PreferencesRepository,
private val newHomeworkNotification: NewHomeworkNotification, private val newHomeworkNotification: NewHomeworkNotification,
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
homeworkRepository.getHomework( homeworkRepository.getHomework(
student = student, student = student,
semester = semester, semester = semester,
start = now().nextOrSameSchoolDay, start = now().nextOrSameSchoolDay,
end = now().nextOrSameSchoolDay, end = now().nextOrSameSchoolDay,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify,
).waitForResult() ).waitForResult()
homeworkRepository.getHomeworkFromDatabase(semester, now(), now().plusDays(7)).first() homeworkRepository.getHomeworkFromDatabase(semester, now(), now().plusDays(7)).first()

View File

@ -3,22 +3,20 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.LuckyNumberRepository import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
import javax.inject.Inject import javax.inject.Inject
class LuckyNumberWork @Inject constructor( class LuckyNumberWork @Inject constructor(
private val luckyNumberRepository: LuckyNumberRepository, private val luckyNumberRepository: LuckyNumberRepository,
private val preferencesRepository: PreferencesRepository,
private val newLuckyNumberNotification: NewLuckyNumberNotification, private val newLuckyNumberNotification: NewLuckyNumberNotification,
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
luckyNumberRepository.getLuckyNumber( luckyNumberRepository.getLuckyNumber(
student = student, student = student,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify,
).waitForResult() ).waitForResult()
luckyNumberRepository.getNotNotifiedLuckyNumber(student)?.let { luckyNumberRepository.getNotNotifiedLuckyNumber(student)?.let {

View File

@ -4,7 +4,6 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -12,17 +11,16 @@ import javax.inject.Inject
class MessageWork @Inject constructor( class MessageWork @Inject constructor(
private val messageRepository: MessageRepository, private val messageRepository: MessageRepository,
private val preferencesRepository: PreferencesRepository,
private val newMessageNotification: NewMessageNotification, private val newMessageNotification: NewMessageNotification,
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
messageRepository.getMessages( messageRepository.getMessages(
student = student, student = student,
semester = semester, semester = semester,
folder = RECEIVED, folder = RECEIVED,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify
).waitForResult() ).waitForResult()
messageRepository.getMessagesFromDatabase(student).first() messageRepository.getMessagesFromDatabase(student).first()

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.NoteRepository import io.github.wulkanowy.data.repositories.NoteRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewNoteNotification import io.github.wulkanowy.services.sync.notifications.NewNoteNotification
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -11,16 +10,15 @@ import javax.inject.Inject
class NoteWork @Inject constructor( class NoteWork @Inject constructor(
private val noteRepository: NoteRepository, private val noteRepository: NoteRepository,
private val preferencesRepository: PreferencesRepository,
private val newNoteNotification: NewNoteNotification, private val newNoteNotification: NewNoteNotification,
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
noteRepository.getNotes( noteRepository.getNotes(
student = student, student = student,
semester = semester, semester = semester,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify,
).waitForResult() ).waitForResult()
noteRepository.getNotesFromDatabase(student).first() noteRepository.getNotesFromDatabase(student).first()

View File

@ -11,7 +11,7 @@ class RecipientWork @Inject constructor(
private val recipientRepository: RecipientRepository private val recipientRepository: RecipientRepository
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
reportingUnitRepository.refreshReportingUnits(student) reportingUnitRepository.refreshReportingUnits(student)
reportingUnitRepository.getReportingUnits(student).let { units -> reportingUnitRepository.getReportingUnits(student).let { units ->

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
@ -11,15 +10,14 @@ import javax.inject.Inject
class SchoolAnnouncementWork @Inject constructor( class SchoolAnnouncementWork @Inject constructor(
private val schoolAnnouncementRepository: SchoolAnnouncementRepository, private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
private val preferencesRepository: PreferencesRepository,
private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification, private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification,
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
schoolAnnouncementRepository.getSchoolAnnouncements( schoolAnnouncementRepository.getSchoolAnnouncements(
student = student, student = student,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify,
).waitForResult() ).waitForResult()

View File

@ -8,7 +8,7 @@ import javax.inject.Inject
class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work { class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
teacherRepository.getTeachers(student, semester, true).waitForResult() teacherRepository.getTeachers(student, semester, true).waitForResult()
} }
} }

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification
import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextOrSameSchoolDay
@ -14,17 +13,16 @@ import javax.inject.Inject
class TimetableWork @Inject constructor( class TimetableWork @Inject constructor(
private val timetableRepository: TimetableRepository, private val timetableRepository: TimetableRepository,
private val changeTimetableNotification: ChangeTimetableNotification, private val changeTimetableNotification: ChangeTimetableNotification,
private val preferencesRepository: PreferencesRepository
) : Work { ) : Work {
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
timetableRepository.getTimetable( timetableRepository.getTimetable(
student = student, student = student,
semester = semester, semester = semester,
start = now().nextOrSameSchoolDay, start = now().nextOrSameSchoolDay,
end = now().nextOrSameSchoolDay, end = now().nextOrSameSchoolDay,
forceRefresh = true, forceRefresh = true,
notify = preferencesRepository.isNotificationsEnable notify = notify,
) )
.waitForResult() .waitForResult()

View File

@ -5,5 +5,5 @@ import io.github.wulkanowy.data.db.entities.Student
interface Work { interface Work {
suspend fun doWork(student: Student, semester: Semester) suspend fun doWork(student: Student, semester: Semester, notify: Boolean)
} }

View File

@ -1,100 +1,82 @@
package io.github.wulkanowy.ui.base package io.github.wulkanowy.ui.base
import android.app.Dialog
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.HorizontalScrollView
import android.widget.Toast import android.widget.Toast
import android.widget.Toast.LENGTH_LONG import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.os.bundleOf
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogErrorBinding import io.github.wulkanowy.databinding.DialogErrorBinding
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.utils.*
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.getString
import io.github.wulkanowy.utils.openAppInMarket
import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser
import okhttp3.internal.http2.StreamResetException
import java.io.InterruptedIOException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() { class ErrorDialog : DialogFragment() {
private lateinit var error: Throwable
@Inject @Inject
lateinit var appInfo: AppInfo lateinit var appInfo: AppInfo
companion object { companion object {
private const val ARGUMENT_KEY = "Data" private const val ARGUMENT_KEY = "error"
fun newInstance(error: Throwable) = ErrorDialog().apply { fun newInstance(error: Throwable) = ErrorDialog().apply {
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) } arguments = bundleOf(ARGUMENT_KEY to error)
} }
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
super.onCreate(savedInstanceState) val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable
setStyle(STYLE_NO_TITLE, 0)
arguments?.run { val binding = DialogErrorBinding.inflate(LayoutInflater.from(context))
error = getSerializable(ARGUMENT_KEY) as Throwable binding.bindErrorDetails(error)
return getAlertDialog(binding, error).apply {
enableReportButtonIfErrorIsReportable(error)
} }
} }
override fun onCreateView( private fun getAlertDialog(binding: DialogErrorBinding, error: Throwable): AlertDialog {
inflater: LayoutInflater, return MaterialAlertDialogBuilder(requireContext()).apply {
container: ViewGroup?,
savedInstanceState: Bundle?
) = DialogErrorBinding.inflate(inflater).apply { binding = this }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val errorStacktrace = error.stackTraceToString() val errorStacktrace = error.stackTraceToString()
setTitle(R.string.all_details)
with(binding) { setView(binding.root)
errorDialogContent.text = errorStacktrace.replace(": ${error.localizedMessage}", "") setNeutralButton(R.string.about_feedback) { _, _ ->
with(errorDialogHorizontalScroll) {
post { fullScroll(HorizontalScrollView.FOCUS_LEFT) }
}
errorDialogCopy.setOnClickListener {
val clip = ClipData.newPlainText("Error details", errorStacktrace)
activity?.getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show()
}
errorDialogCancel.setOnClickListener { dismiss() }
errorDialogReport.setOnClickListener {
openConfirmDialog { openEmailClient(errorStacktrace) } openConfirmDialog { openEmailClient(errorStacktrace) }
} }
errorDialogHumanizedMessage.text = resources.getString(error) setNegativeButton(android.R.string.cancel) { _, _ -> }
setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) }
}.create()
}
private fun DialogErrorBinding.bindErrorDetails(error: Throwable) {
return with(this) {
errorDialogHumanizedMessage.text = resources.getErrorString(error)
errorDialogErrorMessage.text = error.localizedMessage errorDialogErrorMessage.text = error.localizedMessage
errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank() errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank()
errorDialogReport.isEnabled = when (error) { errorDialogContent.text = error.stackTraceToString()
is UnknownHostException, .replace(": ${error.localizedMessage}", "")
is InterruptedIOException,
is ConnectException,
is StreamResetException,
is SocketTimeoutException,
is ServiceUnavailableException,
is FeatureDisabledException,
is FeatureNotAvailableException -> false
else -> true
} }
} }
private fun AlertDialog.enableReportButtonIfErrorIsReportable(error: Throwable) {
setOnShowListener {
getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported()
}
}
private fun copyErrorToClipboard(errorStacktrace: String) {
val clip = ClipData.newPlainText("Error details", errorStacktrace)
requireActivity().getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
Toast.makeText(requireContext(), R.string.all_copied, LENGTH_LONG).show()
} }
private fun openConfirmDialog(callback: () -> Unit) { private fun openConfirmDialog(callback: () -> Unit) {
@ -127,4 +109,8 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
} }
) )
} }
private fun showMessage(text: String) {
Toast.makeText(requireContext(), text, LENGTH_LONG).show()
}
} }

View File

@ -5,7 +5,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
import io.github.wulkanowy.utils.getString import io.github.wulkanowy.utils.getErrorString
import io.github.wulkanowy.utils.security.ScramblerException import io.github.wulkanowy.utils.security.ScramblerException
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -26,7 +26,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
} }
protected open fun proceed(error: Throwable) { protected open fun proceed(error: Throwable) {
showErrorMessage(context.resources.getString(error), error) showErrorMessage(context.resources.getErrorString(error), error)
when (error) { when (error) {
is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl) is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl)
is ScramblerException, is BadCredentialsException -> onSessionExpired() is ScramblerException, is BadCredentialsException -> onSessionExpired()

View File

@ -13,13 +13,8 @@ import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
import io.github.wulkanowy.ui.modules.debug.DebugFragment import io.github.wulkanowy.ui.modules.debug.DebugFragment
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.getCompatDrawable import java.time.Instant
import io.github.wulkanowy.utils.openAppInMarket
import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.toLocalDateTime
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -38,7 +33,7 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
override val versionRes: Triple<String, String, Drawable?>? override val versionRes: Triple<String, String, Drawable?>?
get() = context?.run { get() = context?.run {
val buildTimestamp = val buildTimestamp =
appInfo.buildTimestamp.toLocalDateTime().toFormattedString("yyyy-MM-dd") Instant.ofEpochMilli(appInfo.buildTimestamp).toFormattedString("yyyy-MM-dd")
val versionSignature = val versionSignature =
"${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp" "${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp"
Triple( Triple(

View File

@ -14,8 +14,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
@ -27,12 +25,10 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.widgets.DividerItemDecoration import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.schoolYearStart import io.github.wulkanowy.utils.openMaterialDatePicker
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate import java.time.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -224,29 +220,15 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
(activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson))
} }
override fun showDatePickerDialog(currentDate: LocalDate) { override fun showDatePickerDialog(selectedDate: LocalDate) {
val baseDate = currentDate.schoolYearStart openMaterialDatePicker(
val rangeStart = baseDate.toTimestamp() selected = selectedDate,
val rangeEnd = LocalDate.now().plusWeeks(1).toTimestamp() rangeStart = selectedDate.firstSchoolDayInSchoolYear,
rangeEnd = LocalDate.now().plusWeeks(1),
val constraintsBuilder = CalendarConstraints.Builder().apply { onDateSelected = {
setValidator(SchoolDaysValidator(rangeStart, rangeEnd)) presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth)
setStart(rangeStart)
setEnd(rangeEnd)
}
val datePicker = MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(constraintsBuilder.build())
.setSelection(currentDate.toTimestamp())
.build()
datePicker.addOnPositiveButtonClickListener {
val date = it.toLocalDateTime()
presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
}
if (!parentFragmentManager.isStateSaved) {
datePicker.show(parentFragmentManager, null)
} }
)
} }
override fun showExcuseDialog() { override fun showExcuseDialog() {

View File

@ -48,7 +48,7 @@ interface AttendanceView : BaseView {
fun showAttendanceDialog(lesson: Attendance) fun showAttendanceDialog(lesson: Attendance)
fun showDatePickerDialog(currentDate: LocalDate) fun showDatePickerDialog(selectedDate: LocalDate)
fun showExcuseDialog() fun showExcuseDialog()

View File

@ -37,9 +37,7 @@ import io.github.wulkanowy.utils.left
import io.github.wulkanowy.utils.nickOrName import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import timber.log.Timber import timber.log.Timber
import java.time.Duration import java.time.*
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.Timer import java.util.Timer
import javax.inject.Inject import javax.inject.Inject
import kotlin.concurrent.timer import kotlin.concurrent.timer
@ -291,7 +289,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
var dateToNavigate = LocalDate.now() var dateToNavigate = LocalDate.now()
fun updateLessonState() { fun updateLessonState() {
val currentDateTime = LocalDateTime.now() val currentDateTime = Instant.now()
val currentDate = LocalDate.now() val currentDate = LocalDate.now()
val tomorrowDate = currentDate.plusDays(1) val tomorrowDate = currentDate.plusDays(1)
@ -361,7 +359,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
binding: ItemDashboardLessonsBinding, binding: ItemDashboardLessonsBinding,
header: TimetableHeader? = null, header: TimetableHeader? = null,
) { ) {
val currentDateTime = LocalDateTime.now() val currentDateTime = Instant.now()
val nextLessons = timetableToShow.filter { it.end.isAfter(currentDateTime) } val nextLessons = timetableToShow.filter { it.end.isAfter(currentDateTime) }
.sortedBy { it.start } .sortedBy { it.start }
@ -386,7 +384,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
private fun updateFirstLessonView( private fun updateFirstLessonView(
binding: ItemDashboardLessonsBinding, binding: ItemDashboardLessonsBinding,
firstLesson: Timetable?, firstLesson: Timetable?,
currentDateTime: LocalDateTime currentDateTime: Instant
) { ) {
val context = binding.root.context val context = binding.root.context
val sansSerifFont = Typeface.create("sans-serif", Typeface.NORMAL) val sansSerifFont = Typeface.create("sans-serif", Typeface.NORMAL)

View File

@ -28,10 +28,7 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.utils.capitalise import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.toFormattedString
import java.time.LocalDate import java.time.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -178,8 +175,8 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
binding.dashboardErrorContainer.isVisible = show binding.dashboardErrorContainer.isVisible = show
} }
override fun setErrorDetails(message: String) { override fun setErrorDetails(error: Throwable) {
binding.dashboardErrorMessage.text = message binding.dashboardErrorMessage.text = requireContext().resources.getErrorString(error)
} }
override fun resetView() { override fun resetView() {

View File

@ -6,37 +6,17 @@ import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.repositories.AdminMessageRepository import io.github.wulkanowy.data.repositories.*
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
import io.github.wulkanowy.data.repositories.ConferenceRepository
import io.github.wulkanowy.data.repositories.ExamRepository
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.calculatePercentage import io.github.wulkanowy.utils.calculatePercentage
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextOrSameSchoolDay
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
import javax.inject.Inject import javax.inject.Inject
class DashboardPresenter @Inject constructor( class DashboardPresenter @Inject constructor(
@ -552,7 +532,7 @@ class DashboardPresenter @Inject constructor(
student = student, student = student,
semester = semester, semester = semester,
forceRefresh = forceRefresh, forceRefresh = forceRefresh,
startDate = LocalDateTime.now() startDate = Instant.now(),
) )
}.onEach { }.onEach {
when (it.status) { when (it.status) {
@ -582,7 +562,7 @@ class DashboardPresenter @Inject constructor(
} }
private fun loadAdminMessage(student: Student, forceRefresh: Boolean) { private fun loadAdminMessage(student: Student, forceRefresh: Boolean) {
flowWithResourceIn { adminMessageRepository.getAdminMessages(student, forceRefresh) } flowWithResourceIn { adminMessageRepository.getAdminMessages(student) }
.map { .map {
val isDismissed = it.data?.id in preferencesRepository.dismissedAdminMessageIds val isDismissed = it.data?.id in preferencesRepository.dismissedAdminMessageIds
it.copy(data = it.data.takeUnless { isDismissed }) it.copy(data = it.data.takeUnless { isDismissed })
@ -716,7 +696,7 @@ class DashboardPresenter @Inject constructor(
itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null
val isGeneralError = val isGeneralError =
filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError
val errorMessage = itemsLoadedList.map { it.error?.stackTraceToString() }.toString() val firstError = itemsLoadedList.mapNotNull { it.error }.firstOrNull()
val filteredOriginalLoadedList = val filteredOriginalLoadedList =
dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
@ -726,7 +706,7 @@ class DashboardPresenter @Inject constructor(
filteredOriginalLoadedList.none { it.error == null } && filteredOriginalLoadedList.isNotEmpty() || wasAccountItemError filteredOriginalLoadedList.none { it.error == null } && filteredOriginalLoadedList.isNotEmpty() || wasAccountItemError
if (isGeneralError && isItemsLoaded) { if (isGeneralError && isItemsLoaded) {
lastError = Exception(errorMessage) lastError = requireNotNull(firstError)
view?.run { view?.run {
showProgress(false) showProgress(false)
@ -734,6 +714,7 @@ class DashboardPresenter @Inject constructor(
if ((forceRefresh && wasGeneralError) || !forceRefresh) { if ((forceRefresh && wasGeneralError) || !forceRefresh) {
showContent(false) showContent(false)
showErrorView(true) showErrorView(true)
setErrorDetails(lastError)
} }
} }
} }

View File

@ -18,7 +18,7 @@ interface DashboardView : BaseView {
fun showErrorView(show: Boolean) fun showErrorView(show: Boolean)
fun setErrorDetails(message: String) fun setErrorDetails(error: Throwable)
fun resetView() fun resetView()

View File

@ -1,7 +1,8 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.data.db.entities.Conference
import java.time.LocalDateTime import java.time.Duration
import java.time.Instant
val debugConferenceItems = listOf( val debugConferenceItems = listOf(
generateConference( generateConference(
@ -53,6 +54,6 @@ private fun generateConference(title: String, subject: String) = Conference(
diaryId = 0, diaryId = 0,
agenda = "", agenda = "",
conferenceId = 0, conferenceId = 0,
date = LocalDateTime.now().plusMinutes(10), date = Instant.now().plus(Duration.ofMinutes(10)),
presentOnConference = "", presentOnConference = "",
) )

View File

@ -5,7 +5,7 @@ import java.time.LocalDate
val debugGradeDetailsItems = listOf( val debugGradeDetailsItems = listOf(
generateGrade("Matematyka", "+"), generateGrade("Matematyka", "+"),
generateGrade("Matematyka", "2="), generateGrade("Matematyka", "120", comment = "%"),
generateGrade("Fizyka", "-"), generateGrade("Fizyka", "-"),
generateGrade("Geografia", "4+"), generateGrade("Geografia", "4+"),
generateGrade("Sieci komputerowe", "1"), generateGrade("Sieci komputerowe", "1"),
@ -17,14 +17,14 @@ val debugGradeDetailsItems = listOf(
generateGrade("Wychowanie fizyczne", "5"), generateGrade("Wychowanie fizyczne", "5"),
) )
private fun generateGrade(subject: String, entry: String) = Grade( private fun generateGrade(subject: String, entry: String, comment: String = "") = Grade(
subject = subject, subject = subject,
entry = entry, entry = entry,
semesterId = 0, semesterId = 0,
studentId = 0, studentId = 0,
value = 0.0, value = 0.0,
modifier = 0.0, modifier = 0.0,
comment = "", comment = comment,
color = "", color = "",
gradeSymbol = "", gradeSymbol = "",
description = "", description = "",

View File

@ -1,7 +1,7 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import java.time.LocalDateTime import java.time.Instant
val debugMessageItems = listOf( val debugMessageItems = listOf(
generateMessage("Kowalski Jan", "Tytuł"), generateMessage("Kowalski Jan", "Tytuł"),
@ -24,7 +24,7 @@ private fun generateMessage(sender: String, subject: String) = Message(
messageId = 0, messageId = 0,
senderId = 0, senderId = 0,
recipient = "", recipient = "",
date = LocalDateTime.now(), date = Instant.now(),
folderId = 0, folderId = 0,
unread = true, unread = true,
removed = false, removed = false,

View File

@ -4,13 +4,13 @@ import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import java.time.LocalDate import java.time.LocalDate
val debugSchoolAnnouncementItems = listOf( val debugSchoolAnnouncementItems = listOf(
generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych\n03.05.2021 - poniedziałek"), generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych<br />03.05.2021 poniedziałek"),
generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do noszenia maseczek"), generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do noszenia maseczek"),
generateAnnouncement("Święto szkoły", "W najbliższych dniach obchodzimy święto szkoły, podczas którego..."), generateAnnouncement("Święto szkoły", "W najbliższych dniach obchodzimy święto szkoły, podczas którego..."),
generateAnnouncement("Rocznica odzyskania przez szkołę sztandaru", "Juz niedługo, bo za tydzień, a dokładnie za 8 dni..."), generateAnnouncement("Rocznica odzyskania przez szkołę sztandaru", "Juz niedługo, bo za tydzień, a dokładnie za 8 dni..."),
generateAnnouncement("Ogłoszenie w sprawie otwarcia stołówki", "Wszyscy uczniowie zainteresowani obiadami w szkole..."), generateAnnouncement("Ogłoszenie w sprawie otwarcia stołówki", "Wszyscy uczniowie zainteresowani obiadami w szkole..."),
generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie pilnym"), generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie pilnym"),
generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych\n21.06.2021 - poniedziałek"), generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych<br />21.06.2021 poniedziałek"),
generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do zdjęcia maseczek"), generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do zdjęcia maseczek"),
generateAnnouncement("Święto państwowe", "W najbliższych dniach obchodzimy święto państwowe, podczas którego..."), generateAnnouncement("Święto państwowe", "W najbliższych dniach obchodzimy święto państwowe, podczas którego..."),
generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie wolnym"), generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie wolnym"),

View File

@ -1,8 +1,9 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import java.time.Duration
import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
import kotlin.random.Random import kotlin.random.Random
val debugTimetableItems = listOf( val debugTimetableItems = listOf(
@ -24,8 +25,8 @@ private fun generateTimetable(subject: String, room: String, roomOld: String) =
diaryId = 0, diaryId = 0,
date = LocalDate.now().minusDays(Random.nextLong(0, 8)), date = LocalDate.now().minusDays(Random.nextLong(0, 8)),
number = 1, number = 1,
start = LocalDateTime.now().plusHours(1), start = Instant.now(),
end = LocalDateTime.now(), end = Instant.now().plus(Duration.ofHours(1)),
subjectOld = "", subjectOld = "",
group = "", group = "",
room = room, room = room,

View File

@ -33,6 +33,7 @@ class GradeStatisticsFragment :
companion object { companion object {
private const val SAVED_CHART_TYPE = "CURRENT_TYPE" private const val SAVED_CHART_TYPE = "CURRENT_TYPE"
private const val SAVED_SUBJECT_NAME = "SUBJECT_NAME"
fun newInstance() = GradeStatisticsFragment() fun newInstance() = GradeStatisticsFragment()
} }
@ -46,8 +47,9 @@ class GradeStatisticsFragment :
binding = FragmentGradeStatisticsBinding.bind(view) binding = FragmentGradeStatisticsBinding.bind(view)
messageContainer = binding.gradeStatisticsRecycler messageContainer = binding.gradeStatisticsRecycler
presenter.onAttachView( presenter.onAttachView(
this, view = this,
savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType type = savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType,
subjectName = savedInstanceState?.getSerializable(SAVED_SUBJECT_NAME) as? String,
) )
} }
@ -56,6 +58,7 @@ class GradeStatisticsFragment :
with(binding.gradeStatisticsRecycler) { with(binding.gradeStatisticsRecycler) {
layoutManager = LinearLayoutManager(requireContext()) layoutManager = LinearLayoutManager(requireContext())
statisticsAdapter.currentDataType = presenter.currentType
adapter = statisticsAdapter adapter = statisticsAdapter
} }
@ -81,7 +84,8 @@ class GradeStatisticsFragment :
} }
} }
override fun updateSubjects(data: ArrayList<String>) { override fun updateSubjects(data: List<String>, selectedIndex: Int) {
binding.gradeStatisticsSubjects.setSelection(selectedIndex)
with(subjectsAdapter) { with(subjectsAdapter) {
clear() clear()
addAll(data) addAll(data)
@ -161,6 +165,7 @@ class GradeStatisticsFragment :
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType) outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType)
outState.putSerializable(SAVED_SUBJECT_NAME, presenter.currentSubjectName)
} }
override fun onDestroyView() { override fun onDestroyView() {

View File

@ -31,16 +31,22 @@ class GradeStatisticsPresenter @Inject constructor(
private var currentSemesterId = 0 private var currentSemesterId = 0
private var currentSubjectName: String = "Wszystkie" var currentSubjectName: String = "Wszystkie"
private set
private lateinit var lastError: Throwable private lateinit var lastError: Throwable
var currentType: GradeStatisticsItem.DataType = GradeStatisticsItem.DataType.PARTIAL var currentType: GradeStatisticsItem.DataType = GradeStatisticsItem.DataType.PARTIAL
private set private set
fun onAttachView(view: GradeStatisticsView, type: GradeStatisticsItem.DataType?) { fun onAttachView(
view: GradeStatisticsView,
type: GradeStatisticsItem.DataType?,
subjectName: String?
) {
super.onAttachView(view) super.onAttachView(view)
currentType = type ?: GradeStatisticsItem.DataType.PARTIAL currentType = type ?: GradeStatisticsItem.DataType.PARTIAL
currentSubjectName = subjectName ?: currentSubjectName
view.initView() view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError errorHandler.showErrorMessage = ::showErrorViewOnError
} }
@ -127,12 +133,17 @@ class GradeStatisticsPresenter @Inject constructor(
when (it.status) { when (it.status) {
Status.LOADING -> Timber.i("Loading grade stats subjects started") Status.LOADING -> Timber.i("Loading grade stats subjects started")
Status.SUCCESS -> { Status.SUCCESS -> {
subjects = it.data!! subjects = requireNotNull(it.data)
Timber.i("Loading grade stats subjects result: Success") Timber.i("Loading grade stats subjects result: Success")
view?.run { view?.run {
view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name }))
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
updateSubjects(
data = it.data.map { subject -> subject.name },
selectedIndex = it.data.indexOfFirst { subject ->
subject.name == currentSubjectName
},
)
} }
} }
Status.ERROR -> { Status.ERROR -> {
@ -151,9 +162,11 @@ class GradeStatisticsPresenter @Inject constructor(
) { ) {
Timber.i("Loading grade stats data started") Timber.i("Loading grade stats data started")
currentSubjectName =
if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName
currentType = type currentType = type
currentSubjectName = when {
preferencesRepository.showAllSubjectsOnStatisticsList -> "Wszystkie"
else -> subjectName
}
flowWithResourceIn { flowWithResourceIn {
val student = studentRepository.getCurrentStudent() val student = studentRepository.getCurrentStudent()
@ -200,9 +213,9 @@ class GradeStatisticsPresenter @Inject constructor(
showRefresh(true) showRefresh(true)
showProgress(false) showProgress(false)
updateData( updateData(
if (isNoContent) emptyList() else it.data!!, newItems = if (isNoContent) emptyList() else it.data!!,
preferencesRepository.gradeColorTheme, newTheme = preferencesRepository.gradeColorTheme,
preferencesRepository.showAllSubjectsOnStatisticsList showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList,
) )
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
} }
@ -215,9 +228,9 @@ class GradeStatisticsPresenter @Inject constructor(
showEmpty(isNoContent) showEmpty(isNoContent)
showErrorView(false) showErrorView(false)
updateData( updateData(
if (isNoContent) emptyList() else it.data, newItems = if (isNoContent) emptyList() else it.data,
preferencesRepository.gradeColorTheme, newTheme = preferencesRepository.gradeColorTheme,
preferencesRepository.showAllSubjectsOnStatisticsList showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList,
) )
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
} }

View File

@ -12,7 +12,7 @@ interface GradeStatisticsView : BaseView {
fun initView() fun initView()
fun updateSubjects(data: ArrayList<String>) fun updateSubjects(data: List<String>, selectedIndex: Int)
fun updateData( fun updateData(
newItems: List<GradeStatisticsItem>, newItems: List<GradeStatisticsItem>,

View File

@ -5,15 +5,13 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogHomeworkAddBinding import io.github.wulkanowy.databinding.DialogHomeworkAddBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
import io.github.wulkanowy.utils.openMaterialDatePicker
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate import java.time.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -23,6 +21,7 @@ class HomeworkAddDialog : BaseDialogFragment<DialogHomeworkAddBinding>(), Homewo
@Inject @Inject
lateinit var presenter: HomeworkAddPresenter lateinit var presenter: HomeworkAddPresenter
// todo: move it to presenter
private var date: LocalDate? = null private var date: LocalDate? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -97,24 +96,16 @@ class HomeworkAddDialog : BaseDialogFragment<DialogHomeworkAddBinding>(), Homewo
dismiss() dismiss()
} }
override fun showDatePickerDialog(currentDate: LocalDate) { override fun showDatePickerDialog(selectedDate: LocalDate) {
val constraintsBuilder = CalendarConstraints.Builder().apply { openMaterialDatePicker(
setStart(LocalDate.now().toEpochDay()) selected = selectedDate,
} rangeStart = LocalDate.now(),
val datePicker = rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
MaterialDatePicker.Builder.datePicker() onDateSelected = {
.setCalendarConstraints(constraintsBuilder.build()) date = it
.setSelection(currentDate.toTimestamp())
.build()
datePicker.addOnPositiveButtonClickListener {
date = it.toLocalDateTime().toLocalDate()
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString()) binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
} }
)
if (!parentFragmentManager.isStateSaved) {
datePicker.show(this.parentFragmentManager, null)
}
} }
override fun onDestroyView() { override fun onDestroyView() {

View File

@ -17,5 +17,5 @@ interface HomeworkAddView : BaseView {
fun closeDialog() fun closeDialog()
fun showDatePickerDialog(currentDate: LocalDate) fun showDatePickerDialog(selectedDate: LocalDate)
} }

View File

@ -4,18 +4,19 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.ActivityLoginBinding import io.github.wulkanowy.databinding.ActivityLoginBinding
import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
import io.github.wulkanowy.utils.UpdateHelper import io.github.wulkanowy.utils.UpdateHelper
import io.github.wulkanowy.utils.setOnSelectPageListener
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -24,21 +25,10 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
@Inject @Inject
override lateinit var presenter: LoginPresenter override lateinit var presenter: LoginPresenter
private val pagerAdapter by lazy {
BaseFragmentPagerAdapter(
fragmentManager = supportFragmentManager,
pagesCount = 5,
lifecycle = lifecycle,
)
}
@Inject @Inject
lateinit var updateHelper: UpdateHelper lateinit var updateHelper: UpdateHelper
override val currentViewIndex get() = binding.loginViewpager.currentItem
companion object { companion object {
fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java)
} }
@ -51,6 +41,50 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
presenter.onAttachView(this) presenter.onAttachView(this)
updateHelper.checkAndInstallUpdates(this) updateHelper.checkAndInstallUpdates(this)
if (savedInstanceState == null) {
openFragment(LoginFormFragment.newInstance(), clearBackStack = true)
}
}
override fun initView() {
with(requireNotNull(supportActionBar)) {
setDisplayHomeAsUpEnabled(true)
setDisplayShowTitleEnabled(false)
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) onBackPressed()
return true
}
fun showActionBar(show: Boolean) {
supportActionBar?.run { if (show) show() else hide() }
}
fun navigateToSymbolFragment(loginData: LoginData) {
openFragment(LoginSymbolFragment.newInstance(loginData))
}
fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>) {
openFragment(LoginStudentSelectFragment.newInstance(studentsWithSemesters))
}
fun onAdvancedLoginClick() {
openFragment(LoginAdvancedFragment.newInstance())
}
fun onRecoverClick() {
openFragment(LoginRecoverFragment.newInstance())
}
private fun openFragment(fragment: Fragment, clearBackStack: Boolean = false) {
supportFragmentManager.commit {
replace(R.id.loginContainer, fragment)
setReorderingAllowed(true)
if (!clearBackStack) addToBackStack(fragment::class.java.name)
}
} }
override fun onResume() { override fun onResume() {
@ -64,78 +98,4 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
updateHelper.onActivityResult(requestCode, resultCode) updateHelper.onActivityResult(requestCode, resultCode)
} }
override fun initView() {
with(requireNotNull(supportActionBar)) {
setDisplayHomeAsUpEnabled(true)
setDisplayShowTitleEnabled(false)
}
with(binding.loginViewpager) {
adapter = pagerAdapter
isUserInputEnabled = false
offscreenPageLimit = 2
setOnSelectPageListener(presenter::onViewSelected)
}
with(pagerAdapter) {
containerId = binding.loginViewpager.id
itemFactory = {
when (it) {
0 -> LoginFormFragment.newInstance()
1 -> LoginSymbolFragment.newInstance()
2 -> LoginStudentSelectFragment.newInstance()
3 -> LoginAdvancedFragment.newInstance()
4 -> LoginRecoverFragment.newInstance()
else -> throw IllegalStateException()
}
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) onBackPressed()
return true
}
override fun switchView(index: Int) {
binding.loginViewpager.setCurrentItem(index, false)
}
override fun showActionBar(show: Boolean) {
supportActionBar?.run { if (show) show() else hide() }
}
override fun onBackPressed() {
presenter.onBackPressed { super.onBackPressed() }
}
override fun notifyInitSymbolFragment(loginData: Triple<String, String, String>) {
(pagerAdapter.getFragmentInstance(1) as? LoginSymbolFragment)
?.onParentInitSymbolFragment(loginData)
}
override fun notifyInitStudentSelectFragment(studentsWithSemesters: List<StudentWithSemesters>) {
(pagerAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)
?.onParentInitStudentSelectFragment(studentsWithSemesters)
}
fun onFormFragmentAccountLogged(
studentsWithSemesters: List<StudentWithSemesters>,
loginData: Triple<String, String, String>
) {
presenter.onFormViewAccountLogged(studentsWithSemesters, loginData)
}
fun onSymbolFragmentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>) {
presenter.onSymbolViewAccountLogged(studentsWithSemesters)
}
fun onAdvancedLoginClick() {
presenter.onAdvancedLoginClick()
}
fun onRecoverClick() {
presenter.onRecoverClick()
}
} }

View File

@ -0,0 +1,9 @@
package io.github.wulkanowy.ui.modules.login
import java.io.Serializable
data class LoginData(
val login: String,
val password: String,
val baseUrl: String,
) : Serializable

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