Compare commits
No commits in common. "2.0.2" and "1.8.2" have entirely different histories.
356 changed files with 3788 additions and 9541 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -119,4 +119,3 @@ Thumbs.db
|
|||
app/src/release/agconnect-services.json
|
||||
app/src/release/agconnect-credentials.json
|
||||
.idea/deploymentTargetDropDown.xml
|
||||
.idea/kotlinc.xml
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -186,7 +186,7 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2023 Wulkanowy
|
||||
Copyright 2022 Wulkanowy
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
@ -16,15 +16,15 @@ apply from: 'hooks.gradle'
|
|||
|
||||
android {
|
||||
namespace 'io.github.wulkanowy'
|
||||
compileSdkVersion 33
|
||||
compileSdkVersion 32
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode 124
|
||||
versionName "2.0.2"
|
||||
targetSdkVersion 32
|
||||
versionCode 117
|
||||
versionName "1.8.2"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
|
@ -162,7 +162,7 @@ play {
|
|||
track = 'production'
|
||||
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
|
||||
userFraction = 0.10d
|
||||
updatePriority = 2
|
||||
updatePriority = 5
|
||||
enabled.set(false)
|
||||
}
|
||||
|
||||
|
@ -177,36 +177,36 @@ huaweiPublish {
|
|||
}
|
||||
|
||||
ext {
|
||||
work_manager = "2.8.1"
|
||||
work_manager = "2.7.1"
|
||||
android_hilt = "1.0.0"
|
||||
room = "2.5.1"
|
||||
room = "2.4.3"
|
||||
chucker = "3.5.2"
|
||||
mockk = "1.13.5"
|
||||
mockk = "1.13.2"
|
||||
coroutines = "1.6.4"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'io.github.wulkanowy:sdk:2.0.1'
|
||||
implementation "io.github.wulkanowy:sdk:1.8.2"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||
|
||||
implementation "androidx.core:core-ktx:1.10.0"
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation "androidx.activity:activity-ktx:1.7.1"
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.5.7"
|
||||
implementation "androidx.annotation:annotation:1.6.0"
|
||||
implementation "androidx.core:core-ktx:1.8.0"
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||
implementation "androidx.activity:activity-ktx:1.5.1"
|
||||
implementation "androidx.appcompat:appcompat:1.5.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.5.4"
|
||||
implementation "androidx.annotation:annotation:1.5.0"
|
||||
|
||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||
implementation "androidx.recyclerview:recyclerview:1.3.0"
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||
implementation "com.google.android.material:material:1.8.0"
|
||||
implementation "com.google.android.material:material:1.7.0"
|
||||
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
|
||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||
implementation 'com.github.lopspower:CircularImageView:4.3.0'
|
||||
|
@ -214,7 +214,7 @@ dependencies {
|
|||
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room"
|
||||
implementation "androidx.room:room-ktx:$room"
|
||||
|
@ -229,30 +229,28 @@ dependencies {
|
|||
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
||||
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:4.11.0"
|
||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
|
||||
|
||||
implementation "com.jakewharton.timber:timber:5.0.1"
|
||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
|
||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
||||
implementation "io.coil-kt:coil:2.3.0"
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
||||
implementation "io.coil-kt:coil:2.2.2"
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
||||
implementation 'org.apache.commons:commons-text:1.10.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:31.5.0')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:31.0.3')
|
||||
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
||||
playImplementation 'com.google.firebase:firebase-messaging:'
|
||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||
playImplementation 'com.google.firebase:firebase-config-ktx'
|
||||
playImplementation 'com.google.android.play:core:1.10.3'
|
||||
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:22.0.0'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:21.3.0'
|
||||
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.9.1.200'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.0.300'
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.300'
|
||||
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||
|
||||
|
@ -265,17 +263,17 @@ dependencies {
|
|||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
testImplementation 'org.robolectric:robolectric:4.10'
|
||||
testImplementation "androidx.test:runner:1.5.2"
|
||||
testImplementation "androidx.test.ext:junit:1.1.5"
|
||||
testImplementation 'org.robolectric:robolectric:4.9'
|
||||
testImplementation "androidx.test:runner:1.5.1"
|
||||
testImplementation "androidx.test.ext:junit:1.1.4"
|
||||
testImplementation "androidx.test:core:1.5.0"
|
||||
testImplementation "androidx.room:room-testing:$room"
|
||||
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
||||
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||
|
||||
androidTestImplementation "androidx.test:core:1.5.0"
|
||||
androidTestImplementation "androidx.test:runner:1.5.2"
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.5"
|
||||
androidTestImplementation "androidx.test:core:1.4.0"
|
||||
androidTestImplementation "androidx.test:runner:1.4.0"
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.3"
|
||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/colorIcon" />
|
||||
<background android:drawable="@color/colorPrimary" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground_dev_mono" />
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/colorPrimary" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
|
||||
</adaptive-icon>
|
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6 KiB |
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -1,7 +0,0 @@
|
|||
package io.github.wulkanowy.utils
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper()
|
|
@ -1,7 +0,0 @@
|
|||
package io.github.wulkanowy.utils
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper()
|
|
@ -9,7 +9,6 @@
|
|||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
|
@ -37,14 +36,13 @@
|
|||
<application
|
||||
android:name=".WulkanowyApp"
|
||||
android:allowBackup="false"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="false"
|
||||
android:theme="@style/WulkanowyTheme"
|
||||
tools:ignore="DataExtractionRules,UnusedAttribute">
|
||||
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
|
||||
<activity
|
||||
android:name=".ui.modules.splash.SplashActivity"
|
||||
android:exported="true"
|
||||
|
@ -72,7 +70,7 @@
|
|||
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/send_message_title"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:theme="@style/WulkanowyTheme.MessageSend"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
|
|
|
@ -34,15 +34,11 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||
@Inject
|
||||
lateinit var adsHelper: AdsHelper
|
||||
|
||||
@Inject
|
||||
lateinit var remoteConfigHelper: RemoteConfigHelper
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
initializeAppLanguage()
|
||||
themeManager.applyDefaultTheme()
|
||||
adsHelper.initialize()
|
||||
remoteConfigHelper.initialize()
|
||||
initLogging()
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
|
|||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
|
@ -36,11 +36,10 @@ internal class DataModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideSdk(chuckerInterceptor: ChuckerInterceptor, remoteConfig: RemoteConfigHelper) =
|
||||
fun provideSdk(chuckerInterceptor: ChuckerInterceptor) =
|
||||
Sdk().apply {
|
||||
androidVersion = android.os.Build.VERSION.RELEASE
|
||||
buildTag = android.os.Build.MODEL
|
||||
userAgentTemplate = remoteConfig.userAgentTemplate
|
||||
setSimpleHttpLogger { Timber.d(it) }
|
||||
|
||||
// for debug only
|
||||
|
@ -80,6 +79,7 @@ internal class DataModule {
|
|||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideRetrofit(
|
||||
|
|
|
@ -48,7 +48,6 @@ import javax.inject.Singleton
|
|||
AutoMigration(from = 46, to = 47),
|
||||
AutoMigration(from = 47, to = 48),
|
||||
AutoMigration(from = 51, to = 52),
|
||||
AutoMigration(from = 54, to = 55, spec = Migration55::class),
|
||||
],
|
||||
version = AppDatabase.VERSION_SCHEMA,
|
||||
exportSchema = true
|
||||
|
@ -57,7 +56,7 @@ import javax.inject.Singleton
|
|||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 55
|
||||
const val VERSION_SCHEMA = 54
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.*
|
||||
import androidx.room.OnConflictStrategy.ABORT
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentName
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import javax.inject.Singleton
|
||||
|
@ -11,7 +11,7 @@ import javax.inject.Singleton
|
|||
@Dao
|
||||
abstract class StudentDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
@Insert(onConflict = ABORT)
|
||||
abstract suspend fun insertAll(student: List<Student>): List<Long>
|
||||
|
||||
@Delete
|
||||
|
@ -20,9 +20,6 @@ abstract class StudentDao {
|
|||
@Update(entity = Student::class)
|
||||
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
||||
|
||||
@Update(entity = Student::class)
|
||||
abstract suspend fun update(studentName: StudentName)
|
||||
|
||||
@Query("SELECT * FROM Students WHERE is_current = 1")
|
||||
abstract suspend fun loadCurrent(): Student?
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ data class Exam(
|
|||
|
||||
val subject: String,
|
||||
|
||||
@Deprecated("not available anymore")
|
||||
val group: String,
|
||||
|
||||
val type: String,
|
||||
|
|
|
@ -2,14 +2,16 @@ package io.github.wulkanowy.data.db.entities
|
|||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
|
||||
@Entity(
|
||||
tableName = "MessageAttachments",
|
||||
primaryKeys = ["message_global_key", "url", "filename"],
|
||||
)
|
||||
@Entity(tableName = "MessageAttachments")
|
||||
data class MessageAttachment(
|
||||
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "real_id")
|
||||
val realId: Int,
|
||||
|
||||
@ColumnInfo(name = "message_global_key")
|
||||
val messageGlobalKey: String,
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
|
||||
@Entity
|
||||
data class StudentName(
|
||||
|
||||
@ColumnInfo(name = "student_name")
|
||||
val studentName: String
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey
|
||||
var id: Long = 0
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.DeleteColumn
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
@DeleteColumn(
|
||||
tableName = "MessageAttachments",
|
||||
columnName = "real_id",
|
||||
)
|
||||
class Migration55 : AutoMigrationSpec {
|
||||
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("DELETE FROM Messages")
|
||||
db.execSQL("DELETE FROM MessageAttachments")
|
||||
}
|
||||
}
|
|
@ -10,9 +10,9 @@ fun List<SdkConference>.mapToEntities(semester: Semester) = map {
|
|||
diaryId = semester.diaryId,
|
||||
agenda = it.agenda,
|
||||
conferenceId = it.id,
|
||||
date = it.date.toInstant(),
|
||||
date = it.dateZoned.toInstant(),
|
||||
presentOnConference = it.presentOnConference,
|
||||
subject = it.topic,
|
||||
title = it.place,
|
||||
subject = it.subject,
|
||||
title = it.title
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ fun List<SdkExam>.mapToEntities(semester: Semester) = map {
|
|||
date = it.date,
|
||||
entryDate = it.entryDate,
|
||||
subject = it.subject,
|
||||
group = "",
|
||||
group = it.group,
|
||||
type = it.type,
|
||||
description = it.description,
|
||||
teacher = it.teacher,
|
||||
|
|
|
@ -2,7 +2,6 @@ package io.github.wulkanowy.data.mappers
|
|||
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
import io.github.wulkanowy.sdk.pojo.MailboxType
|
||||
import timber.log.Timber
|
||||
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
|
||||
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
|
||||
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
|
||||
|
@ -17,16 +16,13 @@ fun List<SdkMessage>.mapToEntities(
|
|||
mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box ->
|
||||
box.fullName == it.mailbox
|
||||
}?.globalKey.let { mailboxKey ->
|
||||
if (mailboxKey == null) {
|
||||
Timber.e("Can't find ${it.mailbox} in $allMailboxes")
|
||||
"unknown"
|
||||
} else mailboxKey
|
||||
requireNotNull(mailboxKey) { "Can't find ${it.mailbox} in $allMailboxes" }
|
||||
},
|
||||
email = student.email,
|
||||
messageId = it.id,
|
||||
correspondents = it.correspondents,
|
||||
subject = it.subject.trim(),
|
||||
date = it.date.toInstant(),
|
||||
date = it.dateZoned.toInstant(),
|
||||
folderId = it.folderId,
|
||||
unread = it.unread,
|
||||
unreadBy = it.unreadBy,
|
||||
|
@ -40,6 +36,7 @@ fun List<SdkMessage>.mapToEntities(
|
|||
fun List<SdkMessageAttachment>.mapToEntities(messageGlobalKey: String) = map {
|
||||
MessageAttachment(
|
||||
messageGlobalKey = messageGlobalKey,
|
||||
realId = it.url.hashCode(),
|
||||
url = it.url,
|
||||
filename = it.filename
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
|||
fun List<SdkDevice>.mapToEntities(student: Student) = map {
|
||||
MobileDevice(
|
||||
userLoginId = student.userLoginId,
|
||||
date = it.createDate.toInstant(),
|
||||
date = it.createDateZoned.toInstant(),
|
||||
deviceId = it.id,
|
||||
name = it.name
|
||||
)
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.*
|
||||
import java.time.Instant
|
||||
import io.github.wulkanowy.sdk.pojo.RegisterStudent as SdkRegisterStudent
|
||||
import io.github.wulkanowy.sdk.pojo.RegisterUser as SdkRegisterUser
|
||||
|
||||
fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser(
|
||||
email = email,
|
||||
login = login,
|
||||
password = password,
|
||||
scrapperBaseUrl = scrapperBaseUrl,
|
||||
loginMode = loginMode,
|
||||
loginType = loginType,
|
||||
symbols = symbols.map { registerSymbol ->
|
||||
RegisterSymbol(
|
||||
symbol = registerSymbol.symbol,
|
||||
error = registerSymbol.error,
|
||||
hebeBaseUrl = registerSymbol.hebeBaseUrl,
|
||||
keyId = registerSymbol.keyId,
|
||||
privatePem = registerSymbol.privatePem,
|
||||
userName = registerSymbol.userName,
|
||||
schools = registerSymbol.schools.map {
|
||||
RegisterUnit(
|
||||
userLoginId = it.userLoginId,
|
||||
schoolId = it.schoolId,
|
||||
schoolName = it.schoolName,
|
||||
schoolShortName = it.schoolShortName,
|
||||
parentIds = it.parentIds,
|
||||
studentIds = it.studentIds,
|
||||
employeeIds = it.employeeIds,
|
||||
error = it.error,
|
||||
students = it.subjects
|
||||
.filterIsInstance<SdkRegisterStudent>()
|
||||
.map { registerSubject ->
|
||||
RegisterStudent(
|
||||
studentId = registerSubject.studentId,
|
||||
studentName = registerSubject.studentName,
|
||||
studentSecondName = registerSubject.studentSecondName,
|
||||
studentSurname = registerSubject.studentSurname,
|
||||
className = registerSubject.className,
|
||||
classId = registerSubject.classId,
|
||||
isParent = registerSubject.isParent,
|
||||
semesters = registerSubject.semesters
|
||||
.mapToEntities(registerSubject.studentId),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
fun RegisterStudent.mapToStudentWithSemesters(
|
||||
user: RegisterUser,
|
||||
symbol: RegisterSymbol,
|
||||
unit: RegisterUnit,
|
||||
colors: List<Long>,
|
||||
): StudentWithSemesters = StudentWithSemesters(
|
||||
semesters = semesters,
|
||||
student = Student(
|
||||
email = user.login, // for compatibility
|
||||
userName = symbol.userName,
|
||||
userLoginId = unit.userLoginId,
|
||||
isParent = isParent,
|
||||
className = className,
|
||||
classId = classId,
|
||||
studentId = studentId,
|
||||
symbol = symbol.symbol,
|
||||
loginType = user.loginType?.name.orEmpty(),
|
||||
schoolName = unit.schoolName,
|
||||
schoolShortName = unit.schoolShortName,
|
||||
schoolSymbol = unit.schoolId,
|
||||
studentName = "$studentName $studentSurname",
|
||||
loginMode = user.loginMode.name,
|
||||
scrapperBaseUrl = user.scrapperBaseUrl.orEmpty(),
|
||||
mobileBaseUrl = symbol.hebeBaseUrl.orEmpty(),
|
||||
certificateKey = symbol.keyId.orEmpty(),
|
||||
privateKey = symbol.privatePem.orEmpty(),
|
||||
password = user.password.orEmpty(),
|
||||
isCurrent = false,
|
||||
registrationDate = Instant.now(),
|
||||
).apply {
|
||||
avatarColor = colors.random()
|
||||
},
|
||||
)
|
|
@ -0,0 +1,37 @@
|
|||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import java.time.Instant
|
||||
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
|
||||
|
||||
fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) = map {
|
||||
StudentWithSemesters(
|
||||
student = Student(
|
||||
email = it.email,
|
||||
password = password,
|
||||
isParent = it.isParent,
|
||||
symbol = it.symbol,
|
||||
studentId = it.studentId,
|
||||
userLoginId = it.userLoginId,
|
||||
userName = it.userName,
|
||||
studentName = it.studentName + " " + it.studentSurname,
|
||||
schoolSymbol = it.schoolSymbol,
|
||||
schoolShortName = it.schoolShortName,
|
||||
schoolName = it.schoolName,
|
||||
className = it.className,
|
||||
classId = it.classId,
|
||||
scrapperBaseUrl = it.scrapperBaseUrl,
|
||||
loginType = it.loginType.name,
|
||||
isCurrent = false,
|
||||
registrationDate = Instant.now(),
|
||||
mobileBaseUrl = it.mobileBaseUrl,
|
||||
privateKey = it.privateKey,
|
||||
certificateKey = it.certificateKey,
|
||||
loginMode = it.loginMode.name,
|
||||
).apply {
|
||||
avatarColor = colors.random()
|
||||
},
|
||||
semesters = it.semesters.mapToEntities(it.studentId)
|
||||
)
|
||||
}
|
|
@ -5,10 +5,10 @@ import io.github.wulkanowy.data.db.entities.Timetable
|
|||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetableFull
|
||||
import io.github.wulkanowy.sdk.pojo.TimetableFull as SdkTimetableFull
|
||||
import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader
|
||||
import io.github.wulkanowy.sdk.pojo.Lesson as SdkLesson
|
||||
import io.github.wulkanowy.sdk.pojo.LessonAdditional as SdkTimetableAdditional
|
||||
import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable
|
||||
import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional
|
||||
|
||||
fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
|
||||
lessons = lessons.mapToEntities(semester),
|
||||
|
@ -16,13 +16,13 @@ fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
|
|||
headers = headers.mapToEntities(semester)
|
||||
)
|
||||
|
||||
fun List<SdkLesson>.mapToEntities(semester: Semester) = map {
|
||||
fun List<SdkTimetable>.mapToEntities(semester: Semester) = map {
|
||||
Timetable(
|
||||
studentId = semester.studentId,
|
||||
diaryId = semester.diaryId,
|
||||
number = it.number,
|
||||
start = it.start.toInstant(),
|
||||
end = it.end.toInstant(),
|
||||
start = it.startZoned.toInstant(),
|
||||
end = it.endZoned.toInstant(),
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
subjectOld = it.subjectOld,
|
||||
|
@ -45,8 +45,8 @@ fun List<SdkTimetableAdditional>.mapToEntities(semester: Semester) = map {
|
|||
diaryId = semester.diaryId,
|
||||
subject = it.subject,
|
||||
date = it.date,
|
||||
start = it.start.toInstant(),
|
||||
end = it.end.toInstant(),
|
||||
start = it.startZoned.toInstant(),
|
||||
end = it.endZoned.toInstant(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.scrapper.Scrapper
|
||||
|
||||
data class RegisterUser(
|
||||
val email: String,
|
||||
val password: String?,
|
||||
val login: String, // may be the same as email
|
||||
val scrapperBaseUrl: String?,
|
||||
val loginType: Scrapper.LoginType?,
|
||||
val loginMode: Sdk.Mode,
|
||||
val symbols: List<RegisterSymbol>,
|
||||
) : java.io.Serializable
|
||||
|
||||
data class RegisterSymbol(
|
||||
val symbol: String,
|
||||
val error: Throwable?,
|
||||
val hebeBaseUrl: String?,
|
||||
val keyId: String?,
|
||||
val privatePem: String?,
|
||||
val userName: String,
|
||||
val schools: List<RegisterUnit>,
|
||||
) : java.io.Serializable
|
||||
|
||||
data class RegisterUnit(
|
||||
val userLoginId: Int,
|
||||
val schoolId: String,
|
||||
val schoolName: String,
|
||||
val schoolShortName: String,
|
||||
val parentIds: List<Int>,
|
||||
val studentIds: List<Int>,
|
||||
val employeeIds: List<Int>,
|
||||
val error: Throwable?,
|
||||
val students: List<RegisterStudent>,
|
||||
) : java.io.Serializable
|
||||
|
||||
data class RegisterStudent(
|
||||
val studentId: Int,
|
||||
val studentName: String,
|
||||
val studentSecondName: String,
|
||||
val studentSurname: String,
|
||||
val className: String,
|
||||
val classId: Int,
|
||||
val isParent: Boolean,
|
||||
val semesters: List<Semester>,
|
||||
) : java.io.Serializable
|
|
@ -19,6 +19,7 @@ class AppCreatorRepository @Inject constructor(
|
|||
) {
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
suspend fun getAppCreators() = withContext(dispatchers.io) {
|
||||
val inputStream = context.assets.open("contributors.json").buffered()
|
||||
json.decodeFromStream<List<Contributor>>(inputStream)
|
||||
|
|
|
@ -59,7 +59,7 @@ class AttendanceRepository @Inject constructor(
|
|||
}
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getAttendance(start.monday, end.sunday)
|
||||
.getAttendance(start.monday, end.sunday, semester.semesterId)
|
||||
.mapToEntities(semester, lessons)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
|
|
|
@ -52,7 +52,7 @@ class ExamRepository @Inject constructor(
|
|||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getExams(start.startExamsDay, start.endExamsDay)
|
||||
.getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
|
|
|
@ -103,10 +103,7 @@ class MessageRepository @Inject constructor(
|
|||
messagesDb.loadMessageWithAttachment(message.messageGlobalKey)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student).getMessageDetails(
|
||||
messageKey = it!!.message.messageGlobalKey,
|
||||
markAsRead = message.unread && markAsRead,
|
||||
)
|
||||
sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey, markAsRead)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||
|
@ -181,7 +178,7 @@ class MessageRepository @Inject constructor(
|
|||
).first()
|
||||
}
|
||||
|
||||
suspend fun deleteMessage(student: Student, mailbox: Mailbox?, message: Message) {
|
||||
suspend fun deleteMessage(student: Student, mailbox: Mailbox, message: Message) {
|
||||
deleteMessages(student, mailbox, listOf(message))
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class NoteRepository @Inject constructor(
|
|||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getNotes()
|
||||
.getNotes(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
|
|
|
@ -2,17 +2,14 @@ package io.github.wulkanowy.data.repositories
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.edit
|
||||
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
||||
import com.fredporciuncula.flow.preferences.Preference
|
||||
import com.fredporciuncula.flow.preferences.Serializer
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.enums.*
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
|
||||
import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.serialization.decodeFromString
|
||||
|
@ -31,35 +28,29 @@ class PreferencesRepository @Inject constructor(
|
|||
private val json: Json,
|
||||
) {
|
||||
|
||||
val startMenuIndex: Int
|
||||
get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt()
|
||||
|
||||
val isShowPresent: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_attendance_present,
|
||||
R.bool.pref_default_attendance_present
|
||||
)
|
||||
|
||||
private val gradeAverageModePref: Preference<GradeAverageMode>
|
||||
get() = getObjectFlow(
|
||||
val gradeAverageMode: GradeAverageMode
|
||||
get() = GradeAverageMode.getByValue(
|
||||
getString(
|
||||
R.string.pref_key_grade_average_mode,
|
||||
R.string.pref_default_grade_average_mode,
|
||||
object : Serializer<GradeAverageMode> {
|
||||
override fun serialize(value: GradeAverageMode) = value.value
|
||||
override fun deserialize(serialized: String) =
|
||||
GradeAverageMode.getByValue(serialized)
|
||||
},
|
||||
R.string.pref_default_grade_average_mode
|
||||
)
|
||||
)
|
||||
|
||||
val gradeAverageModeFlow: Flow<GradeAverageMode>
|
||||
get() = gradeAverageModePref.asFlow()
|
||||
|
||||
private val gradeAverageForceCalcPref: Preference<Boolean>
|
||||
get() = flowSharedPref.getBoolean(
|
||||
context.getString(R.string.pref_key_grade_average_force_calc),
|
||||
context.resources.getBoolean(R.bool.pref_default_grade_average_force_calc)
|
||||
val gradeAverageForceCalc: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_grade_average_force_calc,
|
||||
R.bool.pref_default_grade_average_force_calc
|
||||
)
|
||||
|
||||
val gradeAverageForceCalcFlow: Flow<Boolean>
|
||||
get() = gradeAverageForceCalcPref.asFlow()
|
||||
|
||||
val gradeExpandMode: GradeExpandMode
|
||||
get() = GradeExpandMode.getByValue(
|
||||
getString(
|
||||
|
@ -149,24 +140,12 @@ class PreferencesRepository @Inject constructor(
|
|||
R.string.pref_default_grade_modifier_plus
|
||||
).toDouble()
|
||||
|
||||
val gradePlusModifierFlow: Flow<Double>
|
||||
get() = getStringFlow(
|
||||
R.string.pref_key_grade_modifier_plus,
|
||||
R.string.pref_default_grade_modifier_plus
|
||||
).asFlow().map { it.toDouble() }
|
||||
|
||||
val gradeMinusModifier: Double
|
||||
get() = getString(
|
||||
R.string.pref_key_grade_modifier_minus,
|
||||
R.string.pref_default_grade_modifier_minus
|
||||
).toDouble()
|
||||
|
||||
val gradeMinusModifierFlow: Flow<Double>
|
||||
get() = getStringFlow(
|
||||
R.string.pref_key_grade_modifier_minus,
|
||||
R.string.pref_default_grade_modifier_minus
|
||||
).asFlow().map { it.toDouble() }
|
||||
|
||||
val fillMessageContent: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_fill_message_content,
|
||||
|
@ -201,17 +180,24 @@ class PreferencesRepository @Inject constructor(
|
|||
R.bool.pref_default_timetable_show_timers
|
||||
)
|
||||
|
||||
var isHomeworkFullscreen: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_homework_fullscreen,
|
||||
R.bool.pref_default_homework_fullscreen
|
||||
)
|
||||
set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply()
|
||||
|
||||
val showSubjectsWithoutGrades: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_subjects_without_grades,
|
||||
R.bool.pref_default_subjects_without_grades
|
||||
)
|
||||
|
||||
val isOptionalArithmeticAverageFlow: Flow<Boolean>
|
||||
get() = flowSharedPref.getBoolean(
|
||||
context.getString(R.string.pref_key_optional_arithmetic_average),
|
||||
context.resources.getBoolean(R.bool.pref_default_optional_arithmetic_average)
|
||||
).asFlow()
|
||||
val isOptionalArithmeticAverage: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_optional_arithmetic_average,
|
||||
R.bool.pref_default_optional_arithmetic_average
|
||||
)
|
||||
|
||||
var lasSyncDate: Instant?
|
||||
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
|
||||
|
@ -329,20 +315,6 @@ class PreferencesRepository @Inject constructor(
|
|||
putBoolean(context.getString(R.string.pref_key_ads_enabled), value)
|
||||
}
|
||||
|
||||
var appMenuItemOrder: List<AppMenuItem>
|
||||
get() {
|
||||
val value = sharedPref.getString(PREF_KEY_APP_MENU_ITEM_ORDER, null)
|
||||
?: return AppMenuItem.defaultAppMenuItemList
|
||||
|
||||
return json.decodeFromString(value)
|
||||
}
|
||||
set(value) = sharedPref.edit {
|
||||
putString(
|
||||
PREF_KEY_APP_MENU_ITEM_ORDER,
|
||||
json.encodeToString(value)
|
||||
)
|
||||
}
|
||||
|
||||
var installationId: String
|
||||
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
|
||||
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
|
||||
|
@ -358,21 +330,6 @@ class PreferencesRepository @Inject constructor(
|
|||
private fun getLong(id: String, default: Int) =
|
||||
sharedPref.getLong(id, context.resources.getString(default).toLong())
|
||||
|
||||
private fun getStringFlow(id: Int, default: Int) =
|
||||
flowSharedPref.getString(context.getString(id), context.getString(default))
|
||||
|
||||
private fun <T : Any> getObjectFlow(
|
||||
@StringRes id: Int,
|
||||
@StringRes default: Int,
|
||||
serializer: Serializer<T>
|
||||
): Preference<T> = flowSharedPref.getObject(
|
||||
key = context.getString(id),
|
||||
serializer = serializer,
|
||||
defaultValue = serializer.deserialize(
|
||||
flowSharedPref.getString(context.getString(default)).get()
|
||||
)
|
||||
)
|
||||
|
||||
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
||||
|
||||
private fun getString(id: String, default: Int) =
|
||||
|
@ -384,7 +341,6 @@ class PreferencesRepository @Inject constructor(
|
|||
sharedPref.getBoolean(id, context.resources.getBoolean(default))
|
||||
|
||||
private companion object {
|
||||
private const val PREF_KEY_APP_MENU_ITEM_ORDER = "app_menu_item_order"
|
||||
private const val PREF_KEY_INSTALLATION_ID = "installation_id"
|
||||
private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position"
|
||||
private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count"
|
||||
|
|
|
@ -40,7 +40,7 @@ class SemesterRepository @Inject constructor(
|
|||
val isNoSemesters = semesters.isEmpty()
|
||||
|
||||
val isRefreshOnModeChangeRequired = when {
|
||||
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE -> {
|
||||
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> {
|
||||
semesters.firstOrNull { it.isCurrent }?.let {
|
||||
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
||||
} == true
|
||||
|
|
|
@ -6,17 +6,14 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentName
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.data.mappers.mapToPojo
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.security.decrypt
|
||||
import io.github.wulkanowy.utils.security.encrypt
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -30,51 +27,45 @@ class StudentRepository @Inject constructor(
|
|||
private val studentDb: StudentDao,
|
||||
private val semesterDb: SemesterDao,
|
||||
private val sdk: Sdk,
|
||||
private val appInfo: AppInfo,
|
||||
private val appDatabase: AppDatabase
|
||||
) {
|
||||
|
||||
suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty()
|
||||
|
||||
suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false
|
||||
|
||||
suspend fun getStudentsApi(
|
||||
pin: String,
|
||||
symbol: String,
|
||||
token: String
|
||||
): RegisterUser = sdk
|
||||
.getStudentsFromHebe(token, pin, symbol, "")
|
||||
.mapToPojo(null)
|
||||
): List<StudentWithSemesters> =
|
||||
sdk.getStudentsFromMobileApi(token, pin, symbol, "")
|
||||
.mapToEntities(colors = appInfo.defaultColorsForAvatar)
|
||||
|
||||
suspend fun getStudentsScrapper(
|
||||
email: String,
|
||||
password: String,
|
||||
scrapperBaseUrl: String,
|
||||
symbol: String
|
||||
): RegisterUser = sdk
|
||||
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||
.mapToPojo(password)
|
||||
|
||||
suspend fun getUserSubjectsFromScrapper(
|
||||
email: String,
|
||||
password: String,
|
||||
scrapperBaseUrl: String,
|
||||
symbol: String
|
||||
): RegisterUser = sdk
|
||||
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||
.mapToPojo(password)
|
||||
): List<StudentWithSemesters> =
|
||||
sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||
.mapToEntities(password, appInfo.defaultColorsForAvatar)
|
||||
|
||||
suspend fun getStudentsHybrid(
|
||||
email: String,
|
||||
password: String,
|
||||
scrapperBaseUrl: String,
|
||||
symbol: String
|
||||
): RegisterUser = sdk
|
||||
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
||||
.mapToPojo(password)
|
||||
): List<StudentWithSemesters> =
|
||||
sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
||||
.mapToEntities(password, appInfo.defaultColorsForAvatar)
|
||||
|
||||
suspend fun getSavedStudents(decryptPass: Boolean = true) =
|
||||
studentDb.loadStudentsWithSemesters()
|
||||
.map {
|
||||
it.apply {
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||
student.password = withContext(dispatchers.io) {
|
||||
decrypt(student.password)
|
||||
}
|
||||
|
@ -84,7 +75,7 @@ class StudentRepository @Inject constructor(
|
|||
|
||||
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) =
|
||||
studentDb.loadStudentWithSemestersById(id)?.apply {
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||
student.password = withContext(dispatchers.io) {
|
||||
decrypt(student.password)
|
||||
}
|
||||
|
@ -94,7 +85,7 @@ class StudentRepository @Inject constructor(
|
|||
suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student {
|
||||
val student = studentDb.loadById(id) ?: throw NoCurrentStudentException()
|
||||
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||
student.password = withContext(dispatchers.io) {
|
||||
decrypt(student.password)
|
||||
}
|
||||
|
@ -105,7 +96,7 @@ class StudentRepository @Inject constructor(
|
|||
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
||||
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
||||
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||
student.password = withContext(dispatchers.io) {
|
||||
decrypt(student.password)
|
||||
}
|
||||
|
@ -118,7 +109,7 @@ class StudentRepository @Inject constructor(
|
|||
val students = studentsWithSemesters.map { it.student }
|
||||
.map {
|
||||
it.apply {
|
||||
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.HEBE) {
|
||||
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) {
|
||||
password = withContext(dispatchers.io) {
|
||||
encrypt(password, context)
|
||||
}
|
||||
|
@ -149,21 +140,4 @@ class StudentRepository @Inject constructor(
|
|||
|
||||
suspend fun isOneUniqueStudent() = getSavedStudents(false)
|
||||
.distinctBy { it.student.studentName }.size == 1
|
||||
|
||||
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.authorizePermission(pesel)
|
||||
|
||||
suspend fun refreshStudentName(student: Student, semester: Semester) {
|
||||
val newCurrentApiStudent = sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getCurrentStudent() ?: return
|
||||
|
||||
val studentName = StudentName(
|
||||
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
||||
).apply { id = student.id }
|
||||
|
||||
studentDb.update(studentName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class TeacherRepository @Inject constructor(
|
|||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getTeachers()
|
||||
.getTeachers(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
|
|
|
@ -13,7 +13,6 @@ import io.github.wulkanowy.utils.*
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -66,7 +65,7 @@ class TimetableRepository @Inject constructor(
|
|||
fetch = {
|
||||
val timetableFull = sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getTimetable(start.monday, end.sunday)
|
||||
.getTimetableFull(start.monday, end.sunday)
|
||||
|
||||
timetableFull.mapToEntities(semester)
|
||||
},
|
||||
|
@ -165,11 +164,6 @@ class TimetableRepository @Inject constructor(
|
|||
timetableHeaderDb.insertAll(new uniqueSubtract old)
|
||||
}
|
||||
|
||||
fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {
|
||||
val refreshKey = getRefreshKey(cacheKey, semester, start, end)
|
||||
return refreshHelper.getLastRefreshTimestamp(refreshKey)
|
||||
}
|
||||
|
||||
suspend fun saveAdditionalList(additionalList: List<TimetableAdditional>) =
|
||||
timetableAdditionalDb.insertAll(additionalList)
|
||||
|
||||
|
|
|
@ -17,11 +17,8 @@ class GetMailboxByStudentUseCase @Inject constructor(
|
|||
private fun List<Mailbox>.filterByStudent(student: Student): Mailbox? {
|
||||
val normalizedStudentName = student.studentName.normalizeStudentName()
|
||||
|
||||
return singleOrNull {
|
||||
return find {
|
||||
it.studentName.normalizeStudentName() == normalizedStudentName
|
||||
} ?: singleOrNull {
|
||||
it.studentName.normalizeStudentName() == normalizedStudentName
|
||||
&& it.schoolNameShort == student.schoolShortName
|
||||
} ?: singleOrNull {
|
||||
it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart()
|
||||
} ?: singleOrNull {
|
||||
|
|
|
@ -4,12 +4,18 @@ import android.os.Build.VERSION.SDK_INT
|
|||
import android.os.Build.VERSION_CODES.O
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.work.*
|
||||
import androidx.work.BackoffPolicy.EXPONENTIAL
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.Data
|
||||
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
|
||||
import androidx.work.ExistingPeriodicWorkPolicy.UPDATE
|
||||
import androidx.work.ExistingPeriodicWorkPolicy.REPLACE
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.NetworkType.CONNECTED
|
||||
import androidx.work.NetworkType.UNMETERED
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
|
@ -54,7 +60,7 @@ class SyncManager @Inject constructor(
|
|||
val serviceInterval = preferencesRepository.servicesInterval
|
||||
|
||||
workManager.enqueueUniquePeriodicWork(
|
||||
SyncWorker::class.java.simpleName, if (restart) UPDATE else KEEP,
|
||||
SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP,
|
||||
PeriodicWorkRequestBuilder<SyncWorker>(serviceInterval, MINUTES)
|
||||
.setInitialDelay(10, MINUTES)
|
||||
.setBackoffCriteria(EXPONENTIAL, 30, MINUTES)
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Intent
|
|||
import android.widget.RemoteViewsService
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||
|
@ -23,13 +24,14 @@ class TimetableWidgetService : RemoteViewsService() {
|
|||
@Inject
|
||||
lateinit var semesterRepo: SemesterRepository
|
||||
|
||||
@Inject
|
||||
lateinit var prefRepository: PreferencesRepository
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPref: SharedPrefProvider
|
||||
|
||||
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
|
||||
Timber.d("TimetableWidgetFactory created")
|
||||
return TimetableWidgetFactory(
|
||||
timetableRepo, studentRepo, semesterRepo, sharedPref, applicationContext, intent
|
||||
)
|
||||
return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, applicationContext, intent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,12 @@ import android.app.ActivityManager
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.utils.FragmentLifecycleLogger
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
|
@ -31,8 +30,6 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||
|
||||
protected var messageContainer: View? = null
|
||||
|
||||
protected var messageAnchor: View? = null
|
||||
|
||||
abstract var presenter: T
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -51,7 +48,6 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||
if (messageContainer != null) {
|
||||
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
|
||||
.setAction(R.string.all_details) { showErrorDetailsDialog(error) }
|
||||
.apply { messageAnchor?.let { anchorView = it } }
|
||||
.show()
|
||||
} else showMessage(text)
|
||||
}
|
||||
|
@ -61,15 +57,12 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||
}
|
||||
|
||||
override fun showMessage(text: String) {
|
||||
if (messageContainer != null) {
|
||||
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
|
||||
.apply { messageAnchor?.let { anchorView = it } }
|
||||
.show()
|
||||
} else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
|
||||
if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
|
||||
else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun showExpiredDialog() {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.main_session_expired)
|
||||
.setMessage(R.string.main_session_relogin)
|
||||
.setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() }
|
||||
|
@ -77,15 +70,10 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||
.show()
|
||||
}
|
||||
|
||||
override fun showAuthDialog() {
|
||||
AuthDialog.newInstance().show(supportFragmentManager, "auth_dialog")
|
||||
}
|
||||
|
||||
override fun showChangePasswordSnackbar(redirectUrl: String) {
|
||||
messageContainer?.let {
|
||||
Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG)
|
||||
.setAction(R.string.all_change) { openInternetBrowser(redirectUrl) }
|
||||
.apply { messageAnchor?.let { anchorView = it } }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.google.android.material.elevation.SurfaceColors
|
||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import javax.inject.Inject
|
||||
|
@ -40,25 +34,10 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView
|
|||
(activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl)
|
||||
}
|
||||
|
||||
override fun showAuthDialog() {
|
||||
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
|
||||
}
|
||||
|
||||
override fun showErrorDetailsDialog(error: Throwable) {
|
||||
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
view.setBackgroundColor(SurfaceColors.SURFACE_3.getColor(requireContext()))
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = binding.root
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName)
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.viewbinding.ViewBinding
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
|
||||
abstract class BaseFragment<VB : ViewBinding>(@LayoutRes layoutId: Int) : Fragment(layoutId),
|
||||
|
@ -43,10 +42,6 @@ abstract class BaseFragment<VB : ViewBinding>(@LayoutRes layoutId: Int) : Fragme
|
|||
(activity as? BaseActivity<*, *>)?.showExpiredDialog()
|
||||
}
|
||||
|
||||
override fun showAuthDialog() {
|
||||
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
|
||||
}
|
||||
|
||||
override fun openClearLoginView() {
|
||||
(activity as? BaseActivity<*, *>)?.openClearLoginView()
|
||||
}
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
open class BasePresenter<T : BaseView>(
|
||||
|
@ -31,7 +26,6 @@ open class BasePresenter<T : BaseView>(
|
|||
onSessionExpired = view::showExpiredDialog
|
||||
onNoCurrentStudent = view::openClearLoginView
|
||||
onPasswordChangeRequired = view::showChangePasswordSnackbar
|
||||
onAuthorizationRequired = view::showAuthDialog
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ interface BaseView {
|
|||
|
||||
fun showExpiredDialog()
|
||||
|
||||
fun showAuthDialog()
|
||||
|
||||
fun openClearLoginView()
|
||||
|
||||
fun showErrorDetailsDialog(error: Throwable)
|
||||
|
|
|
@ -4,13 +4,13 @@ import android.app.Dialog
|
|||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import android.widget.Toast.LENGTH_LONG
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isGone
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
|
@ -20,7 +20,7 @@ import io.github.wulkanowy.utils.*
|
|||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
||||
class ErrorDialog : DialogFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
@ -28,8 +28,6 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
private lateinit var error: Throwable
|
||||
|
||||
companion object {
|
||||
private const val ARGUMENT_KEY = "error"
|
||||
|
||||
|
@ -38,31 +36,32 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
error = requireArguments().serializable(ARGUMENT_KEY)
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable
|
||||
|
||||
val binding = DialogErrorBinding.inflate(layoutInflater)
|
||||
binding.bindErrorDetails(error)
|
||||
|
||||
return getAlertDialog(binding, error).apply {
|
||||
enableReportButtonIfErrorIsReportable(error)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
private fun getAlertDialog(binding: DialogErrorBinding, error: Throwable): AlertDialog {
|
||||
return MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
val errorStacktrace = error.stackTraceToString()
|
||||
setTitle(R.string.all_details)
|
||||
setView(DialogErrorBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
setView(binding.root)
|
||||
setNeutralButton(R.string.about_feedback) { _, _ ->
|
||||
openConfirmDialog { openEmailClient(errorStacktrace) }
|
||||
}
|
||||
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) }
|
||||
}.create().apply {
|
||||
setOnShowListener {
|
||||
getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported()
|
||||
}
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
with(binding) {
|
||||
private fun DialogErrorBinding.bindErrorDetails(error: Throwable) {
|
||||
return with(this) {
|
||||
errorDialogHumanizedMessage.text = resources.getErrorString(error)
|
||||
errorDialogErrorMessage.text = error.localizedMessage
|
||||
errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank()
|
||||
|
@ -71,6 +70,12 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -78,7 +83,7 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||
}
|
||||
|
||||
private fun openConfirmDialog(callback: () -> Unit) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.dialog_error_check_update)
|
||||
.setMessage(R.string.dialog_error_check_update_message)
|
||||
.setNeutralButton(R.string.about_feedback) { _, _ -> callback() }
|
||||
|
@ -108,4 +113,8 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showMessage(text: String) {
|
||||
Toast.makeText(requireContext(), text, LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.base
|
|||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.AuthorizationRequiredException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||
import io.github.wulkanowy.utils.getErrorString
|
||||
|
@ -21,8 +20,6 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
|||
|
||||
var onPasswordChangeRequired: (String) -> Unit = {}
|
||||
|
||||
var onAuthorizationRequired: () -> Unit = {}
|
||||
|
||||
fun dispatch(error: Throwable) {
|
||||
Timber.e(error, "An exception occurred while the Wulkanowy was running")
|
||||
proceed(error)
|
||||
|
@ -34,7 +31,6 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
|||
is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl)
|
||||
is ScramblerException, is BadCredentialsException -> onSessionExpired()
|
||||
is NoCurrentStudentException -> onNoCurrentStudent()
|
||||
is AuthorizationRequiredException -> onAuthorizationRequired()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,6 +39,5 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
|||
onSessionExpired = {}
|
||||
onNoCurrentStudent = {}
|
||||
onPasswordChangeRequired = {}
|
||||
onAuthorizationRequired = {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.GET_ACTIVITIES
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.enums.AppTheme
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
|
||||
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -27,40 +25,31 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
|
|||
when (activity) {
|
||||
is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black)
|
||||
is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black)
|
||||
is SendMessageActivity -> activity.setTheme(R.style.WulkanowyTheme_MessageSend_Black)
|
||||
}
|
||||
}
|
||||
} else if (activity is TimetableWidgetConfigureActivity || activity is LuckyNumberWidgetConfigureActivity) {
|
||||
DynamicColors.applyToActivityIfAvailable(activity)
|
||||
}
|
||||
}
|
||||
|
||||
fun applyDefaultTheme() {
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
when (preferencesRepository.appTheme) {
|
||||
AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
AppTheme.DARK, AppTheme.BLACK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
AppTheme.SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
AppTheme.LIGHT -> MODE_NIGHT_NO
|
||||
AppTheme.DARK, AppTheme.BLACK -> MODE_NIGHT_YES
|
||||
AppTheme.SYSTEM -> MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun isThemeApplicable(activity: AppCompatActivity): Boolean =
|
||||
getPackageInfo(activity)
|
||||
private fun isThemeApplicable(activity: AppCompatActivity) =
|
||||
activity.packageManager
|
||||
.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
||||
.activities
|
||||
.singleOrNull { it.name == activity::class.java.canonicalName }
|
||||
?.theme
|
||||
.let {
|
||||
it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar
|
||||
|| it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun getPackageInfo(activity: AppCompatActivity): PackageInfo {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
activity.packageManager.getPackageInfo(
|
||||
activity.packageName,
|
||||
PackageManager.PackageInfoFlags.of(GET_ACTIVITIES.toLong())
|
||||
)
|
||||
} else activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
||||
|| it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_Black
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,11 @@ import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
|||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||
import io.github.wulkanowy.ui.modules.luckynumber.history.LuckyNumberHistoryFragment
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
|
||||
import io.github.wulkanowy.ui.modules.more.MoreFragment
|
||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
||||
import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment
|
||||
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
||||
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.time.LocalDate
|
||||
|
@ -42,13 +39,10 @@ sealed class Destination {
|
|||
NOTE(Note),
|
||||
CONFERENCE(Conference),
|
||||
SCHOOL_ANNOUNCEMENT(SchoolAnnouncement),
|
||||
SCHOOL_AND_TEACHERS(SchoolAndTeachers),
|
||||
LUCKY_NUMBER(LuckyNumber),
|
||||
LUCKY_NUMBER_HISTORY(LuckyNumberHistory),
|
||||
SCHOOL(School),
|
||||
LUCKY_NUMBER(More),
|
||||
MORE(More),
|
||||
MESSAGE(Message),
|
||||
MOBILE_DEVICE(MobileDevice),
|
||||
SETTINGS(Settings);
|
||||
MESSAGE(Message);
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
@ -109,9 +103,9 @@ sealed class Destination {
|
|||
}
|
||||
|
||||
@Serializable
|
||||
object SchoolAndTeachers : Destination() {
|
||||
override val destinationType get() = Type.SCHOOL_AND_TEACHERS
|
||||
override val destinationFragment get() = SchoolAndTeachersFragment.newInstance()
|
||||
object School : Destination() {
|
||||
override val destinationType get() = Type.SCHOOL
|
||||
override val destinationFragment get() = SchoolFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
@ -120,12 +114,6 @@ sealed class Destination {
|
|||
override val destinationFragment get() = LuckyNumberFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object LuckyNumberHistory : Destination() {
|
||||
override val destinationType get() = Type.LUCKY_NUMBER_HISTORY
|
||||
override val destinationFragment get() = LuckyNumberHistoryFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object More : Destination() {
|
||||
override val destinationType get() = Type.MORE
|
||||
|
@ -137,16 +125,4 @@ sealed class Destination {
|
|||
override val destinationType get() = Type.MESSAGE
|
||||
override val destinationFragment get() = MessageFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object MobileDevice : Destination() {
|
||||
override val destinationType get() = Type.MOBILE_DEVICE
|
||||
override val destinationFragment get() = MobileDeviceFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Settings : Destination() {
|
||||
override val destinationType get() = Type.SETTINGS
|
||||
override val destinationFragment get() = SettingsFragment.newInstance()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ class AccountFragment : BaseFragment<FragmentAccountBinding>(R.layout.fragment_a
|
|||
|
||||
override val titleStringId = R.string.account_title
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
|
|
@ -6,10 +6,8 @@ import android.view.MenuInflater
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
|
@ -23,7 +21,6 @@ import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
|
|||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -40,12 +37,12 @@ class AccountDetailsFragment :
|
|||
|
||||
private const val ARGUMENT_KEY = "Data"
|
||||
|
||||
fun newInstance(student: Student) = AccountDetailsFragment().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to student)
|
||||
fun newInstance(student: Student) =
|
||||
AccountDetailsFragment().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, student) }
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -54,7 +51,7 @@ class AccountDetailsFragment :
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentAccountDetailsBinding.bind(view)
|
||||
presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY))
|
||||
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
|
@ -115,7 +112,7 @@ class AccountDetailsFragment :
|
|||
|
||||
override fun showLogoutConfirmDialog() {
|
||||
context?.let {
|
||||
MaterialAlertDialogBuilder(it)
|
||||
AlertDialog.Builder(it)
|
||||
.setTitle(R.string.account_logout_student)
|
||||
.setMessage(R.string.account_confirm)
|
||||
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.databinding.DialogAccountEditBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -26,21 +24,28 @@ class AccountEditDialog : BaseDialogFragment<DialogAccountEditBinding>(), Accoun
|
|||
|
||||
private const val ARGUMENT_KEY = "student_with_semesters"
|
||||
|
||||
fun newInstance(student: Student) = AccountEditDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to student)
|
||||
fun newInstance(student: Student) =
|
||||
AccountEditDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(ARGUMENT_KEY, student)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(DialogAccountEditBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
.create()
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY))
|
||||
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountquick
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.DialogAccountQuickBinding
|
||||
|
@ -14,7 +13,6 @@ import io.github.wulkanowy.ui.modules.account.AccountAdapter
|
|||
import io.github.wulkanowy.ui.modules.account.AccountFragment
|
||||
import io.github.wulkanowy.ui.modules.account.AccountItem
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -32,23 +30,27 @@ class AccountQuickDialog : BaseDialogFragment<DialogAccountQuickBinding>(), Acco
|
|||
|
||||
fun newInstance(studentsWithSemesters: List<StudentWithSemesters>) =
|
||||
AccountQuickDialog().apply {
|
||||
arguments = bundleOf(STUDENTS_ARGUMENT_KEY to studentsWithSemesters.toTypedArray())
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(STUDENTS_ARGUMENT_KEY, studentsWithSemesters.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(
|
||||
DialogAccountQuickBinding.inflate(layoutInflater)
|
||||
.apply { binding = this }.root
|
||||
)
|
||||
.create()
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val studentsWithSemesters = requireArguments()
|
||||
.serializable<Array<StudentWithSemesters>>(STUDENTS_ARGUMENT_KEY).toList()
|
||||
val studentsWithSemesters =
|
||||
(requireArguments()[STUDENTS_ARGUMENT_KEY] as Array<StudentWithSemesters>).toList()
|
||||
|
||||
presenter.onAttachView(this, studentsWithSemesters)
|
||||
}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.databinding.DialogAttendanceBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.utils.descriptionRes
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AttendanceDialog : BaseDialogFragment<DialogAttendanceBinding>() {
|
||||
class AttendanceDialog : DialogFragment() {
|
||||
|
||||
private var binding: DialogAttendanceBinding by lifecycleAwareVariable()
|
||||
|
||||
private lateinit var attendance: Attendance
|
||||
|
||||
|
@ -23,20 +22,23 @@ class AttendanceDialog : BaseDialogFragment<DialogAttendanceBinding>() {
|
|||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: Attendance) = AttendanceDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to exam)
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
attendance = requireArguments().serializable(ARGUMENT_KEY)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
attendance = getSerializable(ARGUMENT_KEY) as Attendance
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(DialogAttendanceBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
.create()
|
||||
}
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
|
@ -4,10 +4,10 @@ import android.content.DialogInterface.BUTTON_POSITIVE
|
|||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.view.View.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
|
@ -84,7 +84,6 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -124,7 +123,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||
|
||||
attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() }
|
||||
|
||||
attendanceNavContainer.elevation = requireContext().dpToPx(3f)
|
||||
attendanceNavContainer.elevation = requireContext().dpToPx(8f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +227,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||
|
||||
override fun showExcuseDialog() {
|
||||
val dialogBinding = DialogExcuseBinding.inflate(LayoutInflater.from(context))
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.attendance_excuse_title)
|
||||
.setView(dialogBinding.root)
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.auth
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.DialogAuthBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AuthDialog : BaseDialogFragment<DialogAuthBinding>(), AuthView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: AuthPresenter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = AuthDialog()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, R.style.FullScreenDialogStyle)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return DialogAuthBinding.inflate(inflater).apply { binding = this }.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
presenter.onAttachView(this)
|
||||
|
||||
binding.authInput.doOnTextChanged { text, _, _, _ ->
|
||||
presenter.onPeselChange(text?.toString())
|
||||
}
|
||||
|
||||
binding.authButton.setOnClickListener { presenter.authorize() }
|
||||
binding.authSuccessButton.setOnClickListener {
|
||||
activity?.recreate()
|
||||
dismiss()
|
||||
}
|
||||
binding.authButtonSkip.setOnClickListener { dismiss() }
|
||||
}
|
||||
|
||||
override fun enableAuthButton(isEnabled: Boolean) {
|
||||
binding.authButton.isEnabled = isEnabled
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
binding.authProgress.isVisible = show
|
||||
}
|
||||
|
||||
override fun showPeselError(show: Boolean) {
|
||||
binding.authInputLayout.error = getString(R.string.auth_api_error).takeIf { show }
|
||||
}
|
||||
|
||||
override fun showInvalidPeselError(show: Boolean) {
|
||||
binding.authInputLayout.error = getString(R.string.auth_invalid_error).takeIf { show }
|
||||
}
|
||||
|
||||
override fun showSuccess(show: Boolean) {
|
||||
binding.authSuccess.isVisible = show
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
binding.authForm.isVisible = show
|
||||
}
|
||||
|
||||
override fun showDescriptionWithName(name: String) {
|
||||
binding.authDescription.text = getString(R.string.auth_description, name).parseAsHtml()
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.auth
|
||||
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class AuthPresenter @Inject constructor(
|
||||
private val semesterRepository: SemesterRepository,
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository
|
||||
) : BasePresenter<AuthView>(errorHandler, studentRepository) {
|
||||
|
||||
private var pesel: String = ""
|
||||
|
||||
override fun onAttachView(view: AuthView) {
|
||||
super.onAttachView(view)
|
||||
view.enableAuthButton(pesel.length == 11)
|
||||
view.showSuccess(false)
|
||||
view.showProgress(false)
|
||||
|
||||
loadName()
|
||||
}
|
||||
|
||||
private fun loadName() {
|
||||
presenterScope.launch {
|
||||
runCatching { studentRepository.getCurrentStudent(false) }
|
||||
.onSuccess { view?.showDescriptionWithName(it.studentName) }
|
||||
.onFailure { errorHandler.dispatch(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun onPeselChange(newPesel: String?) {
|
||||
pesel = newPesel.orEmpty()
|
||||
|
||||
view?.enableAuthButton(pesel.length == 11)
|
||||
view?.showPeselError(false)
|
||||
view?.showInvalidPeselError(false)
|
||||
}
|
||||
|
||||
fun authorize() {
|
||||
presenterScope.launch {
|
||||
view?.showProgress(true)
|
||||
view?.showContent(false)
|
||||
|
||||
if (!isValidPESEL(pesel)) {
|
||||
view?.showInvalidPeselError(true)
|
||||
view?.showProgress(false)
|
||||
view?.showContent(true)
|
||||
return@launch
|
||||
}
|
||||
|
||||
runCatching {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
|
||||
val isSuccess = studentRepository.authorizePermission(student, semester, pesel)
|
||||
if (isSuccess) {
|
||||
studentRepository.refreshStudentName(student, semester)
|
||||
}
|
||||
isSuccess
|
||||
}
|
||||
.onFailure { errorHandler.dispatch(it) }
|
||||
.onSuccess {
|
||||
if (it) {
|
||||
view?.showSuccess(true)
|
||||
view?.showContent(false)
|
||||
view?.showPeselError(false)
|
||||
} else {
|
||||
view?.showSuccess(false)
|
||||
view?.showContent(true)
|
||||
view?.showPeselError(true)
|
||||
}
|
||||
}
|
||||
|
||||
view?.showProgress(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isValidPESEL(peselString: String): Boolean {
|
||||
if (peselString.length != 11) {
|
||||
return false
|
||||
}
|
||||
|
||||
val weights = intArrayOf(1, 3, 7, 9, 1, 3, 7, 9, 1, 3)
|
||||
var sum = 0
|
||||
|
||||
for (i in 0 until 10) {
|
||||
sum += weights[i] * Character.getNumericValue(peselString[i])
|
||||
}
|
||||
|
||||
sum %= 10
|
||||
sum = 10 - sum
|
||||
sum %= 10
|
||||
|
||||
return sum == Character.getNumericValue(peselString[10])
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.auth
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface AuthView : BaseView {
|
||||
|
||||
fun enableAuthButton(isEnabled: Boolean)
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
fun showPeselError(show: Boolean)
|
||||
|
||||
fun showInvalidPeselError(show: Boolean)
|
||||
|
||||
fun showSuccess(show: Boolean)
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showDescriptionWithName(name: String)
|
||||
}
|
|
@ -1,20 +1,19 @@
|
|||
package io.github.wulkanowy.ui.modules.conference
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.data.db.entities.Conference
|
||||
import io.github.wulkanowy.databinding.DialogConferenceBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ConferenceDialog : BaseDialogFragment<DialogConferenceBinding>() {
|
||||
class ConferenceDialog : DialogFragment() {
|
||||
|
||||
private var binding: DialogConferenceBinding by lifecycleAwareVariable()
|
||||
|
||||
private lateinit var conference: Conference
|
||||
|
||||
|
@ -23,20 +22,23 @@ class ConferenceDialog : BaseDialogFragment<DialogConferenceBinding>() {
|
|||
private const val ARGUMENT_KEY = "item"
|
||||
|
||||
fun newInstance(conference: Conference) = ConferenceDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to conference)
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, conference) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
conference = requireArguments().serializable(ARGUMENT_KEY)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.let {
|
||||
conference = it.getSerializable(ARGUMENT_KEY) as Conference
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(DialogConferenceBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
.create()
|
||||
}
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogConferenceBinding.inflate(inflater).also { binding = it }.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
|
@ -16,7 +16,7 @@ import javax.inject.Inject
|
|||
|
||||
@AndroidEntryPoint
|
||||
class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.fragment_conference),
|
||||
ConferenceView, MainView.TitledView, MainView.MainChildView {
|
||||
ConferenceView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: ConferencePresenter
|
||||
|
@ -109,14 +109,6 @@ class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.frag
|
|||
(activity as? MainActivity)?.showDialogFragment(ConferenceDialog.newInstance(conference))
|
||||
}
|
||||
|
||||
override fun onFragmentReselected() {
|
||||
if (::presenter.isInitialized) presenter.onFragmentReselected()
|
||||
}
|
||||
|
||||
override fun resetView() {
|
||||
binding.conferenceRecycler.smoothScrollToPosition(0)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
|
|
|
@ -96,11 +96,4 @@ class ConferencePresenter @Inject constructor(
|
|||
.onResourceError(errorHandler::dispatch)
|
||||
.launch()
|
||||
}
|
||||
|
||||
fun onFragmentReselected() {
|
||||
Timber.i("Conference is reselected")
|
||||
if (view?.isViewEmpty == false) {
|
||||
view?.resetView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,4 @@ interface ConferenceView : BaseView {
|
|||
fun showContent(show: Boolean)
|
||||
|
||||
fun openConferenceDialog(conference: Conference)
|
||||
|
||||
fun resetView()
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import androidx.recyclerview.widget.DefaultItemAnimator
|
|||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.FragmentDashboardBinding
|
||||
|
@ -62,7 +61,6 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
|||
fun newInstance() = DashboardFragment()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -149,7 +147,7 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
|||
val values = requireContext().resources.getStringArray(R.array.dashboard_tile_values)
|
||||
val selectedItemsState = values.map { value -> selectedItems.any { it.name == value } }
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.pref_dashboard_appearance_tiles_title)
|
||||
.setMultiChoiceItems(entries, selectedItemsState.toBooleanArray()) { _, _, _ -> }
|
||||
.setPositiveButton(android.R.string.ok) { dialog, _ ->
|
||||
|
|
|
@ -606,7 +606,7 @@ class DashboardPresenter @Inject constructor(
|
|||
}
|
||||
is Resource.Error -> {
|
||||
Timber.i("Loading dashboard admin message result: An exception occurred")
|
||||
Timber.e(it.error)
|
||||
errorHandler.dispatch(it.error)
|
||||
updateData(
|
||||
dashboardItem = DashboardItem.AdminMessages(
|
||||
adminMessage = null,
|
||||
|
@ -748,7 +748,7 @@ class DashboardPresenter @Inject constructor(
|
|||
itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null
|
||||
val isGeneralError =
|
||||
filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError
|
||||
val firstError = itemsLoadedList.firstNotNullOfOrNull { it.error }
|
||||
val firstError = itemsLoadedList.mapNotNull { it.error }.firstOrNull()
|
||||
|
||||
val filteredOriginalLoadedList =
|
||||
dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -9,7 +8,6 @@ import io.github.wulkanowy.data.enums.GradeColorTheme
|
|||
import io.github.wulkanowy.databinding.SubitemDashboardGradesBinding
|
||||
import io.github.wulkanowy.databinding.SubitemDashboardSmallGradeBinding
|
||||
import io.github.wulkanowy.utils.getBackgroundColor
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
|
||||
class DashboardGradesAdapter : RecyclerView.Adapter<DashboardGradesAdapter.ViewHolder>() {
|
||||
|
||||
|
@ -39,9 +37,7 @@ class DashboardGradesAdapter : RecyclerView.Adapter<DashboardGradesAdapter.ViewH
|
|||
|
||||
with(subitemBinding.dashboardSmallGradeSubitemValue) {
|
||||
text = it.entry
|
||||
backgroundTintList = ColorStateList.valueOf(
|
||||
context.getCompatColor(it.getBackgroundColor(gradeColorTheme))
|
||||
)
|
||||
setBackgroundResource(it.getBackgroundColor(gradeColorTheme))
|
||||
}
|
||||
|
||||
dashboardGradesSubitemGradeContainer.addView(subitemBinding.root)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package io.github.wulkanowy.ui.modules.debug.logviewer
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.Intent.*
|
||||
import android.content.Intent.EXTRA_EMAIL
|
||||
import android.content.Intent.EXTRA_STREAM
|
||||
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
|
@ -34,7 +36,6 @@ class LogViewerFragment : BaseFragment<FragmentLogviewerBinding>(R.layout.fragme
|
|||
fun newInstance() = LogViewerFragment()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
package io.github.wulkanowy.ui.modules.exam
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.databinding.DialogExamBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.openCalendarEventAdd
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import java.time.LocalTime
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ExamDialog : BaseDialogFragment<DialogExamBinding>() {
|
||||
class ExamDialog : DialogFragment() {
|
||||
|
||||
private var binding: DialogExamBinding by lifecycleAwareVariable()
|
||||
|
||||
private lateinit var exam: Exam
|
||||
|
||||
|
@ -25,20 +24,23 @@ class ExamDialog : BaseDialogFragment<DialogExamBinding>() {
|
|||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: Exam) = ExamDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to exam)
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
exam = requireArguments().serializable(ARGUMENT_KEY)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
exam = getSerializable(ARGUMENT_KEY) as Exam
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(DialogExamBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
.create()
|
||||
}
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogExamBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
|
@ -2,7 +2,9 @@ package io.github.wulkanowy.ui.modules.exam
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.View.*
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
|
@ -18,7 +20,7 @@ import javax.inject.Inject
|
|||
|
||||
@AndroidEntryPoint
|
||||
class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam), ExamView,
|
||||
MainView.TitledView, MainView.MainChildView {
|
||||
MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: ExamPresenter
|
||||
|
@ -62,7 +64,7 @@ class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam),
|
|||
examPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
|
||||
examNextButton.setOnClickListener { presenter.onNextWeek() }
|
||||
|
||||
examNavContainer.elevation = requireContext().dpToPx(3f)
|
||||
examNavContainer.elevation = requireContext().dpToPx(8f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,14 +126,6 @@ class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam),
|
|||
(activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam))
|
||||
}
|
||||
|
||||
override fun onFragmentReselected() {
|
||||
if (::presenter.isInitialized) presenter.onViewReselected()
|
||||
}
|
||||
|
||||
override fun resetView() {
|
||||
binding.examRecycler.smoothScrollToPosition(0)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
||||
|
|
|
@ -175,17 +175,4 @@ class ExamPresenter @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onViewReselected() {
|
||||
Timber.i("Exam view is reselected")
|
||||
|
||||
baseDate = now().nextOrSameSchoolDay
|
||||
|
||||
if (currentDate != baseDate) {
|
||||
reloadView(baseDate)
|
||||
loadData()
|
||||
} else if (view?.isViewEmpty == false) {
|
||||
view?.resetView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,4 @@ interface ExamView : BaseView {
|
|||
fun showPreButton(show: Boolean)
|
||||
|
||||
fun showExamDialog(exam: Exam)
|
||||
|
||||
fun resetView()
|
||||
}
|
||||
|
|
|
@ -12,72 +12,50 @@ import io.github.wulkanowy.sdk.Sdk
|
|||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import javax.inject.Inject
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@OptIn(FlowPreview::class)
|
||||
class GradeAverageProvider @Inject constructor(
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val gradeRepository: GradeRepository,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) {
|
||||
|
||||
private data class AverageCalcParams(
|
||||
val gradeAverageMode: GradeAverageMode,
|
||||
val forceAverageCalc: Boolean,
|
||||
val isOptionalArithmeticAverage: Boolean,
|
||||
val plusModifier: Double,
|
||||
val minusModifier: Double,
|
||||
)
|
||||
private val plusModifier get() = preferencesRepository.gradePlusModifier
|
||||
|
||||
fun getGradesDetailsWithAverage(
|
||||
student: Student,
|
||||
semesterId: Int,
|
||||
forceRefresh: Boolean
|
||||
): Flow<Resource<List<GradeSubject>>> = combine(
|
||||
flow = preferencesRepository.gradeAverageModeFlow,
|
||||
flow2 = preferencesRepository.gradeAverageForceCalcFlow,
|
||||
flow3 = preferencesRepository.isOptionalArithmeticAverageFlow,
|
||||
flow4 = preferencesRepository.gradePlusModifierFlow,
|
||||
flow5 = preferencesRepository.gradeMinusModifierFlow,
|
||||
) { gradeAverageMode, forceAverageCalc, isOptionalArithmeticAverage, plusModifier, minusModifier ->
|
||||
AverageCalcParams(
|
||||
gradeAverageMode = gradeAverageMode,
|
||||
forceAverageCalc = forceAverageCalc,
|
||||
isOptionalArithmeticAverage = isOptionalArithmeticAverage,
|
||||
plusModifier = plusModifier,
|
||||
minusModifier = minusModifier,
|
||||
)
|
||||
}.flatMapLatest { params ->
|
||||
private val minusModifier get() = preferencesRepository.gradeMinusModifier
|
||||
|
||||
private val isOptionalArithmeticAverage get() = preferencesRepository.isOptionalArithmeticAverage
|
||||
|
||||
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) =
|
||||
flatResourceFlow {
|
||||
val semesters = semesterRepository.getSemesters(student)
|
||||
when (params.gradeAverageMode) {
|
||||
|
||||
when (preferencesRepository.gradeAverageMode) {
|
||||
ONE_SEMESTER -> getGradeSubjects(
|
||||
student = student,
|
||||
semester = semesters.single { it.semesterId == semesterId },
|
||||
forceRefresh = forceRefresh,
|
||||
params = params,
|
||||
forceRefresh = forceRefresh
|
||||
)
|
||||
BOTH_SEMESTERS -> calculateCombinedAverage(
|
||||
student = student,
|
||||
semesters = semesters,
|
||||
semesterId = semesterId,
|
||||
forceRefresh = forceRefresh,
|
||||
config = params,
|
||||
averageMode = BOTH_SEMESTERS
|
||||
)
|
||||
ALL_YEAR -> calculateCombinedAverage(
|
||||
student = student,
|
||||
semesters = semesters,
|
||||
semesterId = semesterId,
|
||||
forceRefresh = forceRefresh,
|
||||
config = params,
|
||||
averageMode = ALL_YEAR
|
||||
)
|
||||
}
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
|
||||
private fun calculateCombinedAverage(
|
||||
|
@ -85,19 +63,19 @@ class GradeAverageProvider @Inject constructor(
|
|||
semesters: List<Semester>,
|
||||
semesterId: Int,
|
||||
forceRefresh: Boolean,
|
||||
config: AverageCalcParams,
|
||||
averageMode: GradeAverageMode
|
||||
): Flow<Resource<List<GradeSubject>>> {
|
||||
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester =
|
||||
semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
|
||||
val selectedSemesterGradeSubjects =
|
||||
getGradeSubjects(student, selectedSemester, forceRefresh, config)
|
||||
getGradeSubjects(student, selectedSemester, forceRefresh)
|
||||
|
||||
if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects
|
||||
|
||||
val firstSemesterGradeSubjects =
|
||||
getGradeSubjects(student, firstSemester, forceRefresh, config)
|
||||
val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh)
|
||||
|
||||
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
|
||||
if (firstSemesterGradeSubject.errorOrNull != null) {
|
||||
|
@ -113,21 +91,21 @@ class GradeAverageProvider @Inject constructor(
|
|||
val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty()
|
||||
.singleOrNull { it.subject == secondSemesterSubject.subject }
|
||||
|
||||
val updatedAverage = if (config.gradeAverageMode == ALL_YEAR) {
|
||||
val updatedAverage = if (averageMode == ALL_YEAR) {
|
||||
calculateAllYearAverage(
|
||||
student = student,
|
||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||
isGradeAverageForceCalc = isGradeAverageForceCalc,
|
||||
secondSemesterSubject = secondSemesterSubject,
|
||||
firstSemesterSubject = firstSemesterSubject,
|
||||
config = config,
|
||||
firstSemesterSubject = firstSemesterSubject
|
||||
)
|
||||
} else {
|
||||
calculateBothSemestersAverage(
|
||||
student = student,
|
||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||
isGradeAverageForceCalc = isGradeAverageForceCalc,
|
||||
secondSemesterSubject = secondSemesterSubject,
|
||||
firstSemesterSubject = firstSemesterSubject,
|
||||
config = config
|
||||
firstSemesterSubject = firstSemesterSubject
|
||||
)
|
||||
}
|
||||
secondSemesterSubject.copy(average = updatedAverage)
|
||||
|
@ -139,17 +117,17 @@ class GradeAverageProvider @Inject constructor(
|
|||
private fun calculateAllYearAverage(
|
||||
student: Student,
|
||||
isAnyVulcanAverage: Boolean,
|
||||
isGradeAverageForceCalc: Boolean,
|
||||
secondSemesterSubject: GradeSubject,
|
||||
firstSemesterSubject: GradeSubject?,
|
||||
config: AverageCalcParams,
|
||||
) = if (!isAnyVulcanAverage || config.forceAverageCalc) {
|
||||
val updatedSecondSemesterGrades = secondSemesterSubject.grades
|
||||
.updateModifiers(student, config)
|
||||
val updatedFirstSemesterGrades = firstSemesterSubject?.grades
|
||||
?.updateModifiers(student, config).orEmpty()
|
||||
firstSemesterSubject: GradeSubject?
|
||||
) = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
|
||||
val updatedSecondSemesterGrades =
|
||||
secondSemesterSubject.grades.updateModifiers(student)
|
||||
val updatedFirstSemesterGrades =
|
||||
firstSemesterSubject?.grades?.updateModifiers(student).orEmpty()
|
||||
|
||||
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
|
||||
config.isOptionalArithmeticAverage
|
||||
isOptionalArithmeticAverage
|
||||
)
|
||||
} else {
|
||||
secondSemesterSubject.average
|
||||
|
@ -158,35 +136,32 @@ class GradeAverageProvider @Inject constructor(
|
|||
private fun calculateBothSemestersAverage(
|
||||
student: Student,
|
||||
isAnyVulcanAverage: Boolean,
|
||||
isGradeAverageForceCalc: Boolean,
|
||||
secondSemesterSubject: GradeSubject,
|
||||
firstSemesterSubject: GradeSubject?,
|
||||
config: AverageCalcParams,
|
||||
): Double {
|
||||
return if (!isAnyVulcanAverage || config.forceAverageCalc) {
|
||||
firstSemesterSubject: GradeSubject?
|
||||
): Double = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
|
||||
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
||||
val secondSemesterAverage = secondSemesterSubject.grades
|
||||
.updateModifiers(student, config)
|
||||
.calcAverage(config.isOptionalArithmeticAverage)
|
||||
val firstSemesterAverage = firstSemesterSubject?.grades
|
||||
?.updateModifiers(student, config)
|
||||
?.calcAverage(config.isOptionalArithmeticAverage) ?: secondSemesterAverage
|
||||
|
||||
val secondSemesterAverage = secondSemesterSubject.grades.updateModifiers(student)
|
||||
.calcAverage(isOptionalArithmeticAverage)
|
||||
val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
|
||||
?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage
|
||||
|
||||
(secondSemesterAverage + firstSemesterAverage) / divider
|
||||
} else {
|
||||
val divider = if (secondSemesterSubject.average > 0) 2 else 1
|
||||
|
||||
secondSemesterSubject.average.plus(
|
||||
(firstSemesterSubject?.average ?: secondSemesterSubject.average)
|
||||
) / divider
|
||||
}
|
||||
(secondSemesterSubject.average + (firstSemesterSubject?.average
|
||||
?: secondSemesterSubject.average)) / divider
|
||||
}
|
||||
|
||||
private fun getGradeSubjects(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean,
|
||||
params: AverageCalcParams,
|
||||
forceRefresh: Boolean
|
||||
): Flow<Resource<List<GradeSubject>>> {
|
||||
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
|
||||
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
|
||||
.mapResourceData { res ->
|
||||
val (details, summaries) = res
|
||||
|
@ -197,15 +172,13 @@ class GradeAverageProvider @Inject constructor(
|
|||
student = student,
|
||||
semester = semester,
|
||||
grades = allGrades.toList(),
|
||||
calcAverage = isAnyAverage,
|
||||
params = params,
|
||||
calcAverage = isAnyAverage
|
||||
).map { summary ->
|
||||
val grades = allGrades[summary.subject].orEmpty()
|
||||
GradeSubject(
|
||||
subject = summary.subject,
|
||||
average = if (!isAnyAverage || params.forceAverageCalc) {
|
||||
grades.updateModifiers(student, params)
|
||||
.calcAverage(params.isOptionalArithmeticAverage)
|
||||
average = if (!isAnyAverage || isGradeAverageForceCalc) {
|
||||
grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage)
|
||||
} else summary.average,
|
||||
points = summary.pointsSum,
|
||||
summary = summary,
|
||||
|
@ -222,8 +195,7 @@ class GradeAverageProvider @Inject constructor(
|
|||
student: Student,
|
||||
semester: Semester,
|
||||
grades: List<Pair<String, List<Grade>>>,
|
||||
calcAverage: Boolean,
|
||||
params: AverageCalcParams,
|
||||
calcAverage: Boolean
|
||||
): List<GradeSummary> {
|
||||
if (isNotEmpty() && size > grades.size) return this
|
||||
|
||||
|
@ -239,16 +211,15 @@ class GradeAverageProvider @Inject constructor(
|
|||
proposedPoints = "",
|
||||
finalPoints = "",
|
||||
pointsSum = "",
|
||||
average = if (calcAverage) details.updateModifiers(student, params)
|
||||
.calcAverage(params.isOptionalArithmeticAverage) else .0
|
||||
average = if (calcAverage) details.updateModifiers(student)
|
||||
.calcAverage(isOptionalArithmeticAverage) else .0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Grade>.updateModifiers(
|
||||
student: Student,
|
||||
params: AverageCalcParams,
|
||||
): List<Grade> = if (student.loginMode == Sdk.Mode.SCRAPPER.name) {
|
||||
map { it.changeModifier(params.plusModifier, params.minusModifier) }
|
||||
private fun List<Grade>.updateModifiers(student: Student): List<Grade> {
|
||||
return if (student.loginMode == Sdk.Mode.SCRAPPER.name) {
|
||||
map { it.changeModifier(plusModifier, minusModifier) }
|
||||
} else this
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import android.view.View
|
|||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
|
@ -52,7 +51,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
|
||||
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -142,7 +140,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) }
|
||||
.toTypedArray()
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setSingleChoiceItems(choices, selectedIndex) { dialog, which ->
|
||||
presenter.onSemesterSelected(which)
|
||||
dialog.dismiss()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package io.github.wulkanowy.ui.modules.grade.details
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -18,10 +17,9 @@ import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding
|
|||
import io.github.wulkanowy.databinding.ItemGradeDetailsBinding
|
||||
import io.github.wulkanowy.ui.base.BaseExpandableAdapter
|
||||
import io.github.wulkanowy.utils.getBackgroundColor
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.BitSet
|
||||
import javax.inject.Inject
|
||||
|
||||
class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<RecyclerView.ViewHolder>() {
|
||||
|
@ -205,9 +203,7 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
|||
with(holder.binding) {
|
||||
gradeItemValue.run {
|
||||
text = grade.entry
|
||||
backgroundTintList = ColorStateList.valueOf(
|
||||
context.getCompatColor(grade.getBackgroundColor(gradeColorTheme))
|
||||
)
|
||||
setBackgroundResource(grade.getBackgroundColor(gradeColorTheme))
|
||||
}
|
||||
gradeItemDescription.text = when {
|
||||
grade.description.isNotBlank() -> grade.description
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
package io.github.wulkanowy.ui.modules.grade.details
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.data.enums.GradeColorTheme
|
||||
import io.github.wulkanowy.databinding.DialogGradeBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.utils.*
|
||||
|
||||
@AndroidEntryPoint
|
||||
class GradeDetailsDialog : BaseDialogFragment<DialogGradeBinding>() {
|
||||
|
||||
class GradeDetailsDialog : DialogFragment() {
|
||||
|
||||
private var binding: DialogGradeBinding by lifecycleAwareVariable()
|
||||
|
||||
private lateinit var grade: Grade
|
||||
|
||||
|
@ -29,25 +27,29 @@ class GradeDetailsDialog : BaseDialogFragment<DialogGradeBinding>() {
|
|||
|
||||
private const val COLOR_THEME_KEY = "Theme"
|
||||
|
||||
fun newInstance(grade: Grade, colorTheme: GradeColorTheme) = GradeDetailsDialog().apply {
|
||||
arguments = bundleOf(
|
||||
ARGUMENT_KEY to grade,
|
||||
COLOR_THEME_KEY to colorTheme
|
||||
)
|
||||
fun newInstance(grade: Grade, colorTheme: GradeColorTheme) =
|
||||
GradeDetailsDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(ARGUMENT_KEY, grade)
|
||||
putSerializable(COLOR_THEME_KEY, colorTheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
grade = requireArguments().serializable(ARGUMENT_KEY)
|
||||
gradeColorTheme = requireArguments().serializable(COLOR_THEME_KEY)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
grade = getSerializable(ARGUMENT_KEY) as Grade
|
||||
gradeColorTheme = getSerializable(COLOR_THEME_KEY) as GradeColorTheme
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(DialogGradeBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
.create()
|
||||
}
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogGradeBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -55,9 +57,10 @@ class GradeDetailsDialog : BaseDialogFragment<DialogGradeBinding>() {
|
|||
with(binding) {
|
||||
gradeDialogSubject.text = grade.subject
|
||||
|
||||
gradeDialogWeightValue.text = grade.weight
|
||||
gradeDialogWeightLayout.backgroundTintList =
|
||||
ColorStateList.valueOf(requireContext().getCompatColor(grade.getGradeColor()))
|
||||
gradeDialogColorAndWeightValue.run {
|
||||
text = context.getString(R.string.grade_weight_value, grade.weight)
|
||||
setBackgroundResource(grade.getGradeColor())
|
||||
}
|
||||
|
||||
gradeDialogDateValue.text = grade.date.toFormattedString()
|
||||
gradeDialogColorValue.text = getString(grade.colorStringId)
|
||||
|
@ -71,12 +74,7 @@ class GradeDetailsDialog : BaseDialogFragment<DialogGradeBinding>() {
|
|||
|
||||
gradeDialogValue.run {
|
||||
text = grade.entry
|
||||
backgroundTintList = ColorStateList.valueOf(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
grade.getBackgroundColor(gradeColorTheme)
|
||||
)
|
||||
)
|
||||
setBackgroundResource(grade.getBackgroundColor(gradeColorTheme))
|
||||
}
|
||||
|
||||
gradeDialogTeacherValue.text = grade.teacher.ifBlank { getString(R.string.all_no_data) }
|
||||
|
|
|
@ -5,7 +5,9 @@ import android.view.Menu
|
|||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.View.*
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
|
@ -40,7 +42,6 @@ class GradeDetailsFragment :
|
|||
override val isViewEmpty
|
||||
get() = gradeDetailsAdapter.itemCount == 0
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
|
|
@ -116,9 +116,7 @@ class GradeStatisticsAdapter @Inject constructor() :
|
|||
}
|
||||
)
|
||||
|
||||
binding.gradeStatisticsTypeSwitch.addOnButtonCheckedListener { _, checkedId, isChecked ->
|
||||
if (!isChecked) return@addOnButtonCheckedListener
|
||||
|
||||
binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
|
||||
currentDataType = when (checkedId) {
|
||||
R.id.gradeStatisticsTypePartial -> GradeStatisticsItem.DataType.PARTIAL
|
||||
R.id.gradeStatisticsTypeSemester -> GradeStatisticsItem.DataType.SEMESTER
|
||||
|
|
|
@ -15,7 +15,6 @@ import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
|||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -49,8 +48,8 @@ class GradeStatisticsFragment :
|
|||
messageContainer = binding.gradeStatisticsRecycler
|
||||
presenter.onAttachView(
|
||||
view = this,
|
||||
type = savedInstanceState?.serializable(SAVED_CHART_TYPE),
|
||||
subjectName = savedInstanceState?.serializable(SAVED_SUBJECT_NAME),
|
||||
type = savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType,
|
||||
subjectName = savedInstanceState?.getSerializable(SAVED_SUBJECT_NAME) as? String,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.view.View.INVISIBLE
|
|||
import android.view.View.VISIBLE
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
|
@ -119,7 +118,7 @@ class GradeSummaryFragment :
|
|||
}
|
||||
|
||||
override fun showCalculatedAverageHelpDialog() {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.grade_summary_calculated_average_help_dialog_title)
|
||||
.setMessage(R.string.grade_summary_calculated_average_help_dialog_message)
|
||||
.setPositiveButton(R.string.all_close) { _, _ -> }
|
||||
|
@ -127,7 +126,7 @@ class GradeSummaryFragment :
|
|||
}
|
||||
|
||||
override fun showFinalAverageHelpDialog() {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.grade_summary_final_average_help_dialog_title)
|
||||
.setMessage(R.string.grade_summary_final_average_help_dialog_message)
|
||||
.setPositiveButton(R.string.all_close) { _, _ -> }
|
||||
|
|
|
@ -21,7 +21,7 @@ import javax.inject.Inject
|
|||
|
||||
@AndroidEntryPoint
|
||||
class HomeworkFragment : BaseFragment<FragmentHomeworkBinding>(R.layout.fragment_homework),
|
||||
HomeworkView, MainView.TitledView, MainView.MainChildView {
|
||||
HomeworkView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: HomeworkPresenter
|
||||
|
@ -67,7 +67,7 @@ class HomeworkFragment : BaseFragment<FragmentHomeworkBinding>(R.layout.fragment
|
|||
|
||||
openAddHomeworkButton.setOnClickListener { presenter.onHomeworkAddButtonClicked() }
|
||||
|
||||
homeworkNavContainer.elevation = requireContext().dpToPx(3f)
|
||||
homeworkNavContainer.elevation = requireContext().dpToPx(8f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,14 +133,6 @@ class HomeworkFragment : BaseFragment<FragmentHomeworkBinding>(R.layout.fragment
|
|||
(activity as? MainActivity)?.showDialogFragment(HomeworkAddDialog())
|
||||
}
|
||||
|
||||
override fun onFragmentReselected() {
|
||||
if (::presenter.isInitialized) presenter.onViewReselected()
|
||||
}
|
||||
|
||||
override fun resetView() {
|
||||
binding.homeworkRecycler.smoothScrollToPosition(0)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
||||
|
|
|
@ -181,17 +181,4 @@ class HomeworkPresenter @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onViewReselected() {
|
||||
Timber.i("Homework view is reselected")
|
||||
|
||||
baseDate = LocalDate.now().nextOrSameSchoolDay
|
||||
|
||||
if (currentDate != baseDate) {
|
||||
reloadView(baseDate)
|
||||
loadData()
|
||||
} else if (view?.isViewEmpty == false) {
|
||||
view?.resetView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,4 @@ interface HomeworkView : BaseView {
|
|||
fun showHomeworkDialog(homework: Homework)
|
||||
|
||||
fun showAddHomeworkDialog()
|
||||
|
||||
fun resetView()
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package io.github.wulkanowy.ui.modules.homework.add
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.DialogHomeworkAddBinding
|
||||
|
@ -24,12 +24,17 @@ class HomeworkAddDialog : BaseDialogFragment<DialogHomeworkAddBinding>(), Homewo
|
|||
// todo: move it to presenter
|
||||
private var date: LocalDate? = null
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(DialogHomeworkAddBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
.create()
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogHomeworkAddBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
presenter.onAttachView(this)
|
||||
|
|
|
@ -31,8 +31,14 @@ class HomeworkDetailsAdapter @Inject constructor() :
|
|||
attachments = value?.attachments.orEmpty()
|
||||
}
|
||||
|
||||
var isHomeworkFullscreen = false
|
||||
|
||||
var onAttachmentClickListener: (url: String) -> Unit = {}
|
||||
|
||||
var onFullScreenClickListener = {}
|
||||
|
||||
var onFullScreenExitClickListener = {}
|
||||
|
||||
var onDeleteClickListener: (homework: Homework) -> Unit = {}
|
||||
|
||||
override fun getItemCount() = 1 + if (attachments.isNotEmpty()) attachments.size + 1 else 0
|
||||
|
@ -76,6 +82,18 @@ class HomeworkDetailsAdapter @Inject constructor() :
|
|||
homeworkDialogTeacher.text = homework?.teacher.ifNullOrBlank { noDataString }
|
||||
homeworkDialogContent.text = homework?.content.ifNullOrBlank { noDataString }
|
||||
homeworkDialogDelete.visibility = if (homework?.isAddedByUser == true) VISIBLE else GONE
|
||||
homeworkDialogFullScreen.visibility = if (isHomeworkFullscreen) GONE else VISIBLE
|
||||
homeworkDialogFullScreenExit.visibility = if (isHomeworkFullscreen) VISIBLE else GONE
|
||||
homeworkDialogFullScreen.setOnClickListener {
|
||||
homeworkDialogFullScreen.visibility = GONE
|
||||
homeworkDialogFullScreenExit.visibility = VISIBLE
|
||||
onFullScreenClickListener()
|
||||
}
|
||||
homeworkDialogFullScreenExit.setOnClickListener {
|
||||
homeworkDialogFullScreen.visibility = VISIBLE
|
||||
homeworkDialogFullScreenExit.visibility = GONE
|
||||
onFullScreenExitClickListener()
|
||||
}
|
||||
homeworkDialogDelete.setOnClickListener {
|
||||
onDeleteClickListener(homework!!)
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
package io.github.wulkanowy.ui.modules.homework.details
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Homework
|
||||
import io.github.wulkanowy.databinding.DialogHomeworkBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -35,20 +35,23 @@ class HomeworkDetailsDialog : BaseDialogFragment<DialogHomeworkBinding>(), Homew
|
|||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(homework: Homework) = HomeworkDetailsDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to homework)
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
homework = requireArguments().serializable(ARGUMENT_KEY)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
homework = getSerializable(ARGUMENT_KEY) as Homework
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(DialogHomeworkBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
.create()
|
||||
}
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogHomeworkBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -64,11 +67,26 @@ class HomeworkDetailsDialog : BaseDialogFragment<DialogHomeworkBinding>(), Homew
|
|||
homeworkDialogClose.setOnClickListener { dismiss() }
|
||||
}
|
||||
|
||||
if (presenter.isHomeworkFullscreen) {
|
||||
dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
|
||||
} else {
|
||||
dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
|
||||
}
|
||||
|
||||
with(binding.homeworkDialogRecycler) {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = detailsAdapter.apply {
|
||||
onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) }
|
||||
onFullScreenClickListener = {
|
||||
dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
|
||||
presenter.isHomeworkFullscreen = true
|
||||
}
|
||||
onFullScreenExitClickListener = {
|
||||
dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
|
||||
presenter.isHomeworkFullscreen = false
|
||||
}
|
||||
onDeleteClickListener = { homework -> presenter.deleteHomework(homework) }
|
||||
isHomeworkFullscreen = presenter.isHomeworkFullscreen
|
||||
homework = this@HomeworkDetailsDialog.homework
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import io.github.wulkanowy.data.logResourceStatus
|
|||
import io.github.wulkanowy.data.onResourceError
|
||||
import io.github.wulkanowy.data.onResourceSuccess
|
||||
import io.github.wulkanowy.data.repositories.HomeworkRepository
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
|
@ -18,8 +19,15 @@ class HomeworkDetailsPresenter @Inject constructor(
|
|||
studentRepository: StudentRepository,
|
||||
private val homeworkRepository: HomeworkRepository,
|
||||
private val analytics: AnalyticsHelper,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) : BasePresenter<HomeworkDetailsView>(errorHandler, studentRepository) {
|
||||
|
||||
var isHomeworkFullscreen
|
||||
get() = preferencesRepository.isHomeworkFullscreen
|
||||
set(value) {
|
||||
preferencesRepository.isHomeworkFullscreen = value
|
||||
}
|
||||
|
||||
override fun onAttachView(view: HomeworkDetailsView) {
|
||||
super.onAttachView(view)
|
||||
view.initView()
|
||||
|
|
|
@ -2,17 +2,13 @@ package io.github.wulkanowy.ui.modules.login
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build.VERSION_CODES.TIRAMISU
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE
|
||||
import androidx.fragment.app.commit
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.ActivityLoginBinding
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
|
||||
|
@ -20,9 +16,6 @@ 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.studentselect.LoginStudentSelectFragment
|
||||
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.notifications.NotificationsFragment
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.UpdateHelper
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -35,9 +28,6 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
|||
@Inject
|
||||
lateinit var updateHelper: UpdateHelper
|
||||
|
||||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
companion object {
|
||||
fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java)
|
||||
}
|
||||
|
@ -65,7 +55,7 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
|||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) onBackPressedDispatcher.onBackPressed()
|
||||
if (item.itemId == android.R.id.home) onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -77,24 +67,8 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
|||
openFragment(LoginSymbolFragment.newInstance(loginData))
|
||||
}
|
||||
|
||||
fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) {
|
||||
openFragment(LoginStudentSelectFragment.newInstance(loginData, registerUser))
|
||||
}
|
||||
|
||||
fun navigateToNotifications() {
|
||||
val isNotificationsPermissionRequired = appInfo.systemVersion >= TIRAMISU
|
||||
val isPermissionGranted = ContextCompat.checkSelfPermission(
|
||||
this, "android.permission.POST_NOTIFICATIONS"
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
if (isNotificationsPermissionRequired && !isPermissionGranted) {
|
||||
openFragment(NotificationsFragment.newInstance(), clearBackStack = true)
|
||||
} else navigateToFinish()
|
||||
}
|
||||
|
||||
fun navigateToFinish() {
|
||||
startActivity(MainActivity.getStartIntent(this))
|
||||
finish()
|
||||
fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
openFragment(LoginStudentSelectFragment.newInstance(studentsWithSemesters))
|
||||
}
|
||||
|
||||
fun onAdvancedLoginClick() {
|
||||
|
@ -106,8 +80,6 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
|||
}
|
||||
|
||||
private fun openFragment(fragment: Fragment, clearBackStack: Boolean = false) {
|
||||
supportFragmentManager.popBackStack(fragment::class.java.name, POP_BACK_STACK_INCLUSIVE)
|
||||
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.loginContainer, fragment)
|
||||
setReorderingAllowed(true)
|
||||
|
|
|
@ -6,5 +6,4 @@ data class LoginData(
|
|||
val login: String,
|
||||
val password: String,
|
||||
val baseUrl: String,
|
||||
val symbol: String?,
|
||||
) : Serializable
|
||||
|
|
|
@ -4,15 +4,13 @@ import android.content.Context
|
|||
import android.database.sqlite.SQLiteConstraintException
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.sdk.hebe.exception.InvalidPinException
|
||||
import io.github.wulkanowy.sdk.hebe.exception.InvalidTokenException
|
||||
import io.github.wulkanowy.sdk.hebe.exception.TokenDeadException
|
||||
import io.github.wulkanowy.sdk.hebe.exception.UnknownTokenException
|
||||
import io.github.wulkanowy.sdk.mobile.exception.InvalidPinException
|
||||
import io.github.wulkanowy.sdk.mobile.exception.InvalidSymbolException
|
||||
import io.github.wulkanowy.sdk.mobile.exception.InvalidTokenException
|
||||
import io.github.wulkanowy.sdk.mobile.exception.TokenDeadException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import javax.inject.Inject
|
||||
import io.github.wulkanowy.sdk.hebe.exception.InvalidSymbolException as InvalidHebeSymbolException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException as InvalidScrapperSymbolException
|
||||
|
||||
class LoginErrorHandler @Inject constructor(
|
||||
@ApplicationContext context: Context,
|
||||
|
@ -34,11 +32,9 @@ class LoginErrorHandler @Inject constructor(
|
|||
is BadCredentialsException -> onBadCredentials(error.message)
|
||||
is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student))
|
||||
is TokenDeadException -> onInvalidToken(resources.getString(R.string.login_expired_token))
|
||||
is UnknownTokenException,
|
||||
is InvalidTokenException -> onInvalidToken(resources.getString(R.string.login_invalid_token))
|
||||
is InvalidPinException -> onInvalidPin(resources.getString(R.string.login_invalid_pin))
|
||||
is InvalidScrapperSymbolException,
|
||||
is InvalidHebeSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol))
|
||||
is InvalidSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol))
|
||||
else -> super.proceed(error)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import android.widget.ArrayAdapter
|
|||
import androidx.core.widget.doOnTextChanged
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.FragmentLoginAdvancedBinding
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
|
@ -34,9 +34,9 @@ class LoginAdvancedFragment :
|
|||
|
||||
override val formLoginType: String
|
||||
get() = when (binding.loginTypeSwitch.checkedRadioButtonId) {
|
||||
R.id.loginTypeApi -> Sdk.Mode.HEBE.name
|
||||
R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER.name
|
||||
else -> Sdk.Mode.HYBRID.name
|
||||
R.id.loginTypeApi -> "API"
|
||||
R.id.loginTypeScrapper -> "SCRAPPER"
|
||||
else -> "HYBRID"
|
||||
}
|
||||
|
||||
override val formUsernameValue: String
|
||||
|
@ -99,7 +99,7 @@ class LoginAdvancedFragment :
|
|||
loginTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
|
||||
presenter.onLoginModeSelected(
|
||||
when (checkedId) {
|
||||
R.id.loginTypeApi -> Sdk.Mode.HEBE
|
||||
R.id.loginTypeApi -> Sdk.Mode.API
|
||||
R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER
|
||||
else -> Sdk.Mode.HYBRID
|
||||
}
|
||||
|
@ -327,8 +327,8 @@ class LoginAdvancedFragment :
|
|||
(activity as? LoginActivity)?.navigateToSymbolFragment(loginData)
|
||||
}
|
||||
|
||||
override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser)
|
||||
override fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package io.github.wulkanowy.ui.modules.login.advanced
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.logResourceStatus
|
||||
import io.github.wulkanowy.data.onResourceNotLoading
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
||||
|
@ -92,16 +91,14 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||
fun onLoginModeSelected(type: Sdk.Mode) {
|
||||
view?.run {
|
||||
when (type) {
|
||||
Sdk.Mode.HEBE -> {
|
||||
Sdk.Mode.API -> {
|
||||
showOnlyMobileApiModeInputs()
|
||||
showMobileApiWarningMessage()
|
||||
}
|
||||
|
||||
Sdk.Mode.SCRAPPER -> {
|
||||
showOnlyScrapperModeInputs()
|
||||
showScraperWarningMessage()
|
||||
}
|
||||
|
||||
Sdk.Mode.HYBRID -> {
|
||||
showOnlyHybridModeInputs()
|
||||
showHybridWarningMessage()
|
||||
|
@ -142,29 +139,23 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||
showProgress(true)
|
||||
showContent(false)
|
||||
}
|
||||
|
||||
is Resource.Success -> {
|
||||
analytics.logEvent(
|
||||
"registration_form",
|
||||
"success" to true,
|
||||
"scrapperBaseUrl" to view?.formHostValue.orEmpty(),
|
||||
"students" to it.data.size,
|
||||
"error" to "No error"
|
||||
)
|
||||
val loginData = LoginData(
|
||||
login = view?.formUsernameValue.orEmpty().trim(),
|
||||
password = view?.formPassValue.orEmpty().trim(),
|
||||
baseUrl = view?.formHostValue.orEmpty().trim(),
|
||||
symbol = view?.formSymbolValue.orEmpty().trim().getNormalizedSymbol(),
|
||||
baseUrl = view?.formHostValue.orEmpty().trim()
|
||||
)
|
||||
when (it.data.symbols.size) {
|
||||
when (it.data.size) {
|
||||
0 -> view?.navigateToSymbol(loginData)
|
||||
else -> view?.navigateToStudentSelect(
|
||||
loginData = loginData,
|
||||
registerUser = it.data,
|
||||
)
|
||||
else -> view?.navigateToStudentSelect(it.data)
|
||||
}
|
||||
}
|
||||
|
||||
is Resource.Error -> {
|
||||
analytics.logEvent(
|
||||
"registration_form",
|
||||
|
@ -182,7 +173,7 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||
}.launch("login")
|
||||
}
|
||||
|
||||
private suspend fun getStudentsAppropriatesToLoginType(): RegisterUser {
|
||||
private suspend fun getStudentsAppropriatesToLoginType(): List<StudentWithSemesters> {
|
||||
val email = view?.formUsernameValue.orEmpty()
|
||||
val password = view?.formPassValue.orEmpty()
|
||||
val endpoint = view?.formHostValue.orEmpty()
|
||||
|
@ -192,11 +183,10 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||
val token = view?.formTokenValue.orEmpty()
|
||||
|
||||
return when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) {
|
||||
Sdk.Mode.HEBE -> studentRepository.getStudentsApi(pin, symbol, token)
|
||||
Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token)
|
||||
Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(
|
||||
email, password, endpoint, symbol
|
||||
)
|
||||
|
||||
Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(
|
||||
email, password, endpoint, symbol
|
||||
)
|
||||
|
@ -215,8 +205,8 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||
|
||||
var isCorrect = true
|
||||
|
||||
when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) {
|
||||
Sdk.Mode.HEBE -> {
|
||||
when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) {
|
||||
Sdk.Mode.API -> {
|
||||
if (pin.isEmpty()) {
|
||||
view?.setErrorPinRequired()
|
||||
isCorrect = false
|
||||
|
@ -232,17 +222,17 @@ class LoginAdvancedPresenter @Inject constructor(
|
|||
isCorrect = false
|
||||
}
|
||||
}
|
||||
|
||||
Sdk.Mode.HYBRID, Sdk.Mode.SCRAPPER -> {
|
||||
if (login.isEmpty()) {
|
||||
view?.setErrorUsernameRequired()
|
||||
isCorrect = false
|
||||
} else {
|
||||
if ("@" in login && "login" in host) {
|
||||
if ("@" in login && "standard" !in host) {
|
||||
view?.setErrorLoginRequired()
|
||||
isCorrect = false
|
||||
}
|
||||
if ("@" !in login && "email" in host) {
|
||||
|
||||
if ("@" !in login && "standard" in host) {
|
||||
view?.setErrorEmailRequired()
|
||||
isCorrect = false
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.github.wulkanowy.ui.modules.login.advanced
|
||||
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
|
||||
|
@ -72,7 +72,7 @@ interface LoginAdvancedView : BaseView {
|
|||
|
||||
fun navigateToSymbol(loginData: LoginData)
|
||||
|
||||
fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser)
|
||||
fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>)
|
||||
|
||||
fun setErrorPinRequired()
|
||||
|
||||
|
|
|
@ -9,13 +9,18 @@ import androidx.core.view.isVisible
|
|||
import androidx.core.widget.doOnTextChanged
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.hideSoftInput
|
||||
import io.github.wulkanowy.utils.openEmailClient
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.setOnEditorDoneSignIn
|
||||
import io.github.wulkanowy.utils.showSoftInput
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -144,14 +149,12 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||
override fun setErrorPassRequired(focus: Boolean) {
|
||||
with(binding.loginFormPassLayout) {
|
||||
error = getString(R.string.error_field_required)
|
||||
setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError))
|
||||
}
|
||||
}
|
||||
|
||||
override fun setErrorPassInvalid(focus: Boolean) {
|
||||
with(binding.loginFormPassLayout) {
|
||||
error = getString(R.string.login_invalid_password)
|
||||
setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +162,6 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||
with(binding) {
|
||||
loginFormUsernameLayout.error = " "
|
||||
loginFormPassLayout.error = " "
|
||||
loginFormPassLayout.setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError))
|
||||
loginFormHostLayout.error = " "
|
||||
loginFormErrorBox.text = message ?: getString(R.string.login_incorrect_password_default)
|
||||
loginFormErrorBox.isVisible = true
|
||||
|
@ -179,7 +181,6 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||
|
||||
override fun clearPassError() {
|
||||
binding.loginFormPassLayout.error = null
|
||||
binding.loginFormPassLayout.setEndIconTintList(null)
|
||||
binding.loginFormErrorBox.isVisible = false
|
||||
}
|
||||
|
||||
|
@ -204,10 +205,6 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||
binding.loginFormContainer.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showOtherOptionsButton(show: Boolean) {
|
||||
binding.loginFormAdvancedButton.isVisible = show
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun showVersion() {
|
||||
binding.loginFormVersion.text = "v${appInfo.versionName}"
|
||||
|
@ -229,8 +226,8 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||
(activity as? LoginActivity)?.navigateToSymbolFragment(loginData)
|
||||
}
|
||||
|
||||
override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser)
|
||||
override fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters)
|
||||
}
|
||||
|
||||
override fun openAdvancedLogin() {
|
||||
|
|
|
@ -7,7 +7,6 @@ import io.github.wulkanowy.ui.base.BasePresenter
|
|||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.ifNullOrBlank
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
|
@ -16,7 +15,6 @@ import javax.inject.Inject
|
|||
class LoginFormPresenter @Inject constructor(
|
||||
studentRepository: StudentRepository,
|
||||
private val loginErrorHandler: LoginErrorHandler,
|
||||
private val appInfo: AppInfo,
|
||||
private val analytics: AnalyticsHelper
|
||||
) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository) {
|
||||
|
||||
|
@ -27,7 +25,6 @@ class LoginFormPresenter @Inject constructor(
|
|||
view.run {
|
||||
initView()
|
||||
showContact(false)
|
||||
showOtherOptionsButton(appInfo.isDebug)
|
||||
showVersion()
|
||||
|
||||
loginErrorHandler.onBadCredentials = {
|
||||
|
@ -96,7 +93,7 @@ class LoginFormPresenter @Inject constructor(
|
|||
if (!validateCredentials(email, password, host)) return
|
||||
|
||||
resourceFlow {
|
||||
studentRepository.getUserSubjectsFromScrapper(
|
||||
studentRepository.getStudentsScrapper(
|
||||
email = email,
|
||||
password = password,
|
||||
scrapperBaseUrl = host,
|
||||
|
@ -112,14 +109,14 @@ class LoginFormPresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
.onResourceSuccess {
|
||||
val loginData = LoginData(email, password, host, symbol)
|
||||
when (it.symbols.size) {
|
||||
0 -> view?.navigateToSymbol(loginData)
|
||||
else -> view?.navigateToStudentSelect(loginData, it)
|
||||
when (it.size) {
|
||||
0 -> view?.navigateToSymbol(LoginData(email, password, host))
|
||||
else -> view?.navigateToStudentSelect(it)
|
||||
}
|
||||
analytics.logEvent(
|
||||
"registration_form",
|
||||
"success" to true,
|
||||
"students" to it.size,
|
||||
"scrapperBaseUrl" to host,
|
||||
"error" to "No error"
|
||||
)
|
||||
|
@ -137,6 +134,7 @@ class LoginFormPresenter @Inject constructor(
|
|||
analytics.logEvent(
|
||||
"registration_form",
|
||||
"success" to false,
|
||||
"students" to -1,
|
||||
"scrapperBaseUrl" to host,
|
||||
"error" to it.message.ifNullOrBlank { "No message" }
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.github.wulkanowy.ui.modules.login.form
|
||||
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
|
||||
|
@ -56,13 +56,11 @@ interface LoginFormView : BaseView {
|
|||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showOtherOptionsButton(show: Boolean)
|
||||
|
||||
fun showVersion()
|
||||
|
||||
fun navigateToSymbol(loginData: LoginData)
|
||||
|
||||
fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser)
|
||||
fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>)
|
||||
|
||||
fun openPrivacyPolicyPage()
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ class LoginRecoverFragment :
|
|||
loginRecoverButton.setOnClickListener { presenter.onRecoverClick() }
|
||||
loginRecoverErrorRetry.setOnClickListener { presenter.onRecoverClick() }
|
||||
loginRecoverErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
loginRecoverLogin.setOnClickListener { (activity as LoginActivity).onBackPressedDispatcher.onBackPressed() }
|
||||
loginRecoverLogin.setOnClickListener { (activity as LoginActivity).onBackPressed() }
|
||||
}
|
||||
|
||||
with(bindingLocal.loginRecoverHost) {
|
||||
|
|
|
@ -2,182 +2,65 @@ package io.github.wulkanowy.ui.modules.login.studentselect
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.*
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.ItemLoginStudentSelectBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
class LoginStudentSelectAdapter @Inject constructor() :
|
||||
ListAdapter<LoginStudentSelectItem, RecyclerView.ViewHolder>(Differ) {
|
||||
RecyclerView.Adapter<LoginStudentSelectAdapter.ItemViewHolder>() {
|
||||
|
||||
override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal
|
||||
private val checkedList = mutableMapOf<Int, Boolean>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (LoginStudentSelectItemType.values()[viewType]) {
|
||||
LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER -> EmptySymbolsHeaderViewHolder(
|
||||
ItemLoginStudentSelectEmptySymbolHeaderBinding.inflate(inflater, parent, false),
|
||||
var items = emptyList<Pair<StudentWithSemesters, Boolean>>()
|
||||
set(value) {
|
||||
field = value
|
||||
checkedList.clear()
|
||||
}
|
||||
|
||||
var onClickListener: (StudentWithSemesters, alreadySaved: Boolean) -> Unit = { _, _ -> }
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemLoginStudentSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
LoginStudentSelectItemType.SYMBOL_HEADER -> SymbolsHeaderViewHolder(
|
||||
ItemLoginStudentSelectHeaderSymbolBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
LoginStudentSelectItemType.SCHOOL_HEADER -> SchoolHeaderViewHolder(
|
||||
ItemLoginStudentSelectHeaderSchoolBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
LoginStudentSelectItemType.STUDENT -> StudentViewHolder(
|
||||
ItemLoginStudentSelectStudentBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
LoginStudentSelectItemType.HELP -> HelpViewHolder(
|
||||
ItemLoginStudentSelectHelpBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is EmptySymbolsHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.EmptySymbolsHeader)
|
||||
is SymbolsHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.SymbolHeader)
|
||||
is SchoolHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.SchoolHeader)
|
||||
is StudentViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.Student)
|
||||
is HelpViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.Help)
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptySymbolsHeaderViewHolder(
|
||||
private val binding: ItemLoginStudentSelectEmptySymbolHeaderBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: LoginStudentSelectItem.EmptySymbolsHeader) {
|
||||
with(binding) {
|
||||
loginStudentSelectEmptySymbolChevron.rotation = if (item.isExpanded) 270f else 90f
|
||||
root.setOnClickListener { item.onClick() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolsHeaderViewHolder(
|
||||
private val binding: ItemLoginStudentSelectHeaderSymbolBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: LoginStudentSelectItem.SymbolHeader) {
|
||||
with(binding) {
|
||||
loginStudentSelectHeaderSymbolValue.text = buildString {
|
||||
append(root.context.getString(R.string.mobile_device_symbol))
|
||||
append(": ")
|
||||
append(item.humanReadableName ?: item.symbol.symbol)
|
||||
if (!item.humanReadableName.isNullOrBlank()) {
|
||||
append(" (${item.symbol.symbol})")
|
||||
}
|
||||
}
|
||||
loginStudentSelectHeaderSymbolUsername.text = item.symbol.userName
|
||||
loginStudentSelectHeaderSymbolUsername.isVisible = item.symbol.userName.isNotBlank()
|
||||
loginStudentSelectHeaderSymbolError.text = item.symbol.error?.message
|
||||
loginStudentSelectHeaderSymbolError.isVisible = item.symbol.error != null
|
||||
loginStudentSelectHeaderSymbolError.maxLines = when {
|
||||
item.isErrorExpanded -> Int.MAX_VALUE
|
||||
else -> 2
|
||||
}
|
||||
|
||||
if (item.symbol.error != null) {
|
||||
root.setOnClickListener { item.onClick(item.symbol) }
|
||||
} else root.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SchoolHeaderViewHolder(
|
||||
private val binding: ItemLoginStudentSelectHeaderSchoolBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: LoginStudentSelectItem.SchoolHeader) {
|
||||
with(binding) {
|
||||
loginStudentSelectHeaderSchoolName.text = buildString {
|
||||
append(item.unit.schoolName.trim())
|
||||
append(" (")
|
||||
append(item.unit.schoolShortName)
|
||||
append(")")
|
||||
}
|
||||
loginStudentSelectHeaderSchoolDetails.isVisible = item.unit.students.isEmpty()
|
||||
loginStudentSelectHeaderSchoolError.text = item.unit.error?.message
|
||||
loginStudentSelectHeaderSchoolError.isVisible = item.unit.error != null
|
||||
loginStudentSelectHeaderSchoolError.maxLines = when {
|
||||
item.isErrorExpanded -> Int.MAX_VALUE
|
||||
else -> 2
|
||||
}
|
||||
|
||||
if (item.unit.error != null) {
|
||||
root.setOnClickListener { item.onClick(item.unit) }
|
||||
} else root.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StudentViewHolder(
|
||||
private val binding: ItemLoginStudentSelectStudentBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: LoginStudentSelectItem.Student) {
|
||||
val student = item.student
|
||||
val semesters = student.semesters
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val (studentAndSemesters, alreadySaved) = items[position]
|
||||
val student = studentAndSemesters.student
|
||||
val semesters = studentAndSemesters.semesters
|
||||
val diary = semesters.maxByOrNull { it.semesterId }
|
||||
|
||||
with(binding) {
|
||||
loginItemName.text = "${student.studentName} ${student.studentSurname}"
|
||||
loginItemName.isEnabled = item.isEnabled
|
||||
loginItemSignedIn.text = if (!item.isEnabled) {
|
||||
root.context.getString(R.string.login_signed_in)
|
||||
} else diary?.diaryName
|
||||
with(holder.binding) {
|
||||
loginItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}"
|
||||
loginItemSchool.text = student.schoolName
|
||||
loginItemName.isEnabled = !alreadySaved
|
||||
loginItemSchool.isEnabled = !alreadySaved
|
||||
loginItemSignedIn.visibility = if (alreadySaved) View.VISIBLE else View.GONE
|
||||
|
||||
with(loginItemCheck) {
|
||||
isEnabled = !alreadySaved
|
||||
keyListener = null
|
||||
isEnabled = item.isEnabled
|
||||
isChecked = item.isSelected || !item.isEnabled
|
||||
isChecked = checkedList[position] ?: false
|
||||
}
|
||||
|
||||
root.isEnabled = item.isEnabled
|
||||
root.setOnClickListener {
|
||||
item.onClick(item)
|
||||
onClickListener(studentAndSemesters, alreadySaved)
|
||||
|
||||
with(loginItemCheck) {
|
||||
if (isEnabled) {
|
||||
isChecked = !isChecked
|
||||
checkedList[position] = isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class HelpViewHolder(
|
||||
private val binding: ItemLoginStudentSelectHelpBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: LoginStudentSelectItem.Help) {
|
||||
with(binding) {
|
||||
loginStudentSelectHelpSymbol.isVisible = item.isSymbolButtonVisible
|
||||
loginStudentSelectHelpSymbol.setOnClickListener { item.onEnterSymbolClick() }
|
||||
loginStudentSelectHelpMail.setOnClickListener { item.onContactUsClick() }
|
||||
loginStudentSelectHelpDiscord.setOnClickListener { item.onDiscordClick() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object Differ : ItemCallback<LoginStudentSelectItem>() {
|
||||
|
||||
override fun areItemsTheSame(
|
||||
oldItem: LoginStudentSelectItem, newItem: LoginStudentSelectItem
|
||||
): Boolean = when {
|
||||
oldItem is LoginStudentSelectItem.EmptySymbolsHeader && newItem is LoginStudentSelectItem.EmptySymbolsHeader -> true
|
||||
oldItem is LoginStudentSelectItem.SymbolHeader && newItem is LoginStudentSelectItem.SymbolHeader -> {
|
||||
oldItem.symbol == newItem.symbol
|
||||
}
|
||||
oldItem is LoginStudentSelectItem.Student && newItem is LoginStudentSelectItem.Student -> {
|
||||
oldItem.student == newItem.student
|
||||
}
|
||||
else -> oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: LoginStudentSelectItem, newItem: LoginStudentSelectItem
|
||||
): Boolean = oldItem == newItem
|
||||
}
|
||||
class ItemViewHolder(val binding: ItemLoginStudentSelectBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
|
|
|
@ -2,20 +2,21 @@ package io.github.wulkanowy.ui.modules.login.studentselect
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.openEmailClient
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -35,73 +36,75 @@ class LoginStudentSelectFragment :
|
|||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
private lateinit var symbolsNames: Array<String>
|
||||
private lateinit var symbolsValues: Array<String>
|
||||
|
||||
override val symbols: Map<String, String> by lazy {
|
||||
symbolsValues.zip(symbolsNames).toMap()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_LOGIN = "LOGIN"
|
||||
private const val ARG_STUDENTS = "STUDENTS"
|
||||
const val ARG_STUDENTS = "STUDENTS"
|
||||
|
||||
fun newInstance(loginData: LoginData, registerUser: RegisterUser) =
|
||||
fun newInstance(studentsWithSemesters: List<StudentWithSemesters>) =
|
||||
LoginStudentSelectFragment().apply {
|
||||
arguments = bundleOf(
|
||||
ARG_LOGIN to loginData,
|
||||
ARG_STUDENTS to registerUser,
|
||||
)
|
||||
arguments = bundleOf(ARG_STUDENTS to studentsWithSemesters)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentLoginStudentSelectBinding.bind(view)
|
||||
|
||||
symbolsNames = resources.getStringArray(R.array.symbols)
|
||||
symbolsValues = resources.getStringArray(R.array.symbols_values)
|
||||
|
||||
presenter.onAttachView(
|
||||
view = this,
|
||||
loginData = requireArguments().serializable(ARG_LOGIN),
|
||||
registerUser = requireArguments().serializable(ARG_STUDENTS),
|
||||
students = requireArguments().getSerializable(ARG_STUDENTS) as List<StudentWithSemesters>,
|
||||
)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
(requireActivity() as LoginActivity).showActionBar(true)
|
||||
|
||||
loginAdapter.onClickListener = presenter::onItemSelected
|
||||
|
||||
with(binding) {
|
||||
loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() }
|
||||
loginStudentSelectRecycler.adapter = loginAdapter
|
||||
loginStudentSelectContactDiscord.setOnClickListener { presenter.onDiscordClick() }
|
||||
loginStudentSelectContactEmail.setOnClickListener { presenter.onEmailClick() }
|
||||
|
||||
with(loginStudentSelectRecycler) {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = loginAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LoginStudentSelectItem>) {
|
||||
loginAdapter.submitList(data)
|
||||
override fun updateData(data: List<Pair<StudentWithSemesters, Boolean>>) {
|
||||
with(loginAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun navigateToSymbol(loginData: LoginData) {
|
||||
(requireActivity() as LoginActivity).navigateToSymbolFragment(loginData)
|
||||
}
|
||||
|
||||
override fun navigateToNext() {
|
||||
(requireActivity() as LoginActivity).navigateToNotifications()
|
||||
override fun openMainView() {
|
||||
startActivity(MainActivity.getStartIntent(requireContext()))
|
||||
requireActivity().finish()
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
binding.loginStudentSelectProgress.isVisible = show
|
||||
binding.loginStudentSelectProgress.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
binding.loginStudentSelectContent.isVisible = show
|
||||
binding.loginStudentSelectContent.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun enableSignIn(enable: Boolean) {
|
||||
binding.loginStudentSelectSignIn.isEnabled = enable
|
||||
}
|
||||
|
||||
override fun showContact(show: Boolean) {
|
||||
binding.loginStudentSelectContact.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun openDiscordInvite() {
|
||||
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
|
||||
}
|
||||
|
@ -122,9 +125,4 @@ class LoginStudentSelectFragment :
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue