Merge branch 'release/0.25.0'
15
.idea/codeStyles/Project.xml
generated
@ -18,18 +18,9 @@
|
|||||||
</option>
|
</option>
|
||||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||||
<option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="false" />
|
|
||||||
<option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="false" />
|
|
||||||
<option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="false" />
|
|
||||||
<option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="false" />
|
|
||||||
<option name="CONTINUATION_INDENT_IN_SUPERTYPE_LISTS" value="false" />
|
|
||||||
<option name="CONTINUATION_INDENT_IN_IF_CONDITIONS" value="false" />
|
|
||||||
<option name="CONTINUATION_INDENT_IN_ELVIS" value="false" />
|
|
||||||
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<MarkdownNavigatorCodeStyleSettings>
|
|
||||||
<option name="RIGHT_MARGIN" value="72" />
|
|
||||||
</MarkdownNavigatorCodeStyleSettings>
|
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
@ -143,13 +134,11 @@
|
|||||||
</arrangement>
|
</arrangement>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="kotlin">
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
|
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
|
||||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
|
||||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
|
||||||
<option name="EXTENDS_LIST_WRAP" value="1" />
|
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
|
@ -11,19 +11,22 @@ apply from: 'hooks.gradle'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 30
|
||||||
buildToolsVersion '30.0.2'
|
buildToolsVersion '30.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "io.github.wulkanowy"
|
applicationId "io.github.wulkanowy"
|
||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 17
|
minSdkVersion 17
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 81
|
versionCode 82
|
||||||
versionName "0.24.3"
|
versionName "0.25.0"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
resValue "string", "app_name", "Wulkanowy"
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
|
resValue "string", "app_name", "Wulkanowy"
|
||||||
|
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
|
||||||
|
|
||||||
manifestPlaceholders = [
|
manifestPlaceholders = [
|
||||||
firebase_enabled: project.hasProperty("enableFirebase")
|
firebase_enabled: project.hasProperty("enableFirebase")
|
||||||
]
|
]
|
||||||
@ -126,19 +129,20 @@ play {
|
|||||||
serviceAccountCredentials = file('key.p12')
|
serviceAccountCredentials = file('key.p12')
|
||||||
defaultToAppBundles = false
|
defaultToAppBundles = false
|
||||||
track = 'alpha'
|
track = 'alpha'
|
||||||
updatePriority = 5
|
updatePriority = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
work_manager = "2.4.0"
|
work_manager = "2.5.0"
|
||||||
room = "2.2.6"
|
work_hilt = "1.0.0-alpha03"
|
||||||
|
room = "2.3.0-beta01"
|
||||||
chucker = "3.4.0"
|
chucker = "3.4.0"
|
||||||
mockk = "1.10.5"
|
mockk = "1.10.5"
|
||||||
moshi = "1.11.0"
|
moshi = "1.11.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "io.github.wulkanowy:sdk:0.24.1"
|
implementation "io.github.wulkanowy:sdk:0.25.0"
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
|
||||||
|
|
||||||
@ -159,10 +163,9 @@ dependencies {
|
|||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
||||||
implementation "com.google.android.material:material:1.2.1"
|
implementation "com.google.android.material:material:1.3.0-rc01"
|
||||||
implementation "com.github.wulkanowy:material-chips-input:2.1.1"
|
implementation "com.github.wulkanowy:material-chips-input:2.1.1"
|
||||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||||
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
|
||||||
|
|
||||||
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
||||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||||
@ -175,12 +178,12 @@ dependencies {
|
|||||||
|
|
||||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||||
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||||
implementation 'androidx.hilt:hilt-work:1.0.0-alpha02'
|
implementation "androidx.hilt:hilt-work:$work_hilt"
|
||||||
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
|
kapt "androidx.hilt:hilt-compiler:$work_hilt"
|
||||||
|
|
||||||
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
|
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
|
||||||
implementation "com.ncapdevi:frag-nav:3.3.0"
|
implementation "com.ncapdevi:frag-nav:3.3.0"
|
||||||
implementation "com.github.YarikSOffice:lingver:1.2.2"
|
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
||||||
|
|
||||||
implementation "com.squareup.moshi:moshi:$moshi"
|
implementation "com.squareup.moshi:moshi:$moshi"
|
||||||
implementation "com.squareup.moshi:moshi-adapters:$moshi"
|
implementation "com.squareup.moshi:moshi-adapters:$moshi"
|
||||||
@ -194,7 +197,7 @@ dependencies {
|
|||||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||||
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
||||||
|
|
||||||
playImplementation platform('com.google.firebase:firebase-bom:26.3.0')
|
playImplementation platform('com.google.firebase:firebase-bom:26.4.0')
|
||||||
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
||||||
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx'
|
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx'
|
||||||
playImplementation "com.google.firebase:firebase-inappmessaging-ktx"
|
playImplementation "com.google.firebase:firebase-inappmessaging-ktx"
|
||||||
@ -204,7 +207,7 @@ dependencies {
|
|||||||
playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
||||||
|
|
||||||
hmsImplementation 'com.huawei.hms:hianalytics:5.1.0.301'
|
hmsImplementation 'com.huawei.hms:hianalytics:5.1.0.301'
|
||||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.2.301'
|
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.5.0.200'
|
||||||
|
|
||||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||||
|
|
||||||
@ -214,6 +217,7 @@ dependencies {
|
|||||||
testImplementation "junit:junit:4.13.1"
|
testImplementation "junit:junit:4.13.1"
|
||||||
testImplementation "io.mockk:mockk:$mockk"
|
testImplementation "io.mockk:mockk:$mockk"
|
||||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
|
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
|
||||||
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
|
||||||
androidTestImplementation "androidx.test:core:1.3.0"
|
androidTestImplementation "androidx.test:core:1.3.0"
|
||||||
androidTestImplementation "androidx.test:runner:1.3.0"
|
androidTestImplementation "androidx.test:runner:1.3.0"
|
||||||
|
2136
app/schemas/io.github.wulkanowy.data.db.AppDatabase/31.json
Normal file
2142
app/schemas/io.github.wulkanowy.data.db.AppDatabase/32.json
Normal file
@ -1,11 +0,0 @@
|
|||||||
package io.github.wulkanowy.data
|
|
||||||
|
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
|
|
||||||
class TestDispatchersProvider : DispatchersProvider() {
|
|
||||||
|
|
||||||
override val backgroundThread: CoroutineDispatcher
|
|
||||||
get() = Dispatchers.Unconfined
|
|
||||||
}
|
|
19
app/src/debug/res/drawable-anydpi-v24/ic_stat_timetable.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group
|
||||||
|
android:scaleX="0.92"
|
||||||
|
android:scaleY="0.92"
|
||||||
|
android:translateX="0.96"
|
||||||
|
android:translateY="0.96">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFF"
|
||||||
|
android:pathData="M3.9512,2A2,2 0,0 0,2 4L2,18A2,2 0,0 0,4 20L10.0996,20C11.3596,21.24 13.09,22 15,22A7,7 0,0 0,15.7988 21.9551L15.7988,19.7832A4.85,4.85 0,0 1,15 19.8496C12.32,19.8496 10.1504,17.68 10.1504,15A4.85,4.85 0,0 1,15 10.1504C17.4677,10.1504 19.4978,11.9912 19.8047,14.375C20.566,14.3758 21.3108,14.5325 21.9922,14.834C21.9491,12.9905 21.2036,11.3226 20,10.0996L20,4A2,2 0,0 0,18 2L4,2A2,2 0,0 0,3.9512 2zM4,5L10,5L10,8L4,8L4,5zM12,5L18,5L18,8L12,8L12,5zM4,10L10.0996,10C9.2596,10.82 8.6291,11.85 8.2891,13L4,13L4,10zM14,12L14,15.6895L15.7988,16.7266L15.7988,14.9922L15.5,14.8203L15.5,12L14,12zM4,15L8,15C8,16.07 8.2399,17.09 8.6699,18L4,18L4,15z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFF"
|
||||||
|
android:pathData="m17.298,24v-8.1249h2.5c0.7143,0 1.3523,0.1618 1.9141,0.4855 0.5655,0.3199 1.0063,0.7775 1.3225,1.3728 0.3162,0.5915 0.4743,1.2649 0.4743,2.0201v0.3739c0,0.7552 -0.1562,1.4267 -0.4687,2.0145 -0.3088,0.5878 -0.7459,1.0435 -1.3114,1.3672C21.1633,23.8326 20.5253,23.9963 19.8148,24ZM18.9721,17.2311v5.4241h0.8091c0.6548,0 1.1551,-0.2139 1.5011,-0.6417 0.346,-0.4278 0.5227,-1.0398 0.5301,-1.8359v-0.4297c0,-0.8259 -0.1711,-1.4509 -0.5134,-1.875 -0.3423,-0.4278 -0.8426,-0.6417 -1.5011,-0.6417z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
BIN
app/src/debug/res/drawable-hdpi/ic_stat_timetable.png
Normal file
After Width: | Height: | Size: 426 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_timetable.png
Normal file
After Width: | Height: | Size: 335 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_timetable.png
Normal file
After Width: | Height: | Size: 519 B |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_timetable.png
Normal file
After Width: | Height: | Size: 700 B |
@ -18,6 +18,18 @@
|
|||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
</intent>
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<data android:scheme="mailto" />
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<data android:scheme="tel" />
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<data android:scheme="geo" />
|
||||||
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||||||
import android.util.Log.DEBUG
|
import android.util.Log.DEBUG
|
||||||
import android.util.Log.INFO
|
import android.util.Log.INFO
|
||||||
import android.util.Log.VERBOSE
|
import android.util.Log.VERBOSE
|
||||||
|
import android.webkit.WebView
|
||||||
import androidx.hilt.work.HiltWorkerFactory
|
import androidx.hilt.work.HiltWorkerFactory
|
||||||
import androidx.multidex.MultiDex
|
import androidx.multidex.MultiDex
|
||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
@ -47,17 +48,18 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
Lingver.init(this)
|
|
||||||
themeManager.applyDefaultTheme()
|
|
||||||
|
|
||||||
|
initializeAppLanguage()
|
||||||
|
themeManager.applyDefaultTheme()
|
||||||
initLogging()
|
initLogging()
|
||||||
logCurrentLanguage()
|
fixWebViewLocale()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initLogging() {
|
private fun initLogging() {
|
||||||
if (appInfo.isDebug) {
|
if (appInfo.isDebug) {
|
||||||
Timber.plant(DebugLogTree())
|
Timber.plant(DebugLogTree())
|
||||||
Timber.plant(FileLoggerTree.Builder()
|
Timber.plant(
|
||||||
|
FileLoggerTree.Builder()
|
||||||
.withFileName("wulkanowy.%g.log")
|
.withFileName("wulkanowy.%g.log")
|
||||||
.withDirName(applicationContext.filesDir.absolutePath)
|
.withDirName(applicationContext.filesDir.absolutePath)
|
||||||
.withFileLimit(10)
|
.withFileLimit(10)
|
||||||
@ -71,14 +73,20 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||||||
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logCurrentLanguage() {
|
private fun initializeAppLanguage() {
|
||||||
val newLang = if (preferencesRepository.appLanguage == "system") {
|
Lingver.init(this)
|
||||||
appInfo.systemLanguage
|
|
||||||
|
if (preferencesRepository.appLanguage == "system") {
|
||||||
|
Lingver.getInstance().setFollowSystemLocale(this)
|
||||||
|
analyticsHelper.logEvent("language", "startup" to appInfo.systemLanguage)
|
||||||
} else {
|
} else {
|
||||||
preferencesRepository.appLanguage
|
analyticsHelper.logEvent("language", "startup" to preferencesRepository.appLanguage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsHelper.logEvent("language", "startup" to newLang)
|
private fun fixWebViewLocale() {
|
||||||
|
//https://stackoverflow.com/questions/40398528/android-webview-language-changes-abruptly-on-android-7-0-and-above
|
||||||
|
WebView(this).destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getWorkManagerConfiguration() = Configuration.Builder()
|
override fun getWorkManagerConfiguration() = Configuration.Builder()
|
||||||
|
@ -33,7 +33,8 @@ internal class RepositoryModule {
|
|||||||
setSimpleHttpLogger { Timber.d(it) }
|
setSimpleHttpLogger { Timber.d(it) }
|
||||||
|
|
||||||
// for debug only
|
// for debug only
|
||||||
addInterceptor(ChuckerInterceptor.Builder(context)
|
addInterceptor(
|
||||||
|
ChuckerInterceptor.Builder(context)
|
||||||
.collector(chuckerCollector)
|
.collector(chuckerCollector)
|
||||||
.alwaysReadResponseBody(true)
|
.alwaysReadResponseBody(true)
|
||||||
.build(), network = true
|
.build(), network = true
|
||||||
@ -43,7 +44,10 @@ internal class RepositoryModule {
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideChuckerCollector(@ApplicationContext context: Context, prefRepository: PreferencesRepository): ChuckerCollector {
|
fun provideChuckerCollector(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
|
prefRepository: PreferencesRepository
|
||||||
|
): ChuckerCollector {
|
||||||
return ChuckerCollector(
|
return ChuckerCollector(
|
||||||
context = context,
|
context = context,
|
||||||
showNotification = prefRepository.isDebugNotificationEnable,
|
showNotification = prefRepository.isDebugNotificationEnable,
|
||||||
@ -53,7 +57,10 @@ internal class RepositoryModule {
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideDatabase(@ApplicationContext context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider)
|
fun provideDatabase(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
|
sharedPrefProvider: SharedPrefProvider,
|
||||||
|
) = AppDatabase.newInstance(context, sharedPrefProvider)
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
@ -65,7 +72,8 @@ internal class RepositoryModule {
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
@ -89,7 +97,8 @@ internal class RepositoryModule {
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideGradeSemesterStatisticsDao(database: AppDatabase) = database.gradeSemesterStatisticsDao
|
fun provideGradeSemesterStatisticsDao(database: AppDatabase) =
|
||||||
|
database.gradeSemesterStatisticsDao
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
@ -166,4 +175,8 @@ internal class RepositoryModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideTimetableAdditionalDao(database: AppDatabase) = database.timetableAdditionalDao
|
fun provideTimetableAdditionalDao(database: AppDatabase) = database.timetableAdditionalDao
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideStudentInfoDao(database: AppDatabase) = database.studentInfoDao
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import io.github.wulkanowy.data.db.dao.ReportingUnitDao
|
|||||||
import io.github.wulkanowy.data.db.dao.SchoolDao
|
import io.github.wulkanowy.data.db.dao.SchoolDao
|
||||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
||||||
import io.github.wulkanowy.data.db.dao.SubjectDao
|
import io.github.wulkanowy.data.db.dao.SubjectDao
|
||||||
import io.github.wulkanowy.data.db.dao.TeacherDao
|
import io.github.wulkanowy.data.db.dao.TeacherDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||||
@ -53,6 +54,7 @@ import io.github.wulkanowy.data.db.entities.ReportingUnit
|
|||||||
import io.github.wulkanowy.data.db.entities.School
|
import io.github.wulkanowy.data.db.entities.School
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||||
import io.github.wulkanowy.data.db.entities.Subject
|
import io.github.wulkanowy.data.db.entities.Subject
|
||||||
import io.github.wulkanowy.data.db.entities.Teacher
|
import io.github.wulkanowy.data.db.entities.Teacher
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
@ -80,6 +82,8 @@ import io.github.wulkanowy.data.db.migrations.Migration28
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration29
|
import io.github.wulkanowy.data.db.migrations.Migration29
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration30
|
import io.github.wulkanowy.data.db.migrations.Migration30
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration31
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration32
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||||
@ -116,6 +120,7 @@ import javax.inject.Singleton
|
|||||||
School::class,
|
School::class,
|
||||||
Conference::class,
|
Conference::class,
|
||||||
TimetableAdditional::class,
|
TimetableAdditional::class,
|
||||||
|
StudentInfo::class,
|
||||||
],
|
],
|
||||||
version = AppDatabase.VERSION_SCHEMA,
|
version = AppDatabase.VERSION_SCHEMA,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
@ -124,7 +129,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 30
|
const val VERSION_SCHEMA = 32
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
||||||
return arrayOf(
|
return arrayOf(
|
||||||
@ -157,6 +162,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration28(),
|
Migration28(),
|
||||||
Migration29(),
|
Migration29(),
|
||||||
Migration30(),
|
Migration30(),
|
||||||
|
Migration31(),
|
||||||
|
Migration32()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,4 +226,6 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
abstract val conferenceDao: ConferenceDao
|
abstract val conferenceDao: ConferenceDao
|
||||||
|
|
||||||
abstract val timetableAdditionalDao: TimetableAdditionalDao
|
abstract val timetableAdditionalDao: TimetableAdditionalDao
|
||||||
|
|
||||||
|
abstract val studentInfoDao: StudentInfoDao
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ import androidx.room.Insert
|
|||||||
import androidx.room.OnConflictStrategy.ABORT
|
import androidx.room.OnConflictStrategy.ABORT
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
|
import androidx.room.Update
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentNick
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -20,6 +22,9 @@ interface StudentDao {
|
|||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(student: Student)
|
suspend fun delete(student: Student)
|
||||||
|
|
||||||
|
@Update(entity = Student::class)
|
||||||
|
suspend fun update(studentNick: StudentNick)
|
||||||
|
|
||||||
@Query("SELECT * FROM Students WHERE is_current = 1")
|
@Query("SELECT * FROM Students WHERE is_current = 1")
|
||||||
suspend fun loadCurrent(): Student?
|
suspend fun loadCurrent(): Student?
|
||||||
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Dao
|
||||||
|
interface StudentInfoDao : BaseDao<StudentInfo> {
|
||||||
|
|
||||||
|
@Query("SELECT * FROM StudentInfo WHERE student_id = :studentId")
|
||||||
|
fun loadStudentInfo(studentId: Int): Flow<StudentInfo?>
|
||||||
|
}
|
@ -7,7 +7,13 @@ import androidx.room.PrimaryKey
|
|||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)])
|
@Entity(
|
||||||
|
tableName = "Students",
|
||||||
|
indices = [Index(
|
||||||
|
value = ["email", "symbol", "student_id", "school_id", "class_id"],
|
||||||
|
unique = true
|
||||||
|
)]
|
||||||
|
)
|
||||||
data class Student(
|
data class Student(
|
||||||
|
|
||||||
@ColumnInfo(name = "scrapper_base_url")
|
@ColumnInfo(name = "scrapper_base_url")
|
||||||
@ -52,7 +58,7 @@ data class Student(
|
|||||||
@ColumnInfo(name = "school_id")
|
@ColumnInfo(name = "school_id")
|
||||||
val schoolSymbol: String,
|
val schoolSymbol: String,
|
||||||
|
|
||||||
@ColumnInfo(name ="school_short")
|
@ColumnInfo(name = "school_short")
|
||||||
val schoolShortName: String,
|
val schoolShortName: String,
|
||||||
|
|
||||||
@ColumnInfo(name = "school_name")
|
@ColumnInfo(name = "school_name")
|
||||||
@ -73,4 +79,6 @@ data class Student(
|
|||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
var id: Long = 0
|
var id: Long = 0
|
||||||
|
|
||||||
|
var nick = ""
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import io.github.wulkanowy.data.enums.Gender
|
||||||
|
import java.io.Serializable
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
@Entity(tableName = "StudentInfo")
|
||||||
|
data class StudentInfo(
|
||||||
|
|
||||||
|
@ColumnInfo(name = "student_id")
|
||||||
|
val studentId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "full_name")
|
||||||
|
val fullName: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "first_name")
|
||||||
|
val firstName: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "second_name")
|
||||||
|
val secondName: String,
|
||||||
|
|
||||||
|
val surname: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "birth_date")
|
||||||
|
val birthDate: LocalDate,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "birth_place")
|
||||||
|
val birthPlace: String,
|
||||||
|
|
||||||
|
val gender: Gender,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "has_polish_citizenship")
|
||||||
|
val hasPolishCitizenship: Boolean,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "family_name")
|
||||||
|
val familyName: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "parents_names")
|
||||||
|
val parentsNames: String,
|
||||||
|
|
||||||
|
val address: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "registered_address")
|
||||||
|
val registeredAddress: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "correspondence_address")
|
||||||
|
val correspondenceAddress: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "phone_number")
|
||||||
|
val phoneNumber: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "cell_phone_number")
|
||||||
|
val cellPhoneNumber: String,
|
||||||
|
|
||||||
|
val email: String,
|
||||||
|
|
||||||
|
@Embedded(prefix = "first_guardian_")
|
||||||
|
val firstGuardian: StudentGuardian,
|
||||||
|
|
||||||
|
@Embedded(prefix = "second_guardian_")
|
||||||
|
val secondGuardian: StudentGuardian
|
||||||
|
|
||||||
|
) : Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
data class StudentGuardian(
|
||||||
|
|
||||||
|
@ColumnInfo(name = "full_name")
|
||||||
|
val fullName: String,
|
||||||
|
|
||||||
|
val kinship: String,
|
||||||
|
|
||||||
|
val address: String,
|
||||||
|
|
||||||
|
val phones: String,
|
||||||
|
|
||||||
|
val email: String
|
||||||
|
) : Serializable
|
@ -0,0 +1,16 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class StudentNick(
|
||||||
|
|
||||||
|
val nick: String
|
||||||
|
|
||||||
|
) : Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration31 : Migration(30, 31) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS StudentInfo (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
student_id INTEGER NOT NULL,
|
||||||
|
full_name TEXT NOT NULL,
|
||||||
|
first_name TEXT NOT NULL,
|
||||||
|
second_name TEXT NOT NULL,
|
||||||
|
surname TEXT NOT NULL,
|
||||||
|
birth_date INTEGER NOT NULL,
|
||||||
|
birth_place TEXT NOT NULL,
|
||||||
|
gender TEXT NOT NULL,
|
||||||
|
has_polish_citizenship INTEGER NOT NULL,
|
||||||
|
family_name TEXT NOT NULL,
|
||||||
|
parents_names TEXT NOT NULL,
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
registered_address TEXT NOT NULL,
|
||||||
|
correspondence_address TEXT NOT NULL,
|
||||||
|
phone_number TEXT NOT NULL,
|
||||||
|
cell_phone_number TEXT NOT NULL,
|
||||||
|
email TEXT NOT NULL,
|
||||||
|
first_guardian_full_name TEXT NOT NULL,
|
||||||
|
first_guardian_kinship TEXT NOT NULL,
|
||||||
|
first_guardian_address TEXT NOT NULL,
|
||||||
|
first_guardian_phones TEXT NOT NULL,
|
||||||
|
first_guardian_email TEXT NOT NULL,
|
||||||
|
second_guardian_full_name TEXT NOT NULL,
|
||||||
|
second_guardian_kinship TEXT NOT NULL,
|
||||||
|
second_guardian_address TEXT NOT NULL,
|
||||||
|
second_guardian_phones TEXT NOT NULL,
|
||||||
|
second_guardian_email TEXT NOT NULL)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration32 : Migration(31, 32) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE Students ADD COLUMN nick TEXT NOT NULL DEFAULT \"\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
|||||||
|
package io.github.wulkanowy.data.enums
|
||||||
|
|
||||||
|
enum class Gender { MALE, FEMALE }
|
@ -0,0 +1,38 @@
|
|||||||
|
package io.github.wulkanowy.data.mappers
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentGuardian
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||||
|
import io.github.wulkanowy.data.enums.Gender
|
||||||
|
import io.github.wulkanowy.sdk.pojo.StudentGuardian as SdkStudentGuardian
|
||||||
|
import io.github.wulkanowy.sdk.pojo.StudentInfo as SdkStudentInfo
|
||||||
|
|
||||||
|
fun SdkStudentInfo.mapToEntity(semester: Semester) = StudentInfo(
|
||||||
|
studentId = semester.studentId,
|
||||||
|
fullName = fullName,
|
||||||
|
firstName = firstName,
|
||||||
|
secondName = secondName,
|
||||||
|
surname = surname,
|
||||||
|
birthDate = birthDate,
|
||||||
|
birthPlace = birthPlace,
|
||||||
|
gender = Gender.valueOf(gender.name),
|
||||||
|
hasPolishCitizenship = hasPolishCitizenship,
|
||||||
|
familyName = familyName,
|
||||||
|
parentsNames = parentsNames,
|
||||||
|
address = address,
|
||||||
|
registeredAddress = registeredAddress,
|
||||||
|
correspondenceAddress = correspondenceAddress,
|
||||||
|
phoneNumber = phoneNumber,
|
||||||
|
cellPhoneNumber = phoneNumber,
|
||||||
|
email = email,
|
||||||
|
firstGuardian = guardians[0].mapToEntity(),
|
||||||
|
secondGuardian = guardians[1].mapToEntity()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun SdkStudentGuardian.mapToEntity() = StudentGuardian(
|
||||||
|
fullName = fullName,
|
||||||
|
kinship = kinship,
|
||||||
|
address = address,
|
||||||
|
phones = phones,
|
||||||
|
email = email
|
||||||
|
)
|
@ -16,16 +16,23 @@ class SchoolRepository @Inject constructor(
|
|||||||
private val sdk: Sdk
|
private val sdk: Sdk
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
|
fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
|
||||||
|
networkBoundResource(
|
||||||
shouldFetch = { it == null || forceRefresh },
|
shouldFetch = { it == null || forceRefresh },
|
||||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||||
fetch = { sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool().mapToEntity(semester) },
|
fetch = {
|
||||||
|
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool()
|
||||||
|
.mapToEntity(semester)
|
||||||
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
if (new != old && old != null) {
|
if (old != null && new != old) {
|
||||||
schoolDb.deleteAll(listOf(old))
|
with(schoolDb) {
|
||||||
|
deleteAll(listOf(old))
|
||||||
|
insertAll(listOf(new))
|
||||||
|
}
|
||||||
|
} else if (old == null) {
|
||||||
schoolDb.insertAll(listOf(new))
|
schoolDb.insertAll(listOf(new))
|
||||||
}
|
}
|
||||||
schoolDb.insertAll(listOf(new))
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.networkBoundResource
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class StudentInfoRepository @Inject constructor(
|
||||||
|
private val studentInfoDao: StudentInfoDao,
|
||||||
|
private val sdk: Sdk
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun getStudentInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
|
||||||
|
networkBoundResource(
|
||||||
|
shouldFetch = { it == null || forceRefresh },
|
||||||
|
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
||||||
|
fetch = {
|
||||||
|
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||||
|
.getStudentInfo().mapToEntity(semester)
|
||||||
|
},
|
||||||
|
saveFetchResult = { old, new ->
|
||||||
|
if (old != null && new != old) {
|
||||||
|
with(studentInfoDao) {
|
||||||
|
deleteAll(listOf(old))
|
||||||
|
insertAll(listOf(new))
|
||||||
|
}
|
||||||
|
} else if (old == null) {
|
||||||
|
studentInfoDao.insertAll(listOf(new))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@ -5,6 +5,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentNick
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
@ -25,39 +26,59 @@ class StudentRepository @Inject constructor(
|
|||||||
private val sdk: Sdk
|
private val sdk: Sdk
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun isStudentSaved(): Boolean = getSavedStudents(false).isNotEmpty()
|
suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty()
|
||||||
|
|
||||||
suspend fun isCurrentStudentSet(): Boolean = studentDb.loadCurrent()?.isCurrent ?: false
|
suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false
|
||||||
|
|
||||||
suspend fun getStudentsApi(pin: String, symbol: String, token: String): List<StudentWithSemesters> {
|
suspend fun getStudentsApi(
|
||||||
return sdk.getStudentsFromMobileApi(token, pin, symbol, "").mapToEntities()
|
pin: String,
|
||||||
}
|
symbol: String,
|
||||||
|
token: String
|
||||||
|
): List<StudentWithSemesters> =
|
||||||
|
sdk.getStudentsFromMobileApi(token, pin, symbol, "").mapToEntities()
|
||||||
|
|
||||||
suspend fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): List<StudentWithSemesters> {
|
suspend fun getStudentsScrapper(
|
||||||
return sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol).mapToEntities(password)
|
email: String,
|
||||||
}
|
password: String,
|
||||||
|
scrapperBaseUrl: String,
|
||||||
|
symbol: String
|
||||||
|
): List<StudentWithSemesters> =
|
||||||
|
sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||||
|
.mapToEntities(password)
|
||||||
|
|
||||||
suspend fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): List<StudentWithSemesters> {
|
suspend fun getStudentsHybrid(
|
||||||
return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol).mapToEntities(password)
|
email: String,
|
||||||
}
|
password: String,
|
||||||
|
scrapperBaseUrl: String,
|
||||||
|
symbol: String
|
||||||
|
): List<StudentWithSemesters> =
|
||||||
|
sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol).mapToEntities(password)
|
||||||
|
|
||||||
suspend fun getSavedStudents(decryptPass: Boolean = true) = withContext(dispatchers.backgroundThread) {
|
suspend fun getSavedStudents(decryptPass: Boolean = true) =
|
||||||
|
withContext(dispatchers.backgroundThread) {
|
||||||
studentDb.loadStudentsWithSemesters().map {
|
studentDb.loadStudentsWithSemesters().map {
|
||||||
it.apply {
|
it.apply {
|
||||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) student.password = decrypt(student.password)
|
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||||
|
student.password = decrypt(student.password)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) {
|
suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) {
|
||||||
studentDb.loadById(id)?.apply {
|
studentDb.loadById(id)?.apply {
|
||||||
if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
|
if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) {
|
||||||
|
password = decrypt(password)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} ?: throw NoCurrentStudentException()
|
} ?: throw NoCurrentStudentException()
|
||||||
|
|
||||||
suspend fun getCurrentStudent(decryptPass: Boolean = true) = withContext(dispatchers.backgroundThread) {
|
suspend fun getCurrentStudent(decryptPass: Boolean = true) =
|
||||||
|
withContext(dispatchers.backgroundThread) {
|
||||||
studentDb.loadCurrent()?.apply {
|
studentDb.loadCurrent()?.apply {
|
||||||
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
|
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) {
|
||||||
|
password = decrypt(password)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} ?: throw NoCurrentStudentException()
|
} ?: throw NoCurrentStudentException()
|
||||||
|
|
||||||
@ -66,8 +87,9 @@ class StudentRepository @Inject constructor(
|
|||||||
|
|
||||||
return withContext(dispatchers.backgroundThread) {
|
return withContext(dispatchers.backgroundThread) {
|
||||||
studentDb.insertAll(studentsWithSemesters.map { it.student }.map {
|
studentDb.insertAll(studentsWithSemesters.map { it.student }.map {
|
||||||
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context))
|
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) {
|
||||||
else it
|
it.copy(password = encrypt(it.password, context))
|
||||||
|
} else it
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +101,7 @@ class StudentRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun logoutStudent(student: Student) {
|
suspend fun logoutStudent(student: Student) = studentDb.delete(student)
|
||||||
studentDb.delete(student)
|
|
||||||
}
|
suspend fun updateStudentNick(studentNick: StudentNick) = studentDb.update(studentNick)
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio
|
|||||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME
|
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
import io.github.wulkanowy.utils.toTimestamp
|
import io.github.wulkanowy.utils.toTimestamp
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -41,17 +42,23 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
private val dispatchersProvider: DispatchersProvider,
|
private val dispatchersProvider: DispatchersProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private fun getRequestCode(time: LocalDateTime, studentId: Int) = (time.toTimestamp() * studentId).toInt()
|
private fun getRequestCode(time: LocalDateTime, studentId: Int) =
|
||||||
|
(time.toTimestamp() * studentId).toInt()
|
||||||
|
|
||||||
private fun getUpcomingLessonTime(index: Int, day: List<Timetable>, lesson: Timetable): LocalDateTime {
|
private fun getUpcomingLessonTime(
|
||||||
return day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
index: Int,
|
||||||
}
|
day: List<Timetable>,
|
||||||
|
lesson: Timetable
|
||||||
|
) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
||||||
|
|
||||||
suspend fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
|
suspend fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
|
||||||
withContext(dispatchersProvider.backgroundThread) {
|
withContext(dispatchersProvider.backgroundThread) {
|
||||||
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
|
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
|
||||||
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
|
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
|
||||||
cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId))
|
cancelScheduledTo(
|
||||||
|
upcomingTime..lesson.start,
|
||||||
|
getRequestCode(upcomingTime, studentId)
|
||||||
|
)
|
||||||
cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId))
|
cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId))
|
||||||
|
|
||||||
Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId")
|
Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId")
|
||||||
@ -61,13 +68,18 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
|
|
||||||
private fun cancelScheduledTo(range: ClosedRange<LocalDateTime>, requestCode: Int) {
|
private fun cancelScheduledTo(range: ClosedRange<LocalDateTime>, requestCode: Int) {
|
||||||
if (now() in range) cancelNotification()
|
if (now() in range) cancelNotification()
|
||||||
alarmManager.cancel(PendingIntent.getBroadcast(context, requestCode, Intent(), FLAG_UPDATE_CURRENT))
|
alarmManager.cancel(
|
||||||
|
PendingIntent.getBroadcast(context, requestCode, Intent(), FLAG_UPDATE_CURRENT)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelNotification() = NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
|
fun cancelNotification() =
|
||||||
|
NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
|
||||||
|
|
||||||
suspend fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
|
suspend fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
|
||||||
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) return cancelScheduled(lessons, student.studentId)
|
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) {
|
||||||
|
return cancelScheduled(lessons, student.studentId)
|
||||||
|
}
|
||||||
|
|
||||||
withContext(dispatchersProvider.backgroundThread) {
|
withContext(dispatchersProvider.backgroundThread) {
|
||||||
lessons.groupBy { it.date }
|
lessons.groupBy { it.date }
|
||||||
@ -82,13 +94,28 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
val intent = createIntent(student, lesson, active.getOrNull(index + 1))
|
val intent = createIntent(student, lesson, active.getOrNull(index + 1))
|
||||||
|
|
||||||
if (lesson.start > now()) {
|
if (lesson.start > now()) {
|
||||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, active, lesson))
|
scheduleBroadcast(
|
||||||
|
intent,
|
||||||
|
student.studentId,
|
||||||
|
NOTIFICATION_TYPE_UPCOMING,
|
||||||
|
getUpcomingLessonTime(index, active, lesson)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lesson.end > now()) {
|
if (lesson.end > now()) {
|
||||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start)
|
scheduleBroadcast(
|
||||||
|
intent,
|
||||||
|
student.studentId,
|
||||||
|
NOTIFICATION_TYPE_CURRENT,
|
||||||
|
lesson.start
|
||||||
|
)
|
||||||
if (active.lastIndex == index) {
|
if (active.lastIndex == index) {
|
||||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end)
|
scheduleBroadcast(
|
||||||
|
intent,
|
||||||
|
student.studentId,
|
||||||
|
NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION,
|
||||||
|
lesson.end
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,7 +126,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent {
|
private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent {
|
||||||
return Intent(context, TimetableNotificationReceiver::class.java).apply {
|
return Intent(context, TimetableNotificationReceiver::class.java).apply {
|
||||||
putExtra(STUDENT_ID, student.studentId)
|
putExtra(STUDENT_ID, student.studentId)
|
||||||
putExtra(STUDENT_NAME, student.studentName)
|
putExtra(STUDENT_NAME, student.nickOrName)
|
||||||
putExtra(LESSON_ROOM, lesson.room)
|
putExtra(LESSON_ROOM, lesson.room)
|
||||||
putExtra(LESSON_START, lesson.start.toTimestamp())
|
putExtra(LESSON_START, lesson.start.toTimestamp())
|
||||||
putExtra(LESSON_END, lesson.end.toTimestamp())
|
putExtra(LESSON_END, lesson.end.toTimestamp())
|
||||||
@ -109,13 +136,23 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scheduleBroadcast(intent: Intent, studentId: Int, notificationType: Int, time: LocalDateTime) {
|
private fun scheduleBroadcast(
|
||||||
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, RTC_WAKEUP, time.toTimestamp(),
|
intent: Intent,
|
||||||
|
studentId: Int,
|
||||||
|
notificationType: Int,
|
||||||
|
time: LocalDateTime
|
||||||
|
) {
|
||||||
|
AlarmManagerCompat.setExactAndAllowWhileIdle(
|
||||||
|
alarmManager, RTC_WAKEUP, time.toTimestamp(),
|
||||||
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
|
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
|
||||||
it.putExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id)
|
it.putExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id)
|
||||||
it.putExtra(LESSON_TYPE, notificationType)
|
it.putExtra(LESSON_TYPE, notificationType)
|
||||||
}, FLAG_UPDATE_CURRENT)
|
}, FLAG_UPDATE_CURRENT)
|
||||||
)
|
)
|
||||||
Timber.d("TimetableNotification scheduled: type: $notificationType, subject: ${intent.getStringExtra(LESSON_TITLE)}, start: $time, student: $studentId")
|
Timber.d(
|
||||||
|
"TimetableNotification scheduled: type: $notificationType, subject: ${
|
||||||
|
intent.getStringExtra(LESSON_TITLE)
|
||||||
|
}, start: $time, student: $studentId"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,12 @@ import androidx.core.app.NotificationCompat
|
|||||||
import androidx.core.app.NotificationCompat.BigTextStyle
|
import androidx.core.app.NotificationCompat.BigTextStyle
|
||||||
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
|
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.hilt.Assisted
|
import androidx.hilt.work.HiltWorker
|
||||||
import androidx.hilt.work.WorkerInject
|
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.Data
|
import androidx.work.Data
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||||
@ -23,7 +24,8 @@ import kotlinx.coroutines.coroutineScope
|
|||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
class SyncWorker @WorkerInject constructor(
|
@HiltWorker
|
||||||
|
class SyncWorker @AssistedInject constructor(
|
||||||
@Assisted appContext: Context,
|
@Assisted appContext: Context,
|
||||||
@Assisted workerParameters: WorkerParameters,
|
@Assisted workerParameters: WorkerParameters,
|
||||||
private val studentRepository: StudentRepository,
|
private val studentRepository: StudentRepository,
|
||||||
@ -58,7 +60,8 @@ class SyncWorker @WorkerInject constructor(
|
|||||||
}
|
}
|
||||||
val result = when {
|
val result = when {
|
||||||
exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
|
exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
|
||||||
Result.failure(Data.Builder()
|
Result.failure(
|
||||||
|
Data.Builder()
|
||||||
.putString("error", exceptions.map { it.stackTraceToString() }.toString())
|
.putString("error", exceptions.map { it.stackTraceToString() }.toString())
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@ -74,13 +77,16 @@ class SyncWorker @WorkerInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun notify(result: Result) {
|
private fun notify(result: Result) {
|
||||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID)
|
notificationManager.notify(
|
||||||
|
Random.nextInt(Int.MAX_VALUE),
|
||||||
|
NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID)
|
||||||
.setContentTitle("Debug notification")
|
.setContentTitle("Debug notification")
|
||||||
.setSmallIcon(R.drawable.ic_stat_push)
|
.setSmallIcon(R.drawable.ic_stat_push)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setColor(applicationContext.getCompatColor(R.color.colorPrimary))
|
.setColor(applicationContext.getCompatColor(R.color.colorPrimary))
|
||||||
.setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result"))
|
.setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result"))
|
||||||
.setPriority(PRIORITY_DEFAULT)
|
.setPriority(PRIORITY_DEFAULT)
|
||||||
.build())
|
.build()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
return DialogErrorBinding.inflate(inflater).apply { binding = this }.root
|
return DialogErrorBinding.inflate(inflater).apply { binding = this }.root
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,11 +114,17 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||||||
chooserTitle = getString(R.string.about_feedback),
|
chooserTitle = getString(R.string.about_feedback),
|
||||||
email = "wulkanowyinc@gmail.com",
|
email = "wulkanowyinc@gmail.com",
|
||||||
subject = "Zgłoszenie błędu",
|
subject = "Zgłoszenie błędu",
|
||||||
body = requireContext().getString(R.string.about_feedback_template,
|
body = requireContext().getString(
|
||||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName
|
R.string.about_feedback_template,
|
||||||
|
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||||
|
appInfo.systemVersion.toString(),
|
||||||
|
"${appInfo.versionName}-${appInfo.buildFlavor}"
|
||||||
) + "\n" + content,
|
) + "\n" + content,
|
||||||
onActivityNotFound = {
|
onActivityNotFound = {
|
||||||
requireContext().openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage)
|
requireContext().openInternetBrowser(
|
||||||
|
"https://github.com/wulkanowy/wulkanowy/issues",
|
||||||
|
::showMessage
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import io.github.wulkanowy.R
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.databinding.ItemAccountBinding
|
import io.github.wulkanowy.databinding.ItemAccountBinding
|
||||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<WidgetConfigureAdapter.ItemViewHolder>() {
|
class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<WidgetConfigureAdapter.ItemViewHolder>() {
|
||||||
@ -28,7 +29,7 @@ class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<Widget
|
|||||||
val (student, isCurrent) = items[position]
|
val (student, isCurrent) = items[position]
|
||||||
|
|
||||||
with(holder.binding) {
|
with(holder.binding) {
|
||||||
accountItemName.text = "${student.studentName} ${student.className}"
|
accountItemName.text = "${student.nickOrName} ${student.className}"
|
||||||
accountItemSchool.text = student.schoolName
|
accountItemSchool.text = student.schoolName
|
||||||
|
|
||||||
with(accountItemImage) {
|
with(accountItemImage) {
|
||||||
|
@ -18,6 +18,8 @@ import io.github.wulkanowy.utils.getCompatDrawable
|
|||||||
import io.github.wulkanowy.utils.openAppInMarket
|
import io.github.wulkanowy.utils.openAppInMarket
|
||||||
import io.github.wulkanowy.utils.openEmailClient
|
import io.github.wulkanowy.utils.openEmailClient
|
||||||
import io.github.wulkanowy.utils.openInternetBrowser
|
import io.github.wulkanowy.utils.openInternetBrowser
|
||||||
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
import io.github.wulkanowy.utils.toLocalDateTime
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -35,7 +37,9 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||||||
|
|
||||||
override val versionRes: Triple<String, String, Drawable?>?
|
override val versionRes: Triple<String, String, Drawable?>?
|
||||||
get() = context?.run {
|
get() = context?.run {
|
||||||
Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about))
|
val buildTimestamp = appInfo.buildTimestamp.toLocalDateTime().toFormattedString("yyyy-MM-dd")
|
||||||
|
val versionSignature = "${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp"
|
||||||
|
Triple(getString(R.string.about_version), versionSignature, getCompatDrawable(R.drawable.ic_all_about))
|
||||||
}
|
}
|
||||||
|
|
||||||
override val creatorsRes: Triple<String, String, Drawable?>?
|
override val creatorsRes: Triple<String, String, Drawable?>?
|
||||||
@ -65,7 +69,11 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||||||
|
|
||||||
override val homepageRes: Triple<String, String, Drawable?>?
|
override val homepageRes: Triple<String, String, Drawable?>?
|
||||||
get() = context?.run {
|
get() = context?.run {
|
||||||
Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage))
|
Triple(
|
||||||
|
getString(R.string.about_homepage),
|
||||||
|
getString(R.string.about_homepage_summary),
|
||||||
|
getCompatDrawable(R.drawable.ic_all_home)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val licensesRes: Triple<String, String, Drawable?>?
|
override val licensesRes: Triple<String, String, Drawable?>?
|
||||||
@ -131,11 +139,17 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||||||
chooserTitle = getString(R.string.about_feedback),
|
chooserTitle = getString(R.string.about_feedback),
|
||||||
email = "wulkanowyinc@gmail.com",
|
email = "wulkanowyinc@gmail.com",
|
||||||
subject = "Zgłoszenie błędu",
|
subject = "Zgłoszenie błędu",
|
||||||
body = getString(R.string.about_feedback_template,
|
body = getString(
|
||||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName
|
R.string.about_feedback_template,
|
||||||
|
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||||
|
appInfo.systemVersion.toString(),
|
||||||
|
"${appInfo.versionName}-${appInfo.buildFlavor}"
|
||||||
),
|
),
|
||||||
onActivityNotFound = {
|
onActivityNotFound = {
|
||||||
requireContext().openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage)
|
requireContext().openInternetBrowser(
|
||||||
|
"https://github.com/wulkanowy/wulkanowy/issues",
|
||||||
|
::showMessage
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,17 @@ import android.view.View.VISIBLE
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.databinding.HeaderAccountBinding
|
import io.github.wulkanowy.databinding.HeaderAccountBinding
|
||||||
import io.github.wulkanowy.databinding.ItemAccountBinding
|
import io.github.wulkanowy.databinding.ItemAccountBinding
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
var isAccountQuickDialogMode = false
|
||||||
|
|
||||||
var items = emptyList<AccountItem<*>>()
|
var items = emptyList<AccountItem<*>>()
|
||||||
|
|
||||||
var onClickListener: (StudentWithSemesters) -> Unit = {}
|
var onClickListener: (StudentWithSemesters) -> Unit = {}
|
||||||
@ -30,53 +31,68 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
|
|||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
AccountItem.ViewType.HEADER.id -> HeaderViewHolder(HeaderAccountBinding.inflate(inflater, parent, false))
|
AccountItem.ViewType.HEADER.id -> HeaderViewHolder(
|
||||||
AccountItem.ViewType.ITEM.id -> ItemViewHolder(ItemAccountBinding.inflate(inflater, parent, false))
|
HeaderAccountBinding.inflate(inflater, parent, false)
|
||||||
|
)
|
||||||
|
AccountItem.ViewType.ITEM.id -> ItemViewHolder(
|
||||||
|
ItemAccountBinding.inflate(inflater, parent, false)
|
||||||
|
)
|
||||||
else -> throw IllegalStateException()
|
else -> throw IllegalStateException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
when (holder) {
|
when (holder) {
|
||||||
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as Account)
|
is HeaderViewHolder -> bindHeaderViewHolder(
|
||||||
is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as StudentWithSemesters)
|
holder.binding,
|
||||||
|
items[position].value as Account,
|
||||||
|
position
|
||||||
|
)
|
||||||
|
is ItemViewHolder -> bindItemViewHolder(
|
||||||
|
holder.binding,
|
||||||
|
items[position].value as StudentWithSemesters
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindHeaderViewHolder(binding: HeaderAccountBinding, account: Account) {
|
private fun bindHeaderViewHolder(
|
||||||
|
binding: HeaderAccountBinding,
|
||||||
|
account: Account,
|
||||||
|
position: Int
|
||||||
|
) {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
|
accountHeaderDivider.visibility = if (position == 0) GONE else VISIBLE
|
||||||
accountHeaderEmail.text = account.email
|
accountHeaderEmail.text = account.email
|
||||||
accountHeaderType.setText(if (account.isParent) R.string.account_type_parent else R.string.account_type_student)
|
accountHeaderType.setText(if (account.isParent) R.string.account_type_parent else R.string.account_type_student)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
private fun bindItemViewHolder(binding: ItemAccountBinding, studentWithSemesters: StudentWithSemesters) {
|
private fun bindItemViewHolder(
|
||||||
|
binding: ItemAccountBinding,
|
||||||
|
studentWithSemesters: StudentWithSemesters
|
||||||
|
) {
|
||||||
val student = studentWithSemesters.student
|
val student = studentWithSemesters.student
|
||||||
val semesters = studentWithSemesters.semesters
|
val semesters = studentWithSemesters.semesters
|
||||||
val diary = semesters.maxByOrNull { it.semesterId }
|
val diary = semesters.maxByOrNull { it.semesterId }
|
||||||
|
val isDuplicatedStudent = items.filter {
|
||||||
|
if (it.value !is StudentWithSemesters) return@filter false
|
||||||
|
val studentToCompare = it.value.student
|
||||||
|
|
||||||
|
studentToCompare.studentId == student.studentId
|
||||||
|
&& studentToCompare.schoolSymbol == student.schoolSymbol
|
||||||
|
&& studentToCompare.symbol == student.symbol
|
||||||
|
}.size > 1 && isAccountQuickDialogMode
|
||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
accountItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}"
|
accountItemName.text = "${student.nickOrName} ${diary?.diaryName.orEmpty()}"
|
||||||
accountItemSchool.text = studentWithSemesters.student.schoolName
|
accountItemSchool.text = studentWithSemesters.student.schoolName
|
||||||
with(accountItemLoginMode) {
|
accountItemAccountType.setText(if (student.isParent) R.string.account_type_parent else R.string.account_type_student)
|
||||||
visibility = when (Sdk.Mode.valueOf(student.loginMode)) {
|
accountItemAccountType.visibility = if (isDuplicatedStudent) VISIBLE else GONE
|
||||||
Sdk.Mode.API -> {
|
|
||||||
setText(R.string.account_login_mobile_api)
|
|
||||||
VISIBLE
|
|
||||||
}
|
|
||||||
Sdk.Mode.HYBRID -> {
|
|
||||||
setText(R.string.account_login_hybrid)
|
|
||||||
VISIBLE
|
|
||||||
}
|
|
||||||
Sdk.Mode.SCRAPPER -> {
|
|
||||||
GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
with(accountItemImage) {
|
with(accountItemImage) {
|
||||||
val colorImage = if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
|
val colorImage =
|
||||||
|
if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
|
||||||
else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
|
else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
|
||||||
|
|
||||||
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
|
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
package io.github.wulkanowy.ui.modules.account
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.Toast
|
|
||||||
import android.widget.Toast.LENGTH_LONG
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import io.github.wulkanowy.R
|
|
||||||
import io.github.wulkanowy.databinding.DialogAccountBinding
|
|
||||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
|
||||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class AccountDialog : BaseDialogFragment<DialogAccountBinding>(), AccountView {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var presenter: AccountPresenter
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var accountAdapter: AccountAdapter
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun newInstance() = AccountDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setStyle(STYLE_NO_TITLE, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
||||||
return DialogAccountBinding.inflate(inflater).apply { binding = this }.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
presenter.onAttachView(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun initView() {
|
|
||||||
accountAdapter.onClickListener = presenter::onItemSelected
|
|
||||||
|
|
||||||
with(binding) {
|
|
||||||
accountDialogAdd.setOnClickListener { presenter.onAddSelected() }
|
|
||||||
accountDialogRemove.setOnClickListener { presenter.onRemoveSelected() }
|
|
||||||
accountDialogRecycler.apply {
|
|
||||||
layoutManager = LinearLayoutManager(context)
|
|
||||||
adapter = accountAdapter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateData(data: List<AccountItem<*>>) {
|
|
||||||
with(accountAdapter) {
|
|
||||||
items = data
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showError(text: String, error: Throwable) {
|
|
||||||
showMessage(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showMessage(text: String) {
|
|
||||||
Toast.makeText(context, text, LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dismissView() {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openLoginView() {
|
|
||||||
activity?.let {
|
|
||||||
startActivity(LoginActivity.getStartIntent(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showConfirmDialog() {
|
|
||||||
context?.let {
|
|
||||||
AlertDialog.Builder(it)
|
|
||||||
.setTitle(R.string.account_logout_student)
|
|
||||||
.setMessage(R.string.account_confirm)
|
|
||||||
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
|
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun recreateMainView() {
|
|
||||||
activity?.recreate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
presenter.onDetachView()
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,111 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.view.get
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.databinding.FragmentAccountBinding
|
||||||
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class AccountFragment : BaseFragment<FragmentAccountBinding>(R.layout.fragment_account),
|
||||||
|
AccountView, MainView.TitledView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: AccountPresenter
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var accountAdapter: AccountAdapter
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun newInstance() = AccountFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val titleStringId = R.string.account_title
|
||||||
|
|
||||||
|
override var subtitleString = ""
|
||||||
|
|
||||||
|
override val isViewEmpty get() = accountAdapter.items.isEmpty()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
binding = FragmentAccountBinding.bind(view)
|
||||||
|
presenter.onAttachView(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
binding.accountErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
|
binding.accountErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
|
||||||
|
binding.accountRecycler.apply {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = accountAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
accountAdapter.onClickListener = presenter::onItemSelected
|
||||||
|
|
||||||
|
with(binding) {
|
||||||
|
accountAdd.setOnClickListener { presenter.onAddSelected() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
menu[0].isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateData(data: List<AccountItem<*>>) {
|
||||||
|
with(accountAdapter) {
|
||||||
|
items = data
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openLoginView() {
|
||||||
|
activity?.let {
|
||||||
|
startActivity(LoginActivity.getStartIntent(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) {
|
||||||
|
(activity as? MainActivity)?.pushView(
|
||||||
|
AccountDetailsFragment.newInstance(
|
||||||
|
studentWithSemesters
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showErrorView(show: Boolean) {
|
||||||
|
binding.accountError.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorDetails(message: String) {
|
||||||
|
binding.accountErrorMessage.text = message
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(show: Boolean) {
|
||||||
|
binding.accountProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showContent(show: Boolean) {
|
||||||
|
with(binding) {
|
||||||
|
accountRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
accountAdd.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.account
|
|||||||
import io.github.wulkanowy.data.Status
|
import io.github.wulkanowy.data.Status
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.services.sync.SyncManager
|
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.utils.afterLoading
|
import io.github.wulkanowy.utils.afterLoading
|
||||||
@ -15,101 +14,91 @@ import javax.inject.Inject
|
|||||||
class AccountPresenter @Inject constructor(
|
class AccountPresenter @Inject constructor(
|
||||||
errorHandler: ErrorHandler,
|
errorHandler: ErrorHandler,
|
||||||
studentRepository: StudentRepository,
|
studentRepository: StudentRepository,
|
||||||
private val syncManager: SyncManager
|
|
||||||
) : BasePresenter<AccountView>(errorHandler, studentRepository) {
|
) : BasePresenter<AccountView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
override fun onAttachView(view: AccountView) {
|
override fun onAttachView(view: AccountView) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
Timber.i("Account dialog view was initialized")
|
Timber.i("Account view was initialized")
|
||||||
|
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onRetry() {
|
||||||
|
view?.run {
|
||||||
|
showErrorView(false)
|
||||||
|
showProgress(true)
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDetailsClick() {
|
||||||
|
view?.showErrorDetailsDialog(lastError)
|
||||||
|
}
|
||||||
|
|
||||||
fun onAddSelected() {
|
fun onAddSelected() {
|
||||||
Timber.i("Select add account")
|
Timber.i("Select add account")
|
||||||
view?.openLoginView()
|
view?.openLoginView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRemoveSelected() {
|
|
||||||
Timber.i("Select remove account")
|
|
||||||
view?.showConfirmDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onLogoutConfirm() {
|
|
||||||
flowWithResource {
|
|
||||||
val student = studentRepository.getCurrentStudent(false)
|
|
||||||
studentRepository.logoutStudent(student)
|
|
||||||
|
|
||||||
val students = studentRepository.getSavedStudents(false)
|
|
||||||
if (students.isNotEmpty()) {
|
|
||||||
studentRepository.switchStudent(students[0])
|
|
||||||
}
|
|
||||||
students
|
|
||||||
}.onEach {
|
|
||||||
when (it.status) {
|
|
||||||
Status.LOADING -> Timber.i("Attempt to logout current user ")
|
|
||||||
Status.SUCCESS -> view?.run {
|
|
||||||
if (it.data!!.isEmpty()) {
|
|
||||||
Timber.i("Logout result: Open login view")
|
|
||||||
syncManager.stopSyncWorker()
|
|
||||||
openClearLoginView()
|
|
||||||
} else {
|
|
||||||
Timber.i("Logout result: Switch to another student")
|
|
||||||
recreateMainView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Status.ERROR -> {
|
|
||||||
Timber.i("Logout result: An exception occurred")
|
|
||||||
errorHandler.dispatch(it.error!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.afterLoading {
|
|
||||||
view?.dismissView()
|
|
||||||
}.launch("logout")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onItemSelected(studentWithSemesters: StudentWithSemesters) {
|
fun onItemSelected(studentWithSemesters: StudentWithSemesters) {
|
||||||
Timber.i("Select student item ${studentWithSemesters.student.id}")
|
view?.openAccountDetailsView(studentWithSemesters)
|
||||||
if (studentWithSemesters.student.isCurrent) {
|
|
||||||
view?.dismissView()
|
|
||||||
} else flowWithResource { studentRepository.switchStudent(studentWithSemesters) }.onEach {
|
|
||||||
when (it.status) {
|
|
||||||
Status.LOADING -> Timber.i("Attempt to change a student")
|
|
||||||
Status.SUCCESS -> {
|
|
||||||
Timber.i("Change a student result: Success")
|
|
||||||
view?.recreateMainView()
|
|
||||||
}
|
|
||||||
Status.ERROR -> {
|
|
||||||
Timber.i("Change a student result: An exception occurred")
|
|
||||||
errorHandler.dispatch(it.error!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.afterLoading {
|
|
||||||
view?.dismissView()
|
|
||||||
}.launch("switch")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAccountItems(items: List<StudentWithSemesters>): List<AccountItem<*>> {
|
private fun createAccountItems(items: List<StudentWithSemesters>): List<AccountItem<*>> {
|
||||||
return items.groupBy { Account(it.student.email, it.student.isParent) }.map { (account, students) ->
|
return items.groupBy {
|
||||||
listOf(AccountItem(account, AccountItem.ViewType.HEADER)) + students.map { student ->
|
Account("${it.student.userName} (${it.student.email})", it.student.isParent)
|
||||||
|
}
|
||||||
|
.map { (account, students) ->
|
||||||
|
listOf(
|
||||||
|
AccountItem(account, AccountItem.ViewType.HEADER)
|
||||||
|
) + students.map { student ->
|
||||||
AccountItem(student, AccountItem.ViewType.ITEM)
|
AccountItem(student, AccountItem.ViewType.ITEM)
|
||||||
}
|
}
|
||||||
}.flatten()
|
}
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadData() {
|
private fun loadData() {
|
||||||
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
|
flowWithResource { studentRepository.getSavedStudents(false) }
|
||||||
|
.onEach {
|
||||||
when (it.status) {
|
when (it.status) {
|
||||||
Status.LOADING -> Timber.i("Loading account data started")
|
Status.LOADING -> {
|
||||||
|
Timber.i("Loading account data started")
|
||||||
|
view?.run {
|
||||||
|
showProgress(true)
|
||||||
|
showContent(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
Status.SUCCESS -> {
|
Status.SUCCESS -> {
|
||||||
Timber.i("Loading account result: Success")
|
Timber.i("Loading account result: Success")
|
||||||
view?.updateData(createAccountItems(it.data!!))
|
view?.updateData(createAccountItems(it.data!!))
|
||||||
|
view?.run {
|
||||||
|
showContent(true)
|
||||||
|
showErrorView(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Status.ERROR -> {
|
Status.ERROR -> {
|
||||||
Timber.i("Loading account result: An exception occurred")
|
Timber.i("Loading account result: An exception occurred")
|
||||||
errorHandler.dispatch(it.error!!)
|
errorHandler.dispatch(it.error!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.launch()
|
}
|
||||||
|
.afterLoading { view?.showProgress(false) }
|
||||||
|
.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
|
view?.run {
|
||||||
|
if (isViewEmpty) {
|
||||||
|
lastError = error
|
||||||
|
setErrorDetails(message)
|
||||||
|
showErrorView(true)
|
||||||
|
showContent(false)
|
||||||
|
showProgress(false)
|
||||||
|
} else showError(message, error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,26 @@
|
|||||||
package io.github.wulkanowy.ui.modules.account
|
package io.github.wulkanowy.ui.modules.account
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.ui.base.BaseView
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
interface AccountView : BaseView {
|
interface AccountView : BaseView {
|
||||||
|
|
||||||
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun updateData(data: List<AccountItem<*>>)
|
fun updateData(data: List<AccountItem<*>>)
|
||||||
|
|
||||||
fun dismissView()
|
|
||||||
|
|
||||||
fun showConfirmDialog()
|
|
||||||
|
|
||||||
fun openLoginView()
|
fun openLoginView()
|
||||||
|
|
||||||
fun recreateMainView()
|
fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters)
|
||||||
|
|
||||||
|
fun showErrorView(show: Boolean)
|
||||||
|
|
||||||
|
fun setErrorDetails(message: String)
|
||||||
|
|
||||||
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
fun showContent(show: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,157 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountdetails
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.get
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.databinding.FragmentAccountDetailsBinding
|
||||||
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.account.accountedit.AccountEditDialog
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||||
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class AccountDetailsFragment :
|
||||||
|
BaseFragment<FragmentAccountDetailsBinding>(R.layout.fragment_account_details),
|
||||||
|
AccountDetailsView, MainView.TitledView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: AccountDetailsPresenter
|
||||||
|
|
||||||
|
override val titleStringId = R.string.account_details_title
|
||||||
|
|
||||||
|
override var subtitleString = ""
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val ARGUMENT_KEY = "Data"
|
||||||
|
|
||||||
|
fun newInstance(studentWithSemesters: StudentWithSemesters) =
|
||||||
|
AccountDetailsFragment().apply {
|
||||||
|
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, studentWithSemesters) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
binding = FragmentAccountDetailsBinding.bind(view)
|
||||||
|
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as StudentWithSemesters)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
binding.accountDetailsErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
|
binding.accountDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
binding.accountDetailsLogout.setOnClickListener { presenter.onRemoveSelected() }
|
||||||
|
binding.accountDetailsSelect.setOnClickListener { presenter.onStudentSelect() }
|
||||||
|
|
||||||
|
binding.accountDetailsPersonalData.setOnClickListener {
|
||||||
|
presenter.onStudentInfoSelected(StudentInfoView.Type.PERSONAL)
|
||||||
|
}
|
||||||
|
binding.accountDetailsAddressData.setOnClickListener {
|
||||||
|
presenter.onStudentInfoSelected(StudentInfoView.Type.ADDRESS)
|
||||||
|
}
|
||||||
|
binding.accountDetailsContactData.setOnClickListener {
|
||||||
|
presenter.onStudentInfoSelected(StudentInfoView.Type.CONTACT)
|
||||||
|
}
|
||||||
|
binding.accountDetailsFamilyData.setOnClickListener {
|
||||||
|
presenter.onStudentInfoSelected(StudentInfoView.Type.FAMILY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
menu[0].isVisible = false
|
||||||
|
inflater.inflate(R.menu.action_menu_account_details, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return if (item.itemId == R.id.accountDetailsMenuEdit) {
|
||||||
|
presenter.onAccountEditSelected()
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showAccountData(student: Student) {
|
||||||
|
with(binding) {
|
||||||
|
accountDetailsName.text = student.nickOrName
|
||||||
|
accountDetailsSchool.text = student.schoolName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun enableSelectStudentButton(enable: Boolean) {
|
||||||
|
binding.accountDetailsSelect.isEnabled = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showAccountEditDetailsDialog(student: Student) {
|
||||||
|
(requireActivity() as MainActivity).showDialogFragment(
|
||||||
|
AccountEditDialog.newInstance(student)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showLogoutConfirmDialog() {
|
||||||
|
context?.let {
|
||||||
|
AlertDialog.Builder(it)
|
||||||
|
.setTitle(R.string.account_logout_student)
|
||||||
|
.setMessage(R.string.account_confirm)
|
||||||
|
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popView() {
|
||||||
|
(requireActivity() as MainActivity).popView(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun recreateMainView() {
|
||||||
|
requireActivity().recreate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openStudentInfoView(
|
||||||
|
infoType: StudentInfoView.Type,
|
||||||
|
studentWithSemesters: StudentWithSemesters
|
||||||
|
) {
|
||||||
|
(requireActivity() as MainActivity).pushView(
|
||||||
|
StudentInfoFragment.newInstance(
|
||||||
|
infoType,
|
||||||
|
studentWithSemesters
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showErrorView(show: Boolean) {
|
||||||
|
binding.accountDetailsError.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorDetails(message: String) {
|
||||||
|
binding.accountDetailsErrorMessage.text = message
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(show: Boolean) {
|
||||||
|
binding.accountDetailsProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showContent(show: Boolean) {
|
||||||
|
binding.accountDetailsContent.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountdetails
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.Resource
|
||||||
|
import io.github.wulkanowy.data.Status
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
|
import io.github.wulkanowy.services.sync.SyncManager
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
|
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||||
|
import io.github.wulkanowy.utils.afterLoading
|
||||||
|
import io.github.wulkanowy.utils.flowWithResource
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AccountDetailsPresenter @Inject constructor(
|
||||||
|
errorHandler: ErrorHandler,
|
||||||
|
studentRepository: StudentRepository,
|
||||||
|
private val syncManager: SyncManager
|
||||||
|
) : BasePresenter<AccountDetailsView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
|
private lateinit var studentWithSemesters: StudentWithSemesters
|
||||||
|
|
||||||
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
|
private var studentId: Long? = null
|
||||||
|
|
||||||
|
fun onAttachView(view: AccountDetailsView, studentWithSemesters: StudentWithSemesters) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
studentId = studentWithSemesters.student.id
|
||||||
|
|
||||||
|
view.initView()
|
||||||
|
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||||
|
Timber.i("Account details view was initialized")
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRetry() {
|
||||||
|
view?.run {
|
||||||
|
showErrorView(false)
|
||||||
|
showProgress(true)
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDetailsClick() {
|
||||||
|
view?.showErrorDetailsDialog(lastError)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData() {
|
||||||
|
flowWithResource { studentRepository.getSavedStudents() }
|
||||||
|
.map { studentWithSemesters ->
|
||||||
|
Resource(
|
||||||
|
data = studentWithSemesters.data?.single { it.student.id == studentId },
|
||||||
|
status = studentWithSemesters.status,
|
||||||
|
error = studentWithSemesters.error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.onEach {
|
||||||
|
when (it.status) {
|
||||||
|
Status.LOADING -> {
|
||||||
|
view?.run {
|
||||||
|
showProgress(true)
|
||||||
|
showContent(false)
|
||||||
|
}
|
||||||
|
Timber.i("Loading account details view started")
|
||||||
|
}
|
||||||
|
Status.SUCCESS -> {
|
||||||
|
Timber.i("Loading account details view result: Success")
|
||||||
|
studentWithSemesters = it.data!!
|
||||||
|
view?.run {
|
||||||
|
showAccountData(studentWithSemesters.student)
|
||||||
|
enableSelectStudentButton(!studentWithSemesters.student.isCurrent)
|
||||||
|
showContent(true)
|
||||||
|
showErrorView(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Status.ERROR -> {
|
||||||
|
Timber.i("Loading account details view result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.afterLoading { view?.showProgress(false) }
|
||||||
|
.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onAccountEditSelected() {
|
||||||
|
view?.showAccountEditDetailsDialog(studentWithSemesters.student)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStudentInfoSelected(infoType: StudentInfoView.Type) {
|
||||||
|
view?.openStudentInfoView(infoType, studentWithSemesters)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStudentSelect() {
|
||||||
|
Timber.i("Select student ${studentWithSemesters.student.id}")
|
||||||
|
|
||||||
|
flowWithResource { studentRepository.switchStudent(studentWithSemesters) }
|
||||||
|
.onEach {
|
||||||
|
when (it.status) {
|
||||||
|
Status.LOADING -> Timber.i("Attempt to change a student")
|
||||||
|
Status.SUCCESS -> {
|
||||||
|
Timber.i("Change a student result: Success")
|
||||||
|
view?.recreateMainView()
|
||||||
|
}
|
||||||
|
Status.ERROR -> {
|
||||||
|
Timber.i("Change a student result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.afterLoading {
|
||||||
|
view?.popView()
|
||||||
|
}.launch("switch")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRemoveSelected() {
|
||||||
|
Timber.i("Select remove account")
|
||||||
|
view?.showLogoutConfirmDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onLogoutConfirm() {
|
||||||
|
flowWithResource {
|
||||||
|
val studentToLogout = studentWithSemesters.student
|
||||||
|
|
||||||
|
studentRepository.logoutStudent(studentToLogout)
|
||||||
|
val students = studentRepository.getSavedStudents(false)
|
||||||
|
|
||||||
|
if (studentToLogout.isCurrent && students.isNotEmpty()) {
|
||||||
|
studentRepository.switchStudent(students[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return@flowWithResource students
|
||||||
|
}.onEach {
|
||||||
|
when (it.status) {
|
||||||
|
Status.LOADING -> Timber.i("Attempt to logout user")
|
||||||
|
Status.SUCCESS -> view?.run {
|
||||||
|
when {
|
||||||
|
it.data!!.isEmpty() -> {
|
||||||
|
Timber.i("Logout result: Open login view")
|
||||||
|
syncManager.stopSyncWorker()
|
||||||
|
openClearLoginView()
|
||||||
|
}
|
||||||
|
studentWithSemesters.student.isCurrent -> {
|
||||||
|
Timber.i("Logout result: Logout student and switch to another")
|
||||||
|
recreateMainView()
|
||||||
|
}
|
||||||
|
else -> Timber.i("Logout result: Logout student")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Status.ERROR -> {
|
||||||
|
Timber.i("Logout result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.afterLoading {
|
||||||
|
view?.popView()
|
||||||
|
}.launch("logout")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
|
view?.run {
|
||||||
|
lastError = error
|
||||||
|
setErrorDetails(message)
|
||||||
|
showErrorView(true)
|
||||||
|
showContent(false)
|
||||||
|
showProgress(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountdetails
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||||
|
|
||||||
|
interface AccountDetailsView : BaseView {
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun showAccountData(student: Student)
|
||||||
|
|
||||||
|
fun showAccountEditDetailsDialog(student: Student)
|
||||||
|
|
||||||
|
fun showLogoutConfirmDialog()
|
||||||
|
|
||||||
|
fun popView()
|
||||||
|
|
||||||
|
fun recreateMainView()
|
||||||
|
|
||||||
|
fun enableSelectStudentButton(enable: Boolean)
|
||||||
|
|
||||||
|
fun openStudentInfoView(
|
||||||
|
infoType: StudentInfoView.Type,
|
||||||
|
studentWithSemesters: StudentWithSemesters
|
||||||
|
)
|
||||||
|
|
||||||
|
fun showErrorView(show: Boolean)
|
||||||
|
|
||||||
|
fun setErrorDetails(message: String)
|
||||||
|
|
||||||
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
fun showContent(show: Boolean)
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
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 javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class AccountEditDialog : BaseDialogFragment<DialogAccountEditBinding>(), AccountEditView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: AccountEditPresenter
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val ARGUMENT_KEY = "student_with_semesters"
|
||||||
|
|
||||||
|
fun newInstance(student: Student) =
|
||||||
|
AccountEditDialog().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(ARGUMENT_KEY, student)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setStyle(STYLE_NO_TITLE, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
with(binding) {
|
||||||
|
accountEditDetailsCancel.setOnClickListener { dismiss() }
|
||||||
|
accountEditDetailsSave.setOnClickListener {
|
||||||
|
presenter.changeStudentNick(binding.accountEditDetailsNickText.text.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showCurrentNick(nick: String) {
|
||||||
|
binding.accountEditDetailsNickText.setText(nick)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popView() {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun recreateMainView() {
|
||||||
|
activity?.recreate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
presenter.onDetachView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.Status
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentNick
|
||||||
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
|
import io.github.wulkanowy.utils.afterLoading
|
||||||
|
import io.github.wulkanowy.utils.flowWithResource
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AccountEditPresenter @Inject constructor(
|
||||||
|
errorHandler: ErrorHandler,
|
||||||
|
studentRepository: StudentRepository
|
||||||
|
) : BasePresenter<AccountEditView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
|
lateinit var student: Student
|
||||||
|
|
||||||
|
fun onAttachView(view: AccountEditView, student: Student) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
this.student = student
|
||||||
|
|
||||||
|
with(view) {
|
||||||
|
initView()
|
||||||
|
showCurrentNick(student.nick.trim())
|
||||||
|
}
|
||||||
|
Timber.i("Account edit dialog view was initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeStudentNick(nick: String) {
|
||||||
|
flowWithResource {
|
||||||
|
val studentNick =
|
||||||
|
StudentNick(nick = nick.trim()).apply { id = student.id }
|
||||||
|
studentRepository.updateStudentNick(studentNick)
|
||||||
|
}.onEach {
|
||||||
|
when (it.status) {
|
||||||
|
Status.LOADING -> Timber.i("Attempt to change a student nick")
|
||||||
|
Status.SUCCESS -> {
|
||||||
|
Timber.i("Change a student nick result: Success")
|
||||||
|
view?.recreateMainView()
|
||||||
|
}
|
||||||
|
Status.ERROR -> {
|
||||||
|
Timber.i("Change a student result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.afterLoading { view?.popView() }
|
||||||
|
.launch()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||||
|
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
|
interface AccountEditView : BaseView {
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun popView()
|
||||||
|
|
||||||
|
fun recreateMainView()
|
||||||
|
|
||||||
|
fun showCurrentNick(nick: String)
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountquick
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.github.wulkanowy.databinding.DialogAccountQuickBinding
|
||||||
|
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||||
|
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 javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class AccountQuickDialog : BaseDialogFragment<DialogAccountQuickBinding>(), AccountQuickView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var accountAdapter: AccountAdapter
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: AccountQuickPresenter
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = AccountQuickDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setStyle(STYLE_NO_TITLE, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
presenter.onAttachView(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
binding.accountQuickDialogManger.setOnClickListener { presenter.onManagerSelected() }
|
||||||
|
|
||||||
|
with(accountAdapter) {
|
||||||
|
isAccountQuickDialogMode = true
|
||||||
|
onClickListener = presenter::onStudentSelect
|
||||||
|
}
|
||||||
|
|
||||||
|
with(binding.accountQuickDialogRecycler) {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = accountAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateData(data: List<AccountItem<*>>) {
|
||||||
|
with(accountAdapter) {
|
||||||
|
items = data
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popView() {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun recreateMainView() {
|
||||||
|
activity?.recreate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openAccountView() {
|
||||||
|
(requireActivity() as MainActivity).pushView(AccountFragment.newInstance())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountquick
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.Status
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
|
import io.github.wulkanowy.ui.modules.account.AccountItem
|
||||||
|
import io.github.wulkanowy.utils.afterLoading
|
||||||
|
import io.github.wulkanowy.utils.flowWithResource
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AccountQuickPresenter @Inject constructor(
|
||||||
|
errorHandler: ErrorHandler,
|
||||||
|
studentRepository: StudentRepository
|
||||||
|
) : BasePresenter<AccountQuickView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
|
override fun onAttachView(view: AccountQuickView) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
view.initView()
|
||||||
|
Timber.i("Account quick dialog view was initialized")
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onManagerSelected() {
|
||||||
|
view?.run {
|
||||||
|
openAccountView()
|
||||||
|
popView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStudentSelect(studentWithSemesters: StudentWithSemesters) {
|
||||||
|
Timber.i("Select student ${studentWithSemesters.student.id}")
|
||||||
|
|
||||||
|
if (studentWithSemesters.student.isCurrent) {
|
||||||
|
view?.popView()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flowWithResource { studentRepository.switchStudent(studentWithSemesters) }
|
||||||
|
.onEach {
|
||||||
|
when (it.status) {
|
||||||
|
Status.LOADING -> Timber.i("Attempt to change a student")
|
||||||
|
Status.SUCCESS -> {
|
||||||
|
Timber.i("Change a student result: Success")
|
||||||
|
view?.recreateMainView()
|
||||||
|
}
|
||||||
|
Status.ERROR -> {
|
||||||
|
Timber.i("Change a student result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.afterLoading { view?.popView() }
|
||||||
|
.launch("switch")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData() {
|
||||||
|
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
|
||||||
|
when (it.status) {
|
||||||
|
Status.LOADING -> Timber.i("Loading account data started")
|
||||||
|
Status.SUCCESS -> {
|
||||||
|
Timber.i("Loading account result: Success")
|
||||||
|
view?.updateData(createAccountItems(it.data!!))
|
||||||
|
}
|
||||||
|
Status.ERROR -> {
|
||||||
|
Timber.i("Loading account result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAccountItems(items: List<StudentWithSemesters>) = items.map {
|
||||||
|
AccountItem(it, AccountItem.ViewType.ITEM)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.account.accountquick
|
||||||
|
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
import io.github.wulkanowy.ui.modules.account.AccountItem
|
||||||
|
|
||||||
|
interface AccountQuickView : BaseView {
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun updateData(data: List<AccountItem<*>>)
|
||||||
|
|
||||||
|
fun recreateMainView()
|
||||||
|
|
||||||
|
fun popView()
|
||||||
|
|
||||||
|
fun openAccountView()
|
||||||
|
}
|
@ -26,6 +26,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
|||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -60,6 +61,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode
|
override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode
|
||||||
|
|
||||||
private var actionMode: ActionMode? = null
|
private var actionMode: ActionMode? = null
|
||||||
|
|
||||||
private val actionModeCallback = object : ActionMode.Callback {
|
private val actionModeCallback = object : ActionMode.Callback {
|
||||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
val inflater = mode.menuInflater
|
val inflater = mode.menuInflater
|
||||||
@ -111,6 +113,8 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
attendanceSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
attendanceSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
attendanceErrorRetry.setOnClickListener { presenter.onRetry() }
|
attendanceErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
attendanceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
attendanceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
|
||||||
@ -222,6 +226,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
setDateRangeLimiter(SchooldaysRangeLimiter())
|
setDateRangeLimiter(SchooldaysRangeLimiter())
|
||||||
version = DatePickerDialog.Version.VERSION_2
|
version = DatePickerDialog.Version.VERSION_2
|
||||||
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
||||||
|
vibrate(false)
|
||||||
show(this@AttendanceFragment.parentFragmentManager, null)
|
show(this@AttendanceFragment.parentFragmentManager, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import io.github.wulkanowy.databinding.FragmentAttendanceSummaryBinding
|
|||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -56,6 +57,8 @@ class AttendanceSummaryFragment :
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
attendanceSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
attendanceSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
attendanceSummarySwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
attendanceSummarySwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
attendanceSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
|
attendanceSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
attendanceSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
attendanceSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import io.github.wulkanowy.databinding.FragmentConferenceBinding
|
|||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -47,7 +48,9 @@ class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.frag
|
|||||||
}
|
}
|
||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
conferenceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
conferenceSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
conferenceSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
conferenceSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
conferenceErrorRetry.setOnClickListener { presenter.onRetry() }
|
conferenceErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
conferenceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
conferenceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
|||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -55,6 +56,8 @@ class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam),
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
examSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
examSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
examSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
examSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
examErrorRetry.setOnClickListener { presenter.onRetry() }
|
examErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
examErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
examErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.ui.modules.grade
|
package io.github.wulkanowy.ui.modules.grade
|
||||||
|
|
||||||
import io.github.wulkanowy.data.Resource
|
import io.github.wulkanowy.data.Resource
|
||||||
|
import io.github.wulkanowy.data.Status
|
||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
@ -33,68 +34,144 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
|
|
||||||
private val minusModifier get() = preferencesRepository.gradeMinusModifier
|
private val minusModifier get() = preferencesRepository.gradeMinusModifier
|
||||||
|
|
||||||
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) = flowWithResourceIn {
|
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) =
|
||||||
|
flowWithResourceIn {
|
||||||
val semesters = semesterRepository.getSemesters(student)
|
val semesters = semesterRepository.getSemesters(student)
|
||||||
|
|
||||||
when (preferencesRepository.gradeAverageMode) {
|
when (preferencesRepository.gradeAverageMode) {
|
||||||
ONE_SEMESTER -> getSemesterDetailsWithAverage(student, semesters.single { it.semesterId == semesterId }, forceRefresh)
|
ONE_SEMESTER -> getGradeSubjects(
|
||||||
BOTH_SEMESTERS -> calculateBothSemestersAverage(student, semesters, semesterId, forceRefresh)
|
student = student,
|
||||||
ALL_YEAR -> calculateAllYearAverage(student, semesters, semesterId, forceRefresh)
|
semester = semesters.single { it.semesterId == semesterId },
|
||||||
|
forceRefresh = forceRefresh
|
||||||
|
)
|
||||||
|
BOTH_SEMESTERS -> calculateCombinedAverage(
|
||||||
|
student = student,
|
||||||
|
semesters = semesters,
|
||||||
|
semesterId = semesterId,
|
||||||
|
forceRefresh = forceRefresh,
|
||||||
|
averageMode = BOTH_SEMESTERS
|
||||||
|
)
|
||||||
|
ALL_YEAR -> calculateCombinedAverage(
|
||||||
|
student = student,
|
||||||
|
semesters = semesters,
|
||||||
|
semesterId = semesterId,
|
||||||
|
forceRefresh = forceRefresh,
|
||||||
|
averageMode = ALL_YEAR
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
|
|
||||||
private fun calculateBothSemestersAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
|
private fun calculateCombinedAverage(
|
||||||
|
student: Student,
|
||||||
|
semesters: List<Semester>,
|
||||||
|
semesterId: Int,
|
||||||
|
forceRefresh: Boolean,
|
||||||
|
averageMode: GradeAverageMode
|
||||||
|
): Flow<Resource<List<GradeSubject>>> {
|
||||||
|
val gradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||||
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
val firstSemester =
|
||||||
|
semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||||
|
|
||||||
val selectedSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh)
|
val selectedSemesterGradeSubjects =
|
||||||
|
getGradeSubjects(student, selectedSemester, forceRefresh)
|
||||||
|
|
||||||
return if (selectedSemester == firstSemester) selectedSemesterDetailsWithAverage else {
|
if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects
|
||||||
val firstSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, firstSemester, forceRefresh)
|
|
||||||
selectedSemesterDetailsWithAverage.combine(firstSemesterDetailsWithAverage) { selectedDetails, secondDetails ->
|
val firstSemesterGradeSubjects =
|
||||||
val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 }
|
getGradeSubjects(student, firstSemester, forceRefresh)
|
||||||
secondDetails.copy(data = selectedDetails.data?.map { selected ->
|
|
||||||
val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject }
|
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
|
||||||
selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
|
if (firstSemesterGradeSubject.status == Status.ERROR) {
|
||||||
val selectedGrades = selected.grades.updateModifiers(student).calcAverage()
|
return@combine firstSemesterGradeSubject
|
||||||
(selectedGrades + (second?.grades?.updateModifiers(student)?.calcAverage() ?: selectedGrades)) / 2
|
|
||||||
} else (selected.average + (second?.average ?: selected.average)) / 2)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isAnyAverage = secondSemesterGradeSubject.data.orEmpty().any { it.average != .0 }
|
||||||
|
val updatedData = secondSemesterGradeSubject.data?.map { secondSemesterSubject ->
|
||||||
|
val firstSemesterSubject = firstSemesterGradeSubject.data.orEmpty()
|
||||||
|
.singleOrNull { it.subject == secondSemesterSubject.subject }
|
||||||
|
|
||||||
|
val updatedAverage = if (averageMode == ALL_YEAR) {
|
||||||
|
calculateAllYearAverage(
|
||||||
|
student = student,
|
||||||
|
isAnyAverage = isAnyAverage,
|
||||||
|
gradeAverageForceCalc = gradeAverageForceCalc,
|
||||||
|
secondSemesterSubject = secondSemesterSubject,
|
||||||
|
firstSemesterSubject = firstSemesterSubject
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
calculateBothSemestersAverage(
|
||||||
|
student = student,
|
||||||
|
isAnyAverage = isAnyAverage,
|
||||||
|
gradeAverageForceCalc = gradeAverageForceCalc,
|
||||||
|
secondSemesterSubject = secondSemesterSubject,
|
||||||
|
firstSemesterSubject = firstSemesterSubject
|
||||||
|
)
|
||||||
|
}
|
||||||
|
secondSemesterSubject.copy(average = updatedAverage)
|
||||||
|
}
|
||||||
|
secondSemesterGradeSubject.copy(data = updatedData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
|
private fun calculateAllYearAverage(
|
||||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
student: Student,
|
||||||
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
isAnyAverage: Boolean,
|
||||||
|
gradeAverageForceCalc: Boolean,
|
||||||
|
secondSemesterSubject: GradeSubject,
|
||||||
|
firstSemesterSubject: GradeSubject?
|
||||||
|
) = if (!isAnyAverage || gradeAverageForceCalc) {
|
||||||
|
val updatedSecondSemesterGrades =
|
||||||
|
secondSemesterSubject.grades.updateModifiers(student)
|
||||||
|
val updatedFirstSemesterGrades =
|
||||||
|
firstSemesterSubject?.grades?.updateModifiers(student).orEmpty()
|
||||||
|
|
||||||
val selectedSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh)
|
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage()
|
||||||
|
} else {
|
||||||
return if (selectedSemester == firstSemester) selectedSemesterDetailsWithAverage else {
|
secondSemesterSubject.average
|
||||||
val firstSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, firstSemester, forceRefresh)
|
|
||||||
selectedSemesterDetailsWithAverage.combine(firstSemesterDetailsWithAverage) { selectedDetails, secondDetails ->
|
|
||||||
val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 }
|
|
||||||
secondDetails.copy(data = selectedDetails.data?.map { selected ->
|
|
||||||
val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject }
|
|
||||||
selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
|
|
||||||
(selected.grades.updateModifiers(student) + second?.grades?.updateModifiers(student).orEmpty()).calcAverage()
|
|
||||||
} else selected.average)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSemesterDetailsWithAverage(student: Student, semester: Semester, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
|
private fun calculateBothSemestersAverage(
|
||||||
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh).map { res ->
|
student: Student,
|
||||||
|
isAnyAverage: Boolean,
|
||||||
|
gradeAverageForceCalc: Boolean,
|
||||||
|
secondSemesterSubject: GradeSubject,
|
||||||
|
firstSemesterSubject: GradeSubject?
|
||||||
|
) = if (!isAnyAverage || gradeAverageForceCalc) {
|
||||||
|
val secondSemesterAverage =
|
||||||
|
secondSemesterSubject.grades.updateModifiers(student).calcAverage()
|
||||||
|
val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
|
||||||
|
?.calcAverage() ?: secondSemesterAverage
|
||||||
|
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
||||||
|
|
||||||
|
(secondSemesterAverage + firstSemesterAverage) / divider
|
||||||
|
} else {
|
||||||
|
(secondSemesterSubject.average + (firstSemesterSubject?.average ?: secondSemesterSubject.average)) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getGradeSubjects(
|
||||||
|
student: Student,
|
||||||
|
semester: Semester,
|
||||||
|
forceRefresh: Boolean
|
||||||
|
): Flow<Resource<List<GradeSubject>>> {
|
||||||
|
val gradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||||
|
|
||||||
|
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
|
||||||
|
.map { res ->
|
||||||
val (details, summaries) = res.data ?: null to null
|
val (details, summaries) = res.data ?: null to null
|
||||||
val isAnyAverage = summaries.orEmpty().any { it.average != .0 }
|
val isAnyAverage = summaries.orEmpty().any { it.average != .0 }
|
||||||
val allGrades = details.orEmpty().groupBy { it.subject }
|
val allGrades = details.orEmpty().groupBy { it.subject }
|
||||||
|
|
||||||
val items = summaries?.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage)?.map { summary ->
|
val items = summaries?.emulateEmptySummaries(
|
||||||
|
student,
|
||||||
|
semester,
|
||||||
|
allGrades.toList(),
|
||||||
|
isAnyAverage
|
||||||
|
)?.map { summary ->
|
||||||
val grades = allGrades[summary.subject].orEmpty()
|
val grades = allGrades[summary.subject].orEmpty()
|
||||||
GradeDetailsWithAverage(
|
GradeSubject(
|
||||||
subject = summary.subject,
|
subject = summary.subject,
|
||||||
average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
|
average = if (!isAnyAverage || gradeAverageForceCalc) {
|
||||||
grades.updateModifiers(student).calcAverage()
|
grades.updateModifiers(student).calcAverage()
|
||||||
} else summary.average,
|
} else summary.average,
|
||||||
points = summary.pointsSum,
|
points = summary.pointsSum,
|
||||||
@ -107,7 +184,12 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<GradeSummary>.emulateEmptySummaries(student: Student, semester: Semester, grades: List<Pair<String, List<Grade>>>, calcAverage: Boolean): List<GradeSummary> {
|
private fun List<GradeSummary>.emulateEmptySummaries(
|
||||||
|
student: Student,
|
||||||
|
semester: Semester,
|
||||||
|
grades: List<Pair<String, List<Grade>>>,
|
||||||
|
calcAverage: Boolean
|
||||||
|
): List<GradeSummary> {
|
||||||
if (isNotEmpty() && size > grades.size) return this
|
if (isNotEmpty() && size > grades.size) return this
|
||||||
|
|
||||||
return grades.mapIndexed { i, (subject, details) ->
|
return grades.mapIndexed { i, (subject, details) ->
|
||||||
|
@ -33,7 +33,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
private var semesterSwitchMenu: MenuItem? = null
|
private var semesterSwitchMenu: MenuItem? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SAVED_SEMESTER_KEY = "CURRENT_SEMESTER"
|
|
||||||
|
|
||||||
fun newInstance() = GradeFragment()
|
fun newInstance() = GradeFragment()
|
||||||
}
|
}
|
||||||
@ -52,7 +51,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding = FragmentGradeBinding.bind(view)
|
binding = FragmentGradeBinding.bind(view)
|
||||||
presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY))
|
presenter.onAttachView(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
@ -161,11 +160,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
|
||||||
super.onSaveInstanceState(outState)
|
|
||||||
outState.putInt(SAVED_SEMESTER_KEY, presenter.selectedIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
@ -21,8 +21,7 @@ class GradePresenter @Inject constructor(
|
|||||||
private val analytics: AnalyticsHelper
|
private val analytics: AnalyticsHelper
|
||||||
) : BasePresenter<GradeView>(errorHandler, studentRepository) {
|
) : BasePresenter<GradeView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
var selectedIndex = 0
|
private var selectedIndex = 0
|
||||||
private set
|
|
||||||
|
|
||||||
private var schoolYear = 0
|
private var schoolYear = 0
|
||||||
|
|
||||||
@ -32,9 +31,8 @@ class GradePresenter @Inject constructor(
|
|||||||
|
|
||||||
private lateinit var lastError: Throwable
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
fun onAttachView(view: GradeView, savedIndex: Int?) {
|
override fun onAttachView(view: GradeView) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
selectedIndex = savedIndex ?: 0
|
|
||||||
view.initView()
|
view.initView()
|
||||||
Timber.i("Grade view was initialized with $selectedIndex index")
|
Timber.i("Grade view was initialized with $selectedIndex index")
|
||||||
errorHandler.showErrorMessage = ::showErrorViewOnError
|
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.modules.grade
|
|||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||||
|
|
||||||
data class GradeDetailsWithAverage(
|
data class GradeSubject(
|
||||||
val subject: String,
|
val subject: String,
|
||||||
val average: Double,
|
val average: Double,
|
||||||
val points: String,
|
val points: String,
|
@ -17,6 +17,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
|
|||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -65,7 +66,9 @@ class GradeDetailsFragment :
|
|||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
adapter = gradeDetailsAdapter
|
adapter = gradeDetailsAdapter
|
||||||
}
|
}
|
||||||
gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
gradeDetailsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
gradeDetailsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
gradeDetailsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
gradeDetailsErrorRetry.setOnClickListener { presenter.onRetry() }
|
gradeDetailsErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
gradeDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
gradeDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@ import io.github.wulkanowy.data.repositories.StudentRepository
|
|||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
|
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC
|
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE
|
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE
|
||||||
|
import io.github.wulkanowy.ui.modules.grade.GradeSubject
|
||||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.afterLoading
|
import io.github.wulkanowy.utils.afterLoading
|
||||||
import io.github.wulkanowy.utils.flowWithResource
|
import io.github.wulkanowy.utils.flowWithResource
|
||||||
@ -201,8 +201,9 @@ class GradeDetailsPresenter @Inject constructor(
|
|||||||
}.launch()
|
}.launch()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateNewGradesAmount(grades: List<GradeDetailsWithAverage>) {
|
private fun updateNewGradesAmount(grades: List<GradeSubject>) {
|
||||||
newGradesAmount = grades.sumBy { item -> item.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
|
newGradesAmount =
|
||||||
|
grades.sumBy { item -> item.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
@ -217,7 +218,7 @@ class GradeDetailsPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
private fun createGradeItems(items: List<GradeDetailsWithAverage>): List<GradeDetailsItem> {
|
private fun createGradeItems(items: List<GradeSubject>): List<GradeDetailsItem> {
|
||||||
return items
|
return items
|
||||||
.let { gradesWithAverages ->
|
.let { gradesWithAverages ->
|
||||||
if (!preferencesRepository.showSubjectsWithoutGrades) {
|
if (!preferencesRepository.showSubjectsWithoutGrades) {
|
||||||
|
@ -78,18 +78,18 @@ class GradeStatisticsAdapter @Inject constructor() :
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
when (holder) {
|
when (holder) {
|
||||||
is PartialViewHolder -> bindPartialChart(holder, items[position].partial!!)
|
is PartialViewHolder -> bindPartialChart(holder.binding, items[position].partial!!)
|
||||||
is SemesterViewHolder -> bindSemesterChart(holder, items[position].semester!!)
|
is SemesterViewHolder -> bindSemesterChart(holder.binding, items[position].semester!!)
|
||||||
is PointsViewHolder -> bindBarChart(holder, items[position].points!!)
|
is PointsViewHolder -> bindBarChart(holder.binding, items[position].points!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindPartialChart(holder: PartialViewHolder, partials: GradePartialStatistics) {
|
private fun bindPartialChart(binding: ItemGradeStatisticsPieBinding, partials: GradePartialStatistics) {
|
||||||
bindPieChart(holder.binding, partials.subject, partials.classAverage, partials.classAmounts)
|
bindPieChart(binding, partials.subject, partials.classAverage, partials.classAmounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindSemesterChart(holder: SemesterViewHolder, semester: GradeSemesterStatistics) {
|
private fun bindSemesterChart(binding: ItemGradeStatisticsPieBinding, semester: GradeSemesterStatistics) {
|
||||||
bindPieChart(holder.binding, semester.subject, semester.average, semester.amounts)
|
bindPieChart(binding, semester.subject, semester.average, semester.amounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindPieChart(binding: ItemGradeStatisticsPieBinding, subject: String, average: String, amounts: List<Int>) {
|
private fun bindPieChart(binding: ItemGradeStatisticsPieBinding, subject: String, average: String, amounts: List<Int>) {
|
||||||
@ -103,9 +103,12 @@ class GradeStatisticsAdapter @Inject constructor() :
|
|||||||
else -> materialGradeColors
|
else -> materialGradeColors
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataset = PieDataSet(amounts.mapIndexed { grade, amount ->
|
val dataset = PieDataSet(
|
||||||
|
amounts.mapIndexed { grade, amount ->
|
||||||
PieEntry(amount.toFloat(), (grade + 1).toString())
|
PieEntry(amount.toFloat(), (grade + 1).toString())
|
||||||
}.reversed().filterNot { it.value == 0f }, "Legenda")
|
}.reversed().filterNot { it.value == 0f },
|
||||||
|
binding.root.context.getString(R.string.grade_statistics_legend)
|
||||||
|
)
|
||||||
|
|
||||||
with(dataset) {
|
with(dataset) {
|
||||||
valueTextSize = 12f
|
valueTextSize = 12f
|
||||||
@ -138,11 +141,13 @@ class GradeStatisticsAdapter @Inject constructor() :
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val numberOfGradesString = amounts.fold(0) { acc, it -> acc + it }
|
||||||
|
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
|
||||||
|
val averageString = binding.root.context.getString(R.string.grade_statistics_average, average)
|
||||||
|
|
||||||
minAngleForSlices = 25f
|
minAngleForSlices = 25f
|
||||||
description.isEnabled = false
|
description.isEnabled = false
|
||||||
centerText = amounts.fold(0) { acc, it -> acc + it }
|
centerText = numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() }.orEmpty()
|
||||||
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } +
|
|
||||||
("\n\nŚrednia: $average").takeIf { average.isNotBlank() }.orEmpty()
|
|
||||||
|
|
||||||
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
|
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
|
||||||
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
|
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
|
||||||
@ -150,8 +155,8 @@ class GradeStatisticsAdapter @Inject constructor() :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindBarChart(holder: PointsViewHolder, points: GradePointsStatistics) {
|
private fun bindBarChart(binding: ItemGradeStatisticsBarBinding, points: GradePointsStatistics) {
|
||||||
with(holder.binding.gradeStatisticsBarTitle) {
|
with(binding.gradeStatisticsBarTitle) {
|
||||||
text = points.subject
|
text = points.subject
|
||||||
visibility = if (items.size == 1) GONE else VISIBLE
|
visibility = if (items.size == 1) GONE else VISIBLE
|
||||||
}
|
}
|
||||||
@ -159,18 +164,18 @@ class GradeStatisticsAdapter @Inject constructor() :
|
|||||||
val dataset = BarDataSet(listOf(
|
val dataset = BarDataSet(listOf(
|
||||||
BarEntry(1f, points.others.toFloat()),
|
BarEntry(1f, points.others.toFloat()),
|
||||||
BarEntry(2f, points.student.toFloat())
|
BarEntry(2f, points.student.toFloat())
|
||||||
), "Legenda")
|
), binding.root.context.getString(R.string.grade_statistics_legend))
|
||||||
|
|
||||||
with(dataset) {
|
with(dataset) {
|
||||||
valueTextSize = 12f
|
valueTextSize = 12f
|
||||||
valueTextColor = holder.binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
valueTextColor = binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||||
valueFormatter = object : ValueFormatter() {
|
valueFormatter = object : ValueFormatter() {
|
||||||
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%"
|
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%"
|
||||||
}
|
}
|
||||||
colors = gradePointsColors
|
colors = gradePointsColors
|
||||||
}
|
}
|
||||||
|
|
||||||
with(holder.binding.gradeStatisticsBar) {
|
with(binding.gradeStatisticsBar) {
|
||||||
setTouchEnabled(false)
|
setTouchEnabled(false)
|
||||||
if (items.size == 1) animateXY(1000, 1000)
|
if (items.size == 1) animateXY(1000, 1000)
|
||||||
data = BarData(dataset).apply {
|
data = BarData(dataset).apply {
|
||||||
@ -179,12 +184,12 @@ class GradeStatisticsAdapter @Inject constructor() :
|
|||||||
}
|
}
|
||||||
legend.setCustom(listOf(
|
legend.setCustom(listOf(
|
||||||
LegendEntry().apply {
|
LegendEntry().apply {
|
||||||
label = "Średnia klasy"
|
label = binding.root.context.getString(R.string.grade_statistics_average_class)
|
||||||
formColor = gradePointsColors[0]
|
formColor = gradePointsColors[0]
|
||||||
form = Legend.LegendForm.SQUARE
|
form = Legend.LegendForm.SQUARE
|
||||||
},
|
},
|
||||||
LegendEntry().apply {
|
LegendEntry().apply {
|
||||||
label = "Uczeń"
|
label = binding.root.context.getString(R.string.grade_statistics_average_student)
|
||||||
formColor = gradePointsColors[1]
|
formColor = gradePointsColors[1]
|
||||||
form = Legend.LegendForm.SQUARE
|
form = Legend.LegendForm.SQUARE
|
||||||
}
|
}
|
||||||
@ -193,7 +198,7 @@ class GradeStatisticsAdapter @Inject constructor() :
|
|||||||
|
|
||||||
description.isEnabled = false
|
description.isEnabled = false
|
||||||
|
|
||||||
holder.binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary).let {
|
binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary).let {
|
||||||
axisLeft.textColor = it
|
axisLeft.textColor = it
|
||||||
axisRight.textColor = it
|
axisRight.textColor = it
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
|
|||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -69,6 +70,8 @@ class GradeStatisticsFragment :
|
|||||||
gradeStatisticsSubjectsContainer.setElevationCompat(requireContext().dpToPx(1f))
|
gradeStatisticsSubjectsContainer.setElevationCompat(requireContext().dpToPx(1f))
|
||||||
|
|
||||||
gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
gradeStatisticsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
gradeStatisticsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
gradeStatisticsErrorRetry.setOnClickListener { presenter.onRetry() }
|
gradeStatisticsErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
gradeStatisticsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
gradeStatisticsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -172,6 +172,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
enableSwipe(true)
|
enableSwipe(true)
|
||||||
showRefresh(true)
|
showRefresh(true)
|
||||||
|
showProgress(false)
|
||||||
updateData(it.data!!, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
|
updateData(it.data!!, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
|
||||||
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
|
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.databinding.FragmentGradeSummaryBinding
|
|||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeView
|
import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -52,7 +53,9 @@ class GradeSummaryFragment :
|
|||||||
adapter = gradeSummaryAdapter
|
adapter = gradeSummaryAdapter
|
||||||
}
|
}
|
||||||
with(binding) {
|
with(binding) {
|
||||||
gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
gradeSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
gradeSummarySwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
gradeSummarySwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
gradeSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
|
gradeSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
gradeSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
gradeSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.StudentRepository
|
|||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
|
import io.github.wulkanowy.ui.modules.grade.GradeSubject
|
||||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.afterLoading
|
import io.github.wulkanowy.utils.afterLoading
|
||||||
import io.github.wulkanowy.utils.flowWithResourceIn
|
import io.github.wulkanowy.utils.flowWithResourceIn
|
||||||
@ -135,14 +135,14 @@ class GradeSummaryPresenter @Inject constructor(
|
|||||||
cancelJobs("load")
|
cancelJobs("load")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createGradeSummaryItems(items: List<GradeDetailsWithAverage>): List<GradeSummary> {
|
private fun createGradeSummaryItems(items: List<GradeSubject>): List<GradeSummary> {
|
||||||
return items
|
return items
|
||||||
.filter { !checkEmpty(it) }
|
.filter { !checkEmpty(it) }
|
||||||
.sortedBy { it.subject }
|
.sortedBy { it.subject }
|
||||||
.map { it.summary.copy(average = it.average) }
|
.map { it.summary.copy(average = it.average) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkEmpty(gradeSummary: GradeDetailsWithAverage): Boolean {
|
private fun checkEmpty(gradeSummary: GradeSubject): Boolean {
|
||||||
return gradeSummary.run {
|
return gradeSummary.run {
|
||||||
summary.finalGrade.isBlank()
|
summary.finalGrade.isBlank()
|
||||||
&& summary.predictedGrade.isBlank()
|
&& summary.predictedGrade.isBlank()
|
||||||
|
@ -15,6 +15,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
|||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -55,6 +56,8 @@ class HomeworkFragment : BaseFragment<FragmentHomeworkBinding>(R.layout.fragment
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
homeworkSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
homeworkSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
homeworkSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
homeworkSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
homeworkErrorRetry.setOnClickListener { presenter.onRetry() }
|
homeworkErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
homeworkErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
homeworkErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import io.github.wulkanowy.data.db.entities.LuckyNumber
|
|||||||
import io.github.wulkanowy.databinding.FragmentLuckyNumberBinding
|
import io.github.wulkanowy.databinding.FragmentLuckyNumberBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -38,7 +39,9 @@ class LuckyNumberFragment :
|
|||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
luckyNumberSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
luckyNumberSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
luckyNumberSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
luckyNumberSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
luckyNumberErrorRetry.setOnClickListener { presenter.onRetry() }
|
luckyNumberErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
luckyNumberErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
luckyNumberErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import android.os.Build.VERSION_CODES.LOLLIPOP
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
@ -28,7 +29,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.databinding.ActivityMainBinding
|
import io.github.wulkanowy.databinding.ActivityMainBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseActivity
|
import io.github.wulkanowy.ui.base.BaseActivity
|
||||||
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
|
||||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
@ -65,25 +66,30 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
|
|
||||||
private val overlayProvider by lazy { ElevationOverlayProvider(this) }
|
private val overlayProvider by lazy { ElevationOverlayProvider(this) }
|
||||||
|
|
||||||
private val navController = FragNavController(supportFragmentManager, R.id.mainFragmentContainer)
|
private val navController =
|
||||||
|
FragNavController(supportFragmentManager, R.id.mainFragmentContainer)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val EXTRA_START_MENU = "extraStartMenu"
|
const val EXTRA_START_MENU = "extraStartMenu"
|
||||||
|
|
||||||
fun getStartIntent(context: Context, startMenu: MainView.Section? = null, clear: Boolean = false): Intent {
|
fun getStartIntent(
|
||||||
return Intent(context, MainActivity::class.java)
|
context: Context,
|
||||||
.apply {
|
startMenu: MainView.Section? = null,
|
||||||
|
clear: Boolean = false
|
||||||
|
) = Intent(context, MainActivity::class.java).apply {
|
||||||
if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
|
if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
|
||||||
startMenu?.let { putExtra(EXTRA_START_MENU, it.id) }
|
startMenu?.let { putExtra(EXTRA_START_MENU, it.id) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override val isRootView get() = navController.isRootFragment
|
override val isRootView get() = navController.isRootFragment
|
||||||
|
|
||||||
override val currentStackSize get() = navController.currentStack?.size
|
override val currentStackSize get() = navController.currentStack?.size
|
||||||
|
|
||||||
override val currentViewTitle get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { getString(it) }
|
override val currentViewTitle
|
||||||
|
get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let {
|
||||||
|
getString(it)
|
||||||
|
}
|
||||||
|
|
||||||
override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString
|
override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString
|
||||||
|
|
||||||
@ -106,7 +112,10 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
messageContainer = binding.mainFragmentContainer
|
messageContainer = binding.mainFragmentContainer
|
||||||
updateHelper.messageContainer = binding.mainFragmentContainer
|
updateHelper.messageContainer = binding.mainFragmentContainer
|
||||||
|
|
||||||
presenter.onAttachView(this, MainView.Section.values().singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) })
|
val section = MainView.Section.values()
|
||||||
|
.singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) }
|
||||||
|
|
||||||
|
presenter.onAttachView(this, section)
|
||||||
|
|
||||||
with(navController) {
|
with(navController) {
|
||||||
initialize(startMenuIndex, savedInstanceState)
|
initialize(startMenuIndex, savedInstanceState)
|
||||||
@ -132,21 +141,49 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
val shortcutsList = mutableListOf<ShortcutInfo>()
|
val shortcutsList = mutableListOf<ShortcutInfo>()
|
||||||
|
|
||||||
listOf(
|
listOf(
|
||||||
Triple(getString(R.string.grade_title), R.drawable.ic_shortcut_grade, MainView.Section.GRADE),
|
Triple(
|
||||||
Triple(getString(R.string.attendance_title), R.drawable.ic_shortcut_attendance, MainView.Section.ATTENDANCE),
|
getString(R.string.grade_title),
|
||||||
Triple(getString(R.string.exam_title), R.drawable.ic_shortcut_exam, MainView.Section.EXAM),
|
R.drawable.ic_shortcut_grade,
|
||||||
Triple(getString(R.string.timetable_title), R.drawable.ic_shortcut_timetable, MainView.Section.TIMETABLE),
|
MainView.Section.GRADE
|
||||||
Triple(getString(R.string.message_title), R.drawable.ic_shortcut_message, MainView.Section.MESSAGE)
|
),
|
||||||
|
Triple(
|
||||||
|
getString(R.string.attendance_title),
|
||||||
|
R.drawable.ic_shortcut_attendance,
|
||||||
|
MainView.Section.ATTENDANCE
|
||||||
|
),
|
||||||
|
Triple(
|
||||||
|
getString(R.string.exam_title),
|
||||||
|
R.drawable.ic_shortcut_exam,
|
||||||
|
MainView.Section.EXAM
|
||||||
|
),
|
||||||
|
Triple(
|
||||||
|
getString(R.string.timetable_title),
|
||||||
|
R.drawable.ic_shortcut_timetable,
|
||||||
|
MainView.Section.TIMETABLE
|
||||||
|
),
|
||||||
|
Triple(
|
||||||
|
getString(R.string.message_title),
|
||||||
|
R.drawable.ic_shortcut_message,
|
||||||
|
MainView.Section.MESSAGE
|
||||||
|
)
|
||||||
).forEach { (title, icon, enum) ->
|
).forEach { (title, icon, enum) ->
|
||||||
shortcutsList.add(ShortcutInfo.Builder(applicationContext, title)
|
shortcutsList.add(
|
||||||
|
ShortcutInfo.Builder(applicationContext, title)
|
||||||
.setShortLabel(title)
|
.setShortLabel(title)
|
||||||
.setLongLabel(title)
|
.setLongLabel(title)
|
||||||
.setIcon(Icon.createWithResource(applicationContext, icon))
|
.setIcon(Icon.createWithResource(applicationContext, icon))
|
||||||
.setIntents(arrayOf(
|
.setIntents(
|
||||||
Intent(applicationContext, MainActivity::class.java).setAction(Intent.ACTION_VIEW),
|
arrayOf(
|
||||||
Intent(applicationContext, MainActivity::class.java).putExtra(EXTRA_START_MENU, enum.id)
|
Intent(applicationContext, MainActivity::class.java)
|
||||||
.setAction(Intent.ACTION_VIEW).addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)))
|
.setAction(Intent.ACTION_VIEW),
|
||||||
.build())
|
Intent(applicationContext, MainActivity::class.java)
|
||||||
|
.putExtra(EXTRA_START_MENU, enum.id)
|
||||||
|
.setAction(Intent.ACTION_VIEW)
|
||||||
|
.addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getSystemService<ShortcutManager>()?.dynamicShortcuts = shortcutsList
|
getSystemService<ShortcutManager>()?.dynamicShortcuts = shortcutsList
|
||||||
@ -160,20 +197,33 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
override fun initView() {
|
override fun initView() {
|
||||||
with(binding.mainToolbar) {
|
with(binding.mainToolbar) {
|
||||||
if (SDK_INT >= LOLLIPOP) stateListAnimator = null
|
if (SDK_INT >= LOLLIPOP) stateListAnimator = null
|
||||||
setBackgroundColor(overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(4f)))
|
setBackgroundColor(
|
||||||
|
overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(4f))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
with(binding.mainBottomNav) {
|
with(binding.mainBottomNav) {
|
||||||
addItems(listOf(
|
addItems(
|
||||||
|
listOf(
|
||||||
AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_main_grade, 0),
|
AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_main_grade, 0),
|
||||||
AHBottomNavigationItem(R.string.attendance_title, R.drawable.ic_main_attendance, 0),
|
AHBottomNavigationItem(
|
||||||
|
R.string.attendance_title,
|
||||||
|
R.drawable.ic_main_attendance,
|
||||||
|
0
|
||||||
|
),
|
||||||
AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_main_exam, 0),
|
AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_main_exam, 0),
|
||||||
AHBottomNavigationItem(R.string.timetable_title, R.drawable.ic_main_timetable, 0),
|
AHBottomNavigationItem(
|
||||||
|
R.string.timetable_title,
|
||||||
|
R.drawable.ic_main_timetable,
|
||||||
|
0
|
||||||
|
),
|
||||||
AHBottomNavigationItem(R.string.more_title, R.drawable.ic_main_more, 0)
|
AHBottomNavigationItem(R.string.more_title, R.drawable.ic_main_more, 0)
|
||||||
))
|
)
|
||||||
|
)
|
||||||
accentColor = getThemeAttrColor(R.attr.colorPrimary)
|
accentColor = getThemeAttrColor(R.attr.colorPrimary)
|
||||||
inactiveColor = getThemeAttrColor(R.attr.colorOnSurface, 153)
|
inactiveColor = getThemeAttrColor(R.attr.colorOnSurface, 153)
|
||||||
defaultBackgroundColor = overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(8f))
|
defaultBackgroundColor =
|
||||||
|
overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(8f))
|
||||||
titleState = ALWAYS_SHOW
|
titleState = ALWAYS_SHOW
|
||||||
currentItem = startMenuIndex
|
currentItem = startMenuIndex
|
||||||
isBehaviorTranslationEnabled = false
|
isBehaviorTranslationEnabled = false
|
||||||
@ -183,6 +233,13 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
|
|
||||||
with(navController) {
|
with(navController) {
|
||||||
setOnViewChangeListener { section, name ->
|
setOnViewChangeListener { section, name ->
|
||||||
|
binding.mainBottomNav.visibility =
|
||||||
|
if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) {
|
||||||
|
View.GONE
|
||||||
|
} else {
|
||||||
|
View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
analytics.setCurrentScreen(this@MainActivity, name)
|
analytics.setCurrentScreen(this@MainActivity, name)
|
||||||
presenter.onViewChange(section)
|
presenter.onViewChange(section)
|
||||||
}
|
}
|
||||||
@ -224,7 +281,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showAccountPicker() {
|
override fun showAccountPicker() {
|
||||||
navController.showDialogFragment(AccountDialog.newInstance())
|
navController.showDialogFragment(AccountQuickDialog.newInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showActionBarElevation(show: Boolean) {
|
override fun showActionBarElevation(show: Boolean) {
|
||||||
|
@ -64,6 +64,8 @@ interface MainView : BaseView {
|
|||||||
LUCKY_NUMBER(8),
|
LUCKY_NUMBER(8),
|
||||||
SETTINGS(9),
|
SETTINGS(9),
|
||||||
ABOUT(10),
|
ABOUT(10),
|
||||||
SCHOOL(11)
|
SCHOOL(11),
|
||||||
|
ACCOUNT(12),
|
||||||
|
STUDENT_INFO(13)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
this@MessagePreviewPresenter.attachments = it.data.attachments
|
this@MessagePreviewPresenter.attachments = it.data.attachments
|
||||||
view?.apply {
|
view?.apply {
|
||||||
setMessageWithAttachment(it.data)
|
setMessageWithAttachment(it.data)
|
||||||
|
showContent(true)
|
||||||
initOptions()
|
initOptions()
|
||||||
}
|
}
|
||||||
analytics.logEvent(
|
analytics.logEvent(
|
||||||
|
@ -19,6 +19,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
|||||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||||
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
|
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
|
||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -74,7 +75,9 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
|||||||
addItemDecoration(DividerItemDecoration(context))
|
addItemDecoration(DividerItemDecoration(context))
|
||||||
}
|
}
|
||||||
with(binding) {
|
with(binding) {
|
||||||
messageTabSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
messageTabSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
messageTabSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
messageTabSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
|
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
|||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
|
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
|
||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -56,7 +57,9 @@ class MobileDeviceFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
mobileDevicesSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
mobileDevicesSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
mobileDevicesSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
mobileDevicesErrorRetry.setOnClickListener { presenter.onRetry() }
|
mobileDevicesErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
mobileDevicesErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
mobileDevicesErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() }
|
mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() }
|
||||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
|
|||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -50,7 +51,9 @@ class NoteFragment : BaseFragment<FragmentNoteBinding>(R.layout.fragment_note),
|
|||||||
addItemDecoration(DividerItemDecoration(context))
|
addItemDecoration(DividerItemDecoration(context))
|
||||||
}
|
}
|
||||||
with(binding) {
|
with(binding) {
|
||||||
noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
noteSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
noteSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
noteSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
noteErrorRetry.setOnClickListener { presenter.onRetry() }
|
noteErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
noteErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
noteErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
|
|||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
|
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
|
||||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import io.github.wulkanowy.utils.openDialer
|
import io.github.wulkanowy.utils.openDialer
|
||||||
import io.github.wulkanowy.utils.openNavigation
|
import io.github.wulkanowy.utils.openNavigation
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -39,7 +40,9 @@ class SchoolFragment : BaseFragment<FragmentSchoolBinding>(R.layout.fragment_sch
|
|||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
schoolSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
schoolSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
schoolSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
schoolSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
schoolErrorRetry.setOnClickListener { presenter.onRetry() }
|
schoolErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
schoolErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
schoolErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
|
||||||
|
@ -81,10 +81,7 @@ class SchoolPresenter @Inject constructor(
|
|||||||
showEmpty(false)
|
showEmpty(false)
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
}
|
}
|
||||||
analytics.logEvent(
|
analytics.logEvent("load_item", "type" to "school")
|
||||||
"load_item",
|
|
||||||
"type" to "school"
|
|
||||||
)
|
|
||||||
} else view?.run {
|
} else view?.run {
|
||||||
Timber.i("Loading school result: No school info found")
|
Timber.i("Loading school result: No school info found")
|
||||||
showContent(!isViewEmpty)
|
showContent(!isViewEmpty)
|
||||||
|
@ -14,6 +14,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
|||||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
|
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
|
||||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -51,7 +52,9 @@ class TeacherFragment : BaseFragment<FragmentTeacherBinding>(R.layout.fragment_t
|
|||||||
addItemDecoration(DividerItemDecoration(context))
|
addItemDecoration(DividerItemDecoration(context))
|
||||||
}
|
}
|
||||||
with(binding) {
|
with(binding) {
|
||||||
teacherSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
teacherSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
teacherSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
teacherSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
teacherErrorRetry.setOnClickListener { presenter.onRetry() }
|
teacherErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
teacherErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
teacherErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,10 @@ class SettingsFragment : PreferenceFragmentCompat(),
|
|||||||
lingver.setLocale(requireContext(), langCode)
|
lingver.setLocale(requireContext(), langCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun updateLanguageToFollowSystem() {
|
||||||
|
lingver.setFollowSystemLocale(requireContext())
|
||||||
|
}
|
||||||
|
|
||||||
override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) {
|
override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) {
|
||||||
findPreference<Preference>(serviceEnablesKey)?.run {
|
findPreference<Preference>(serviceEnablesKey)?.run {
|
||||||
summary = if (isHolidays) getString(R.string.pref_services_suspended) else ""
|
summary = if (isHolidays) getString(R.string.pref_services_suspended) else ""
|
||||||
|
@ -42,14 +42,18 @@ class SettingsPresenter @Inject constructor(
|
|||||||
when (key) {
|
when (key) {
|
||||||
serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startPeriodicSyncWorker() else stopSyncWorker() }
|
serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startPeriodicSyncWorker() else stopSyncWorker() }
|
||||||
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startPeriodicSyncWorker(true)
|
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startPeriodicSyncWorker(true)
|
||||||
isDebugNotificationEnableKey -> chuckerCollector.showNotification = isDebugNotificationEnable
|
isDebugNotificationEnableKey -> chuckerCollector.showNotification =
|
||||||
|
isDebugNotificationEnable
|
||||||
appThemeKey -> view?.recreateView()
|
appThemeKey -> view?.recreateView()
|
||||||
isUpcomingLessonsNotificationsEnableKey -> if (!isUpcomingLessonsNotificationsEnable) timetableNotificationHelper.cancelNotification()
|
isUpcomingLessonsNotificationsEnableKey -> if (!isUpcomingLessonsNotificationsEnable) timetableNotificationHelper.cancelNotification()
|
||||||
appLanguageKey -> view?.run {
|
appLanguageKey -> view?.run {
|
||||||
val newLang = if (appLanguage == "system") appInfo.systemLanguage else appLanguage
|
if (appLanguage == "system") {
|
||||||
analytics.logEvent("language", "setting_changed" to newLang)
|
updateLanguageToFollowSystem()
|
||||||
|
analytics.logEvent("language", "setting_changed" to appInfo.systemLanguage)
|
||||||
updateLanguage(newLang)
|
} else {
|
||||||
|
updateLanguage(appLanguage)
|
||||||
|
analytics.logEvent("language", "setting_changed" to appLanguage)
|
||||||
|
}
|
||||||
recreateView()
|
recreateView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +75,10 @@ class SettingsPresenter @Inject constructor(
|
|||||||
analytics.logEvent("sync_now", "status" to "success")
|
analytics.logEvent("sync_now", "status" to "success")
|
||||||
}
|
}
|
||||||
WorkInfo.State.FAILED -> {
|
WorkInfo.State.FAILED -> {
|
||||||
showError(syncFailedString, Throwable(workInfo.outputData.getString("error")))
|
showError(
|
||||||
|
syncFailedString,
|
||||||
|
Throwable(workInfo.outputData.getString("error"))
|
||||||
|
)
|
||||||
analytics.logEvent("sync_now", "status" to "failed")
|
analytics.logEvent("sync_now", "status" to "failed")
|
||||||
}
|
}
|
||||||
else -> Timber.d("Sync now state: ${workInfo.state}")
|
else -> Timber.d("Sync now state: ${workInfo.state}")
|
||||||
|
@ -14,6 +14,8 @@ interface SettingsView : BaseView {
|
|||||||
|
|
||||||
fun updateLanguage(langCode: String)
|
fun updateLanguage(langCode: String)
|
||||||
|
|
||||||
|
fun updateLanguageToFollowSystem()
|
||||||
|
|
||||||
fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean)
|
fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean)
|
||||||
|
|
||||||
fun setSyncInProgress(inProgress: Boolean)
|
fun setSyncInProgress(inProgress: Boolean)
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.studentinfo
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import io.github.wulkanowy.databinding.ItemStudentInfoBinding
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class StudentInfoAdapter @Inject constructor() :
|
||||||
|
RecyclerView.Adapter<StudentInfoAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
var items = listOf<Pair<String, String>>()
|
||||||
|
|
||||||
|
var onItemClickListener: (position: Int) -> Unit = {}
|
||||||
|
|
||||||
|
var onItemLongClickListener: (text: String) -> Unit = {}
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
|
||||||
|
ItemStudentInfoBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val item = items[position]
|
||||||
|
|
||||||
|
with(holder.binding) {
|
||||||
|
studentInfoItemTitle.text = item.first
|
||||||
|
studentInfoItemSubtitle.text = item.second
|
||||||
|
|
||||||
|
with(root) {
|
||||||
|
setOnClickListener { onItemClickListener(position) }
|
||||||
|
setOnLongClickListener {
|
||||||
|
onItemLongClickListener(studentInfoItemSubtitle.text.toString())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(val binding: ItemStudentInfoBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
}
|
@ -0,0 +1,232 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.studentinfo
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.getSystemService
|
||||||
|
import androidx.core.view.get
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.data.enums.Gender
|
||||||
|
import io.github.wulkanowy.databinding.FragmentStudentInfoBinding
|
||||||
|
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.utils.getThemeAttrColor
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class StudentInfoFragment :
|
||||||
|
BaseFragment<FragmentStudentInfoBinding>(R.layout.fragment_student_info), StudentInfoView,
|
||||||
|
MainView.TitledView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: StudentInfoPresenter
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var studentInfoAdapter: StudentInfoAdapter
|
||||||
|
|
||||||
|
override val titleStringId: Int
|
||||||
|
get() = R.string.student_info_title
|
||||||
|
|
||||||
|
override val isViewEmpty get() = studentInfoAdapter.items.isEmpty()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val INFO_TYPE_ARGUMENT_KEY = "info_type"
|
||||||
|
|
||||||
|
private const val STUDENT_ARGUMENT_KEY = "student_with_semesters"
|
||||||
|
|
||||||
|
fun newInstance(type: StudentInfoView.Type, studentWithSemesters: StudentWithSemesters) =
|
||||||
|
StudentInfoFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(INFO_TYPE_ARGUMENT_KEY, type)
|
||||||
|
putSerializable(STUDENT_ARGUMENT_KEY, studentWithSemesters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
with(binding) {
|
||||||
|
studentInfoSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
studentInfoSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
studentInfoSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
|
studentInfoErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
|
studentInfoErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
}
|
||||||
|
|
||||||
|
with(studentInfoAdapter) {
|
||||||
|
onItemClickListener = presenter::onItemSelected
|
||||||
|
onItemLongClickListener = presenter::onItemLongClick
|
||||||
|
}
|
||||||
|
|
||||||
|
with(binding.studentInfoRecycler) {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
addItemDecoration(DividerItemDecoration(context, RecyclerView.VERTICAL))
|
||||||
|
setHasFixedSize(true)
|
||||||
|
adapter = studentInfoAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateData(data: List<Pair<String, String>>) {
|
||||||
|
with(studentInfoAdapter) {
|
||||||
|
items = data
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
menu[0].isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showPersonalTypeData(studentInfo: StudentInfo) {
|
||||||
|
updateData(
|
||||||
|
listOf(
|
||||||
|
getString(R.string.student_info_first_name) to studentInfo.firstName,
|
||||||
|
getString(R.string.student_info_second_name) to studentInfo.secondName,
|
||||||
|
getString(R.string.student_info_last_name) to studentInfo.surname,
|
||||||
|
getString(R.string.student_info_gender) to getString(if (studentInfo.gender == Gender.MALE) R.string.student_info_male else R.string.student_info_female),
|
||||||
|
getString(R.string.student_info_polish_citizenship) to getString(if (studentInfo.hasPolishCitizenship) R.string.all_yes else R.string.all_no),
|
||||||
|
getString(R.string.student_info_family_name) to studentInfo.familyName,
|
||||||
|
getString(R.string.student_info_parents_name) to studentInfo.parentsNames
|
||||||
|
).map {
|
||||||
|
if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showContactTypeData(studentInfo: StudentInfo) {
|
||||||
|
updateData(
|
||||||
|
listOf(
|
||||||
|
getString(R.string.student_info_phone) to studentInfo.phoneNumber,
|
||||||
|
getString(R.string.student_info_cellphone) to studentInfo.cellPhoneNumber,
|
||||||
|
getString(R.string.student_info_email) to studentInfo.email
|
||||||
|
).map {
|
||||||
|
if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
override fun showFamilyTypeData(studentInfo: StudentInfo) {
|
||||||
|
updateData(
|
||||||
|
listOf(
|
||||||
|
studentInfo.firstGuardian.kinship.capitalize() to studentInfo.firstGuardian.fullName,
|
||||||
|
studentInfo.secondGuardian.kinship.capitalize() to studentInfo.secondGuardian.fullName
|
||||||
|
).map {
|
||||||
|
if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showAddressTypeData(studentInfo: StudentInfo) {
|
||||||
|
updateData(
|
||||||
|
listOf(
|
||||||
|
getString(R.string.student_info_address) to studentInfo.address,
|
||||||
|
getString(R.string.student_info_registered_address) to studentInfo.registeredAddress,
|
||||||
|
getString(R.string.student_info_correspondence_address) to studentInfo.correspondenceAddress
|
||||||
|
).map {
|
||||||
|
if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showFirstGuardianTypeData(studentInfo: StudentInfo) {
|
||||||
|
updateData(
|
||||||
|
listOf(
|
||||||
|
getString(R.string.student_info_full_name) to studentInfo.firstGuardian.fullName,
|
||||||
|
getString(R.string.student_info_kinship) to studentInfo.firstGuardian.kinship,
|
||||||
|
getString(R.string.student_info_guardian_address) to studentInfo.firstGuardian.address,
|
||||||
|
getString(R.string.student_info_phones) to studentInfo.firstGuardian.phones,
|
||||||
|
getString(R.string.student_info_email) to studentInfo.firstGuardian.email
|
||||||
|
).map {
|
||||||
|
if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showSecondGuardianTypeData(studentInfo: StudentInfo) {
|
||||||
|
updateData(
|
||||||
|
listOf(
|
||||||
|
getString(R.string.student_info_full_name) to studentInfo.secondGuardian.fullName,
|
||||||
|
getString(R.string.student_info_kinship) to studentInfo.secondGuardian.kinship,
|
||||||
|
getString(R.string.student_info_guardian_address) to studentInfo.secondGuardian.address,
|
||||||
|
getString(R.string.student_info_phones) to studentInfo.secondGuardian.phones,
|
||||||
|
getString(R.string.student_info_email) to studentInfo.secondGuardian.email
|
||||||
|
).map {
|
||||||
|
if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openStudentInfoView(
|
||||||
|
infoType: StudentInfoView.Type,
|
||||||
|
studentWithSemesters: StudentWithSemesters
|
||||||
|
) {
|
||||||
|
(requireActivity() as MainActivity).pushView(newInstance(infoType, studentWithSemesters))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEmpty(show: Boolean) {
|
||||||
|
binding.studentInfoEmpty.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showErrorView(show: Boolean) {
|
||||||
|
binding.studentInfoError.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setErrorDetails(message: String) {
|
||||||
|
binding.studentInfoErrorMessage.text = message
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(show: Boolean) {
|
||||||
|
binding.studentInfoProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun enableSwipe(enable: Boolean) {
|
||||||
|
binding.studentInfoSwipe.isEnabled = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showContent(show: Boolean) {
|
||||||
|
binding.studentInfoRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hideRefresh() {
|
||||||
|
binding.studentInfoSwipe.isRefreshing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun copyToClipboard(text: String) {
|
||||||
|
val clipData = ClipData.newPlainText("student_info_wulkanowy", text)
|
||||||
|
requireActivity().getSystemService<ClipboardManager>()?.setPrimaryClip(clipData)
|
||||||
|
Toast.makeText(context, R.string.all_copied, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.studentinfo
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.Status
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.data.repositories.StudentInfoRepository
|
||||||
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
|
import io.github.wulkanowy.utils.afterLoading
|
||||||
|
import io.github.wulkanowy.utils.flowWithResourceIn
|
||||||
|
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class StudentInfoPresenter @Inject constructor(
|
||||||
|
errorHandler: ErrorHandler,
|
||||||
|
studentRepository: StudentRepository,
|
||||||
|
private val studentInfoRepository: StudentInfoRepository,
|
||||||
|
private val analytics: AnalyticsHelper
|
||||||
|
) : BasePresenter<StudentInfoView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
|
private lateinit var infoType: StudentInfoView.Type
|
||||||
|
|
||||||
|
private lateinit var studentWithSemesters: StudentWithSemesters
|
||||||
|
|
||||||
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
|
fun onAttachView(
|
||||||
|
view: StudentInfoView,
|
||||||
|
type: StudentInfoView.Type,
|
||||||
|
studentWithSemesters: StudentWithSemesters
|
||||||
|
) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
infoType = type
|
||||||
|
this.studentWithSemesters = studentWithSemesters
|
||||||
|
view.initView()
|
||||||
|
Timber.i("Student info $infoType view was initialized")
|
||||||
|
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSwipeRefresh() {
|
||||||
|
loadData(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRetry() {
|
||||||
|
view?.run {
|
||||||
|
showErrorView(false)
|
||||||
|
showProgress(true)
|
||||||
|
}
|
||||||
|
loadData(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDetailsClick() {
|
||||||
|
view?.showErrorDetailsDialog(lastError)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onItemSelected(position: Int) {
|
||||||
|
if (infoType != StudentInfoView.Type.FAMILY) return
|
||||||
|
|
||||||
|
if (position == 0) {
|
||||||
|
view?.openStudentInfoView(StudentInfoView.Type.FIRST_GUARDIAN, studentWithSemesters)
|
||||||
|
} else {
|
||||||
|
view?.openStudentInfoView(StudentInfoView.Type.SECOND_GUARDIAN, studentWithSemesters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onItemLongClick(text: String) {
|
||||||
|
view?.copyToClipboard(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData(forceRefresh: Boolean = false) {
|
||||||
|
flowWithResourceIn {
|
||||||
|
val semester = studentWithSemesters.semesters.getCurrentOrLast()
|
||||||
|
studentInfoRepository.getStudentInfo(
|
||||||
|
studentWithSemesters.student,
|
||||||
|
semester,
|
||||||
|
forceRefresh
|
||||||
|
)
|
||||||
|
}.onEach {
|
||||||
|
when (it.status) {
|
||||||
|
Status.LOADING -> Timber.i("Loading student info $infoType started")
|
||||||
|
Status.SUCCESS -> {
|
||||||
|
if (it.data != null) {
|
||||||
|
Timber.i("Loading student info $infoType result: Success")
|
||||||
|
showCorrectData(it.data)
|
||||||
|
view?.run {
|
||||||
|
showContent(true)
|
||||||
|
showEmpty(false)
|
||||||
|
showErrorView(false)
|
||||||
|
}
|
||||||
|
analytics.logEvent("load_item", "type" to "student_info")
|
||||||
|
} else {
|
||||||
|
Timber.i("Loading student info $infoType result: No school info found")
|
||||||
|
view?.run {
|
||||||
|
showContent(!isViewEmpty)
|
||||||
|
showEmpty(isViewEmpty)
|
||||||
|
showErrorView(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Status.ERROR -> {
|
||||||
|
Timber.i("Loading student info $infoType result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.afterLoading {
|
||||||
|
view?.run {
|
||||||
|
hideRefresh()
|
||||||
|
showProgress(false)
|
||||||
|
enableSwipe(true)
|
||||||
|
}
|
||||||
|
}.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showCorrectData(studentInfo: StudentInfo) {
|
||||||
|
when (infoType) {
|
||||||
|
StudentInfoView.Type.PERSONAL -> view?.showPersonalTypeData(studentInfo)
|
||||||
|
StudentInfoView.Type.CONTACT -> view?.showContactTypeData(studentInfo)
|
||||||
|
StudentInfoView.Type.ADDRESS -> view?.showAddressTypeData(studentInfo)
|
||||||
|
StudentInfoView.Type.FAMILY -> view?.showFamilyTypeData(studentInfo)
|
||||||
|
StudentInfoView.Type.SECOND_GUARDIAN -> view?.showSecondGuardianTypeData(studentInfo)
|
||||||
|
StudentInfoView.Type.FIRST_GUARDIAN -> view?.showFirstGuardianTypeData(studentInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
|
view?.run {
|
||||||
|
if (isViewEmpty) {
|
||||||
|
lastError = error
|
||||||
|
setErrorDetails(message)
|
||||||
|
showErrorView(true)
|
||||||
|
showEmpty(false)
|
||||||
|
showContent(false)
|
||||||
|
showProgress(false)
|
||||||
|
} else showError(message, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.studentinfo
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
|
interface StudentInfoView : BaseView {
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
PERSONAL, ADDRESS, CONTACT, FAMILY, FIRST_GUARDIAN, SECOND_GUARDIAN
|
||||||
|
}
|
||||||
|
|
||||||
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun updateData(data: List<Pair<String, String>>)
|
||||||
|
|
||||||
|
fun showPersonalTypeData(studentInfo: StudentInfo)
|
||||||
|
|
||||||
|
fun showContactTypeData(studentInfo: StudentInfo)
|
||||||
|
|
||||||
|
fun showAddressTypeData(studentInfo: StudentInfo)
|
||||||
|
|
||||||
|
fun showFamilyTypeData(studentInfo: StudentInfo)
|
||||||
|
|
||||||
|
fun showFirstGuardianTypeData(studentInfo: StudentInfo)
|
||||||
|
|
||||||
|
fun showSecondGuardianTypeData(studentInfo: StudentInfo)
|
||||||
|
|
||||||
|
fun openStudentInfoView(infoType: Type, studentWithSemesters: StudentWithSemesters)
|
||||||
|
|
||||||
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
|
fun showErrorView(show: Boolean)
|
||||||
|
|
||||||
|
fun setErrorDetails(message: String)
|
||||||
|
|
||||||
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
fun enableSwipe(enable: Boolean)
|
||||||
|
|
||||||
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
|
fun hideRefresh()
|
||||||
|
|
||||||
|
fun copyToClipboard(text: String)
|
||||||
|
}
|
@ -21,6 +21,7 @@ import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragme
|
|||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -69,6 +70,8 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
timetableSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
timetableSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
timetableErrorRetry.setOnClickListener { presenter.onRetry() }
|
timetableErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
timetableErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
timetableErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
|
||||||
@ -176,6 +179,7 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
|||||||
setDateRangeLimiter(SchooldaysRangeLimiter())
|
setDateRangeLimiter(SchooldaysRangeLimiter())
|
||||||
version = DatePickerDialog.Version.VERSION_2
|
version = DatePickerDialog.Version.VERSION_2
|
||||||
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
||||||
|
vibrate(false)
|
||||||
show(this@TimetableFragment.parentFragmentManager, null)
|
show(this@TimetableFragment.parentFragmentManager, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
|||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -53,6 +54,8 @@ class AdditionalLessonsFragment :
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
additionalLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
additionalLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
additionalLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
additionalLessonsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
additionalLessonsErrorRetry.setOnClickListener { presenter.onRetry() }
|
additionalLessonsErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
|
|
||||||
additionalLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() }
|
additionalLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() }
|
||||||
@ -128,6 +131,7 @@ class AdditionalLessonsFragment :
|
|||||||
setDateRangeLimiter(SchooldaysRangeLimiter())
|
setDateRangeLimiter(SchooldaysRangeLimiter())
|
||||||
version = DatePickerDialog.Version.VERSION_2
|
version = DatePickerDialog.Version.VERSION_2
|
||||||
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
||||||
|
vibrate(false)
|
||||||
show(this@AdditionalLessonsFragment.parentFragmentManager, null)
|
show(this@AdditionalLessonsFragment.parentFragmentManager, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
|||||||
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
import io.github.wulkanowy.utils.getCompatDrawable
|
import io.github.wulkanowy.utils.getCompatDrawable
|
||||||
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -60,6 +61,8 @@ class CompletedLessonsFragment :
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
|
completedLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
|
completedLessonsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||||
completedLessonErrorRetry.setOnClickListener { presenter.onRetry() }
|
completedLessonErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
|
||||||
@ -147,6 +150,7 @@ class CompletedLessonsFragment :
|
|||||||
setDateRangeLimiter(SchooldaysRangeLimiter())
|
setDateRangeLimiter(SchooldaysRangeLimiter())
|
||||||
version = DatePickerDialog.Version.VERSION_2
|
version = DatePickerDialog.Version.VERSION_2
|
||||||
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
||||||
|
vibrate(false)
|
||||||
show(this@CompletedLessonsFragment.parentFragmentManager, null)
|
show(this@CompletedLessonsFragment.parentFragmentManager, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
|||||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||||
import io.github.wulkanowy.utils.nextSchoolDay
|
import io.github.wulkanowy.utils.nextSchoolDay
|
||||||
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
import io.github.wulkanowy.utils.previousSchoolDay
|
import io.github.wulkanowy.utils.previousSchoolDay
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@ -151,8 +152,14 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
|||||||
|
|
||||||
val remoteView = RemoteViews(context.packageName, layoutId).apply {
|
val remoteView = RemoteViews(context.packageName, layoutId).apply {
|
||||||
setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty)
|
setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty)
|
||||||
setTextViewText(R.id.timetableWidgetDate, date.toFormattedString("EEEE, dd.MM").capitalize())
|
setTextViewText(
|
||||||
setTextViewText(R.id.timetableWidgetName, student?.studentName ?: context.getString(R.string.all_no_data))
|
R.id.timetableWidgetDate,
|
||||||
|
date.toFormattedString("EEEE, dd.MM").capitalize()
|
||||||
|
)
|
||||||
|
setTextViewText(
|
||||||
|
R.id.timetableWidgetName,
|
||||||
|
student?.nickOrName ?: context.getString(R.string.all_no_data)
|
||||||
|
)
|
||||||
setRemoteAdapter(R.id.timetableWidgetList, adapterIntent)
|
setRemoteAdapter(R.id.timetableWidgetList, adapterIntent)
|
||||||
setOnClickPendingIntent(R.id.timetableWidgetNext, nextNavIntent)
|
setOnClickPendingIntent(R.id.timetableWidgetNext, nextNavIntent)
|
||||||
setOnClickPendingIntent(R.id.timetableWidgetPrev, prevNavIntent)
|
setOnClickPendingIntent(R.id.timetableWidgetPrev, prevNavIntent)
|
||||||
|
@ -4,7 +4,9 @@ import android.content.res.Resources
|
|||||||
import android.os.Build.MANUFACTURER
|
import android.os.Build.MANUFACTURER
|
||||||
import android.os.Build.MODEL
|
import android.os.Build.MODEL
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
|
import io.github.wulkanowy.BuildConfig.BUILD_TIMESTAMP
|
||||||
import io.github.wulkanowy.BuildConfig.DEBUG
|
import io.github.wulkanowy.BuildConfig.DEBUG
|
||||||
|
import io.github.wulkanowy.BuildConfig.FLAVOR
|
||||||
import io.github.wulkanowy.BuildConfig.VERSION_CODE
|
import io.github.wulkanowy.BuildConfig.VERSION_CODE
|
||||||
import io.github.wulkanowy.BuildConfig.VERSION_NAME
|
import io.github.wulkanowy.BuildConfig.VERSION_NAME
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -17,6 +19,10 @@ open class AppInfo @Inject constructor() {
|
|||||||
|
|
||||||
open val versionCode get() = VERSION_CODE
|
open val versionCode get() = VERSION_CODE
|
||||||
|
|
||||||
|
open val buildTimestamp get() = BUILD_TIMESTAMP
|
||||||
|
|
||||||
|
open val buildFlavor get() = FLAVOR
|
||||||
|
|
||||||
open val versionName get() = VERSION_NAME
|
open val versionName get() = VERSION_NAME
|
||||||
|
|
||||||
open val systemVersion get() = SDK_INT
|
open val systemVersion get() = SDK_INT
|
||||||
@ -28,4 +34,9 @@ open class AppInfo @Inject constructor() {
|
|||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
open val systemLanguage: String
|
open val systemLanguage: String
|
||||||
get() = Resources.getSystem().configuration.locale.language
|
get() = Resources.getSystem().configuration.locale.language
|
||||||
|
|
||||||
|
open val defaultColorsForAvatar = listOf(
|
||||||
|
0xe57373, 0xf06292, 0xba68c8, 0x9575cd, 0x7986cb, 0x64b5f6, 0x4fc3f7, 0x4dd0e1, 0x4db6ac,
|
||||||
|
0x81c784, 0xaed581, 0xff8a65, 0xd4e157, 0xffd54f, 0xffb74d, 0xa1887f, 0x90a4ae
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.utils
|
|||||||
|
|
||||||
import io.github.wulkanowy.data.Resource
|
import io.github.wulkanowy.data.Resource
|
||||||
import io.github.wulkanowy.data.Status
|
import io.github.wulkanowy.data.Status
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
@ -71,24 +72,14 @@ inline fun <ResultType, RequestType, T> networkBoundResource(
|
|||||||
|
|
||||||
fun <T> flowWithResource(block: suspend () -> T) = flow {
|
fun <T> flowWithResource(block: suspend () -> T) = flow {
|
||||||
emit(Resource.loading())
|
emit(Resource.loading())
|
||||||
emit(try {
|
emit(Resource.success(block()))
|
||||||
Resource.success(block())
|
}.catch { emit(Resource.error(it)) }
|
||||||
} catch (e: Throwable) {
|
|
||||||
Resource.error(e)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@OptIn(FlowPreview::class)
|
||||||
fun <T> flowWithResourceIn(block: suspend () -> Flow<Resource<T>>) = flow {
|
fun <T> flowWithResourceIn(block: suspend () -> Flow<Resource<T>>) = flow {
|
||||||
emit(Resource.loading())
|
emit(Resource.loading())
|
||||||
|
emitAll(block().filter { it.status != Status.LOADING || (it.status == Status.LOADING && it.data != null) })
|
||||||
block()
|
}.catch { emit(Resource.error(it)) }
|
||||||
.catch { emit(Resource.error(it)) }
|
|
||||||
.collect {
|
|
||||||
if (it.status != Status.LOADING || (it.status == Status.LOADING && it.data != null)) { // LOADING without data is already emitted
|
|
||||||
emit(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.afterLoading(callback: () -> Unit) = onEach {
|
fun <T> Flow<Resource<T>>.afterLoading(callback: () -> Unit) = onEach {
|
||||||
if (it.status != Status.LOADING) callback()
|
if (it.status != Status.LOADING) callback()
|
||||||
@ -96,4 +87,5 @@ fun <T> Flow<Resource<T>>.afterLoading(callback: () -> Unit) = onEach {
|
|||||||
|
|
||||||
suspend fun <T> Flow<Resource<T>>.toFirstResult() = filter { it.status != Status.LOADING }.first()
|
suspend fun <T> Flow<Resource<T>>.toFirstResult() = filter { it.status != Status.LOADING }.first()
|
||||||
|
|
||||||
suspend fun <T> Flow<Resource<T>>.waitForResult() = takeWhile { it.status == Status.LOADING }.collect()
|
suspend fun <T> Flow<Resource<T>>.waitForResult() =
|
||||||
|
takeWhile { it.status == Status.LOADING }.collect()
|
||||||
|
@ -2,6 +2,8 @@ package io.github.wulkanowy.utils
|
|||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import io.github.wulkanowy.ui.modules.about.AboutFragment
|
import io.github.wulkanowy.ui.modules.about.AboutFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.account.AccountFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
|
||||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
@ -13,6 +15,7 @@ import io.github.wulkanowy.ui.modules.more.MoreFragment
|
|||||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
||||||
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
|
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
|
||||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||||
|
|
||||||
fun Fragment.toSection(): MainView.Section? {
|
fun Fragment.toSection(): MainView.Section? {
|
||||||
@ -29,6 +32,9 @@ fun Fragment.toSection(): MainView.Section? {
|
|||||||
is SettingsFragment -> MainView.Section.SETTINGS
|
is SettingsFragment -> MainView.Section.SETTINGS
|
||||||
is AboutFragment -> MainView.Section.ABOUT
|
is AboutFragment -> MainView.Section.ABOUT
|
||||||
is SchoolAndTeachersFragment -> MainView.Section.SCHOOL
|
is SchoolAndTeachersFragment -> MainView.Section.SCHOOL
|
||||||
|
is AccountFragment -> MainView.Section.ACCOUNT
|
||||||
|
is AccountDetailsFragment -> MainView.Section.ACCOUNT
|
||||||
|
is StudentInfoFragment -> MainView.Section.STUDENT_INFO
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,16 @@ fun List<Grade>.calcAverage(): Double {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmName("calcSummaryAverage")
|
@JvmName("calcSummaryAverage")
|
||||||
fun List<GradeSummary>.calcAverage(): Double {
|
fun List<GradeSummary>.calcAverage() = asSequence()
|
||||||
return asSequence().mapNotNull {
|
.mapNotNull {
|
||||||
if (it.finalGrade.matches("[0-6]".toRegex())) it.finalGrade.toDouble() else null
|
if (it.finalGrade.matches("[0-6]".toRegex())) {
|
||||||
}.average().let { if (it.isNaN()) 0.0 else it }
|
it.finalGrade.toDouble()
|
||||||
}
|
} else null
|
||||||
|
}
|
||||||
|
.average()
|
||||||
|
.let { if (it.isNaN()) 0.0 else it }
|
||||||
|
|
||||||
fun Grade.getBackgroundColor(theme: String): Int {
|
fun Grade.getBackgroundColor(theme: String) = when (theme) {
|
||||||
return when (theme) {
|
|
||||||
"grade_color" -> getGradeColor()
|
"grade_color" -> getGradeColor()
|
||||||
"material" -> when (value.toInt()) {
|
"material" -> when (value.toInt()) {
|
||||||
6 -> R.color.grade_material_six
|
6 -> R.color.grade_material_six
|
||||||
@ -43,23 +45,19 @@ fun Grade.getBackgroundColor(theme: String): Int {
|
|||||||
1 -> R.color.grade_vulcan_one
|
1 -> R.color.grade_vulcan_one
|
||||||
else -> R.color.grade_vulcan_default
|
else -> R.color.grade_vulcan_default
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Grade.getGradeColor(): Int {
|
fun Grade.getGradeColor() = when (color) {
|
||||||
return when (color) {
|
|
||||||
"000000" -> R.color.grade_black
|
"000000" -> R.color.grade_black
|
||||||
"F04C4C" -> R.color.grade_red
|
"F04C4C" -> R.color.grade_red
|
||||||
"20A4F7" -> R.color.grade_blue
|
"20A4F7" -> R.color.grade_blue
|
||||||
"6ECD07" -> R.color.grade_green
|
"6ECD07" -> R.color.grade_green
|
||||||
"B16CF1" -> R.color.grade_purple
|
"B16CF1" -> R.color.grade_purple
|
||||||
else -> R.color.grade_material_default
|
else -> R.color.grade_material_default
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline val Grade.colorStringId: Int
|
inline val Grade.colorStringId: Int
|
||||||
get() {
|
get() = when (color) {
|
||||||
return when (color) {
|
|
||||||
"000000" -> R.string.all_black
|
"000000" -> R.string.all_black
|
||||||
"F04C4C" -> R.string.all_red
|
"F04C4C" -> R.string.all_red
|
||||||
"20A4F7" -> R.string.all_blue
|
"20A4F7" -> R.string.all_blue
|
||||||
@ -67,12 +65,9 @@ inline val Grade.colorStringId: Int
|
|||||||
"B16CF1" -> R.string.all_purple
|
"B16CF1" -> R.string.all_purple
|
||||||
else -> R.string.all_empty_color
|
else -> R.string.all_empty_color
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun Grade.changeModifier(plusModifier: Double, minusModifier: Double): Grade {
|
fun Grade.changeModifier(plusModifier: Double, minusModifier: Double) = when {
|
||||||
return when {
|
|
||||||
modifier > 0 -> copy(modifier = plusModifier)
|
modifier > 0 -> copy(modifier = plusModifier)
|
||||||
modifier < 0 -> copy(modifier = -minusModifier)
|
modifier < 0 -> copy(modifier = -minusModifier)
|
||||||
else -> this
|
else -> this
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
|
||||||
|
inline val Student.nickOrName get() = if (nick.isBlank()) studentName else nick
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.utils
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.time.DayOfWeek.FRIDAY
|
import java.time.DayOfWeek.FRIDAY
|
||||||
import java.time.DayOfWeek.MONDAY
|
import java.time.DayOfWeek.MONDAY
|
||||||
import java.time.DayOfWeek.SATURDAY
|
import java.time.DayOfWeek.SATURDAY
|
||||||
@ -8,12 +9,12 @@ import java.time.DayOfWeek.SUNDAY
|
|||||||
import java.time.Instant.ofEpochMilli
|
import java.time.Instant.ofEpochMilli
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import java.time.LocalDateTime.now
|
||||||
import java.time.LocalDateTime.ofInstant
|
import java.time.LocalDateTime.ofInstant
|
||||||
import java.time.Month
|
import java.time.Month
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.time.format.DateTimeFormatter.ofPattern
|
import java.time.format.DateTimeFormatter.ofPattern
|
||||||
import java.time.format.TextStyle.FULL
|
|
||||||
import java.time.temporal.TemporalAdjusters.firstInMonth
|
import java.time.temporal.TemporalAdjusters.firstInMonth
|
||||||
import java.time.temporal.TemporalAdjusters.next
|
import java.time.temporal.TemporalAdjusters.next
|
||||||
import java.time.temporal.TemporalAdjusters.previous
|
import java.time.temporal.TemporalAdjusters.previous
|
||||||
@ -33,24 +34,10 @@ fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = for
|
|||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
fun Month.getFormattedName(): String {
|
fun Month.getFormattedName(): String {
|
||||||
return getDisplayName(FULL, Locale.getDefault())
|
val formatter = SimpleDateFormat("LLLL", Locale.getDefault())
|
||||||
.let {
|
|
||||||
when (it) {
|
val date = now().withMonth(value)
|
||||||
"stycznia" -> "Styczeń"
|
return formatter.format(date.toInstant(ZoneOffset.UTC).toEpochMilli()).capitalize()
|
||||||
"lutego" -> "Luty"
|
|
||||||
"marca" -> "Marzec"
|
|
||||||
"kwietnia" -> "Kwiecień"
|
|
||||||
"maja" -> "Maj"
|
|
||||||
"czerwca" -> "Czerwiec"
|
|
||||||
"lipca" -> "Lipiec"
|
|
||||||
"sierpnia" -> "Sierpień"
|
|
||||||
"września" -> "Wrzesień"
|
|
||||||
"października" -> "Październik"
|
|
||||||
"listopada" -> "Listopad"
|
|
||||||
"grudnia" -> "Grudzień"
|
|
||||||
else -> it
|
|
||||||
}
|
|
||||||
}.capitalize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline val LocalDate.nextSchoolDay: LocalDate
|
inline val LocalDate.nextSchoolDay: LocalDate
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
Wersja 0.24.3
|
Wersja 0.25.0
|
||||||
- naprawiliśmy odczytywanie wiadomości
|
- naprawiliśmy przełączanie semestrów przy przełączaniu uczniów
|
||||||
- naprawiliśmy niekończące się ładowanie w ocenach na drugim semestrze
|
- naprawiliśmy błąd przy odświeżaniu ocen gdy włączony był inny niż domyślny tryb liczenia średniej
|
||||||
- naprawiliśmy ciemny motyw na MIUI 12
|
- dodaliśmy menadżer kont, gdzie można podejrzeć informacje o uczniu oraz zmienić pseudonim
|
||||||
- dodaliśmy automatyczne odświeżanie danych w aplikacji
|
- zmieniliśmy ikonę planu lekcji oraz kolor animacji odświeżania w ciemnym motywie
|
||||||
- naprawiliśmy wysyłanie wiadomości kiedy uczeń zalogowany był/jest przez konto ucznia i rodzica
|
|
||||||
- dodaliśmy zakładkę lekcji dodatkowych (na górnym pasku w planie lekcji)
|
|
||||||
|
|
||||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||||
|
16
app/src/main/res/drawable-anydpi-v24/ic_stat_timetable.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group
|
||||||
|
android:scaleX="0.92"
|
||||||
|
android:scaleY="0.92"
|
||||||
|
android:translateX="0.96"
|
||||||
|
android:translateY="0.96">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFF"
|
||||||
|
android:pathData="M14,12H15.5V14.82L17.94,16.23L17.19,17.53L14,15.69V12M4,2H18A2,2 0 0,1 20,4V10.1C21.24,11.36 22,13.09 22,15A7,7 0 0,1 15,22C13.09,22 11.36,21.24 10.1,20H4A2,2 0 0,1 2,18V4A2,2 0 0,1 4,2M4,15V18H8.67C8.24,17.09 8,16.07 8,15H4M4,8H10V5H4V8M18,8V5H12V8H18M4,13H8.29C8.63,11.85 9.26,10.82 10.1,10H4V13M15,10.15A4.85,4.85 0 0,0 10.15,15C10.15,17.68 12.32,19.85 15,19.85A4.85,4.85 0 0,0 19.85,15C19.85,12.32 17.68,10.15 15,10.15Z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
Before Width: | Height: | Size: 312 B After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 275 B After Width: | Height: | Size: 315 B |
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 499 B |
Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 671 B |
9
app/src/main/res/drawable/ic_account_details_family.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M16,4c0,-1.11 0.89,-2 2,-2s2,0.89 2,2s-0.89,2 -2,2S16,5.11 16,4zM20,22v-6h2.5l-2.54,-7.63C19.68,7.55 18.92,7 18.06,7h-0.12c-0.86,0 -1.63,0.55 -1.9,1.37l-0.86,2.58C16.26,11.55 17,12.68 17,14v8H20zM12.5,11.5c0.83,0 1.5,-0.67 1.5,-1.5s-0.67,-1.5 -1.5,-1.5S11,9.17 11,10S11.67,11.5 12.5,11.5zM5.5,6c1.11,0 2,-0.89 2,-2s-0.89,-2 -2,-2s-2,0.89 -2,2S4.39,6 5.5,6zM7.5,22v-7H9V9c0,-1.1 -0.9,-2 -2,-2H4C2.9,7 2,7.9 2,9v6h1.5v7H7.5zM14,22v-4h1v-4c0,-0.82 -0.68,-1.5 -1.5,-1.5h-2c-0.82,0 -1.5,0.68 -1.5,1.5v4h1v4H14z" />
|
||||||
|
</vector>
|
@ -5,5 +5,5 @@
|
|||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFF"
|
android:fillColor="#FFF"
|
||||||
android:pathData="M19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,9h14v10zM5,7L5,5h14v2L5,7zM7,11h10v2L7,13zM7,15h7v2L7,17z" />
|
android:pathData="M14,12H15.5V14.82L17.94,16.23L17.19,17.53L14,15.69V12M4,2H18A2,2 0 0,1 20,4V10.1C21.24,11.36 22,13.09 22,15A7,7 0 0,1 15,22C13.09,22 11.36,21.24 10.1,20H4A2,2 0 0,1 2,18V4A2,2 0 0,1 4,2M4,15V18H8.67C8.24,17.09 8,16.07 8,15H4M4,8H10V5H4V8M18,8V5H12V8H18M4,13H8.29C8.63,11.85 9.26,10.82 10.1,10H4V13M15,10.15A4.85,4.85 0 0,0 10.15,15C10.15,17.68 12.32,19.85 15,19.85A4.85,4.85 0 0,0 19.85,15C19.85,12.32 17.68,10.15 15,10.15Z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -5,5 +5,5 @@
|
|||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFF"
|
android:fillColor="#FFF"
|
||||||
android:pathData="M19,19V8H5V19H19M16,1H18V3H19C20.11,3 21,3.9 21,5V19C21,20.11 20.11,21 19,21H5C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3H6V1H8V3H16V1M11,9.5H13V12.5H16V14.5H13V17.5H11V14.5H8V12.5H11V9.5Z" />
|
android:pathData="M6,1L6,3L5,3C3.89,3 3,3.89 3,5v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.89 2,-2L21,5C21,3.9 20.11,3 19,3L18,3L18,1L16,1L16,3L8,3L8,1ZM5,5L19,5L19,7L5,7ZM5,9L19,9L19,19L5,19ZM11,10v3L8,13v2h3v3h2v-3h3v-2h-3v-3z" />
|
||||||
</vector>
|
</vector>
|
||||||
|