Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
c4672b8de9 | |||
1b40e339b7 | |||
ee5ac46493 | |||
ef398f7409 | |||
5331bf90cd | |||
a495fcbc5f | |||
f11354dd35 | |||
4bb1198735 | |||
3eb74da945 | |||
5161fdd543 | |||
377e0c3a0d | |||
1c9860091a | |||
a383f7409d | |||
9a8fb593c0 | |||
f4c6e0ad1b | |||
b30b7c3318 | |||
897eac050a | |||
83974b6550 | |||
7efd106658 | |||
9cedab979c | |||
510e2d5b88 | |||
63d6a0b325 | |||
ede5914d70 | |||
09c968f273 | |||
f1479d489b | |||
fba4e85311 | |||
4a5991ade4 | |||
a735c378f1 | |||
217ebfc549 | |||
df5155f1c7 | |||
b93c0222a2 | |||
083ca34f1b | |||
5d5dfd4eb4 | |||
429fdfa4a0 | |||
302d723cfb | |||
8f50ee82b3 | |||
9dc1220496 | |||
ae39bd94e5 | |||
85ce23845f | |||
890d60811b | |||
49e68f5c8b | |||
1df4679db8 |
2
LICENSE
@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 Wulkanowy
|
||||
Copyright 2023 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 32
|
||||
compileSdkVersion 33
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 32
|
||||
versionCode 118
|
||||
versionName "1.8.3"
|
||||
targetSdkVersion 33
|
||||
versionCode 121
|
||||
versionName "1.9.2"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
@ -161,8 +161,8 @@ play {
|
||||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
|
||||
userFraction = 0.10d
|
||||
updatePriority = 5
|
||||
userFraction = 0.50d
|
||||
updatePriority = 2
|
||||
enabled.set(false)
|
||||
}
|
||||
|
||||
@ -181,23 +181,23 @@ ext {
|
||||
android_hilt = "1.0.0"
|
||||
room = "2.4.3"
|
||||
chucker = "3.5.2"
|
||||
mockk = "1.13.2"
|
||||
mockk = "1.13.3"
|
||||
coroutines = "1.6.4"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:1.8.3"
|
||||
implementation "io.github.wulkanowy:sdk:1.9.2"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||
|
||||
implementation "androidx.core:core-ktx:1.8.0"
|
||||
implementation "androidx.core:core-ktx:1.9.0"
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||
implementation "androidx.activity:activity-ktx:1.5.1"
|
||||
implementation "androidx.activity:activity-ktx:1.6.1"
|
||||
implementation "androidx.appcompat:appcompat:1.5.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.5.4"
|
||||
implementation "androidx.fragment:fragment-ktx:1.5.5"
|
||||
implementation "androidx.annotation:annotation:1.5.0"
|
||||
|
||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||
@ -237,20 +237,22 @@ dependencies {
|
||||
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
|
||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
||||
implementation "io.coil-kt:coil:2.2.2"
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
|
||||
implementation 'org.apache.commons:commons-text:1.10.0'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:31.0.3')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:31.1.1')
|
||||
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
||||
playImplementation 'com.google.firebase:firebase-messaging:'
|
||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||
playImplementation 'com.google.firebase:firebase-config-ktx'
|
||||
playImplementation 'com.google.android.play:core:1.10.3'
|
||||
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:21.3.0'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:21.4.0'
|
||||
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.300'
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.9.0.301'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.302'
|
||||
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||
|
||||
@ -263,7 +265,7 @@ dependencies {
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
testImplementation 'org.robolectric:robolectric:4.9'
|
||||
testImplementation 'org.robolectric:robolectric:4.9.2'
|
||||
testImplementation "androidx.test:runner:1.5.1"
|
||||
testImplementation "androidx.test.ext:junit:1.1.4"
|
||||
testImplementation "androidx.test:core:1.5.0"
|
||||
@ -271,9 +273,9 @@ dependencies {
|
||||
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
||||
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||
|
||||
androidTestImplementation "androidx.test:core:1.4.0"
|
||||
androidTestImplementation "androidx.test:runner:1.4.0"
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.3"
|
||||
androidTestImplementation "androidx.test:core:1.5.0"
|
||||
androidTestImplementation "androidx.test:runner:1.5.1"
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.4"
|
||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
}
|
||||
|
2435
app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json
Normal file
@ -2,4 +2,5 @@
|
||||
<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>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground_dev_mono" />
|
||||
</adaptive-icon>
|
||||
|
@ -1,5 +0,0 @@
|
||||
<?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>
|
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 13 KiB |
@ -0,0 +1,7 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper()
|
@ -0,0 +1,7 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper()
|
@ -8,7 +8,8 @@
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<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.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
@ -36,13 +37,14 @@
|
||||
<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="GoogleAppIndexingWarning,UnusedAttribute">
|
||||
tools:ignore="DataExtractionRules,UnusedAttribute">
|
||||
<activity
|
||||
android:name=".ui.modules.splash.SplashActivity"
|
||||
android:exported="true"
|
||||
|
@ -34,11 +34,15 @@ 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,6 +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
|
||||
@ -36,10 +37,11 @@ internal class DataModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideSdk(chuckerInterceptor: ChuckerInterceptor) =
|
||||
fun provideSdk(chuckerInterceptor: ChuckerInterceptor, remoteConfig: RemoteConfigHelper) =
|
||||
Sdk().apply {
|
||||
androidVersion = android.os.Build.VERSION.RELEASE
|
||||
buildTag = android.os.Build.MODEL
|
||||
userAgentTemplate = remoteConfig.userAgentTemplate
|
||||
setSimpleHttpLogger { Timber.d(it) }
|
||||
|
||||
// for debug only
|
||||
|
@ -48,6 +48,7 @@ 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
|
||||
@ -56,7 +57,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 54
|
||||
const val VERSION_SCHEMA = 55
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
|
@ -2,16 +2,14 @@ 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")
|
||||
@Entity(
|
||||
tableName = "MessageAttachments",
|
||||
primaryKeys = ["message_global_key", "url", "filename"],
|
||||
)
|
||||
data class MessageAttachment(
|
||||
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "real_id")
|
||||
val realId: Int,
|
||||
|
||||
@ColumnInfo(name = "message_global_key")
|
||||
val messageGlobalKey: String,
|
||||
|
||||
|
@ -0,0 +1,17 @@
|
||||
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")
|
||||
}
|
||||
}
|
@ -40,7 +40,6 @@ fun List<SdkMessage>.mapToEntities(
|
||||
fun List<SdkMessageAttachment>.mapToEntities(messageGlobalKey: String) = map {
|
||||
MessageAttachment(
|
||||
messageGlobalKey = messageGlobalKey,
|
||||
realId = it.url.hashCode(),
|
||||
url = it.url,
|
||||
filename = it.filename
|
||||
)
|
||||
|
@ -0,0 +1,87 @@
|
||||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.*
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.mapper.mapSemesters
|
||||
import java.time.Instant
|
||||
import io.github.wulkanowy.sdk.scrapper.register.RegisterStudent as SdkRegisterStudent
|
||||
import io.github.wulkanowy.sdk.scrapper.register.RegisterUser as SdkRegisterUser
|
||||
|
||||
fun SdkRegisterUser.mapToPojo(password: String) = RegisterUser(
|
||||
email = email,
|
||||
login = login,
|
||||
password = password,
|
||||
baseUrl = baseUrl,
|
||||
loginType = loginType,
|
||||
symbols = symbols.map { registerSymbol ->
|
||||
RegisterSymbol(
|
||||
symbol = registerSymbol.symbol,
|
||||
error = registerSymbol.error,
|
||||
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
|
||||
.mapSemesters()
|
||||
.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,
|
||||
schoolName = unit.schoolName,
|
||||
schoolShortName = unit.schoolShortName,
|
||||
schoolSymbol = unit.schoolId,
|
||||
studentName = "$studentName $studentSurname",
|
||||
loginMode = Sdk.Mode.SCRAPPER.name,
|
||||
scrapperBaseUrl = user.baseUrl,
|
||||
mobileBaseUrl = "",
|
||||
certificateKey = "",
|
||||
privateKey = "",
|
||||
password = user.password,
|
||||
isCurrent = false,
|
||||
registrationDate = Instant.now(),
|
||||
).apply {
|
||||
avatarColor = colors.random()
|
||||
},
|
||||
)
|
@ -0,0 +1,43 @@
|
||||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
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 baseUrl: String,
|
||||
val loginType: Scrapper.LoginType,
|
||||
val symbols: List<RegisterSymbol>,
|
||||
) : java.io.Serializable
|
||||
|
||||
data class RegisterSymbol(
|
||||
val symbol: String,
|
||||
val error: Throwable?,
|
||||
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
|
@ -103,7 +103,10 @@ class MessageRepository @Inject constructor(
|
||||
messagesDb.loadMessageWithAttachment(message.messageGlobalKey)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey, markAsRead)
|
||||
sdk.init(student).getMessageDetails(
|
||||
messageKey = it!!.message.messageGlobalKey,
|
||||
markAsRead = message.unread && markAsRead,
|
||||
)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||
@ -178,7 +181,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))
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.mappers.mapToPojo
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
@ -52,6 +54,14 @@ class StudentRepository @Inject constructor(
|
||||
sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||
.mapToEntities(password, appInfo.defaultColorsForAvatar)
|
||||
|
||||
suspend fun getUserSubjectsFromScrapper(
|
||||
email: String,
|
||||
password: String,
|
||||
scrapperBaseUrl: String,
|
||||
symbol: String
|
||||
): RegisterUser = sdk.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||
.mapToPojo(password)
|
||||
|
||||
suspend fun getStudentsHybrid(
|
||||
email: String,
|
||||
password: String,
|
||||
|
@ -37,7 +37,7 @@ class ErrorDialog : DialogFragment() {
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable
|
||||
val error = requireArguments().serializable<Throwable>(ARGUMENT_KEY)
|
||||
|
||||
val binding = DialogErrorBinding.inflate(layoutInflater)
|
||||
binding.bindErrorDetails(error)
|
||||
|
@ -1,6 +1,9 @@
|
||||
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 androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
@ -41,9 +44,8 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
|
||||
)
|
||||
}
|
||||
|
||||
private fun isThemeApplicable(activity: AppCompatActivity) =
|
||||
activity.packageManager
|
||||
.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
||||
private fun isThemeApplicable(activity: AppCompatActivity): Boolean =
|
||||
getPackageInfo(activity)
|
||||
.activities
|
||||
.singleOrNull { it.name == activity::class.java.canonicalName }
|
||||
?.theme
|
||||
@ -52,4 +54,14 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
|
||||
|| it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black
|
||||
|| it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_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)
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ 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,6 +6,7 @@ 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 dagger.hilt.android.AndroidEntryPoint
|
||||
@ -21,6 +22,7 @@ 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
|
||||
@ -37,12 +39,12 @@ class AccountDetailsFragment :
|
||||
|
||||
private const val ARGUMENT_KEY = "Data"
|
||||
|
||||
fun newInstance(student: Student) =
|
||||
AccountDetailsFragment().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, student) }
|
||||
}
|
||||
fun newInstance(student: Student) = AccountDetailsFragment().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to student)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
@ -51,7 +53,7 @@ class AccountDetailsFragment :
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentAccountDetailsBinding.bind(view)
|
||||
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
|
||||
presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY))
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
|
@ -4,11 +4,13 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import 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
|
||||
@ -24,12 +26,9 @@ class AccountEditDialog : BaseDialogFragment<DialogAccountEditBinding>(), Accoun
|
||||
|
||||
private const val ARGUMENT_KEY = "student_with_semesters"
|
||||
|
||||
fun newInstance(student: Student) =
|
||||
AccountEditDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(ARGUMENT_KEY, student)
|
||||
}
|
||||
}
|
||||
fun newInstance(student: Student) = AccountEditDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to student)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -45,7 +44,7 @@ class AccountEditDialog : BaseDialogFragment<DialogAccountEditBinding>(), Accoun
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
|
||||
presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY))
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
|
@ -4,6 +4,7 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
@ -13,6 +14,7 @@ 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
|
||||
@ -30,9 +32,7 @@ class AccountQuickDialog : BaseDialogFragment<DialogAccountQuickBinding>(), Acco
|
||||
|
||||
fun newInstance(studentsWithSemesters: List<StudentWithSemesters>) =
|
||||
AccountQuickDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(STUDENTS_ARGUMENT_KEY, studentsWithSemesters.toTypedArray())
|
||||
}
|
||||
arguments = bundleOf(STUDENTS_ARGUMENT_KEY to studentsWithSemesters.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,8 +49,8 @@ class AccountQuickDialog : BaseDialogFragment<DialogAccountQuickBinding>(), Acco
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val studentsWithSemesters =
|
||||
(requireArguments()[STUDENTS_ARGUMENT_KEY] as Array<StudentWithSemesters>).toList()
|
||||
val studentsWithSemesters = requireArguments()
|
||||
.serializable<Array<StudentWithSemesters>>(STUDENTS_ARGUMENT_KEY).toList()
|
||||
|
||||
presenter.onAttachView(this, studentsWithSemesters)
|
||||
}
|
||||
|
@ -4,11 +4,13 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.databinding.DialogAttendanceBinding
|
||||
import io.github.wulkanowy.utils.descriptionRes
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
|
||||
class AttendanceDialog : DialogFragment() {
|
||||
@ -22,16 +24,14 @@ class AttendanceDialog : DialogFragment() {
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: Attendance) = AttendanceDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
arguments = bundleOf(ARGUMENT_KEY to exam)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
attendance = getSerializable(ARGUMENT_KEY) as Attendance
|
||||
}
|
||||
attendance = requireArguments().serializable(ARGUMENT_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -84,6 +84,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -4,11 +4,13 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.data.db.entities.Conference
|
||||
import io.github.wulkanowy.databinding.DialogConferenceBinding
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
|
||||
class ConferenceDialog : DialogFragment() {
|
||||
@ -22,16 +24,14 @@ class ConferenceDialog : DialogFragment() {
|
||||
private const val ARGUMENT_KEY = "item"
|
||||
|
||||
fun newInstance(conference: Conference) = ConferenceDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, conference) }
|
||||
arguments = bundleOf(ARGUMENT_KEY to conference)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.let {
|
||||
conference = it.getSerializable(ARGUMENT_KEY) as Conference
|
||||
}
|
||||
conference = requireArguments().serializable(ARGUMENT_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -57,4 +57,4 @@ class ConferenceDialog : DialogFragment() {
|
||||
conferenceDialogAgendaTitle.isVisible = conference.agenda.isNotBlank()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
||||
fun newInstance() = DashboardFragment()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -1,9 +1,7 @@
|
||||
package io.github.wulkanowy.ui.modules.debug.logviewer
|
||||
|
||||
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.content.Intent.*
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
@ -36,6 +34,7 @@ class LogViewerFragment : BaseFragment<FragmentLogviewerBinding>(R.layout.fragme
|
||||
fun newInstance() = LogViewerFragment()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -4,12 +4,14 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.databinding.DialogExamBinding
|
||||
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
|
||||
|
||||
@ -24,16 +26,14 @@ class ExamDialog : DialogFragment() {
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: Exam) = ExamDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
arguments = bundleOf(ARGUMENT_KEY to exam)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
exam = getSerializable(ARGUMENT_KEY) as Exam
|
||||
}
|
||||
exam = requireArguments().serializable(ARGUMENT_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -51,6 +51,7 @@ 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)
|
||||
|
@ -5,6 +5,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
@ -27,22 +28,19 @@ class GradeDetailsDialog : DialogFragment() {
|
||||
|
||||
private const val COLOR_THEME_KEY = "Theme"
|
||||
|
||||
fun newInstance(grade: Grade, colorTheme: GradeColorTheme) =
|
||||
GradeDetailsDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(ARGUMENT_KEY, grade)
|
||||
putSerializable(COLOR_THEME_KEY, colorTheme)
|
||||
}
|
||||
}
|
||||
fun newInstance(grade: Grade, colorTheme: GradeColorTheme) = GradeDetailsDialog().apply {
|
||||
arguments = bundleOf(
|
||||
ARGUMENT_KEY to grade,
|
||||
COLOR_THEME_KEY to colorTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
grade = getSerializable(ARGUMENT_KEY) as Grade
|
||||
gradeColorTheme = getSerializable(COLOR_THEME_KEY) as GradeColorTheme
|
||||
}
|
||||
grade = requireArguments().serializable(ARGUMENT_KEY)
|
||||
gradeColorTheme = requireArguments().serializable(COLOR_THEME_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -5,9 +5,7 @@ import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.View.*
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
@ -42,6 +40,7 @@ class GradeDetailsFragment :
|
||||
override val isViewEmpty
|
||||
get() = gradeDetailsAdapter.itemCount == 0
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -15,6 +15,7 @@ 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
|
||||
|
||||
@ -48,8 +49,8 @@ class GradeStatisticsFragment :
|
||||
messageContainer = binding.gradeStatisticsRecycler
|
||||
presenter.onAttachView(
|
||||
view = this,
|
||||
type = savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType,
|
||||
subjectName = savedInstanceState?.getSerializable(SAVED_SUBJECT_NAME) as? String,
|
||||
type = savedInstanceState?.serializable(SAVED_CHART_TYPE),
|
||||
subjectName = savedInstanceState?.serializable(SAVED_SUBJECT_NAME),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
@ -14,6 +15,7 @@ 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,16 +37,14 @@ class HomeworkDetailsDialog : BaseDialogFragment<DialogHomeworkBinding>(), Homew
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(homework: Homework) = HomeworkDetailsDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) }
|
||||
arguments = bundleOf(ARGUMENT_KEY to homework)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
homework = getSerializable(ARGUMENT_KEY) as Homework
|
||||
}
|
||||
homework = requireArguments().serializable(ARGUMENT_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -2,13 +2,17 @@ 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.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.databinding.ActivityLoginBinding
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
|
||||
@ -16,6 +20,9 @@ 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
|
||||
|
||||
@ -28,6 +35,9 @@ 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)
|
||||
}
|
||||
@ -55,7 +65,7 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) onBackPressed()
|
||||
if (item.itemId == android.R.id.home) onBackPressedDispatcher.onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -67,8 +77,24 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
||||
openFragment(LoginSymbolFragment.newInstance(loginData))
|
||||
}
|
||||
|
||||
fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
openFragment(LoginStudentSelectFragment.newInstance(studentsWithSemesters))
|
||||
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 onAdvancedLoginClick() {
|
||||
@ -80,6 +106,8 @@ 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,4 +6,5 @@ data class LoginData(
|
||||
val login: String,
|
||||
val password: String,
|
||||
val baseUrl: String,
|
||||
val symbol: String?,
|
||||
) : Serializable
|
||||
|
@ -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.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.databinding.FragmentLoginAdvancedBinding
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
@ -327,8 +327,8 @@ class LoginAdvancedFragment :
|
||||
(activity as? LoginActivity)?.navigateToSymbolFragment(loginData)
|
||||
}
|
||||
|
||||
override fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters)
|
||||
override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -4,9 +4,15 @@ import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.logResourceStatus
|
||||
import io.github.wulkanowy.data.onResourceNotLoading
|
||||
import io.github.wulkanowy.data.pojos.RegisterStudent
|
||||
import io.github.wulkanowy.data.pojos.RegisterSymbol
|
||||
import io.github.wulkanowy.data.pojos.RegisterUnit
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.scrapper.Scrapper
|
||||
import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
||||
@ -142,19 +148,23 @@ class LoginAdvancedPresenter @Inject constructor(
|
||||
is Resource.Success -> {
|
||||
analytics.logEvent(
|
||||
"registration_form",
|
||||
"success" to true,
|
||||
"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()
|
||||
)
|
||||
when (it.data.size) {
|
||||
0 -> view?.navigateToSymbol(loginData)
|
||||
else -> view?.navigateToStudentSelect(it.data)
|
||||
}
|
||||
"success" to true,
|
||||
"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(),
|
||||
)
|
||||
when (it.data.size) {
|
||||
0 -> view?.navigateToSymbol(loginData)
|
||||
else -> view?.navigateToStudentSelect(
|
||||
loginData = loginData,
|
||||
registerUser = it.data.toRegisterUser(loginData),
|
||||
)
|
||||
}
|
||||
}
|
||||
is Resource.Error -> {
|
||||
analytics.logEvent(
|
||||
@ -173,6 +183,58 @@ class LoginAdvancedPresenter @Inject constructor(
|
||||
}.launch("login")
|
||||
}
|
||||
|
||||
private fun List<StudentWithSemesters>.toRegisterUser(loginData: LoginData) = RegisterUser(
|
||||
email = loginData.login,
|
||||
password = loginData.password,
|
||||
login = loginData.login,
|
||||
baseUrl = loginData.baseUrl,
|
||||
loginType = firstOrNull()?.student?.loginType?.let(
|
||||
Scrapper.LoginType::valueOf
|
||||
) ?: Scrapper.LoginType.AUTO,
|
||||
symbols = this
|
||||
.groupBy { students -> students.student.symbol }
|
||||
.map { (symbol, students) ->
|
||||
RegisterSymbol(
|
||||
symbol = symbol,
|
||||
error = null,
|
||||
userName = "",
|
||||
schools = students
|
||||
.groupBy { student ->
|
||||
Triple(
|
||||
first = student.student.schoolSymbol,
|
||||
second = student.student.userLoginId,
|
||||
third = student.student.schoolShortName
|
||||
)
|
||||
}
|
||||
.map { (groupKey, students) ->
|
||||
val (schoolId, loginId, schoolName) = groupKey
|
||||
RegisterUnit(
|
||||
students = students.map {
|
||||
RegisterStudent(
|
||||
studentId = it.student.studentId,
|
||||
studentName = it.student.studentName,
|
||||
studentSecondName = it.student.studentName,
|
||||
studentSurname = it.student.studentName,
|
||||
className = it.student.className,
|
||||
classId = it.student.classId,
|
||||
isParent = it.student.isParent,
|
||||
semesters = it.semesters,
|
||||
)
|
||||
},
|
||||
userLoginId = loginId,
|
||||
schoolId = schoolId,
|
||||
schoolName = schoolName,
|
||||
schoolShortName = schoolName,
|
||||
parentIds = listOf(),
|
||||
studentIds = listOf(),
|
||||
employeeIds = listOf(),
|
||||
error = null
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
private suspend fun getStudentsAppropriatesToLoginType(): List<StudentWithSemesters> {
|
||||
val email = view?.formUsernameValue.orEmpty()
|
||||
val password = view?.formPassValue.orEmpty()
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy.ui.modules.login.advanced
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
|
||||
@ -72,7 +73,7 @@ interface LoginAdvancedView : BaseView {
|
||||
|
||||
fun navigateToSymbol(loginData: LoginData)
|
||||
|
||||
fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>)
|
||||
fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser)
|
||||
|
||||
fun setErrorPinRequired()
|
||||
|
||||
|
@ -9,7 +9,7 @@ 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.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
@ -226,8 +226,8 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
||||
(activity as? LoginActivity)?.navigateToSymbolFragment(loginData)
|
||||
}
|
||||
|
||||
override fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters)
|
||||
override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser)
|
||||
}
|
||||
|
||||
override fun openAdvancedLogin() {
|
||||
|
@ -93,7 +93,7 @@ class LoginFormPresenter @Inject constructor(
|
||||
if (!validateCredentials(email, password, host)) return
|
||||
|
||||
resourceFlow {
|
||||
studentRepository.getStudentsScrapper(
|
||||
studentRepository.getUserSubjectsFromScrapper(
|
||||
email = email,
|
||||
password = password,
|
||||
scrapperBaseUrl = host,
|
||||
@ -109,14 +109,14 @@ class LoginFormPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
.onResourceSuccess {
|
||||
when (it.size) {
|
||||
0 -> view?.navigateToSymbol(LoginData(email, password, host))
|
||||
else -> view?.navigateToStudentSelect(it)
|
||||
val loginData = LoginData(email, password, host, symbol)
|
||||
when (it.symbols.size) {
|
||||
0 -> view?.navigateToSymbol(loginData)
|
||||
else -> view?.navigateToStudentSelect(loginData, it)
|
||||
}
|
||||
analytics.logEvent(
|
||||
"registration_form",
|
||||
"success" to true,
|
||||
"students" to it.size,
|
||||
"scrapperBaseUrl" to host,
|
||||
"error" to "No error"
|
||||
)
|
||||
@ -134,7 +134,6 @@ 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.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
|
||||
@ -60,7 +60,7 @@ interface LoginFormView : BaseView {
|
||||
|
||||
fun navigateToSymbol(loginData: LoginData)
|
||||
|
||||
fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>)
|
||||
fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser)
|
||||
|
||||
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).onBackPressed() }
|
||||
loginRecoverLogin.setOnClickListener { (activity as LoginActivity).onBackPressedDispatcher.onBackPressed() }
|
||||
}
|
||||
|
||||
with(bindingLocal.loginRecoverHost) {
|
||||
|
@ -2,65 +2,182 @@ 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.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.ItemLoginStudentSelectBinding
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
class LoginStudentSelectAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<LoginStudentSelectAdapter.ItemViewHolder>() {
|
||||
ListAdapter<LoginStudentSelectItem, RecyclerView.ViewHolder>(Differ) {
|
||||
|
||||
private val checkedList = mutableMapOf<Int, Boolean>()
|
||||
override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal
|
||||
|
||||
var items = emptyList<Pair<StudentWithSemesters, Boolean>>()
|
||||
set(value) {
|
||||
field = value
|
||||
checkedList.clear()
|
||||
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),
|
||||
)
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var onClickListener: (StudentWithSemesters, alreadySaved: Boolean) -> Unit = { _, _ -> }
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
private class EmptySymbolsHeaderViewHolder(
|
||||
private val binding: ItemLoginStudentSelectEmptySymbolHeaderBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
||||
ItemLoginStudentSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
@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(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
|
||||
isChecked = checkedList[position] ?: false
|
||||
fun bind(item: LoginStudentSelectItem.EmptySymbolsHeader) {
|
||||
with(binding) {
|
||||
loginStudentSelectEmptySymbolChevron.rotation = if (item.isExpanded) 270f else 90f
|
||||
root.setOnClickListener { item.onClick() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
onClickListener(studentAndSemesters, alreadySaved)
|
||||
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
|
||||
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(loginItemCheck) {
|
||||
if (isEnabled) {
|
||||
isChecked = !isChecked
|
||||
checkedList[position] = isChecked
|
||||
}
|
||||
keyListener = null
|
||||
isEnabled = item.isEnabled
|
||||
isChecked = item.isSelected || !item.isEnabled
|
||||
}
|
||||
|
||||
root.isEnabled = item.isEnabled
|
||||
root.setOnClickListener {
|
||||
item.onClick(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemLoginStudentSelectBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -2,21 +2,20 @@ 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.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.core.view.isVisible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
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.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
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
|
||||
@ -36,12 +35,23 @@ class LoginStudentSelectFragment :
|
||||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
companion object {
|
||||
const val ARG_STUDENTS = "STUDENTS"
|
||||
private lateinit var symbolsNames: Array<String>
|
||||
private lateinit var symbolsValues: Array<String>
|
||||
|
||||
fun newInstance(studentsWithSemesters: List<StudentWithSemesters>) =
|
||||
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"
|
||||
|
||||
fun newInstance(loginData: LoginData, registerUser: RegisterUser) =
|
||||
LoginStudentSelectFragment().apply {
|
||||
arguments = bundleOf(ARG_STUDENTS to studentsWithSemesters)
|
||||
arguments = bundleOf(
|
||||
ARG_LOGIN to loginData,
|
||||
ARG_STUDENTS to registerUser,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,62 +59,50 @@ class LoginStudentSelectFragment :
|
||||
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,
|
||||
students = requireArguments().getSerializable(ARG_STUDENTS) as List<StudentWithSemesters>,
|
||||
loginData = requireArguments().serializable(ARG_LOGIN),
|
||||
registerUser = requireArguments().serializable(ARG_STUDENTS),
|
||||
)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
(requireActivity() as LoginActivity).showActionBar(true)
|
||||
|
||||
loginAdapter.onClickListener = presenter::onItemSelected
|
||||
|
||||
with(binding) {
|
||||
loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() }
|
||||
loginStudentSelectContactDiscord.setOnClickListener { presenter.onDiscordClick() }
|
||||
loginStudentSelectContactEmail.setOnClickListener { presenter.onEmailClick() }
|
||||
|
||||
with(loginStudentSelectRecycler) {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = loginAdapter
|
||||
}
|
||||
loginStudentSelectRecycler.adapter = loginAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<Pair<StudentWithSemesters, Boolean>>) {
|
||||
with(loginAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
override fun updateData(data: List<LoginStudentSelectItem>) {
|
||||
loginAdapter.submitList(data)
|
||||
}
|
||||
|
||||
override fun openMainView() {
|
||||
startActivity(MainActivity.getStartIntent(requireContext()))
|
||||
requireActivity().finish()
|
||||
override fun navigateToSymbol(loginData: LoginData) {
|
||||
(requireActivity() as LoginActivity).navigateToSymbolFragment(loginData)
|
||||
}
|
||||
|
||||
override fun navigateToNext() {
|
||||
(requireActivity() as LoginActivity).navigateToNotifications()
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
binding.loginStudentSelectProgress.visibility = if (show) VISIBLE else GONE
|
||||
binding.loginStudentSelectProgress.isVisible = show
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
binding.loginStudentSelectContent.visibility = if (show) VISIBLE else GONE
|
||||
binding.loginStudentSelectContent.isVisible = show
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@ -125,4 +123,9 @@ class LoginStudentSelectFragment :
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import io.github.wulkanowy.data.pojos.RegisterStudent
|
||||
import io.github.wulkanowy.data.pojos.RegisterSymbol
|
||||
import io.github.wulkanowy.data.pojos.RegisterUnit
|
||||
|
||||
sealed class LoginStudentSelectItem(val type: LoginStudentSelectItemType) {
|
||||
|
||||
data class EmptySymbolsHeader(
|
||||
val isExpanded: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
) : LoginStudentSelectItem(LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER)
|
||||
|
||||
data class SymbolHeader(
|
||||
val symbol: RegisterSymbol,
|
||||
val humanReadableName: String?,
|
||||
val isErrorExpanded: Boolean,
|
||||
val onClick: (RegisterSymbol) -> Unit,
|
||||
) : LoginStudentSelectItem(LoginStudentSelectItemType.SYMBOL_HEADER)
|
||||
|
||||
data class SchoolHeader(
|
||||
val unit: RegisterUnit,
|
||||
val isErrorExpanded: Boolean,
|
||||
val onClick: (RegisterUnit) -> Unit,
|
||||
) : LoginStudentSelectItem(LoginStudentSelectItemType.SCHOOL_HEADER)
|
||||
|
||||
data class Student(
|
||||
val symbol: RegisterSymbol,
|
||||
val unit: RegisterUnit,
|
||||
val student: RegisterStudent,
|
||||
val isEnabled: Boolean,
|
||||
val isSelected: Boolean,
|
||||
val onClick: (Student) -> Unit,
|
||||
) : LoginStudentSelectItem(LoginStudentSelectItemType.STUDENT)
|
||||
|
||||
data class Help(
|
||||
val onEnterSymbolClick: () -> Unit,
|
||||
val onContactUsClick: () -> Unit,
|
||||
val onDiscordClick: () -> Unit,
|
||||
val isSymbolButtonVisible: Boolean,
|
||||
) : LoginStudentSelectItem(LoginStudentSelectItemType.HELP)
|
||||
}
|
||||
|
||||
enum class LoginStudentSelectItemType {
|
||||
EMPTY_SYMBOLS_HEADER,
|
||||
SYMBOL_HEADER,
|
||||
SCHOOL_HEADER,
|
||||
STUDENT,
|
||||
HELP,
|
||||
}
|
@ -1,15 +1,24 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.logResourceStatus
|
||||
import io.github.wulkanowy.data.mappers.mapToStudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterStudent
|
||||
import io.github.wulkanowy.data.pojos.RegisterSymbol
|
||||
import io.github.wulkanowy.data.pojos.RegisterUnit
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.sdk.scrapper.login.AccountPermissionException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
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 kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
@ -19,18 +28,30 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
studentRepository: StudentRepository,
|
||||
private val loginErrorHandler: LoginErrorHandler,
|
||||
private val syncManager: SyncManager,
|
||||
private val analytics: AnalyticsHelper
|
||||
private val analytics: AnalyticsHelper,
|
||||
private val appInfo: AppInfo,
|
||||
) : BasePresenter<LoginStudentSelectView>(loginErrorHandler, studentRepository) {
|
||||
|
||||
private var lastError: Throwable? = null
|
||||
|
||||
private val selectedStudents = mutableListOf<StudentWithSemesters>()
|
||||
private lateinit var registerUser: RegisterUser
|
||||
private lateinit var loginData: LoginData
|
||||
|
||||
fun onAttachView(view: LoginStudentSelectView, students: List<StudentWithSemesters>) {
|
||||
private lateinit var students: List<StudentWithSemesters>
|
||||
private var isEmptySymbolsExpanded = false
|
||||
private var expandedSymbolError: RegisterSymbol? = null
|
||||
private var expandedSchoolError: RegisterUnit? = null
|
||||
|
||||
private val selectedStudents = mutableListOf<LoginStudentSelectItem.Student>()
|
||||
|
||||
fun onAttachView(
|
||||
view: LoginStudentSelectView,
|
||||
loginData: LoginData,
|
||||
registerUser: RegisterUser,
|
||||
) {
|
||||
super.onAttachView(view)
|
||||
with(view) {
|
||||
initView()
|
||||
showContact(false)
|
||||
enableSignIn(false)
|
||||
loginErrorHandler.onStudentDuplicate = {
|
||||
showMessage(it)
|
||||
@ -38,50 +59,171 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
if (students.size == 1) registerStudents(students)
|
||||
loadData(students)
|
||||
this.loginData = loginData
|
||||
this.registerUser = registerUser
|
||||
loadData()
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
resetSelectedState()
|
||||
|
||||
resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
|
||||
students = it.dataOrNull.orEmpty()
|
||||
when (it) {
|
||||
is Resource.Loading -> Timber.d("Login student select students load started")
|
||||
is Resource.Success -> refreshItems()
|
||||
is Resource.Error -> {
|
||||
errorHandler.dispatch(it.error)
|
||||
lastError = it.error
|
||||
refreshItems()
|
||||
}
|
||||
}
|
||||
}.launch()
|
||||
}
|
||||
|
||||
private fun createItems(): List<LoginStudentSelectItem> = buildList {
|
||||
val notEmptySymbols = registerUser.symbols.filter { it.schools.isNotEmpty() }
|
||||
val emptySymbols = registerUser.symbols.filter { it.schools.isEmpty() }
|
||||
|
||||
if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.symbol }) {
|
||||
add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.symbol }))
|
||||
}
|
||||
|
||||
addAll(createNotEmptySymbolItems(notEmptySymbols, students))
|
||||
addAll(createEmptySymbolItems(emptySymbols, notEmptySymbols.isNotEmpty()))
|
||||
|
||||
val helpItem = LoginStudentSelectItem.Help(
|
||||
onEnterSymbolClick = ::onEnterSymbol,
|
||||
onContactUsClick = ::onEmailClick,
|
||||
onDiscordClick = ::onDiscordClick,
|
||||
isSymbolButtonVisible = "login" !in loginData.baseUrl,
|
||||
)
|
||||
add(helpItem)
|
||||
}
|
||||
|
||||
private fun createNotEmptySymbolItems(
|
||||
notEmptySymbols: List<RegisterSymbol>,
|
||||
students: List<StudentWithSemesters>,
|
||||
) = buildList {
|
||||
notEmptySymbols.forEach { registerSymbol ->
|
||||
val symbolHeader = LoginStudentSelectItem.SymbolHeader(
|
||||
symbol = registerSymbol,
|
||||
humanReadableName = view?.symbols?.get(registerSymbol.symbol),
|
||||
isErrorExpanded = expandedSymbolError == registerSymbol,
|
||||
onClick = ::onSymbolItemClick,
|
||||
)
|
||||
add(symbolHeader)
|
||||
|
||||
registerSymbol.schools.forEach { registerUnit ->
|
||||
val schoolHeader = LoginStudentSelectItem.SchoolHeader(
|
||||
unit = registerUnit,
|
||||
isErrorExpanded = expandedSchoolError == registerUnit,
|
||||
onClick = ::onUnitItemClick,
|
||||
)
|
||||
add(schoolHeader)
|
||||
|
||||
registerUnit.students.forEach {
|
||||
add(createStudentItem(it, registerSymbol, registerUnit, students))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createStudentItem(
|
||||
student: RegisterStudent,
|
||||
symbol: RegisterSymbol,
|
||||
school: RegisterUnit,
|
||||
students: List<StudentWithSemesters>,
|
||||
) = LoginStudentSelectItem.Student(
|
||||
symbol = symbol,
|
||||
unit = school,
|
||||
student = student,
|
||||
onClick = ::onItemSelected,
|
||||
isEnabled = students.none {
|
||||
it.student.email == registerUser.login
|
||||
&& it.student.symbol == symbol.symbol
|
||||
&& it.student.studentId == student.studentId
|
||||
&& it.student.schoolSymbol == school.schoolId
|
||||
&& it.student.classId == student.classId
|
||||
},
|
||||
isSelected = selectedStudents
|
||||
.filter { it.symbol.symbol == symbol.symbol }
|
||||
.filter { it.unit.schoolId == school.schoolId }
|
||||
.filter { it.student.studentId == student.studentId }
|
||||
.filter { it.student.classId == student.classId }
|
||||
.size == 1,
|
||||
)
|
||||
|
||||
private fun createEmptySymbolItems(
|
||||
emptySymbols: List<RegisterSymbol>,
|
||||
isNotEmptySymbolsExist: Boolean,
|
||||
) = buildList {
|
||||
val filteredEmptySymbols = emptySymbols.filter {
|
||||
it.error !is InvalidSymbolException
|
||||
}.ifEmpty { emptySymbols.takeIf { !isNotEmptySymbolsExist }.orEmpty() }
|
||||
|
||||
if (filteredEmptySymbols.isNotEmpty() && isNotEmptySymbolsExist) {
|
||||
val emptyHeader = LoginStudentSelectItem.EmptySymbolsHeader(
|
||||
isExpanded = isEmptySymbolsExpanded,
|
||||
onClick = ::onEmptySymbolsToggle,
|
||||
)
|
||||
add(emptyHeader)
|
||||
if (isEmptySymbolsExpanded) {
|
||||
filteredEmptySymbols.forEach {
|
||||
add(createEmptySymbolItem(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredEmptySymbols.isNotEmpty() && !isNotEmptySymbolsExist) {
|
||||
filteredEmptySymbols.forEach {
|
||||
add(createEmptySymbolItem(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEmptySymbolItem(registerSymbol: RegisterSymbol) =
|
||||
LoginStudentSelectItem.SymbolHeader(
|
||||
symbol = registerSymbol,
|
||||
humanReadableName = view?.symbols?.get(registerSymbol.symbol),
|
||||
isErrorExpanded = expandedSymbolError == registerSymbol,
|
||||
onClick = ::onSymbolItemClick,
|
||||
)
|
||||
|
||||
fun onSignIn() {
|
||||
registerStudents(selectedStudents)
|
||||
}
|
||||
|
||||
fun onItemSelected(studentWithSemester: StudentWithSemesters, alreadySaved: Boolean) {
|
||||
if (alreadySaved) return
|
||||
private fun onEmptySymbolsToggle() {
|
||||
isEmptySymbolsExpanded = !isEmptySymbolsExpanded
|
||||
|
||||
refreshItems()
|
||||
}
|
||||
|
||||
private fun onItemSelected(item: LoginStudentSelectItem.Student) {
|
||||
if (!item.isEnabled) return
|
||||
|
||||
selectedStudents
|
||||
.removeAll { it == studentWithSemester }
|
||||
.let { if (!it) selectedStudents.add(studentWithSemester) }
|
||||
.removeAll {
|
||||
it.student.studentId == item.student.studentId &&
|
||||
it.student.classId == item.student.classId &&
|
||||
it.unit.schoolId == item.unit.schoolId &&
|
||||
it.symbol.symbol == item.symbol.symbol
|
||||
}
|
||||
.let { if (!it) selectedStudents.add(item) }
|
||||
|
||||
view?.enableSignIn(selectedStudents.isNotEmpty())
|
||||
refreshItems()
|
||||
}
|
||||
|
||||
private fun compareStudents(a: Student, b: Student): Boolean {
|
||||
return a.email == b.email
|
||||
&& a.symbol == b.symbol
|
||||
&& a.studentId == b.studentId
|
||||
&& a.schoolSymbol == b.schoolSymbol
|
||||
&& a.classId == b.classId
|
||||
private fun onSymbolItemClick(symbol: RegisterSymbol) {
|
||||
expandedSymbolError = if (symbol != expandedSymbolError) symbol else null
|
||||
refreshItems()
|
||||
}
|
||||
|
||||
private fun loadData(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
resetSelectedState()
|
||||
|
||||
resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
|
||||
when (it) {
|
||||
is Resource.Loading -> Timber.d("Login student select students load started")
|
||||
is Resource.Success -> view?.updateData(studentsWithSemesters.map { studentWithSemesters ->
|
||||
studentWithSemesters to it.data.any { item ->
|
||||
compareStudents(studentWithSemesters.student, item.student)
|
||||
}
|
||||
})
|
||||
is Resource.Error -> {
|
||||
errorHandler.dispatch(it.error)
|
||||
lastError = it.error
|
||||
view?.updateData(studentsWithSemesters.map { student -> student to false })
|
||||
}
|
||||
}
|
||||
}.launch()
|
||||
private fun onUnitItemClick(unit: RegisterUnit) {
|
||||
expandedSchoolError = if (unit != expandedSchoolError) unit else null
|
||||
refreshItems()
|
||||
}
|
||||
|
||||
private fun resetSelectedState() {
|
||||
@ -89,7 +231,20 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
view?.enableSignIn(false)
|
||||
}
|
||||
|
||||
private fun registerStudents(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
private fun refreshItems() {
|
||||
view?.updateData(createItems())
|
||||
}
|
||||
|
||||
private fun registerStudents(students: List<LoginStudentSelectItem>) {
|
||||
val studentsWithSemesters = students
|
||||
.filterIsInstance<LoginStudentSelectItem.Student>().map { item ->
|
||||
item.student.mapToStudentWithSemesters(
|
||||
user = registerUser,
|
||||
symbol = item.symbol,
|
||||
unit = item.unit,
|
||||
colors = appInfo.defaultColorsForAvatar,
|
||||
)
|
||||
}
|
||||
resourceFlow { studentRepository.saveStudents(studentsWithSemesters) }
|
||||
.logResourceStatus("registration")
|
||||
.onEach {
|
||||
@ -100,14 +255,13 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
is Resource.Success -> {
|
||||
syncManager.startOneTimeSyncWorker(quiet = true)
|
||||
view?.openMainView()
|
||||
view?.navigateToNext()
|
||||
logRegisterEvent(studentsWithSemesters)
|
||||
}
|
||||
is Resource.Error -> {
|
||||
view?.apply {
|
||||
showProgress(false)
|
||||
showContent(true)
|
||||
showContact(true)
|
||||
}
|
||||
lastError = it.error
|
||||
loginErrorHandler.dispatch(it.error)
|
||||
@ -117,12 +271,37 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}.launch("register")
|
||||
}
|
||||
|
||||
fun onDiscordClick() {
|
||||
private fun onEnterSymbol() {
|
||||
view?.navigateToSymbol(loginData)
|
||||
}
|
||||
|
||||
private fun onDiscordClick() {
|
||||
view?.openDiscordInvite()
|
||||
}
|
||||
|
||||
fun onEmailClick() {
|
||||
view?.openEmail(lastError?.message.ifNullOrBlank { "empty" })
|
||||
private fun onEmailClick() {
|
||||
view?.openEmail(lastError?.message.ifNullOrBlank {
|
||||
loginData.baseUrl + "/" + loginData.symbol + "\n" + registerUser.symbols.filterNot {
|
||||
(it.error is AccountPermissionException || it.error is InvalidSymbolException) && it.symbol != loginData.symbol
|
||||
}.joinToString(";\n") { symbol ->
|
||||
buildString {
|
||||
append(" -")
|
||||
append(symbol.symbol)
|
||||
append("(${symbol.error?.message?.let { it.take(46) + "..." } ?: symbol.schools.size})")
|
||||
if (symbol.schools.isNotEmpty()) {
|
||||
append(": ")
|
||||
}
|
||||
append(symbol.schools.joinToString(", ") { unit ->
|
||||
buildString {
|
||||
append(unit.schoolShortName)
|
||||
append("(${unit.error?.message?.let { it.take(46) + "..." } ?: unit.students.size})")
|
||||
}
|
||||
})
|
||||
}
|
||||
} + "\nPozostałe: " + registerUser.symbols.filter {
|
||||
it.error is AccountPermissionException || it.error is InvalidSymbolException
|
||||
}.joinToString(", ") { it.symbol }
|
||||
})
|
||||
}
|
||||
|
||||
private fun logRegisterEvent(
|
||||
|
@ -1,15 +1,19 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
|
||||
interface LoginStudentSelectView : BaseView {
|
||||
|
||||
val symbols: Map<String, String>
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<Pair<StudentWithSemesters, Boolean>>)
|
||||
fun updateData(data: List<LoginStudentSelectItem>)
|
||||
|
||||
fun openMainView()
|
||||
fun navigateToSymbol(loginData: LoginData)
|
||||
|
||||
fun navigateToNext()
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
@ -17,8 +21,6 @@ interface LoginStudentSelectView : BaseView {
|
||||
|
||||
fun enableSignIn(enable: Boolean)
|
||||
|
||||
fun showContact(show: Boolean)
|
||||
|
||||
fun openDiscordInvite()
|
||||
|
||||
fun openEmail(lastError: String)
|
||||
|
@ -12,17 +12,13 @@ import androidx.core.text.parseAsHtml
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.hideSoftInput
|
||||
import io.github.wulkanowy.utils.openEmailClient
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.showSoftInput
|
||||
import io.github.wulkanowy.utils.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@ -46,6 +42,8 @@ class LoginSymbolFragment :
|
||||
}
|
||||
}
|
||||
|
||||
override val symbolValue: String? get() = binding.loginSymbolName.text?.toString()
|
||||
|
||||
override val symbolNameError: CharSequence?
|
||||
get() = binding.loginSymbolNameLayout.error
|
||||
|
||||
@ -54,7 +52,7 @@ class LoginSymbolFragment :
|
||||
binding = FragmentLoginSymbolBinding.bind(view)
|
||||
presenter.onAttachView(
|
||||
view = this,
|
||||
loginData = requireArguments().getSerializable(SAVED_LOGIN_DATA) as LoginData,
|
||||
loginData = requireArguments().serializable(SAVED_LOGIN_DATA),
|
||||
)
|
||||
}
|
||||
|
||||
@ -62,7 +60,7 @@ class LoginSymbolFragment :
|
||||
(requireActivity() as LoginActivity).showActionBar(true)
|
||||
|
||||
with(binding) {
|
||||
loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) }
|
||||
loginSymbolSignIn.setOnClickListener { presenter.attemptLogin() }
|
||||
loginSymbolFaq.setOnClickListener { presenter.onFaqClick() }
|
||||
loginSymbolContactEmail.setOnClickListener { presenter.onEmailClick() }
|
||||
|
||||
@ -95,10 +93,21 @@ class LoginSymbolFragment :
|
||||
}
|
||||
}
|
||||
|
||||
override fun setErrorSymbolRequire() {
|
||||
binding.loginSymbolNameLayout.apply {
|
||||
override fun setErrorSymbolInvalid() {
|
||||
with(binding.loginSymbolNameLayout) {
|
||||
requestFocus()
|
||||
error = getString(R.string.error_field_required)
|
||||
error = getString(R.string.login_invalid_symbol)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setErrorSymbolRequire() {
|
||||
setErrorSymbol(getString(R.string.error_field_required))
|
||||
}
|
||||
|
||||
override fun setErrorSymbol(message: String) {
|
||||
with(binding.loginSymbolNameLayout) {
|
||||
requestFocus()
|
||||
error = message
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,8 +138,8 @@ class LoginSymbolFragment :
|
||||
binding.loginSymbolContainer.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters)
|
||||
override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) {
|
||||
(activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
@ -1,9 +1,13 @@
|
||||
package io.github.wulkanowy.ui.modules.login.symbol
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
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.scrapper.getNormalizedSymbol
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
||||
@ -23,9 +27,14 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
|
||||
lateinit var loginData: LoginData
|
||||
|
||||
private var registerUser: RegisterUser? = null
|
||||
|
||||
fun onAttachView(view: LoginSymbolView, loginData: LoginData) {
|
||||
super.onAttachView(view)
|
||||
this.loginData = loginData
|
||||
loginErrorHandler.onBadCredentials = {
|
||||
view.setErrorSymbol(it.orEmpty())
|
||||
}
|
||||
with(view) {
|
||||
initView()
|
||||
showContact(false)
|
||||
@ -39,21 +48,25 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
view?.apply { if (symbolNameError != null) clearSymbolError() }
|
||||
}
|
||||
|
||||
fun attemptLogin(symbol: String) {
|
||||
if (symbol.isBlank()) {
|
||||
fun attemptLogin() {
|
||||
if (view?.symbolValue.isNullOrBlank()) {
|
||||
view?.setErrorSymbolRequire()
|
||||
return
|
||||
}
|
||||
|
||||
loginData = loginData.copy(
|
||||
symbol = view?.symbolValue?.getNormalizedSymbol(),
|
||||
)
|
||||
resourceFlow {
|
||||
studentRepository.getStudentsScrapper(
|
||||
studentRepository.getUserSubjectsFromScrapper(
|
||||
email = loginData.login,
|
||||
password = loginData.password,
|
||||
scrapperBaseUrl = loginData.baseUrl,
|
||||
symbol = symbol,
|
||||
symbol = loginData.symbol.orEmpty(),
|
||||
)
|
||||
}.onEach {
|
||||
when (it) {
|
||||
}.onEach { user ->
|
||||
registerUser = user.dataOrNull
|
||||
when (user) {
|
||||
is Resource.Loading -> view?.run {
|
||||
Timber.i("Login with symbol started")
|
||||
hideSoftKeyboard()
|
||||
@ -61,7 +74,7 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
showContent(false)
|
||||
}
|
||||
is Resource.Success -> {
|
||||
when (it.data.size) {
|
||||
when (user.data.symbols.size) {
|
||||
0 -> {
|
||||
Timber.i("Login with symbol result: Empty student list")
|
||||
view?.run {
|
||||
@ -70,16 +83,26 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Timber.i("Login with symbol result: Success")
|
||||
view?.navigateToStudentSelect(requireNotNull(it.data))
|
||||
val enteredSymbolDetails = user.data.symbols
|
||||
.firstOrNull()
|
||||
?.takeIf { it.symbol == loginData.symbol }
|
||||
|
||||
if (enteredSymbolDetails?.error is InvalidSymbolException) {
|
||||
view?.run {
|
||||
setErrorSymbolInvalid()
|
||||
showContact(true)
|
||||
}
|
||||
} else {
|
||||
Timber.i("Login with symbol result: Success")
|
||||
view?.navigateToStudentSelect(loginData, requireNotNull(user.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
analytics.logEvent(
|
||||
"registration_symbol",
|
||||
"success" to true,
|
||||
"students" to it.data.size,
|
||||
"scrapperBaseUrl" to loginData.baseUrl,
|
||||
"symbol" to symbol,
|
||||
"symbol" to view?.symbolValue,
|
||||
"error" to "No error"
|
||||
)
|
||||
}
|
||||
@ -90,11 +113,11 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
"success" to false,
|
||||
"students" to -1,
|
||||
"scrapperBaseUrl" to loginData.baseUrl,
|
||||
"symbol" to symbol,
|
||||
"error" to it.error.message.ifNullOrBlank { "No message" }
|
||||
"symbol" to view?.symbolValue,
|
||||
"error" to user.error.message.ifNullOrBlank { "No message" }
|
||||
)
|
||||
loginErrorHandler.dispatch(it.error)
|
||||
lastError = it.error
|
||||
loginErrorHandler.dispatch(user.error)
|
||||
lastError = user.error
|
||||
view?.showContact(true)
|
||||
}
|
||||
}
|
||||
@ -111,6 +134,12 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onEmailClick() {
|
||||
view?.openEmail(loginData.baseUrl, lastError?.message.ifNullOrBlank { "empty" })
|
||||
view?.openEmail(loginData.baseUrl, lastError?.message.ifNullOrBlank {
|
||||
registerUser?.symbols?.flatMap { symbol ->
|
||||
symbol.schools.map { it.error?.message } + symbol.error?.message
|
||||
}?.filterNotNull()?.distinct()?.joinToString(";") {
|
||||
it.take(46) + "..."
|
||||
} ?: "blank"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package io.github.wulkanowy.ui.modules.login.symbol
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
|
||||
interface LoginSymbolView : BaseView {
|
||||
|
||||
val symbolValue: String?
|
||||
|
||||
val symbolNameError: CharSequence?
|
||||
|
||||
fun initView()
|
||||
@ -13,8 +16,12 @@ interface LoginSymbolView : BaseView {
|
||||
|
||||
fun setErrorSymbolIncorrect()
|
||||
|
||||
fun setErrorSymbolInvalid()
|
||||
|
||||
fun setErrorSymbolRequire()
|
||||
|
||||
fun setErrorSymbol(message: String)
|
||||
|
||||
fun clearSymbolError()
|
||||
|
||||
fun clearAndFocusSymbol()
|
||||
@ -27,7 +34,7 @@ interface LoginSymbolView : BaseView {
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun navigateToStudentSelect(studentsWithSemesters: List<StudentWithSemesters>)
|
||||
fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser)
|
||||
|
||||
fun showContact(show: Boolean)
|
||||
|
||||
|
@ -6,6 +6,8 @@ import android.os.Build.VERSION_CODES.P
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.addCallback
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
@ -50,6 +52,8 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
private var onBackCallback: OnBackPressedCallback? = null
|
||||
|
||||
private var accountMenu: MenuItem? = null
|
||||
|
||||
private val overlayProvider by lazy { ElevationOverlayProvider(this) }
|
||||
@ -88,6 +92,9 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
this.savedInstanceState = savedInstanceState
|
||||
messageContainer = binding.mainMessageContainer
|
||||
updateHelper.messageContainer = binding.mainFragmentContainer
|
||||
onBackCallback = onBackPressedDispatcher.addCallback(this, enabled = false) {
|
||||
presenter.onBackPressed()
|
||||
}
|
||||
|
||||
val destination = intent.getStringExtra(EXTRA_START_DESTINATION)
|
||||
?.takeIf { savedInstanceState == null }
|
||||
@ -266,6 +273,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
|
||||
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
|
||||
navController.pushFragment(fragment)
|
||||
onBackCallback?.isEnabled = !isRootView
|
||||
}
|
||||
|
||||
override fun popView(depth: Int) {
|
||||
@ -273,10 +281,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
|
||||
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
|
||||
navController.safelyPopFragments(depth)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
presenter.onBackPressed { super.onBackPressed() }
|
||||
onBackCallback?.isEnabled = !isRootView
|
||||
}
|
||||
|
||||
override fun showStudentAvatar(student: Student) {
|
||||
|
@ -139,12 +139,9 @@ class MainPresenter @Inject constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
fun onBackPressed(default: () -> Unit) {
|
||||
fun onBackPressed() {
|
||||
Timber.i("Back pressed in main view")
|
||||
view?.run {
|
||||
if (isRootView) default()
|
||||
else popView()
|
||||
}
|
||||
view?.popView()
|
||||
}
|
||||
|
||||
fun onTabSelected(index: Int, wasSelected: Boolean): Boolean {
|
||||
|
@ -10,6 +10,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||
import io.github.wulkanowy.databinding.DialogMailboxChooserBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.utils.parcelableArray
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@ -52,8 +53,7 @@ class MailboxChooserDialog : BaseDialogFragment<DialogMailboxChooserBinding>(),
|
||||
presenter.onAttachView(
|
||||
view = this,
|
||||
requireMailbox = requireArguments().getBoolean(REQUIRED_KEY, false),
|
||||
mailboxes = requireArguments().getParcelableArray(MAILBOX_KEY).orEmpty()
|
||||
.toList() as List<Mailbox>,
|
||||
mailboxes = requireArguments().parcelableArray<Mailbox>(MAILBOX_KEY).orEmpty().toList(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
@ -23,6 +24,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.shareText
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -66,13 +68,12 @@ class MessagePreviewFragment :
|
||||
companion object {
|
||||
const val MESSAGE_ID_KEY = "message_id"
|
||||
|
||||
fun newInstance(message: Message): MessagePreviewFragment {
|
||||
return MessagePreviewFragment().apply {
|
||||
arguments = Bundle().apply { putSerializable(MESSAGE_ID_KEY, message) }
|
||||
}
|
||||
fun newInstance(message: Message) = MessagePreviewFragment().apply {
|
||||
arguments = bundleOf(MESSAGE_ID_KEY to message)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
@ -83,8 +84,8 @@ class MessagePreviewFragment :
|
||||
binding = FragmentMessagePreviewBinding.bind(view)
|
||||
messageContainer = binding.messagePreviewContainer
|
||||
presenter.onAttachView(
|
||||
this,
|
||||
(savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message
|
||||
view = this,
|
||||
message = (savedInstanceState ?: arguments)?.serializable(MESSAGE_ID_KEY),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
runCatching {
|
||||
val student = studentRepository.getCurrentStudent(decryptPass = true)
|
||||
val mailbox = messageRepository.getMailboxByStudent(student)
|
||||
messageRepository.deleteMessage(student, mailbox!!, message!!)
|
||||
messageRepository.deleteMessage(student, mailbox, message!!)
|
||||
}
|
||||
.onFailure {
|
||||
retryCallback = { onMessageDelete() }
|
||||
|
@ -28,6 +28,7 @@ import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialo
|
||||
import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.LISTENER_KEY
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.hideSoftInput
|
||||
import io.github.wulkanowy.utils.nullableSerializable
|
||||
import io.github.wulkanowy.utils.showSoftInput
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -108,12 +109,12 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
|
||||
|
||||
presenter.onAttachView(
|
||||
view = this,
|
||||
reason = intent.getSerializableExtra(EXTRA_REASON) as? String,
|
||||
message = intent.getSerializableExtra(EXTRA_MESSAGE) as? Message,
|
||||
reply = intent.getSerializableExtra(EXTRA_REPLY) as? Boolean
|
||||
reason = intent.nullableSerializable(EXTRA_REASON),
|
||||
message = intent.nullableSerializable(EXTRA_MESSAGE),
|
||||
reply = intent.nullableSerializable(EXTRA_REPLY)
|
||||
)
|
||||
supportFragmentManager.setFragmentResultListener(LISTENER_KEY, this) { _, bundle ->
|
||||
presenter.onMailboxSelected(bundle.getSerializable(MAILBOX_KEY) as? Mailbox)
|
||||
presenter.onMailboxSelected(bundle.nullableSerializable(MAILBOX_KEY))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import android.view.View.*
|
||||
import android.widget.CompoundButton
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -27,6 +28,7 @@ import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.hideSoftInput
|
||||
import io.github.wulkanowy.utils.nullableSerializable
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@ -43,12 +45,8 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
|
||||
const val MESSAGE_TAB_FOLDER_ID = "message_tab_folder_id"
|
||||
|
||||
fun newInstance(folder: MessageFolder): MessageTabFragment {
|
||||
return MessageTabFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(MESSAGE_TAB_FOLDER_ID, folder.name)
|
||||
}
|
||||
}
|
||||
fun newInstance(folder: MessageFolder) = MessageTabFragment().apply {
|
||||
arguments = bundleOf(MESSAGE_TAB_FOLDER_ID to folder.name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,6 +84,7 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
@ -130,11 +129,12 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
|
||||
setFragmentResultListener(requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!) { _, bundle ->
|
||||
presenter.onMailboxSelected(
|
||||
mailbox = bundle.getSerializable(MailboxChooserDialog.MAILBOX_KEY) as? Mailbox,
|
||||
mailbox = bundle.nullableSerializable(MailboxChooserDialog.MAILBOX_KEY),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.action_menu_message_tab, menu)
|
||||
|
@ -6,6 +6,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
@ -13,6 +14,7 @@ import io.github.wulkanowy.databinding.DialogNoteBinding
|
||||
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
|
||||
class NoteDialog : DialogFragment() {
|
||||
@ -25,17 +27,15 @@ class NoteDialog : DialogFragment() {
|
||||
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: Note) = NoteDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
fun newInstance(note: Note) = NoteDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to note)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
note = getSerializable(ARGUMENT_KEY) as Note
|
||||
}
|
||||
note = requireArguments().serializable(ARGUMENT_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -0,0 +1,64 @@
|
||||
package io.github.wulkanowy.ui.modules.notifications
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.FragmentNotificationsBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.utils.openNotificationSettings
|
||||
|
||||
@AndroidEntryPoint
|
||||
class NotificationsFragment :
|
||||
BaseFragment<FragmentNotificationsBinding>(R.layout.fragment_notifications) {
|
||||
|
||||
private val permission = "android.permission.POST_NOTIFICATIONS"
|
||||
|
||||
private val requestPermissionLauncher = registerForActivityResult(RequestPermission()) {
|
||||
if (it) {
|
||||
navigateToFinish()
|
||||
} else showSettingsDialog()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance() = NotificationsFragment()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentNotificationsBinding.bind(view)
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
with(binding) {
|
||||
notificationsSkip.setOnClickListener { navigateToFinish() }
|
||||
notificationsEnable.setOnClickListener { requestPermission() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSettingsDialog() {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.notifications_header_title)
|
||||
.setMessage(R.string.notifications_header_description)
|
||||
.setNegativeButton(R.string.notifications_skip) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
navigateToFinish()
|
||||
}
|
||||
.setPositiveButton(R.string.pref_notification_go_to_settings) { _, _ ->
|
||||
requireActivity().openNotificationSettings()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun requestPermission() {
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
|
||||
private fun navigateToFinish() {
|
||||
(requireActivity() as LoginActivity).navigateToFinish()
|
||||
}
|
||||
}
|
@ -2,10 +2,10 @@ package io.github.wulkanowy.ui.modules.schoolannouncement
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||
import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding
|
||||
import io.github.wulkanowy.utils.parseUonetHtml
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -28,7 +28,7 @@ class SchoolAnnouncementAdapter @Inject constructor() :
|
||||
with(holder.binding) {
|
||||
schoolAnnouncementItemDate.text = item.date.toFormattedString()
|
||||
schoolAnnouncementItemType.text = item.subject
|
||||
schoolAnnouncementItemContent.text = item.content.parseAsHtml()
|
||||
schoolAnnouncementItemContent.text = item.content.parseUonetHtml()
|
||||
|
||||
root.setOnClickListener { onItemClickListener(item) }
|
||||
}
|
||||
|
@ -4,11 +4,13 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||
import io.github.wulkanowy.databinding.DialogSchoolAnnouncementBinding
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.parseUonetHtml
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
|
||||
class SchoolAnnouncementDialog : DialogFragment() {
|
||||
@ -21,17 +23,15 @@ class SchoolAnnouncementDialog : DialogFragment() {
|
||||
|
||||
private const val ARGUMENT_KEY = "item"
|
||||
|
||||
fun newInstance(exam: SchoolAnnouncement) = SchoolAnnouncementDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
fun newInstance(announcement: SchoolAnnouncement) = SchoolAnnouncementDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to announcement)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
announcement = getSerializable(ARGUMENT_KEY) as SchoolAnnouncement
|
||||
}
|
||||
announcement = requireArguments().serializable(ARGUMENT_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -46,7 +46,7 @@ class SchoolAnnouncementDialog : DialogFragment() {
|
||||
with(binding) {
|
||||
announcementDialogSubjectValue.text = announcement.subject
|
||||
announcementDialogDateValue.text = announcement.date.toFormattedString()
|
||||
announcementDialogDescriptionValue.text = announcement.content.parseAsHtml()
|
||||
announcementDialogDescriptionValue.text = announcement.content.parseUonetHtml()
|
||||
|
||||
announcementDialogClose.setOnClickListener { dismiss() }
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
package io.github.wulkanowy.ui.modules.settings.notifications
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
@ -26,7 +24,7 @@ import io.github.wulkanowy.ui.base.ErrorDialog
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import timber.log.Timber
|
||||
import io.github.wulkanowy.utils.openNotificationSettings
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@ -42,7 +40,14 @@ class NotificationsFragment : PreferenceFragmentCompat(),
|
||||
|
||||
override val titleStringId get() = R.string.pref_settings_notifications_title
|
||||
|
||||
private val notificationsPermission = "android.permission.POST_NOTIFICATIONS"
|
||||
|
||||
override val isNotificationPermissionGranted: Boolean
|
||||
get() = ContextCompat.checkSelfPermission(
|
||||
requireContext(), notificationsPermission
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
override val isNotificationPiggybackPermissionGranted: Boolean
|
||||
get() {
|
||||
val packageNameList =
|
||||
NotificationManagerCompat.getEnabledListenerPackages(requireContext())
|
||||
@ -51,6 +56,13 @@ class NotificationsFragment : PreferenceFragmentCompat(),
|
||||
return appPackageName in packageNameList
|
||||
}
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||
if (it) {
|
||||
presenter.onNotificationsPermissionResult()
|
||||
} else openNotificationsPermissionDialog()
|
||||
}
|
||||
|
||||
private val notificationSettingsPiggybackContract =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
presenter.onNotificationPiggybackPermissionResult()
|
||||
@ -156,25 +168,29 @@ class NotificationsFragment : PreferenceFragmentCompat(),
|
||||
.show()
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun openSystemSettings() {
|
||||
val intent = if (appInfo.systemVersion >= Build.VERSION_CODES.O) {
|
||||
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
|
||||
putExtra("android.provider.extra.APP_PACKAGE", requireActivity().packageName)
|
||||
}
|
||||
} else {
|
||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", requireActivity().packageName, null)
|
||||
}
|
||||
}
|
||||
try {
|
||||
requireActivity().startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
requireActivity().openNotificationSettings()
|
||||
}
|
||||
|
||||
override fun openNotificationPermissionDialog() {
|
||||
override fun requestNotificationPermissions() {
|
||||
requestPermissionLauncher.launch(notificationsPermission)
|
||||
}
|
||||
|
||||
override fun openNotificationsPermissionDialog() {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.notifications_header_title)
|
||||
.setMessage(R.string.notifications_header_description)
|
||||
.setPositiveButton(R.string.pref_notification_go_to_settings) { _, _ ->
|
||||
requireActivity().openNotificationSettings()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
setNotificationPreferencesChecked(false)
|
||||
}
|
||||
.setOnDismissListener { setNotificationPreferencesChecked(false) }
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun openNotificationPiggyBackPermissionDialog() {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(getString(R.string.pref_notification_piggyback_popup_title))
|
||||
.setMessage(getString(R.string.pref_notification_piggyback_popup_description))
|
||||
@ -202,6 +218,11 @@ class NotificationsFragment : PreferenceFragmentCompat(),
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun setNotificationPreferencesChecked(isChecked: Boolean) {
|
||||
findPreference<SwitchPreferenceCompat>(getString(R.string.pref_key_notifications_enable))?.isChecked =
|
||||
isChecked
|
||||
}
|
||||
|
||||
override fun setNotificationPiggybackPreferenceChecked(isChecked: Boolean) {
|
||||
findPreference<SwitchPreferenceCompat>(getString(R.string.pref_key_notifications_piggyback))?.isChecked =
|
||||
isChecked
|
||||
|
@ -26,12 +26,13 @@ class NotificationsPresenter @Inject constructor(
|
||||
|
||||
with(view) {
|
||||
enableNotification(
|
||||
preferencesRepository.notificationsEnableKey,
|
||||
preferencesRepository.isServiceEnabled
|
||||
notificationKey = preferencesRepository.notificationsEnableKey,
|
||||
enable = preferencesRepository.isServiceEnabled
|
||||
)
|
||||
initView(appInfo.isDebug)
|
||||
}
|
||||
|
||||
checkNotificationsPermissionState()
|
||||
checkNotificationPiggybackState()
|
||||
|
||||
Timber.i("Settings notifications view was initialized")
|
||||
@ -49,12 +50,17 @@ class NotificationsPresenter @Inject constructor(
|
||||
view?.openNotificationExactAlarmSettings()
|
||||
}
|
||||
}
|
||||
notificationsEnableKey -> {
|
||||
if (isNotificationsEnable && view?.isNotificationPermissionGranted == false) {
|
||||
view?.requestNotificationPermissions()
|
||||
}
|
||||
}
|
||||
isDebugNotificationEnableKey -> {
|
||||
chuckerCollector.showNotification = isDebugNotificationEnable
|
||||
}
|
||||
isNotificationPiggybackEnabledKey -> {
|
||||
if (isNotificationPiggybackEnabled && view?.isNotificationPermissionGranted == false) {
|
||||
view?.openNotificationPermissionDialog()
|
||||
if (isNotificationPiggybackEnabled && view?.isNotificationPiggybackPermissionGranted == false) {
|
||||
view?.openNotificationPiggyBackPermissionDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,9 +76,15 @@ class NotificationsPresenter @Inject constructor(
|
||||
view?.openSystemSettings()
|
||||
}
|
||||
|
||||
fun onNotificationsPermissionResult() {
|
||||
view?.run {
|
||||
setNotificationPreferencesChecked(isNotificationPermissionGranted)
|
||||
}
|
||||
}
|
||||
|
||||
fun onNotificationPiggybackPermissionResult() {
|
||||
view?.run {
|
||||
setNotificationPiggybackPreferenceChecked(isNotificationPermissionGranted)
|
||||
setNotificationPiggybackPreferenceChecked(isNotificationPiggybackPermissionGranted)
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,10 +92,18 @@ class NotificationsPresenter @Inject constructor(
|
||||
view?.setUpcomingLessonsNotificationPreferenceChecked(timetableNotificationHelper.canScheduleExactAlarms())
|
||||
}
|
||||
|
||||
private fun checkNotificationsPermissionState() {
|
||||
if (preferencesRepository.isNotificationsEnable) {
|
||||
view?.run {
|
||||
setNotificationPreferencesChecked(isNotificationPermissionGranted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkNotificationPiggybackState() {
|
||||
if (preferencesRepository.isNotificationPiggybackEnabled) {
|
||||
view?.run {
|
||||
setNotificationPiggybackPreferenceChecked(isNotificationPermissionGranted)
|
||||
setNotificationPiggybackPreferenceChecked(isNotificationPiggybackPermissionGranted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ interface NotificationsView : BaseView {
|
||||
|
||||
val isNotificationPermissionGranted: Boolean
|
||||
|
||||
val isNotificationPiggybackPermissionGranted: Boolean
|
||||
|
||||
fun initView(showDebugNotificationSwitch: Boolean)
|
||||
|
||||
fun showFixSyncDialog()
|
||||
@ -14,10 +16,16 @@ interface NotificationsView : BaseView {
|
||||
|
||||
fun enableNotification(notificationKey: String, enable: Boolean)
|
||||
|
||||
fun openNotificationPermissionDialog()
|
||||
fun requestNotificationPermissions()
|
||||
|
||||
fun openNotificationsPermissionDialog()
|
||||
|
||||
fun openNotificationPiggyBackPermissionDialog()
|
||||
|
||||
fun openNotificationExactAlarmSettings()
|
||||
|
||||
fun setNotificationPreferencesChecked(isChecked: Boolean)
|
||||
|
||||
fun setNotificationPiggybackPreferenceChecked(isChecked: Boolean)
|
||||
|
||||
fun setUpcomingLessonsNotificationPreferenceChecked(isChecked: Boolean)
|
||||
|
@ -8,6 +8,7 @@ import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.get
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -24,6 +25,8 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.capitalise
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.nullableSerializable
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@ -38,7 +41,9 @@ class StudentInfoFragment :
|
||||
lateinit var studentInfoAdapter: StudentInfoAdapter
|
||||
|
||||
override val titleStringId: Int
|
||||
get() = when (requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as? StudentInfoView.Type) {
|
||||
get() = when (
|
||||
requireArguments().nullableSerializable<StudentInfoView.Type>(INFO_TYPE_ARGUMENT_KEY)
|
||||
) {
|
||||
StudentInfoView.Type.PERSONAL -> R.string.account_personal_data
|
||||
StudentInfoView.Type.CONTACT -> R.string.account_contact
|
||||
StudentInfoView.Type.ADDRESS -> R.string.account_address
|
||||
@ -58,13 +63,14 @@ class StudentInfoFragment :
|
||||
|
||||
fun newInstance(type: StudentInfoView.Type, studentWithSemesters: StudentWithSemesters) =
|
||||
StudentInfoFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(INFO_TYPE_ARGUMENT_KEY, type)
|
||||
putSerializable(STUDENT_ARGUMENT_KEY, studentWithSemesters)
|
||||
}
|
||||
arguments = bundleOf(
|
||||
INFO_TYPE_ARGUMENT_KEY to type,
|
||||
STUDENT_ARGUMENT_KEY to studentWithSemesters
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
@ -74,9 +80,9 @@ class StudentInfoFragment :
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentStudentInfoBinding.bind(view)
|
||||
presenter.onAttachView(
|
||||
this,
|
||||
requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as StudentInfoView.Type,
|
||||
requireArguments().getSerializable(STUDENT_ARGUMENT_KEY) as StudentWithSemesters
|
||||
view = this,
|
||||
type = requireArguments().serializable(INFO_TYPE_ARGUMENT_KEY),
|
||||
studentWithSemesters = requireArguments().serializable(STUDENT_ARGUMENT_KEY),
|
||||
)
|
||||
}
|
||||
|
||||
@ -153,7 +159,6 @@ class StudentInfoFragment :
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun showFamilyTypeData(studentInfo: StudentInfo) {
|
||||
val items = buildList {
|
||||
add(studentInfo.firstGuardian?.let {
|
||||
|
@ -8,14 +8,12 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.databinding.DialogTimetableBinding
|
||||
import io.github.wulkanowy.utils.capitalise
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.*
|
||||
import java.time.Instant
|
||||
|
||||
class TimetableDialog : DialogFragment() {
|
||||
@ -28,17 +26,15 @@ class TimetableDialog : DialogFragment() {
|
||||
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: Timetable) = TimetableDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
fun newInstance(lesson: Timetable) = TimetableDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to lesson)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
lesson = getSerializable(ARGUMENT_KEY) as Timetable
|
||||
}
|
||||
lesson = requireArguments().serializable(ARGUMENT_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -7,6 +7,7 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@ -39,9 +40,7 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
||||
private const val ARGUMENT_DATE_KEY = "ARGUMENT_DATE"
|
||||
|
||||
fun newInstance(date: LocalDate? = null) = TimetableFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
date?.let { putLong(ARGUMENT_DATE_KEY, it.toEpochDay()) }
|
||||
}
|
||||
arguments = date?.let { bundleOf(ARGUMENT_DATE_KEY to it.toEpochDay()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +50,7 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
||||
|
||||
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
@ -4,10 +4,12 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.databinding.DialogLessonCompletedBinding
|
||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||
import io.github.wulkanowy.utils.serializable
|
||||
|
||||
class CompletedLessonDialog : DialogFragment() {
|
||||
|
||||
@ -19,17 +21,15 @@ class CompletedLessonDialog : DialogFragment() {
|
||||
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: CompletedLesson) = CompletedLessonDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
fun newInstance(lesson: CompletedLesson) = CompletedLessonDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to lesson)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
completedLesson = getSerializable(ARGUMENT_KEY) as CompletedLesson
|
||||
}
|
||||
completedLesson = requireArguments().serializable(ARGUMENT_KEY)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -0,0 +1,9 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
abstract class BaseRemoteConfigHelper {
|
||||
|
||||
open fun initialize() = Unit
|
||||
|
||||
open val userAgentTemplate: String
|
||||
get() = RemoteConfigDefaults.USER_AGENT_TEMPLATE.value as String
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import java.io.Serializable
|
||||
|
||||
inline fun <reified T : Serializable> Bundle.serializable(key: String): T = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java)!!
|
||||
else -> @Suppress("DEPRECATION") getSerializable(key) as T
|
||||
}
|
||||
|
||||
inline fun <reified T : Serializable> Bundle.nullableSerializable(key: String): T? = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java)
|
||||
else -> @Suppress("DEPRECATION") getSerializable(key) as T?
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION", "UNCHECKED_CAST")
|
||||
inline fun <reified T : Serializable> Bundle.parcelableArray(key: String): Array<T>? = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getParcelableArray(key, T::class.java)
|
||||
else -> getParcelableArray(key) as Array<T>?
|
||||
}
|
||||
|
||||
inline fun <reified T : Serializable> Intent.serializable(key: String): T = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java)!!
|
||||
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T
|
||||
}
|
||||
|
||||
inline fun <reified T : Serializable> Intent.nullableSerializable(key: String): T? = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java)
|
||||
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T?
|
||||
}
|
@ -1,11 +1,15 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.Settings
|
||||
import io.github.wulkanowy.BuildConfig
|
||||
import timber.log.Timber
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
|
||||
@ -86,6 +90,23 @@ fun Context.openDialer(phone: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.openNotificationSettings() {
|
||||
val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
|
||||
putExtra("android.provider.extra.APP_PACKAGE", packageName)
|
||||
}
|
||||
} else {
|
||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", packageName, null)
|
||||
}
|
||||
}
|
||||
try {
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.shareText(text: String, subject: String?) {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
|
@ -0,0 +1,8 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
enum class RemoteConfigDefaults(val value: Any) {
|
||||
USER_AGENT_TEMPLATE(""),
|
||||
;
|
||||
|
||||
val key get() = name.lowercase()
|
||||
}
|
@ -1,7 +1,15 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import androidx.core.text.parseAsHtml
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
|
||||
inline fun String?.ifNullOrBlank(defaultValue: () -> String) =
|
||||
if (isNullOrBlank()) defaultValue() else this
|
||||
|
||||
fun String.capitalise() =
|
||||
replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
|
||||
replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
|
||||
|
||||
fun String.parseUonetHtml() = this
|
||||
.let(StringEscapeUtils::unescapeHtml4)
|
||||
.replace("\n", "<br/>")
|
||||
.parseAsHtml()
|
||||
|
@ -1,8 +1,7 @@
|
||||
Wersja 1.8.3
|
||||
Wersja 1.9.2
|
||||
|
||||
- naprawiliśmy logowanie dla użytkowników systemu Resman Rzeszów
|
||||
- dodaliśmy wsparcie dla nowej platformy z Tomaszowa Mazowieckiego
|
||||
- poprawiliśmy dopasowywanie skrzynek pocztowych do uczniów
|
||||
- naprawiliśmy literówkę w tytule wiadomości z szablonem usprawiedliwienia
|
||||
- naprawiliśmy oznaczanie wiadomości jako odczytanych (problem dotyczył głównie kont rodziców z wieloma dziećmi w tej samej szkole)
|
||||
- naprawiliśmy zapisywanie załączników do wiadomości w sytuacji, gdy ten sam załącznik był dodany do więcej niż jednej wiadomości
|
||||
- usprawniliśmy ekran z wyborem uczniów i wpisywaniem symbolu przy pierwszym logowaniu
|
||||
|
||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||
|
@ -0,0 +1,20 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1926"
|
||||
android:viewportHeight="1926">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M920,1357.7v-184.5h42.7q19.3,0.3 34.3,6.6 15.2,6.2 25.6,17.5 10.6,11.2 16,26.9 5.6,15.6 5.7, 34.6v13.5q-0.1,19 -5.7,34.7 -5.4,15.6 -16,26.9 -10.4,11.2 -25.6,17.5 -15,6.2 -34.3,6.3zM943.8,1192.5v146.1h18.9q15, -0.1 25.8,-5.2 11,-5.2 18,-14 7.3,-9 10.7,-21 3.5,-12 3.6,-26v-13.9q0,-14 -3.6,-26t-10.7,-20.7q-7,-9 -18,-14 -10.9,-5 -25.8,-5.3zM1179.5,1272.5h-77v65.4h89.8v19.9L1079,1357.8v-184.6h112.2v20h-88.7v59.4h77zM1288.9, 1320l43.6,-146.8h25l-58.2,184.6h-20.4l-58.3,-184.6h25z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#000" />
|
||||
<clip-path android:pathData="M 0 0 L 1926 0 L 1926 1111 L 850 1111 L 850 1926 L 0 1926 z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M1082,1260v4l-6,73c0,5 -5,9 -10,9H879c-4,0 -8,-2 -10,-5l-31,-58 -29,-71v-8l48,-94c2,-3 2,-5 1, -8l-24,-65c-1,-2 0,-5 1,-7l39,-84c4,-8 17,-8 20,1l12,38 38,101c1,2 3,5 6,6l80,27c3,1 6,3 7,6l45,135zM852,717c0,-23 15,-43 35,-52l-2,-8c0,-11 13,-20 29,-20h1a38,38 0,0 1,36 -18l3,-2c11,-16 37,-27 68,-27 12,0 23,2 32,4 3,-3 8,-6 14,-6s12,3 14,8c8,-9 22,-16 38,-16 25,0 45,16 45,36l-1,7 3,4c16,6 27,17 27,30 0,14 -15,27 -34,32 -2,0 -3,1 -3,3 0,11 -11,21 -26,22v1c0,22 -41,40 -92,40 -11,0 -21,-1 -31,-3v1c0,9 -12,16 -26,16h-2l2,7c0,14 -18,25 -40,25h-7l-4, 2c-2,7 -8,12 -15,12 -9,0 -16,-7 -16,-16 0,-5 1,-8 4,-11l1,-4c-2,-3 -2,-5 -2,-8 0,-2 -2,-3 -3,-3 -27,-6 -48,-29 -48,-56z" />
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M1336,1346h-125c-3,0 -6,-1 -8,-4l-84,-118 -1,-2 -42,-122 -5,-5 -102,-52 -5,-6 -15,-75 -2,-3 -34,-51c-2,-3 -2,-6 -1,-9l21,-45c2,-4 5,-6 9,-6l39,-2c3,0 5,-1 6,-3l29,-22c5,-4 14,-2 17,4l98,201 1,2 13,81 2,5 197,216c6,6 1,16 -8,16zM801,1302c1,3 1,6 -1,8l-19,31c-2,3 -5,5 -9,5H591c-8,0 -13,-8 -9,-15l116,-171 2,-3 53, -228c1,-3 3,-5 5,-6l72,-40c3,-1 5,-4 5,-7l10,-42c2,-9 15,-11 20,-3l3,6c2,2 2,5 1,8l-66,190v5l16,91v5l-40,88v6l22,72z" />
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_launcher_foreground_mono.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1926"
|
||||
android:viewportHeight="1926">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M1082,1260v4l-6,73c0,5 -5,9 -10,9H879c-4,0 -8,-2 -10,-5l-31,-58 -29,-71v-8l48,-94c2,-3 2,-5 1, -8l-24,-65c-1,-2 0,-5 1,-7l39,-84c4,-8 17,-8 20,1l12,38 38,101c1,2 3,5 6,6l80,27c3,1 6,3 7,6l45,135zM852,717c0,-23 15,-43 35,-52l-2,-8c0,-11 13,-20 29,-20h1a38,38 0,0 1,36 -18l3,-2c11,-16 37,-27 68,-27 12,0 23,2 32,4 3,-3 8,-6 14,-6s12,3 14,8c8,-9 22,-16 38,-16 25,0 45,16 45,36l-1,7 3,4c16,6 27,17 27,30 0,14 -15,27 -34,32 -2,0 -3,1 -3,3 0,11 -11,21 -26,22v1c0,22 -41,40 -92,40 -11,0 -21,-1 -31,-3v1c0,9 -12,16 -26,16h-2l2,7c0,14 -18,25 -40,25h-7l-4, 2c-2,7 -8,12 -15,12 -9,0 -16,-7 -16,-16 0,-5 1,-8 4,-11l1,-4c-2,-3 -2,-5 -2,-8 0,-2 -2,-3 -3,-3 -27,-6 -48,-29 -48,-56z" />
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M1336,1346h-125c-3,0 -6,-1 -8,-4l-84,-118 -1,-2 -42,-122 -5,-5 -102,-52 -5,-6 -15,-75 -2,-3 -34,-51c-2,-3 -2,-6 -1,-9l21,-45c2,-4 5,-6 9,-6l39,-2c3,0 5,-1 6,-3l29,-22c5,-4 14,-2 17,4l98,201 1,2 13,81 2,5 197,216c6,6 1,16 -8,16zM801,1302c1,3 1,6 -1,8l-19,31c-2,3 -5,5 -9,5H591c-8,0 -13,-8 -9,-15l116,-171 2,-3 53, -228c1,-3 3,-5 5,-6l72,-40c3,-1 5,-4 5,-7l10,-42c2,-9 15,-11 20,-3l3,6c2,2 2,5 1,8l-66,190v5l16,91v5l-40,88v6l22,72z" />
|
||||
</vector>
|
@ -40,7 +40,7 @@
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginTop="28dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:text="@string/all_title"
|
||||
android:text="@string/conference_place"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@ -71,7 +71,7 @@
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:text="@string/all_subject"
|
||||
android:text="@string/conference_topic"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -111,9 +111,11 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accountDetailsName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -122,9 +124,11 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accountDetailsSchool"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -3,92 +3,14 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/loginStudentSelectProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.modules.login.studentselect.LoginStudentSelectFragment">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/loginStudentSelectContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/loginStudentSelectContact"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<View
|
||||
android:id="@+id/loginStudentSelectContactTopDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginStudentSelectContactHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginLeft="32dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginRight="32dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/login_contact_header"
|
||||
android:textSize="14sp"
|
||||
app:fontFamily="sans-serif-medium" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/loginStudentSelectContactButtons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginStudentSelectContactEmail"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/login_contact_email"
|
||||
app:icon="@drawable/ic_more_messages" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginStudentSelectContactDiscord"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/login_contact_discord"
|
||||
app:icon="@drawable/ic_about_discord" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/loginStudentSelectContactBottomDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginStudentSelectHeader"
|
||||
android:layout_width="match_parent"
|
||||
@ -106,22 +28,24 @@
|
||||
app:layout_constraintBottom_toTopOf="@id/loginStudentSelectRecycler"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/loginStudentSelectContact"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/loginStudentSelectRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="144dp"
|
||||
android:fadingEdge="vertical"
|
||||
android:fadingEdgeLength="100dp"
|
||||
android:requiresFadingEdge="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/loginStudentSelectSignIn"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_max="432dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/loginStudentSelectHeader"
|
||||
tools:itemCount="6"
|
||||
tools:listitem="@layout/item_login_student_select" />
|
||||
tools:itemCount="33"
|
||||
tools:listitem="@layout/item_login_student_select_student" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginStudentSelectSignIn"
|
||||
@ -136,4 +60,12 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/loginStudentSelectRecycler" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/loginStudentSelectProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
|
69
app/src/main/res/layout/fragment_notifications.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.modules.notifications.NotificationsFragment">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/notifications_header_icon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:src="@drawable/ic_settings_notifications"
|
||||
app:layout_constraintBottom_toTopOf="@id/notifications_header_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.4"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notifications_header_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/notifications_header_title"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/notifications_header_description"
|
||||
app:layout_constraintTop_toBottomOf="@id/notifications_header_icon"
|
||||
app:lineHeight="30sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notifications_header_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/notifications_header_description"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/notifications_skip"
|
||||
app:layout_constraintTop_toBottomOf="@id/notifications_header_title"
|
||||
app:lineHeight="24sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notifications_skip"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/notifications_skip"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notifications_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/notifications_enable"
|
||||
app:icon="@drawable/ic_settings_notifications"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -26,27 +26,31 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dashboard_account_item_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/dashboard_account_item_avatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="John Smith" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dashboard_account_item_school_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/dashboard_account_item_avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/dashboard_account_item_name"
|
||||
tools:text="Szkoła Wulkanowego " />
|
||||
tools:text="Szkoła Wulkanowego" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
@ -56,4 +60,4 @@
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_student_select_empty_symbol_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/login_other_search_locations"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/login_student_select_empty_symbol_chevron"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:rotation="90"
|
||||
android:src="@drawable/ic_chevron_right"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?android:textColorPrimary"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_student_select_header_school_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Publiczna szkoła Wulkanowego" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_student_select_header_school_details"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="@string/login_no_active_student"
|
||||
android:textColor="?colorTimetableChange"
|
||||
app:layout_constraintTop_toBottomOf="@id/login_student_select_header_school_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_student_select_header_school_error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="?colorError"
|
||||
app:layout_constraintTop_toBottomOf="@id/login_student_select_header_school_details"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_student_select_header_symbol_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="powiatjaroslawski" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_student_select_header_symbol_username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
app:layout_constraintTop_toBottomOf="@id/login_student_select_header_symbol_value"
|
||||
tools:text="Jan Kowalski" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_student_select_header_symbol_error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="?colorError"
|
||||
app:layout_constraintTop_toBottomOf="@id/login_student_select_header_symbol_username"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
61
app/src/main/res/layout/item_login_student_select_help.xml
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/login_student_select_help_symbol"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/login_symbol_enter"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_student_select_help_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/login_contact_header"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/login_student_select_help_mail"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/login_student_select_help_symbol" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/login_student_select_help_mail"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:contentDescription="@string/login_contact_email"
|
||||
app:icon="@drawable/ic_more_messages"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/login_student_select_help_title"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/login_student_select_help_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/login_student_select_help_symbol" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/login_student_select_help_discord"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:contentDescription="@string/login_contact_discord"
|
||||
app:icon="@drawable/ic_about_discord"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/login_student_select_help_mail"
|
||||
app:layout_constraintTop_toTopOf="@id/login_student_select_help_mail" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -4,7 +4,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:minHeight="72dp"
|
||||
android:minHeight="56dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
tools:context=".ui.modules.login.studentselect.LoginStudentSelectAdapter">
|
||||
@ -14,9 +14,10 @@
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="28dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:clickable="false"
|
||||
tools:text=" " />
|
||||
|
||||
<TextView
|
||||
@ -32,34 +33,18 @@
|
||||
tools:text="@tools:sample/full_names" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginItemSchool"
|
||||
android:id="@+id/loginItemSignedIn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/loginItemName"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_toEndOf="@id/loginItemCheck"
|
||||
android:ellipsize="end"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginItemSignedIn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="20dp"
|
||||
android:layout_below="@id/loginItemSchool"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_toEndOf="@id/loginItemCheck"
|
||||
android:ellipsize="end"
|
||||
android:enabled="false"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="1"
|
||||
android:minHeight="20dp"
|
||||
android:text="@string/login_signed_in"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
android:textSize="14sp" />
|
||||
</RelativeLayout>
|
@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/colorPrimary" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground_mono" />
|
||||
</adaptive-icon>
|
||||
|
@ -1,5 +0,0 @@
|
||||
<?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" />
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 12 KiB |