forked from github/wulkanowy-mirror
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
a191f03cdf | |||
63404b8576 | |||
1b7760ff88 | |||
24f58835e7 | |||
b032c459d1 | |||
df0a1e59cc | |||
dbbc8069b1 | |||
f84040109c | |||
baf1420193 | |||
4a36d78709 | |||
4464812651 | |||
72a35481e5 | |||
017c200115 | |||
2bf7755157 | |||
269af4b7ba | |||
7431738366 | |||
034b99c7ab | |||
74e98e4430 | |||
cbf3215dd1 | |||
c18877466f | |||
6cd6cae1e0 | |||
333f7bfa16 | |||
aa6dcaff94 | |||
2bff468e56 | |||
f2855d598d | |||
1ebc296bfe | |||
a2a18e5652 | |||
cd1ceea860 |
@ -7,11 +7,11 @@ references:
|
||||
|
||||
container_config: &container_config
|
||||
docker:
|
||||
- image: circleci/android:api-28-alpha
|
||||
- image: circleci/android:api-28
|
||||
working_directory: *workspace_root
|
||||
environment:
|
||||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
_JAVA_OPTS: -Xmx3072m
|
||||
|
||||
attach_workspace: &attach_workspace
|
||||
attach_workspace:
|
||||
|
25
.idea/codeStyles/Project.xml
generated
25
.idea/codeStyles/Project.xml
generated
@ -21,31 +21,6 @@
|
||||
<option name="CONTINUATION_INDENT_IN_ELVIS" value="false" />
|
||||
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
||||
</JetCodeStyleSettings>
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<XML>
|
||||
<option name="XML_KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
|
||||
|
@ -11,10 +11,10 @@ cache:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
#branches:
|
||||
# only:
|
||||
# - master
|
||||
# - 0.7.x
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- 0.7.x
|
||||
|
||||
android:
|
||||
licenses:
|
||||
|
@ -16,13 +16,13 @@ android {
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 28
|
||||
versionCode 31
|
||||
versionName "0.7.5"
|
||||
versionCode 34
|
||||
versionName "0.8.1"
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
manifestPlaceholders = [
|
||||
fabric_api_key: System.getenv("FABRIC_API_KEY") ?: "null",
|
||||
fabric_api_key : System.getenv("FABRIC_API_KEY") ?: "null",
|
||||
crashlytics_enabled: project.hasProperty("enableCrashlytics")
|
||||
]
|
||||
javaCompileOptions {
|
||||
@ -85,48 +85,49 @@ play {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation('io.github.wulkanowy:api:0.7.5') { exclude module: "threetenbp" }
|
||||
implementation 'io.github.wulkanowy:api:0.8.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
||||
implementation "androidx.appcompat:appcompat:1.0.2"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation "com.google.android.material:material:1.0.0"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
|
||||
implementation "android.arch.work:work-runtime:1.0.0"
|
||||
implementation "android.arch.work:work-rxjava2:1.0.0"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation "com.google.android.material:material:1.1.0-alpha05"
|
||||
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
|
||||
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||
|
||||
implementation "androidx.room:room-runtime:2.1.0-alpha06"
|
||||
implementation "androidx.room:room-rxjava2:2.1.0-alpha06"
|
||||
kapt "androidx.room:room-compiler:2.1.0-alpha06"
|
||||
implementation "androidx.work:work-runtime:2.0.1"
|
||||
implementation "androidx.work:work-rxjava2:2.0.1"
|
||||
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
|
||||
implementation "androidx.room:room-runtime:2.1.0-alpha07"
|
||||
implementation "androidx.room:room-rxjava2:2.1.0-alpha07"
|
||||
kapt "androidx.room:room-compiler:2.1.0-alpha07"
|
||||
|
||||
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.3'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.3'
|
||||
|
||||
implementation "com.google.dagger:dagger-android-support:2.21"
|
||||
kapt "com.google.dagger:dagger-compiler:2.21"
|
||||
kapt "com.google.dagger:dagger-android-processor:2.21"
|
||||
implementation "com.google.dagger:dagger-android-support:2.22.1"
|
||||
kapt "com.google.dagger:dagger-compiler:2.22.1"
|
||||
kapt "com.google.dagger:dagger-android-processor:2.22.1"
|
||||
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
|
||||
|
||||
implementation "eu.davidea:flexible-adapter:5.1.0"
|
||||
implementation "eu.davidea:flexible-adapter-ui:1.0.0"
|
||||
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
|
||||
implementation 'com.ncapdevi:frag-nav:3.2.0'
|
||||
|
||||
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
|
||||
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||
|
||||
implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.7"
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.8"
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.0"
|
||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:3.12.1"
|
||||
|
||||
implementation "com.mikepenz:aboutlibraries:6.2.3"
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
|
||||
|
||||
implementation 'com.google.firebase:firebase-core:16.0.8'
|
||||
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'
|
||||
@ -138,15 +139,15 @@ dependencies {
|
||||
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation "io.mockk:mockk:1.9.2"
|
||||
testImplementation "org.mockito:mockito-inline:2.25.1"
|
||||
testImplementation "org.mockito:mockito-inline:2.27.0"
|
||||
testImplementation 'org.threeten:threetenbp:1.3.8'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.1.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
||||
androidTestImplementation "io.mockk:mockk-android:1.9.2"
|
||||
androidTestImplementation 'org.mockito:mockito-android:2.25.1'
|
||||
androidTestImplementation "androidx.room:room-testing:2.1.0-alpha06"
|
||||
androidTestImplementation 'org.mockito:mockito-android:2.27.0'
|
||||
androidTestImplementation "androidx.room:room-testing:2.1.0-alpha07"
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
}
|
||||
|
||||
|
@ -31,17 +31,17 @@ task jacocoTestReport(type: JacocoReport) {
|
||||
'**/*_Provide*Factory*.*',
|
||||
'**/*_Factory.*']
|
||||
|
||||
classDirectories = fileTree(
|
||||
classDirectories.setFrom(fileTree(
|
||||
dir: "$buildDir/intermediates/classes/debug",
|
||||
excludes: excludes
|
||||
) + fileTree(
|
||||
dir: "$buildDir/tmp/kotlin-classes/debug",
|
||||
excludes: excludes
|
||||
)
|
||||
))
|
||||
|
||||
sourceDirectories = files("$project.projectDir/src/main/java")
|
||||
executionData = fileTree(
|
||||
sourceDirectories.setFrom(files("$project.projectDir/src/main/java"))
|
||||
executionData.setFrom(fileTree(
|
||||
dir: project.projectDir,
|
||||
includes: ["**/*.exec", "**/*.ec"]
|
||||
)
|
||||
))
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import io.github.wulkanowy.api.grades.Grade as GradeApi
|
||||
@ -109,4 +110,73 @@ class GradeRepositoryTest {
|
||||
assertTrue { grades[2].isRead }
|
||||
assertTrue { grades[3].isRead }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun subtractLocaleDuplicateGrades() {
|
||||
gradeLocal.saveGrades(listOf(
|
||||
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
|
||||
))
|
||||
|
||||
every { mockApi.getGrades(1) } returns Single.just(listOf(
|
||||
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
|
||||
))
|
||||
|
||||
val grades = GradeRepository(settings, gradeLocal, gradeRemote)
|
||||
.getGrades(studentMock, semesterMock, true).blockingGet()
|
||||
|
||||
assertEquals(2, grades.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun subtractRemoteDuplicateGrades() {
|
||||
gradeLocal.saveGrades(listOf(
|
||||
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
|
||||
))
|
||||
|
||||
every { mockApi.getGrades(1) } returns Single.just(listOf(
|
||||
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
|
||||
))
|
||||
|
||||
val grades = GradeRepository(settings, gradeLocal, gradeRemote)
|
||||
.getGrades(studentMock, semesterMock, true).blockingGet()
|
||||
|
||||
assertEquals(3, grades.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun emptyLocal() {
|
||||
gradeLocal.saveGrades(listOf())
|
||||
|
||||
every { mockApi.getGrades(1) } returns Single.just(listOf(
|
||||
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
|
||||
))
|
||||
|
||||
val grades = GradeRepository(settings, gradeLocal, gradeRemote)
|
||||
.getGrades(studentMock, semesterMock, true).blockingGet()
|
||||
|
||||
assertEquals(3, grades.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun emptyRemote() {
|
||||
gradeLocal.saveGrades(listOf(
|
||||
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
|
||||
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
|
||||
))
|
||||
|
||||
every { mockApi.getGrades(1) } returns Single.just(listOf())
|
||||
|
||||
val grades = GradeRepository(settings, gradeLocal, gradeRemote)
|
||||
.getGrades(studentMock, semesterMock, true).blockingGet()
|
||||
|
||||
assertEquals(0, grades.size)
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class StudentLocalTest {
|
||||
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = ""))
|
||||
studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "")))
|
||||
.blockingGet()
|
||||
|
||||
val student = studentLocal.getCurrentStudent(true).blockingGet()
|
||||
|
@ -34,6 +34,7 @@
|
||||
android:name=".ui.modules.login.LoginActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/login_title"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.main.MainActivity"
|
||||
@ -45,13 +46,31 @@
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/send_message_title"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".services.widgets.TimetableWidgetService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
|
||||
<receiver
|
||||
android:name=".ui.widgets.timetable.TimetableWidgetProvider"
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
||||
android:exported="true"
|
||||
android:label="@string/timetable_title">
|
||||
<intent-filter>
|
||||
@ -62,6 +81,17 @@
|
||||
android:resource="@xml/provider_widget_timetable" />
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
|
||||
android:label="@string/lucky_number_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/provider_widget_lucky_number" />
|
||||
</receiver>
|
||||
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
|
@ -1,7 +1,6 @@
|
||||
package io.github.wulkanowy
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.multidex.MultiDex
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.WorkManager
|
||||
@ -13,24 +12,21 @@ import dagger.android.support.DaggerApplication
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.utils.Log
|
||||
import io.fabric.sdk.android.Fabric
|
||||
import io.github.wulkanowy.BuildConfig.CRASHLYTICS_ENABLED
|
||||
import io.github.wulkanowy.BuildConfig.DEBUG
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.di.DaggerAppComponent
|
||||
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||
import io.github.wulkanowy.utils.CrashlyticsTree
|
||||
import io.github.wulkanowy.utils.DebugLogTree
|
||||
import io.reactivex.exceptions.UndeliverableException
|
||||
import io.reactivex.plugins.RxJavaPlugins
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.lang.Exception
|
||||
import javax.inject.Inject
|
||||
|
||||
class WulkanowyApp : DaggerApplication() {
|
||||
|
||||
@Inject
|
||||
lateinit var prefRepository: PreferencesRepository
|
||||
|
||||
@Inject
|
||||
lateinit var workerFactory: SyncWorkerFactory
|
||||
|
||||
@ -42,32 +38,36 @@ class WulkanowyApp : DaggerApplication() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
AndroidThreeTen.init(this)
|
||||
initializeFabric()
|
||||
if (DEBUG) enableDebugLog()
|
||||
AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme)
|
||||
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build())
|
||||
RxJavaPlugins.setErrorHandler(::onError)
|
||||
|
||||
initCrashlytics()
|
||||
initLogging()
|
||||
}
|
||||
|
||||
private fun enableDebugLog() {
|
||||
Timber.plant(DebugLogTree())
|
||||
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
||||
private fun initLogging() {
|
||||
if (DEBUG) {
|
||||
Timber.plant(DebugLogTree())
|
||||
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
||||
} else {
|
||||
Timber.plant(CrashlyticsTree())
|
||||
}
|
||||
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
||||
}
|
||||
|
||||
private fun initializeFabric() {
|
||||
private fun initCrashlytics() {
|
||||
Fabric.with(Fabric.Builder(this).kits(
|
||||
Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!BuildConfig.CRASHLYTICS_ENABLED).build()).build()
|
||||
).debuggable(BuildConfig.DEBUG).build())
|
||||
Timber.plant(CrashlyticsTree())
|
||||
Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!CRASHLYTICS_ENABLED).build()).build()
|
||||
).debuggable(DEBUG).build())
|
||||
}
|
||||
|
||||
private fun onError(t: Throwable) {
|
||||
if (t is UndeliverableException && t.cause is IOException || t.cause is InterruptedException) {
|
||||
Timber.e(t.cause, "An undeliverable error occurred")
|
||||
} else throw t
|
||||
private fun onError(error: Throwable) {
|
||||
if (error is UndeliverableException && error.cause is IOException || error.cause is InterruptedException) {
|
||||
Timber.e(error.cause, "An undeliverable error occurred")
|
||||
} else throw error
|
||||
}
|
||||
|
||||
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
|
||||
return DaggerAppComponent.builder().create(this)
|
||||
return DaggerAppComponent.factory().create(this)
|
||||
}
|
||||
}
|
||||
|
@ -6,18 +6,16 @@ import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@SuppressLint("ApplySharedPref")
|
||||
class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) {
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
fun putLong(key: String, value: Long, sync: Boolean = false) {
|
||||
sharedPref.edit().putLong(key, value).apply {
|
||||
if (sync) commit() else apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getLong(key: String, defaultValue: Long): Long {
|
||||
return sharedPref.getLong(key, defaultValue)
|
||||
}
|
||||
fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue)
|
||||
|
||||
fun delete(key: String) {
|
||||
sharedPref.edit().remove(key).apply()
|
||||
|
@ -14,7 +14,7 @@ import javax.inject.Singleton
|
||||
interface StudentDao {
|
||||
|
||||
@Insert(onConflict = ABORT)
|
||||
fun insert(student: Student): Long
|
||||
fun insertAll(student: List<Student>): List<Long>
|
||||
|
||||
@Delete
|
||||
fun delete(student: Student)
|
||||
|
@ -8,7 +8,7 @@ class Migration11 : Migration(10, 11) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("""
|
||||
CREATE TABLE IF NOT EXISTS Grades_temp (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
is_read INTEGER NOT NULL,
|
||||
is_notified INTEGER NOT NULL,
|
||||
semester_id INTEGER NOT NULL,
|
||||
|
@ -6,6 +6,7 @@ import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.friday
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import java.net.UnknownHostException
|
||||
@ -31,8 +32,8 @@ class AttendanceRepository @Inject constructor(
|
||||
local.getAttendance(semester, dates.first, dates.second)
|
||||
.toSingle(emptyList())
|
||||
.doOnSuccess { oldAttendance ->
|
||||
local.deleteAttendance(oldAttendance - newAttendance)
|
||||
local.saveAttendance(newAttendance - oldAttendance)
|
||||
local.deleteAttendance(oldAttendance.uniqueSubtract(newAttendance))
|
||||
local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance))
|
||||
}
|
||||
}.flatMap {
|
||||
local.getAttendance(semester, dates.first, dates.second)
|
||||
|
@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -25,8 +26,8 @@ class AttendanceSummaryRepository @Inject constructor(
|
||||
}.flatMap { new ->
|
||||
local.getAttendanceSummary(semester, subjectId).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteAttendanceSummary(old - new)
|
||||
local.saveAttendanceSummary(new - old)
|
||||
local.deleteAttendanceSummary(old.uniqueSubtract(new))
|
||||
local.saveAttendanceSummary(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) })
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.friday
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import java.net.UnknownHostException
|
||||
@ -31,8 +32,8 @@ class CompletedLessonsRepository @Inject constructor(
|
||||
local.getCompletedLessons(semester, dates.first, dates.second)
|
||||
.toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteCompleteLessons(old - new)
|
||||
local.saveCompletedLessons(new - old)
|
||||
local.deleteCompleteLessons(old.uniqueSubtract(new))
|
||||
local.saveCompletedLessons(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap {
|
||||
local.getCompletedLessons(semester, dates.first, dates.second)
|
||||
|
@ -6,6 +6,7 @@ import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.friday
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import java.net.UnknownHostException
|
||||
@ -27,12 +28,12 @@ class ExamRepository @Inject constructor(
|
||||
.flatMap {
|
||||
if (it) remote.getExams(semester, dates.first, dates.second)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { newExams ->
|
||||
}.flatMap { new ->
|
||||
local.getExams(semester, dates.first, dates.second)
|
||||
.toSingle(emptyList())
|
||||
.doOnSuccess { oldExams ->
|
||||
local.deleteExams(oldExams - newExams)
|
||||
local.saveExams(newExams - oldExams)
|
||||
.doOnSuccess { old ->
|
||||
local.deleteExams(old.uniqueSubtract(new))
|
||||
local.saveExams(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap {
|
||||
local.getExams(semester, dates.first, dates.second)
|
||||
|
@ -5,6 +5,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
@ -24,13 +25,12 @@ class GradeRepository @Inject constructor(
|
||||
.flatMap {
|
||||
if (it) remote.getGrades(semester)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { newGrades ->
|
||||
}.flatMap { new ->
|
||||
local.getGrades(semester).toSingle(emptyList())
|
||||
.doOnSuccess { oldGrades ->
|
||||
val notifyBreakDate = oldGrades.maxBy { it.date }?.date
|
||||
?: student.registrationDate.toLocalDate()
|
||||
local.deleteGrades(oldGrades - newGrades)
|
||||
local.saveGrades((newGrades - oldGrades)
|
||||
.doOnSuccess { old ->
|
||||
val notifyBreakDate = old.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate()
|
||||
local.deleteGrades(old.uniqueSubtract(new))
|
||||
local.saveGrades(new.uniqueSubtract(old)
|
||||
.onEach {
|
||||
if (it.date >= notifyBreakDate) it.apply {
|
||||
isRead = false
|
||||
|
@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -22,11 +23,11 @@ class GradeSummaryRepository @Inject constructor(
|
||||
.flatMap {
|
||||
if (it) remote.getGradeSummary(semester)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { newGradesSummary ->
|
||||
}.flatMap { new ->
|
||||
local.getGradesSummary(semester).toSingle(emptyList())
|
||||
.doOnSuccess { oldGradesSummary ->
|
||||
local.deleteGradesSummary(oldGradesSummary - newGradesSummary)
|
||||
local.saveGradesSummary(newGradesSummary - oldGradesSummary)
|
||||
.doOnSuccess { old ->
|
||||
local.deleteGradesSummary(old.uniqueSubtract(new))
|
||||
local.saveGradesSummary(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) })
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -22,11 +23,11 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
.flatMap {
|
||||
if (it) remote.getGradeStatistics(semester, isSemester)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { newGradesStats ->
|
||||
}.flatMap { new ->
|
||||
local.getGradesStatistics(semester, isSemester).toSingle(emptyList())
|
||||
.doOnSuccess { oldGradesStats ->
|
||||
local.deleteGradesStatistics(oldGradesStats - newGradesStats)
|
||||
local.saveGradesStatistics(newGradesStats - oldGradesStats)
|
||||
.doOnSuccess { old ->
|
||||
local.deleteGradesStatistics(old.uniqueSubtract(new))
|
||||
local.saveGradesStatistics(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import io.github.wulkanowy.data.db.entities.Homework
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.friday
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import java.net.UnknownHostException
|
||||
@ -26,11 +27,11 @@ class HomeworkRepository @Inject constructor(
|
||||
.flatMap {
|
||||
if (it) remote.getHomework(semester, monday, friday)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { newGrades ->
|
||||
}.flatMap { new ->
|
||||
local.getHomework(semester, monday, friday).toSingle(emptyList())
|
||||
.doOnSuccess { oldGrades ->
|
||||
local.deleteHomework(oldGrades - newGrades)
|
||||
local.saveHomework(newGrades - oldGrades)
|
||||
.doOnSuccess { old ->
|
||||
local.deleteHomework(old.uniqueSubtract(new))
|
||||
local.saveHomework(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getHomework(semester, monday, friday).toSingle(emptyList()) })
|
||||
}
|
||||
|
@ -59,4 +59,8 @@ class MessageRemote @Inject constructor(private val api: Api) {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteMessage(message: Message): Single<Boolean> {
|
||||
return api.deleteMessages(listOf(Pair(message.realId, message.folderId)))
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,9 @@ import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -33,8 +35,8 @@ class MessageRepository @Inject constructor(
|
||||
}.flatMap { new ->
|
||||
local.getMessages(student, folder).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteMessages(old - new)
|
||||
local.saveMessages((new - old)
|
||||
local.deleteMessages(old.uniqueSubtract(new))
|
||||
local.saveMessages(new.uniqueSubtract(old)
|
||||
.onEach {
|
||||
it.isNotified = !notify
|
||||
})
|
||||
@ -89,4 +91,20 @@ class MessageRepository @Inject constructor(
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteMessage(message: Message): Maybe<Boolean> {
|
||||
return ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMap {
|
||||
if (it) remote.deleteMessage(message)
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
.filter { it }
|
||||
.doOnSuccess {
|
||||
if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply {
|
||||
id = message.id
|
||||
content = message.content
|
||||
}))
|
||||
else local.deleteMessages(listOf(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
@ -27,8 +28,8 @@ class NoteRepository @Inject constructor(
|
||||
}.flatMap { new ->
|
||||
local.getNotes(student).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteNotes(old - new)
|
||||
local.saveNotes((new - old)
|
||||
local.deleteNotes(old.uniqueSubtract(new))
|
||||
local.saveNotes(new.uniqueSubtract(old)
|
||||
.onEach {
|
||||
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
|
||||
isRead = false
|
||||
|
@ -17,12 +17,15 @@ class PreferencesRepository @Inject constructor(
|
||||
val isShowPresent: Boolean
|
||||
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true)
|
||||
|
||||
val gradeAverageMode: String
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester"
|
||||
|
||||
val isGradeExpandable: Boolean
|
||||
get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false)
|
||||
|
||||
val currentThemeKey: String = context.getString(R.string.pref_key_theme)
|
||||
val currentTheme: Int
|
||||
get() = sharedPref.getString(currentThemeKey, "1")?.toIntOrNull() ?: 1
|
||||
val appThemeKey: String = context.getString(R.string.pref_key_app_theme)
|
||||
val appTheme: String
|
||||
get() = sharedPref.getString(appThemeKey, "light") ?: "light"
|
||||
|
||||
val gradeColorTheme: String
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan"
|
||||
@ -50,8 +53,7 @@ class PreferencesRepository @Inject constructor(
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0
|
||||
|
||||
val gradeMinusModifier: Double
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble()
|
||||
?: 0.0
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0
|
||||
|
||||
val fillMessageContent: Boolean
|
||||
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true)
|
||||
|
@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -31,8 +32,8 @@ class RecipientRepository @Inject constructor(
|
||||
}.flatMap { new ->
|
||||
local.getRecipients(student, role, unit).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteRecipients(old - new)
|
||||
local.saveRecipients(new - old)
|
||||
local.deleteRecipients(old.uniqueSubtract(new))
|
||||
local.saveRecipients(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap {
|
||||
local.getRecipients(student, role, unit).toSingle(emptyList())
|
||||
|
@ -5,6 +5,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
||||
import io.github.wulkanowy.data.ApiHelper
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
@ -30,8 +31,8 @@ class ReportingUnitRepository @Inject constructor(
|
||||
}.flatMap { new ->
|
||||
local.getReportingUnits(student).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteReportingUnits(old - new)
|
||||
local.saveReportingUnits(new - old)
|
||||
local.deleteReportingUnits(old.uniqueSubtract(new))
|
||||
local.saveReportingUnits(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getReportingUnits(student).toSingle(emptyList()) }
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
||||
import io.github.wulkanowy.data.ApiHelper
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
@ -31,8 +32,8 @@ class SemesterRepository @Inject constructor(
|
||||
if (currentSemesters.size == 1) {
|
||||
local.getSemesters(student).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteSemesters(old - new)
|
||||
local.saveSemesters(new - old)
|
||||
local.deleteSemesters(old.uniqueSubtract(new))
|
||||
local.saveSemesters(new.uniqueSubtract(old))
|
||||
}
|
||||
} else {
|
||||
Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}")
|
||||
|
@ -17,8 +17,8 @@ class StudentLocal @Inject constructor(
|
||||
private val context: Context
|
||||
) {
|
||||
|
||||
fun saveStudent(student: Student): Single<Long> {
|
||||
return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) }
|
||||
fun saveStudents(students: List<Student>): Single<List<Long>> {
|
||||
return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) }
|
||||
}
|
||||
|
||||
fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {
|
||||
|
@ -41,8 +41,8 @@ class StudentRepository @Inject constructor(
|
||||
.toSingle()
|
||||
}
|
||||
|
||||
fun saveStudent(student: Student): Single<Long> {
|
||||
return local.saveStudent(student)
|
||||
fun saveStudents(students: List<Student>): Single<List<Long>> {
|
||||
return local.saveStudents(students)
|
||||
}
|
||||
|
||||
fun switchStudent(student: Student): Completable {
|
||||
|
@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Subject
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -26,8 +27,8 @@ class SubjectRepository @Inject constructor(
|
||||
local.getSubjects(semester)
|
||||
.toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteSubjects(old - new)
|
||||
local.saveSubjects(new - old)
|
||||
local.deleteSubjects(old.uniqueSubtract(new))
|
||||
local.saveSubjects(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap {
|
||||
local.getSubjects(semester).toSingle(emptyList())
|
||||
|
@ -6,6 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.utils.friday
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import java.net.UnknownHostException
|
||||
@ -25,14 +26,14 @@ class TimetableRepository @Inject constructor(
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
||||
if (it) remote.getTimetable(semester, monday, friday)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { newTimetable ->
|
||||
}.flatMap { new ->
|
||||
local.getTimetable(semester, monday, friday)
|
||||
.toSingle(emptyList())
|
||||
.doOnSuccess { oldTimetable ->
|
||||
local.deleteTimetable(oldTimetable - newTimetable)
|
||||
local.saveTimetable((newTimetable - oldTimetable).map { item ->
|
||||
.doOnSuccess { old ->
|
||||
local.deleteTimetable(old.uniqueSubtract(new))
|
||||
local.saveTimetable(new.uniqueSubtract(old).map { item ->
|
||||
item.apply {
|
||||
oldTimetable.singleOrNull { this.start == it.start }?.let {
|
||||
old.singleOrNull { this.start == it.start }?.let {
|
||||
return@map copy(
|
||||
room = if (room.isEmpty()) it.room else room,
|
||||
teacher = if (teacher.isEmpty()) it.teacher else teacher
|
||||
|
@ -17,6 +17,6 @@ import javax.inject.Singleton
|
||||
BuilderModule::class])
|
||||
interface AppComponent : AndroidInjector<WulkanowyApp> {
|
||||
|
||||
@Component.Builder
|
||||
abstract class Builder : AndroidInjector.Builder<WulkanowyApp>()
|
||||
@Component.Factory
|
||||
interface Factory : AndroidInjector.Factory<WulkanowyApp>
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.BuildConfig.DEBUG
|
||||
import io.github.wulkanowy.WulkanowyApp
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
@ -30,11 +29,11 @@ internal class AppModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideFirebaseAnalyticsHelper(context: Context) = FirebaseAnalyticsHelper(FirebaseAnalytics.getInstance(context))
|
||||
fun provideFirebaseAnalytics(context: Context) = FirebaseAnalytics.getInstance(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAppWidgetManager(context: Context) = AppWidgetManager.getInstance(context)
|
||||
fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context)
|
||||
|
||||
@Singleton
|
||||
@Named("isDebug")
|
||||
|
@ -5,11 +5,14 @@ import dagger.android.ContributesAndroidInjector
|
||||
import io.github.wulkanowy.di.scopes.PerActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginModule
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainModule
|
||||
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
|
||||
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
||||
import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
|
||||
|
||||
@Module
|
||||
internal abstract class BuilderModule {
|
||||
@ -29,6 +32,15 @@ internal abstract class BuilderModule {
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindMessageSendActivity(): SendMessageActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindTimetableWidgetAccountActivity(): TimetableWidgetConfigureActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLuckyNumberWidgetAccountActivity(): LuckyNumberWidgetConfigureActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -47,8 +47,8 @@ class GradeWork @Inject constructor(
|
||||
.setDefaults(DEFAULT_ALL)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 0), FLAG_UPDATE_CURRENT))
|
||||
PendingIntent.getActivity(context, MenuView.GRADE.id,
|
||||
MainActivity.getStartIntent(context, MenuView.GRADE, true), FLAG_UPDATE_CURRENT))
|
||||
.setStyle(NotificationCompat.InboxStyle().run {
|
||||
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size))
|
||||
grades.forEach { addLine("${it.subject}: ${it.entry}") }
|
||||
|
@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -47,9 +47,8 @@ class LuckyNumberWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
PendingIntent.getActivity(context, MenuView.MESSAGE.id,
|
||||
MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import io.github.wulkanowy.data.repositories.message.MessageRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -48,8 +48,8 @@ class MessageWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getActivity(context, MenuView.MESSAGE.id,
|
||||
MainActivity.getStartIntent(context, MenuView.MESSAGE, true), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
.setStyle(NotificationCompat.InboxStyle().run {
|
||||
setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size))
|
||||
|
@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.note.NoteRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -47,9 +47,8 @@ class NoteWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
PendingIntent.getActivity(context, MenuView.NOTE.id,
|
||||
MainActivity.getStartIntent(context, MenuView.NOTE, true), FLAG_UPDATE_CURRENT))
|
||||
.setStyle(NotificationCompat.InboxStyle().run {
|
||||
setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size))
|
||||
notes.forEach { addLine("${it.teacher}: ${it.category}") }
|
||||
|
@ -7,7 +7,7 @@ import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
||||
import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -2,18 +2,36 @@ package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
||||
import dagger.android.support.DaggerAppCompatActivity
|
||||
import dagger.android.AndroidInjection
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.support.HasSupportFragmentInjector
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.utils.FragmentLifecycleLogger
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
|
||||
abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentInjector {
|
||||
|
||||
@Inject
|
||||
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
|
||||
|
||||
@Inject
|
||||
lateinit var fragmentLifecycleLogger: FragmentLifecycleLogger
|
||||
|
||||
@Inject
|
||||
lateinit var themeManager: ThemeManager
|
||||
|
||||
protected lateinit var messageContainer: View
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
themeManager.applyTheme(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
}
|
||||
|
||||
@ -31,4 +49,6 @@ abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
|
||||
super.onDestroy()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun supportFragmentInjector() = supportFragmentInjector
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.content.pm.PackageManager.GET_ACTIVITIES
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class ThemeManager @Inject constructor(private val preferencesRepository: PreferencesRepository) {
|
||||
|
||||
fun applyTheme(activity: AppCompatActivity) {
|
||||
if (isThemeApplicable(activity)) {
|
||||
activity.delegate.apply {
|
||||
when (preferencesRepository.appTheme) {
|
||||
"light" -> setLocalNightMode(MODE_NIGHT_NO)
|
||||
"dark" -> setLocalNightMode(MODE_NIGHT_YES)
|
||||
"black" -> {
|
||||
setLocalNightMode(MODE_NIGHT_YES)
|
||||
activity.setTheme(R.style.WulkanowyTheme_Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isThemeApplicable(activity: AppCompatActivity): Boolean {
|
||||
return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
||||
.activities.singleOrNull { it.name == activity::class.java.canonicalName }?.theme
|
||||
.let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar }
|
||||
}
|
||||
}
|
@ -44,7 +44,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
.withFields(R.string::class.java.fields)
|
||||
.withCheckCachedDetection(false)
|
||||
.withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio",
|
||||
"OkHttp", "Butterknife", "CircleImageView")
|
||||
"Butterknife", "CircleImageView")
|
||||
.withOnExtraListener { presenter.onExtraSelect(it) })
|
||||
}.let {
|
||||
fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it)
|
||||
|
@ -17,7 +17,7 @@ class AboutPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: AboutView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("About view is attached")
|
||||
Timber.i("About view was initialized")
|
||||
}
|
||||
|
||||
fun onExtraSelect(type: Libs.SpecialButton?) {
|
||||
|
@ -15,7 +15,6 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.dialog_account.*
|
||||
import javax.inject.Inject
|
||||
@ -97,11 +96,8 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
|
||||
}
|
||||
}
|
||||
|
||||
override fun recreateView() {
|
||||
activity?.also {
|
||||
startActivity(MainActivity.getStartIntent(it))
|
||||
it.finish()
|
||||
}
|
||||
override fun recreateMainView() {
|
||||
activity?.recreate()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -19,8 +19,8 @@ class AccountPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: AccountView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Account dialog is attached")
|
||||
view.initView()
|
||||
Timber.i("Account dialog view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ class AccountPresenter @Inject constructor(
|
||||
openClearLoginView()
|
||||
} else {
|
||||
Timber.i("Logout result: Switch to another student")
|
||||
recreateView()
|
||||
recreateMainView()
|
||||
}
|
||||
}
|
||||
}, {
|
||||
@ -73,9 +73,10 @@ class AccountPresenter @Inject constructor(
|
||||
disposable.add(studentRepository.switchStudent(item.student)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally { view?.dismissView() }
|
||||
.subscribe({
|
||||
Timber.i("Change a student result: Success")
|
||||
view?.recreateView()
|
||||
view?.recreateMainView()
|
||||
}, {
|
||||
Timber.i("Change a student result: An exception occurred")
|
||||
errorHandler.dispatch(it)
|
||||
|
@ -16,6 +16,6 @@ interface AccountView : BaseView {
|
||||
|
||||
fun openClearLoginView()
|
||||
|
||||
fun recreateView()
|
||||
fun recreateMainView()
|
||||
}
|
||||
|
||||
|
@ -77,12 +77,12 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
|
||||
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_attendance, menu)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_attendance, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,8 @@ class AttendancePresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: AttendanceView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Attendance view is attached")
|
||||
view.initView()
|
||||
Timber.i("Attendance view was initialized")
|
||||
loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ class AttendanceSummaryPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Attendance summary view is attached with subject id ${subjectId ?: -1}")
|
||||
view.initView()
|
||||
Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}")
|
||||
loadData(subjectId ?: -1)
|
||||
loadSubjects()
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ class ExamPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: ExamView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Exam view is attached")
|
||||
view.initView()
|
||||
Timber.i("Exam view was initialized")
|
||||
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
package io.github.wulkanowy.ui.modules.grade
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
|
||||
class GradeAverageProvider @Inject constructor(
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val gradeRepository: GradeRepository
|
||||
) {
|
||||
fun getGradeAverage(student: Student, semesters: List<Semester>, selectedSemesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
return when (preferencesRepository.gradeAverageMode) {
|
||||
"all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh)
|
||||
"only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh)
|
||||
else -> throw IllegalArgumentException("Incorrect grade average mode: ${preferencesRepository.gradeAverageMode} ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
val plusModifier = preferencesRepository.gradePlusModifier
|
||||
val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
return gradeRepository.getGrades(student, selectedSemester, forceRefresh)
|
||||
.flatMap { firstGrades ->
|
||||
if (selectedSemester == firstSemester) Single.just(firstGrades)
|
||||
else gradeRepository.getGrades(student, firstSemester)
|
||||
.map { secondGrades -> secondGrades + firstGrades }
|
||||
}.map { grades ->
|
||||
grades.map { it.changeModifier(plusModifier, minusModifier) }
|
||||
.groupBy { it.subject }
|
||||
.mapValues { it.value.calcAverage() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOnlyOneSemesterAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val plusModifier = preferencesRepository.gradePlusModifier
|
||||
val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
return gradeRepository.getGrades(student, selectedSemester, forceRefresh)
|
||||
.map { grades ->
|
||||
grades.map { it.changeModifier(plusModifier, minusModifier) }
|
||||
.groupBy { it.subject }
|
||||
.mapValues { it.value.calcAverage() }
|
||||
}
|
||||
}
|
||||
}
|
@ -57,9 +57,9 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
|
||||
presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_grade, menu)
|
||||
semesterSwitchMenu = menu?.findItem(R.id.gradeMenuSemester)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_grade, menu)
|
||||
semesterSwitchMenu = menu.findItem(R.id.gradeMenuSemester)
|
||||
presenter.onCreateMenu()
|
||||
}
|
||||
|
||||
@ -82,8 +82,8 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
|
||||
gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,12 @@ class GradePresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: GradeView, savedIndex: Int?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Grade view is attached")
|
||||
selectedIndex = savedIndex ?: 0
|
||||
view.run {
|
||||
initView()
|
||||
enableSwipe(false)
|
||||
}
|
||||
Timber.i("Grade view was initialized with $selectedIndex index")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -67,8 +67,8 @@ class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_grade_details, menu)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_grade_details, menu)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
@ -88,8 +88,8 @@ class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.
|
||||
gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,9 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
|
||||
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import io.github.wulkanowy.utils.getBackgroundColor
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
@ -23,6 +22,7 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
private val studentRepository: StudentRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val averageProvider: GradeAverageProvider,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BaseSessionPresenter<GradeDetailsView>(errorHandler) {
|
||||
|
||||
@ -109,11 +109,16 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
|
||||
Timber.i("Loading grade details data started")
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } }
|
||||
.flatMap { gradeRepository.getGrades(it.second, it.first.first { item -> item.semesterId == semesterId }, forceRefresh) }
|
||||
.map { it.sortedByDescending { grade -> grade.date } }
|
||||
.map { it.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } }
|
||||
.map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } }
|
||||
.flatMap { (student, semesters) ->
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
|
||||
.flatMap { averages ->
|
||||
gradeRepository.getGrades(student, semesters.first { semester -> semester.semesterId == semesterId })
|
||||
.map { it.sortedByDescending { grade -> grade.date } }
|
||||
.map { it.groupBy { grade -> grade.subject }.toSortedMap() }
|
||||
.map { createGradeItems(it, averages) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
@ -139,32 +144,36 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
})
|
||||
}
|
||||
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>): List<GradeDetailsHeader> {
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>, averages: Map<String, Double>): List<GradeDetailsHeader> {
|
||||
val isGradeExpandable = preferencesRepository.isGradeExpandable
|
||||
val gradeColorTheme = preferencesRepository.gradeColorTheme
|
||||
|
||||
val noDescriptionString = view?.noDescriptionString.orEmpty()
|
||||
val weightString = view?.weightString.orEmpty()
|
||||
|
||||
return items.map {
|
||||
it.value.calcAverage().let { average ->
|
||||
GradeDetailsHeader(
|
||||
subject = it.key,
|
||||
average = formatAverage(average),
|
||||
number = view?.getGradeNumberString(it.value.size).orEmpty(),
|
||||
newGrades = it.value.filter { grade -> !grade.isRead }.size,
|
||||
isExpandable = preferencesRepository.isGradeExpandable
|
||||
).apply {
|
||||
subItems = it.value.map { item ->
|
||||
GradeDetailsItem(
|
||||
grade = item,
|
||||
valueBgColor = item.getBackgroundColor(preferencesRepository.gradeColorTheme),
|
||||
weightString = view?.weightString.orEmpty(),
|
||||
noDescriptionString = view?.noDescriptionString.orEmpty()
|
||||
)
|
||||
}
|
||||
GradeDetailsHeader(
|
||||
subject = it.key,
|
||||
average = formatAverage(averages[it.key]),
|
||||
number = view?.getGradeNumberString(it.value.size).orEmpty(),
|
||||
newGrades = it.value.filter { grade -> !grade.isRead }.size,
|
||||
isExpandable = isGradeExpandable
|
||||
).apply {
|
||||
subItems = it.value.map { item ->
|
||||
GradeDetailsItem(
|
||||
grade = item,
|
||||
valueBgColor = item.getBackgroundColor(gradeColorTheme),
|
||||
weightString = weightString,
|
||||
noDescriptionString = noDescriptionString
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatAverage(average: Double): String {
|
||||
private fun formatAverage(average: Double?): String {
|
||||
return view?.run {
|
||||
if (average == 0.0) emptyAverageString
|
||||
if (average == null || average == .0) emptyAverageString
|
||||
else averageString.format(average)
|
||||
}.orEmpty()
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.summary
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
|
||||
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import timber.log.Timber
|
||||
import java.lang.String.format
|
||||
import java.util.Locale.FRANCE
|
||||
@ -20,10 +18,9 @@ import javax.inject.Inject
|
||||
class GradeSummaryPresenter @Inject constructor(
|
||||
private val errorHandler: SessionErrorHandler,
|
||||
private val gradeSummaryRepository: GradeSummaryRepository,
|
||||
private val gradeRepository: GradeRepository,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val averageProvider: GradeAverageProvider,
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BaseSessionPresenter<GradeSummaryView>(errorHandler) {
|
||||
@ -36,25 +33,13 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
|
||||
Timber.i("Loading grade summary data started")
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } }
|
||||
.map { pair -> pair.first.first { it.semesterId == semesterId } to pair.second }
|
||||
.flatMap {
|
||||
gradeSummaryRepository.getGradesSummary(it.first, forceRefresh)
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } }
|
||||
.flatMap { (student, semesters) ->
|
||||
gradeSummaryRepository.getGradesSummary(semesters.first { it.semesterId == semesterId }, forceRefresh)
|
||||
.map { it.sortedBy { subject -> subject.subject } }
|
||||
.flatMap { gradesSummary ->
|
||||
gradeRepository.getGrades(it.second, it.first, forceRefresh)
|
||||
.map { grades ->
|
||||
grades.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) }
|
||||
.groupBy { grade -> grade.subject }
|
||||
.mapValues { entry -> entry.value.calcAverage() }
|
||||
.filterValues { value -> value != 0.0 }
|
||||
.let { averages ->
|
||||
createGradeSummaryItems(gradesSummary, averages) to
|
||||
GradeSummaryScrollableHeader(
|
||||
formatAverage(gradesSummary.calcAverage()),
|
||||
formatAverage(averages.values.average())
|
||||
)
|
||||
}
|
||||
}
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
|
||||
.map { averages -> createGradeSummaryItemsAndHeader(gradesSummary, averages) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
@ -66,14 +51,14 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
enableSwipe(true)
|
||||
notifyParentDataLoaded(semesterId)
|
||||
}
|
||||
}.subscribe({
|
||||
}.subscribe({ (gradeSummaryItems, gradeSummaryHeader) ->
|
||||
Timber.i("Loading grade summary result: Success")
|
||||
view?.run {
|
||||
showEmpty(it.first.isEmpty())
|
||||
showContent(it.first.isNotEmpty())
|
||||
updateData(it.first, it.second)
|
||||
showEmpty(gradeSummaryItems.isEmpty())
|
||||
showContent(gradeSummaryItems.isNotEmpty())
|
||||
updateData(gradeSummaryItems, gradeSummaryHeader)
|
||||
}
|
||||
analytics.logEvent("load_grade_summary", "items" to it.first.size, "force_refresh" to forceRefresh)
|
||||
analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh)
|
||||
}) {
|
||||
Timber.i("Loading grade summary result: An exception occurred")
|
||||
view?.run { showEmpty(isViewEmpty) }
|
||||
@ -104,16 +89,24 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
disposable.clear()
|
||||
}
|
||||
|
||||
private fun createGradeSummaryItems(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
|
||||
: List<GradeSummaryItem> {
|
||||
return gradesSummary.filter { !checkEmpty(it, averages) }.map { it ->
|
||||
GradeSummaryItem(
|
||||
title = it.subject,
|
||||
average = formatAverage(averages.getOrElse(it.subject) { 0.0 }, ""),
|
||||
predictedGrade = it.predictedGrade,
|
||||
finalGrade = it.finalGrade
|
||||
)
|
||||
}
|
||||
private fun createGradeSummaryItemsAndHeader(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
|
||||
: Pair<List<GradeSummaryItem>, GradeSummaryScrollableHeader> {
|
||||
return averages.filterValues { value -> value != 0.0 }
|
||||
.let { filteredAverages ->
|
||||
gradesSummary.filter { !checkEmpty(it, filteredAverages) }
|
||||
.map {
|
||||
GradeSummaryItem(
|
||||
title = it.subject,
|
||||
average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""),
|
||||
predictedGrade = it.predictedGrade,
|
||||
finalGrade = it.finalGrade
|
||||
)
|
||||
}.let {
|
||||
it to GradeSummaryScrollableHeader(
|
||||
formatAverage(gradesSummary.calcAverage()),
|
||||
formatAverage(filteredAverages.values.average()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkEmpty(gradeSummary: GradeSummary, averages: Map<String, Double>): Boolean {
|
||||
|
@ -34,8 +34,8 @@ class HomeworkPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: HomeworkView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Homework view is attached")
|
||||
view.initView()
|
||||
Timber.i("Homework view was initialized")
|
||||
loadData(LocalDate.ofEpochDay(date ?: LocalDate.now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePrese
|
||||
initAdapter()
|
||||
showActionBar(false)
|
||||
}
|
||||
Timber.i("Login view is attached")
|
||||
Timber.i("Login view was initialized")
|
||||
}
|
||||
|
||||
fun onFormViewAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy.ui.modules.login.form
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -53,7 +54,8 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() }
|
||||
loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() }
|
||||
loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() }
|
||||
loginFormSignIn.setOnClickListener { presenter.attemptLogin() }
|
||||
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
|
||||
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
|
||||
|
||||
loginFormPass.setOnEditorActionListener { _, id, _ ->
|
||||
if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false
|
||||
@ -130,6 +132,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
}
|
||||
}
|
||||
|
||||
override fun showPrivacyPolicy() {
|
||||
loginFormPrivacyLink.visibility = VISIBLE
|
||||
}
|
||||
|
||||
override fun notifyParentAccountLogged(students: List<Student>) {
|
||||
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
|
||||
loginFormName.text.toString(),
|
||||
@ -138,6 +144,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
))
|
||||
}
|
||||
|
||||
override fun openPrivacyPolicyPage() {
|
||||
startActivity(Intent.parseUri("https://wulkanowy.github.io/polityka-prywatnosci.html", 0))
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
presenter.onDetachView()
|
||||
|
@ -22,7 +22,8 @@ class LoginFormPresenter @Inject constructor(
|
||||
super.onAttachView(view)
|
||||
view.run {
|
||||
initView()
|
||||
if (isDebug) showVersion()
|
||||
if (isDebug) showVersion() else showPrivacyPolicy()
|
||||
|
||||
errorHandler.onBadCredentials = {
|
||||
setErrorPassIncorrect()
|
||||
showSoftKeyboard()
|
||||
@ -31,6 +32,10 @@ class LoginFormPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onPrivacyLinkClick() {
|
||||
view?.openPrivacyPolicyPage()
|
||||
}
|
||||
|
||||
fun onHostSelected() {
|
||||
view?.apply {
|
||||
clearPassError()
|
||||
@ -47,7 +52,7 @@ class LoginFormPresenter @Inject constructor(
|
||||
view?.clearNameError()
|
||||
}
|
||||
|
||||
fun attemptLogin() {
|
||||
fun onSignInClick() {
|
||||
val email = view?.formNameValue.orEmpty()
|
||||
val password = view?.formPassValue.orEmpty()
|
||||
val endpoint = view?.formHostValue.orEmpty()
|
||||
|
@ -37,5 +37,9 @@ interface LoginFormView : BaseView {
|
||||
|
||||
fun showVersion()
|
||||
|
||||
fun showPrivacyPolicy()
|
||||
|
||||
fun notifyParentAccountLogged(students: List<Student>)
|
||||
|
||||
fun openPrivacyPolicyPage()
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
@ -45,6 +42,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() }
|
||||
loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } }
|
||||
|
||||
loginStudentSelectRecycler.apply {
|
||||
@ -54,14 +52,11 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LoginStudentSelectItem>) {
|
||||
loginAdapter.updateDataSet(data, true)
|
||||
loginAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun openMainView() {
|
||||
activity?.let {
|
||||
startActivity(MainActivity.getStartIntent(it)
|
||||
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
|
||||
}
|
||||
activity?.let { startActivity(MainActivity.getStartIntent(context = it, clear = true)) }
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
@ -69,11 +64,11 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
loginStudentSelectRecycler.visibility = if (show) VISIBLE else GONE
|
||||
loginStudentSelectContent.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showActionBar(show: Boolean) {
|
||||
(activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() }
|
||||
override fun enableSignIn(enable: Boolean) {
|
||||
loginStudentSelectSignIn.isEnabled = enable
|
||||
}
|
||||
|
||||
fun onParentInitStudentSelectFragment(students: List<Student>) {
|
||||
|
@ -13,15 +13,15 @@ import kotlinx.android.synthetic.main.item_login_student_select.*
|
||||
|
||||
class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
|
||||
|
||||
override fun getLayoutRes(): Int = R.layout.item_login_student_select
|
||||
override fun getLayoutRes() = R.layout.item_login_student_select
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ItemViewHolder {
|
||||
return ItemViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
loginItemName.text = "${student.studentName} ${student.className}"
|
||||
loginItemSchool.text = student.schoolName
|
||||
}
|
||||
@ -43,7 +43,17 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
||||
}
|
||||
|
||||
class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
|
||||
override val containerView: View
|
||||
get() = itemView
|
||||
|
||||
init {
|
||||
loginItemCheck.setOnClickListener { super.onClick(loginItemContainer) }
|
||||
}
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
super.onClick(view)
|
||||
loginItemCheck.apply { isChecked = !isChecked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.io.Serializable
|
||||
import javax.inject.Inject
|
||||
@ -22,10 +21,13 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
|
||||
var students = emptyList<Student>()
|
||||
|
||||
var selectedStudents = mutableListOf<Student>()
|
||||
|
||||
fun onAttachView(view: LoginStudentSelectView, students: Serializable?) {
|
||||
super.onAttachView(view)
|
||||
view.run {
|
||||
initView()
|
||||
enableSignIn(false)
|
||||
errorHandler.onStudentDuplicate = {
|
||||
showMessage(it)
|
||||
Timber.i("The student already registered in the app was selected")
|
||||
@ -37,13 +39,21 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onSignIn() {
|
||||
registerStudents(selectedStudents)
|
||||
}
|
||||
|
||||
fun onParentInitStudentSelectView(students: List<Student>) {
|
||||
loadData(students)
|
||||
if (students.size == 1) registerStudents(students)
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is LoginStudentSelectItem) {
|
||||
registerStudent(item.student)
|
||||
selectedStudents.removeAll { it == item.student }
|
||||
.let { if (!it) selectedStudents.add(item.student) }
|
||||
|
||||
view?.enableSignIn(selectedStudents.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,33 +64,30 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerStudent(student: Student) {
|
||||
disposable.add(studentRepository.saveStudent(student)
|
||||
.map { student.apply { id = it } }
|
||||
.onErrorResumeNext { studentRepository.logoutStudent(student).andThen(Single.error(it)) }
|
||||
.flatMapCompletable { studentRepository.switchStudent(student) }
|
||||
private fun registerStudents(students: List<Student>) {
|
||||
disposable.add(studentRepository.saveStudents(students)
|
||||
.map { students.first().apply { id = it.first() } }
|
||||
.flatMapCompletable { studentRepository.switchStudent(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
view?.apply {
|
||||
showProgress(true)
|
||||
showContent(false)
|
||||
showActionBar(false)
|
||||
}
|
||||
Timber.i("Registration started")
|
||||
}
|
||||
.subscribe({
|
||||
analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to "No error")
|
||||
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") }
|
||||
Timber.i("Registration result: Success")
|
||||
view?.openMainView()
|
||||
}, {
|
||||
analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to it.localizedMessage)
|
||||
}, { error ->
|
||||
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.localizedMessage) }
|
||||
Timber.i("Registration result: An exception occurred ")
|
||||
errorHandler.dispatch(it)
|
||||
errorHandler.dispatch(error)
|
||||
view?.apply {
|
||||
showProgress(false)
|
||||
showContent(true)
|
||||
showActionBar(true)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -14,5 +14,5 @@ interface LoginStudentSelectView : BaseView {
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showActionBar(show: Boolean)
|
||||
fun enableSignIn(enable: Boolean)
|
||||
}
|
||||
|
@ -21,12 +21,12 @@ class LuckyNumberPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: LuckyNumberView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Lucky number view is attached")
|
||||
view.run {
|
||||
initView()
|
||||
showContent(false)
|
||||
enableSwipe(false)
|
||||
}
|
||||
Timber.i("Lucky number view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,77 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.activity_widget_configure.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetConfigureActivity : BaseActivity(), LuckyNumberWidgetConfigureView {
|
||||
|
||||
@Inject
|
||||
lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: LuckyNumberWidgetConfigurePresenter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setResult(RESULT_CANCELED)
|
||||
setContentView(R.layout.activity_widget_configure)
|
||||
|
||||
intent.extras.let {
|
||||
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID))
|
||||
}
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
widgetConfigureRecycler.apply {
|
||||
adapter = configureAdapter
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
}
|
||||
configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LuckyNumberWidgetConfigureItem>) {
|
||||
configureAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun updateLuckyNumberWidget(widgetId: Int) {
|
||||
sendBroadcast(Intent(this, LuckyNumberWidgetProvider::class.java)
|
||||
.apply {
|
||||
action = ACTION_APPWIDGET_UPDATE
|
||||
putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
|
||||
})
|
||||
}
|
||||
|
||||
override fun setSuccessResult(widgetId: Int) {
|
||||
setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) })
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
Toast.makeText(this, text, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun finishView() {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun openLoginView() {
|
||||
startActivity(LoginActivity.getStartIntent(this))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
presenter.onDetachView()
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_account.*
|
||||
|
||||
class LuckyNumberWidgetConfigureItem(var student: Student, val isCurrent: Boolean) :
|
||||
AbstractFlexibleItem<LuckyNumberWidgetConfigureItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_account
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
accountItemImage.setBackgroundResource(if (isCurrent) R.drawable.ic_account_circular_border else 0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TimetableWidgetConfigureItem
|
||||
|
||||
if (student != other.student) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = student.hashCode()
|
||||
result = 31 * result + student.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetConfigurePresenter @Inject constructor(
|
||||
private val errorHandler: ErrorHandler,
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val sharedPref: SharedPrefHelper
|
||||
) : BasePresenter<LuckyNumberWidgetConfigureView>(errorHandler) {
|
||||
|
||||
private var appWidgetId: Int? = null
|
||||
|
||||
fun onAttachView(view: LuckyNumberWidgetConfigureView, appWidgetId: Int?) {
|
||||
super.onAttachView(view)
|
||||
this.appWidgetId = appWidgetId
|
||||
view.initView()
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelect(item: AbstractFlexibleItem<*>) {
|
||||
if (item is LuckyNumberWidgetConfigureItem) {
|
||||
registerStudent(item.student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(studentRepository.getSavedStudents(false)
|
||||
.map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } }
|
||||
.map { (students, currentStudentId) ->
|
||||
students.map { student -> LuckyNumberWidgetConfigureItem(student, student.id == currentStudentId) }
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({
|
||||
when {
|
||||
it.isEmpty() -> view?.openLoginView()
|
||||
it.size == 1 -> registerStudent(it.single().student)
|
||||
else -> view?.updateData(it)
|
||||
}
|
||||
}, { errorHandler.dispatch(it) }))
|
||||
}
|
||||
|
||||
private fun registerStudent(student: Student) {
|
||||
appWidgetId?.also {
|
||||
sharedPref.putLong(getStudentWidgetKey(it), student.id)
|
||||
view?.apply {
|
||||
updateLuckyNumberWidget(it)
|
||||
setSuccessResult(it)
|
||||
}
|
||||
}
|
||||
view?.finishView()
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
|
||||
|
||||
interface LuckyNumberWidgetConfigureView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<LuckyNumberWidgetConfigureItem>)
|
||||
|
||||
fun updateLuckyNumberWidget(widgetId: Int)
|
||||
|
||||
fun setSuccessResult(widgetId: Int)
|
||||
|
||||
fun finishView()
|
||||
|
||||
fun openLoginView()
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OPTIONS
|
||||
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
|
||||
import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.widget.RemoteViews
|
||||
import dagger.android.AndroidInjection
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.reactivex.Maybe
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetProvider : BroadcastReceiver() {
|
||||
|
||||
@Inject
|
||||
lateinit var studentRepository: StudentRepository
|
||||
|
||||
@Inject
|
||||
lateinit var semesterRepository: SemesterRepository
|
||||
|
||||
@Inject
|
||||
lateinit var luckyNumberRepository: LuckyNumberRepository
|
||||
|
||||
@Inject
|
||||
lateinit var schedulers: SchedulersProvider
|
||||
|
||||
@Inject
|
||||
lateinit var appWidgetManager: AppWidgetManager
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPref: SharedPrefHelper
|
||||
|
||||
companion object {
|
||||
fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId"
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
AndroidInjection.inject(this, context)
|
||||
when (intent.action) {
|
||||
ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent)
|
||||
ACTION_APPWIDGET_DELETED -> onDelete(intent)
|
||||
ACTION_APPWIDGET_OPTIONS_CHANGED -> onOptionsChange(context, intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onUpdate(context: Context, intent: Intent) {
|
||||
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS).forEach { appWidgetId ->
|
||||
RemoteViews(context.packageName, R.layout.widget_luckynumber).apply {
|
||||
setTextViewText(R.id.luckyNumberWidgetNumber,
|
||||
getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)?.luckyNumber?.toString() ?: "#"
|
||||
)
|
||||
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer,
|
||||
PendingIntent.getActivity(context, MenuView.LUCKY_NUMBER.id,
|
||||
MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
|
||||
}.also {
|
||||
setStyles(it, intent)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDelete(intent: Intent) {
|
||||
intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let {
|
||||
if (it != 0) sharedPref.delete(getStudentWidgetKey(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLuckyNumber(studentId: Long, appWidgetId: Int): LuckyNumber? {
|
||||
return try {
|
||||
studentRepository.isStudentSaved()
|
||||
.filter { true }
|
||||
.flatMap { studentRepository.getSavedStudents().toMaybe() }
|
||||
.flatMap { students ->
|
||||
students.singleOrNull { student -> student.id == studentId }
|
||||
.let { student ->
|
||||
when {
|
||||
student != null -> Maybe.just(student)
|
||||
studentId != 0L -> {
|
||||
studentRepository.getCurrentStudent(false)
|
||||
.toMaybe()
|
||||
.doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) }
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
|
||||
.flatMap { luckyNumberRepository.getLuckyNumber(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred in lucky number provider")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun onOptionsChange(context: Context, intent: Intent) {
|
||||
intent.extras?.let { extras ->
|
||||
RemoteViews(context.packageName, R.layout.widget_luckynumber).apply {
|
||||
setStyles(this, intent)
|
||||
appWidgetManager.updateAppWidget(extras.getInt(EXTRA_APPWIDGET_ID), this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
private fun setStyles(views: RemoteViews, intent: Intent) {
|
||||
val options = intent.extras?.getBundle(EXTRA_APPWIDGET_OPTIONS)
|
||||
|
||||
val maxWidth = options?.getInt(OPTION_APPWIDGET_MAX_WIDTH) ?: 150
|
||||
val maxHeight = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: 40
|
||||
|
||||
Timber.d("New lucky number widget measurement: %dx%d", maxWidth, maxHeight)
|
||||
|
||||
when {
|
||||
// 1x1
|
||||
maxWidth < 150 && maxHeight < 110 -> {
|
||||
Timber.d("Lucky number widget size: 1x1")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 1x2
|
||||
maxWidth < 150 && maxHeight > 110 -> {
|
||||
Timber.d("Lucky number widget size: 1x2")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 2x1
|
||||
maxWidth >= 150 && maxHeight <= 110 -> {
|
||||
Timber.d("Lucky number widget size: 2x1")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 2x2 and bigger
|
||||
else -> {
|
||||
Timber.d("Lucky number widget size: 2x2 and bigger")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,8 +22,12 @@ import io.github.wulkanowy.ui.modules.account.AccountDialog
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.more.MoreFragment
|
||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.safelyPopFragment
|
||||
@ -40,9 +44,15 @@ class MainActivity : BaseActivity(), MainView {
|
||||
lateinit var navController: FragNavController
|
||||
|
||||
companion object {
|
||||
const val EXTRA_START_MENU_INDEX = "extraStartMenuIndex"
|
||||
const val EXTRA_START_MENU = "extraStartMenu"
|
||||
|
||||
fun getStartIntent(context: Context) = Intent(context, MainActivity::class.java)
|
||||
fun getStartIntent(context: Context, startMenu: MainView.MenuView? = null, clear: Boolean = false): Intent {
|
||||
return Intent(context, MainActivity::class.java)
|
||||
.apply {
|
||||
if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
|
||||
startMenu?.let { putExtra(EXTRA_START_MENU, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val isRootView: Boolean
|
||||
@ -56,14 +66,27 @@ class MainActivity : BaseActivity(), MainView {
|
||||
|
||||
override var startMenuIndex = 0
|
||||
|
||||
override var startMenuMoreIndex = -1
|
||||
|
||||
private val moreMenuFragments = listOf<Fragment>(
|
||||
MessageFragment.newInstance(),
|
||||
HomeworkFragment.newInstance(),
|
||||
NoteFragment.newInstance(),
|
||||
LuckyNumberFragment.newInstance()
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(mainToolbar)
|
||||
messageContainer = mainFragmentContainer
|
||||
|
||||
presenter.onAttachView(this, intent.getIntExtra(EXTRA_START_MENU_INDEX, -1))
|
||||
navController.initialize(startMenuIndex, savedInstanceState)
|
||||
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_START_MENU) as? MainView.MenuView)
|
||||
|
||||
navController.run {
|
||||
initialize(startMenuIndex, savedInstanceState)
|
||||
pushFragment(moreMenuFragments.getOrNull(startMenuMoreIndex))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
@ -74,7 +97,7 @@ class MainActivity : BaseActivity(), MainView {
|
||||
override fun initView() {
|
||||
mainBottomNav.run {
|
||||
addItems(
|
||||
mutableListOf(
|
||||
listOf(
|
||||
AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_menu_main_grade_26dp, 0),
|
||||
AHBottomNavigationItem(R.string.attendance_title, R.drawable.ic_menu_main_attendance_24dp, 0),
|
||||
AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_menu_main_exam_24dp, 0),
|
||||
@ -168,9 +191,10 @@ class MainActivity : BaseActivity(), MainView {
|
||||
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle?) {
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
navController.onSaveInstanceState(outState)
|
||||
intent.removeExtra(EXTRA_START_MENU)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -22,22 +22,19 @@ class MainPresenter @Inject constructor(
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BasePresenter<MainView>(errorHandler) {
|
||||
|
||||
fun onAttachView(view: MainView, initMenuIndex: Int) {
|
||||
fun onAttachView(view: MainView, initMenu: MainView.MenuView?) {
|
||||
super.onAttachView(view)
|
||||
view.run {
|
||||
startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex
|
||||
Timber.i("Main view is attached with $startMenuIndex menu index")
|
||||
view.apply {
|
||||
getProperViewIndexes(initMenu).let { (main, more) ->
|
||||
startMenuIndex = main
|
||||
startMenuMoreIndex = more
|
||||
}
|
||||
initView()
|
||||
Timber.i("Main view was initialized with $startMenuIndex menu index and $startMenuMoreIndex more index")
|
||||
}
|
||||
|
||||
syncManager.startSyncWorker()
|
||||
|
||||
analytics.logEvent(APP_OPEN, DESTINATION to when (initMenuIndex) {
|
||||
1 -> "Grades"
|
||||
3 -> "Timetable"
|
||||
4 -> "More"
|
||||
else -> "User action"
|
||||
})
|
||||
analytics.logEvent(APP_OPEN, DESTINATION to initMenu?.name)
|
||||
}
|
||||
|
||||
fun onViewChange() {
|
||||
@ -104,4 +101,12 @@ class MainPresenter @Inject constructor(
|
||||
errorHandler.dispatch(it)
|
||||
}))
|
||||
}
|
||||
|
||||
private fun getProperViewIndexes(initMenu: MainView.MenuView?): Pair<Int, Int> {
|
||||
return when {
|
||||
initMenu?.id in 0..3 -> initMenu!!.id to -1
|
||||
(initMenu?.id ?: 0) > 3 -> 4 to initMenu!!.id - 4
|
||||
else -> prefRepository.startMenuIndex to -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ interface MainView : BaseView {
|
||||
|
||||
var startMenuIndex: Int
|
||||
|
||||
var startMenuMoreIndex: Int
|
||||
|
||||
val isRootView: Boolean
|
||||
|
||||
val currentViewTitle: String?
|
||||
@ -37,4 +39,15 @@ interface MainView : BaseView {
|
||||
|
||||
val titleStringId: Int
|
||||
}
|
||||
|
||||
enum class MenuView(val id: Int) {
|
||||
GRADE(0),
|
||||
ATTENDANCE(1),
|
||||
EXAM(2),
|
||||
TIMETABLE(3),
|
||||
MESSAGE(4),
|
||||
HOMEWORK(5),
|
||||
NOTE(6),
|
||||
LUCKY_NUMBER(7),
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
|
||||
@ -75,12 +76,20 @@ class MessageFragment : BaseFragment(), MessageView, MainView.TitledView {
|
||||
messageProgress.visibility = if (show) VISIBLE else INVISIBLE
|
||||
}
|
||||
|
||||
fun onDeleteMessage(message: Message) {
|
||||
presenter.onDeleteMessage(message)
|
||||
}
|
||||
|
||||
fun onChildFragmentLoaded() {
|
||||
presenter.onChildViewLoaded()
|
||||
}
|
||||
|
||||
override fun notifyChildMessageDeleted(tabId: Int) {
|
||||
(pagerAdapter.getFragmentInstance(tabId) as? MessageTabFragment)?.onParentDeleteMessage()
|
||||
}
|
||||
|
||||
override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) {
|
||||
(pagerAdapter.getFragmentInstance(index) as? MessageView.MessageChildView)?.onParentLoadData(forceRefresh)
|
||||
(pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)?.onParentLoadData(forceRefresh)
|
||||
}
|
||||
|
||||
override fun openSendMessage() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.message
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
@ -15,10 +16,10 @@ class MessagePresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: MessageView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Message view is attached")
|
||||
disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread)
|
||||
.subscribe {
|
||||
view.initView()
|
||||
Timber.i("Message view was initialized")
|
||||
loadData()
|
||||
})
|
||||
}
|
||||
@ -43,6 +44,15 @@ class MessagePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onDeleteMessage(message: Message) {
|
||||
view?.notifyChildMessageDeleted(
|
||||
when (message.removed) {
|
||||
true -> 2
|
||||
else -> message.folderId - 1
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun onSendMessageButtonClicked() {
|
||||
view?.openSendMessage()
|
||||
}
|
||||
|
@ -14,10 +14,7 @@ interface MessageView : BaseView {
|
||||
|
||||
fun notifyChildLoadData(index: Int, forceRefresh: Boolean)
|
||||
|
||||
fun notifyChildMessageDeleted(tabId: Int)
|
||||
|
||||
fun openSendMessage()
|
||||
|
||||
interface MessageChildView {
|
||||
|
||||
fun onParentLoadData(forceRefresh: Boolean)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,9 @@ import android.view.ViewGroup
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
|
||||
import kotlinx.android.synthetic.main.fragment_message_preview.*
|
||||
import javax.inject.Inject
|
||||
@ -25,6 +27,8 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
lateinit var presenter: MessagePreviewPresenter
|
||||
|
||||
private var menuReplyButton: MenuItem? = null
|
||||
private var menuForwardButton: MenuItem? = null
|
||||
private var menuDeleteButton: MenuItem? = null
|
||||
|
||||
override val titleStringId: Int
|
||||
get() = R.string.message_title
|
||||
@ -32,6 +36,9 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
override val noSubjectString: String
|
||||
get() = getString(R.string.message_no_subject)
|
||||
|
||||
override val deleteMessageSuccessString: String
|
||||
get() = getString(R.string.message_delete_success)
|
||||
|
||||
companion object {
|
||||
const val MESSAGE_ID_KEY = "message_id"
|
||||
|
||||
@ -57,15 +64,21 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getInt(MESSAGE_ID_KEY) ?: 0)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_message_preview, menu)
|
||||
menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_message_preview, menu)
|
||||
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply)
|
||||
menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward)
|
||||
menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete)
|
||||
presenter.onCreateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.messagePreviewMenuReply) presenter.onReply()
|
||||
else false
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.messagePreviewMenuReply -> presenter.onReply()
|
||||
R.id.messagePreviewMenuForward -> presenter.onForward()
|
||||
R.id.messagePreviewMenuDelete -> presenter.onMessageDelete()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun setSubject(subject: String) {
|
||||
@ -92,8 +105,22 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
messagePreviewProgress.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showReplyButton(show: Boolean) {
|
||||
override fun showContent(show: Boolean) {
|
||||
messagePreviewContentContainer.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showOptions(show: Boolean) {
|
||||
menuReplyButton?.isVisible = show
|
||||
menuForwardButton?.isVisible = show
|
||||
menuDeleteButton?.isVisible = show
|
||||
}
|
||||
|
||||
override fun setDeletedOptionsLabels() {
|
||||
menuDeleteButton?.setTitle(R.string.message_delete_forever)
|
||||
}
|
||||
|
||||
override fun setNotDeletedOptionsLabels() {
|
||||
menuDeleteButton?.setTitle(R.string.message_move_to_bin)
|
||||
}
|
||||
|
||||
override fun showMessageError() {
|
||||
@ -101,9 +128,21 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
}
|
||||
|
||||
override fun openMessageReply(message: Message?) {
|
||||
context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message, true)) }
|
||||
}
|
||||
|
||||
override fun openMessageForward(message: Message?) {
|
||||
context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) }
|
||||
}
|
||||
|
||||
override fun popView() {
|
||||
(activity as MainActivity).popView()
|
||||
}
|
||||
|
||||
override fun notifyParentMessageDeleted(message: Message) {
|
||||
fragmentManager?.fragments?.forEach { if (it is MessageFragment) it.onDeleteMessage(message) }
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt(MESSAGE_ID_KEY, presenter.messageId)
|
||||
|
@ -22,7 +22,7 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
|
||||
var messageId: Int = 0
|
||||
|
||||
private var replyMessage: Message? = null
|
||||
private var message: Message? = null
|
||||
|
||||
fun onAttachView(view: MessagePreviewView, id: Int) {
|
||||
super.onAttachView(view)
|
||||
@ -41,13 +41,13 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
.doFinally { view?.showProgress(false) }
|
||||
.subscribe({ message ->
|
||||
Timber.i("Loading message $id preview result: Success ")
|
||||
replyMessage = message
|
||||
this@MessagePreviewPresenter.message = message
|
||||
view?.run {
|
||||
message.let {
|
||||
setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString)
|
||||
setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
|
||||
setContent(it.content.orEmpty())
|
||||
showReplyButton(true)
|
||||
initOptions()
|
||||
|
||||
if (it.recipient.isNotBlank()) setRecipient(it.recipient)
|
||||
else setSender(it.sender)
|
||||
@ -63,13 +63,69 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onReply(): Boolean {
|
||||
return if (replyMessage != null) {
|
||||
view?.openMessageReply(replyMessage)
|
||||
return if (message != null) {
|
||||
view?.openMessageReply(message)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
fun onForward(): Boolean {
|
||||
return if (message != null) {
|
||||
view?.openMessageForward(message)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
private fun deleteMessage() {
|
||||
message?.let { message ->
|
||||
disposable.add(messageRepository.deleteMessage(message)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
view?.run {
|
||||
showContent(false)
|
||||
showProgress(true)
|
||||
showOptions(false)
|
||||
}
|
||||
}
|
||||
.doFinally {
|
||||
view?.showProgress(false)
|
||||
}
|
||||
.subscribe({
|
||||
view?.run {
|
||||
notifyParentMessageDeleted(message)
|
||||
showMessage(deleteMessageSuccessString)
|
||||
popView()
|
||||
}
|
||||
}, { error ->
|
||||
view?.showMessageError()
|
||||
errorHandler.dispatch(error)
|
||||
}, {
|
||||
view?.showMessageError()
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onMessageDelete(): Boolean {
|
||||
deleteMessage()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun initOptions() {
|
||||
view?.apply {
|
||||
showOptions(message != null)
|
||||
message?.let {
|
||||
when (it.removed) {
|
||||
true -> setDeletedOptionsLabels()
|
||||
false -> setNotDeletedOptionsLabels()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateOptionsMenu() {
|
||||
view?.showReplyButton(replyMessage != null)
|
||||
initOptions()
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ interface MessagePreviewView : BaseSessionView {
|
||||
|
||||
val noSubjectString: String
|
||||
|
||||
val deleteMessageSuccessString: String
|
||||
|
||||
fun setSubject(subject: String)
|
||||
|
||||
fun setRecipient(recipient: String)
|
||||
@ -19,9 +21,21 @@ interface MessagePreviewView : BaseSessionView {
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
fun showReplyButton(show: Boolean)
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showOptions(show: Boolean)
|
||||
|
||||
fun setDeletedOptionsLabels()
|
||||
|
||||
fun setNotDeletedOptionsLabels()
|
||||
|
||||
fun showMessageError()
|
||||
|
||||
fun openMessageReply(message: Message?)
|
||||
|
||||
fun openMessageForward(message: Message?)
|
||||
|
||||
fun popView()
|
||||
|
||||
fun notifyParentMessageDeleted(message: Message)
|
||||
}
|
||||
|
@ -26,11 +26,14 @@ class SendMessageActivity : BaseActivity(), SendMessageView {
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_MESSAGE = "EXTRA_MESSAGE"
|
||||
private const val EXTRA_REPLY = "EXTRA_REPLY"
|
||||
|
||||
fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java)
|
||||
|
||||
fun getStartIntent(context: Context, message: Message?): Intent {
|
||||
return getStartIntent(context).putExtra(EXTRA_MESSAGE, message)
|
||||
fun getStartIntent(context: Context, message: Message?, reply: Boolean = false): Intent {
|
||||
return getStartIntent(context)
|
||||
.putExtra(EXTRA_MESSAGE, message)
|
||||
.putExtra(EXTRA_REPLY, reply)
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +62,7 @@ class SendMessageActivity : BaseActivity(), SendMessageView {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
messageContainer = sendMessageContainer
|
||||
|
||||
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message)
|
||||
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
|
@ -30,18 +30,25 @@ class SendMessagePresenter @Inject constructor(
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BasePresenter<SendMessageView>(errorHandler) {
|
||||
|
||||
fun onAttachView(view: SendMessageView, message: Message?) {
|
||||
fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Send message view is attached")
|
||||
loadData(message)
|
||||
Timber.i("Send message view was initialized")
|
||||
loadData(message, reply)
|
||||
view.apply {
|
||||
message?.let {
|
||||
setSubject("RE: ${message.subject}")
|
||||
if (preferencesRepository.fillMessageContent) {
|
||||
setContent(when (message.sender.isNotEmpty()) {
|
||||
true -> "\n\nOd: ${message.sender}\n"
|
||||
false -> "\n\nDo: ${message.recipient}\n"
|
||||
} + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}")
|
||||
setSubject(when (reply) {
|
||||
true -> "RE: "
|
||||
else -> "FW: "
|
||||
} + message.subject)
|
||||
if (preferencesRepository.fillMessageContent || reply != true) {
|
||||
setContent(
|
||||
when (reply) {
|
||||
true -> "\n\n"
|
||||
else -> ""
|
||||
} + when (message.sender.isNotEmpty()) {
|
||||
true -> "Od: ${message.sender}\n"
|
||||
false -> "Do: ${message.recipient}\n"
|
||||
} + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,7 +59,7 @@ class SendMessagePresenter @Inject constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun loadData(message: Message?) {
|
||||
private fun loadData(message: Message?, reply: Boolean?) {
|
||||
var reportingUnit: ReportingUnit? = null
|
||||
var recipients: List<Recipient> = emptyList()
|
||||
var selectedRecipient: List<Recipient> = emptyList()
|
||||
@ -69,7 +76,7 @@ class SendMessagePresenter @Inject constructor(
|
||||
recipients = it
|
||||
}
|
||||
.flatMapCompletable {
|
||||
if (message == null) Completable.complete()
|
||||
if (message == null || reply != true) Completable.complete()
|
||||
else recipientRepository.getMessageRecipients(student, message)
|
||||
.doOnSuccess {
|
||||
Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size)
|
||||
|
@ -17,13 +17,12 @@ import io.github.wulkanowy.ui.base.session.BaseSessionFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.message.MessageItem
|
||||
import io.github.wulkanowy.ui.modules.message.MessageView
|
||||
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_message_tab.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.MessageChildView {
|
||||
class MessageTabFragment : BaseSessionFragment(), MessageTabView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: MessageTabPresenter
|
||||
@ -76,7 +75,7 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.Me
|
||||
}
|
||||
|
||||
override fun updateData(data: List<MessageItem>) {
|
||||
tabAdapter.updateDataSet(data, true)
|
||||
tabAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun updateItem(item: AbstractFlexibleItem<*>) {
|
||||
@ -115,10 +114,14 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.Me
|
||||
(parentFragment as? MessageFragment)?.onChildFragmentLoaded()
|
||||
}
|
||||
|
||||
override fun onParentLoadData(forceRefresh: Boolean) {
|
||||
fun onParentLoadData(forceRefresh: Boolean) {
|
||||
presenter.onParentViewLoadData(forceRefresh)
|
||||
}
|
||||
|
||||
fun onParentDeleteMessage() {
|
||||
presenter.onDeleteMessage()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID, presenter.folder.name)
|
||||
|
@ -34,7 +34,29 @@ class MessageTabPresenter @Inject constructor(
|
||||
onParentViewLoadData(true)
|
||||
}
|
||||
|
||||
fun onDeleteMessage() {
|
||||
loadData(false)
|
||||
}
|
||||
|
||||
fun onParentViewLoadData(forceRefresh: Boolean) {
|
||||
loadData(forceRefresh)
|
||||
}
|
||||
|
||||
fun onMessageItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item is MessageItem) {
|
||||
Timber.i("Select message ${item.message.realId} item")
|
||||
view?.run {
|
||||
openMessage(item.message.realId)
|
||||
if (item.message.unread) {
|
||||
item.message.unread = false
|
||||
updateItem(item)
|
||||
updateMessage(item.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData(forceRefresh: Boolean) {
|
||||
Timber.i("Loading $folder message data started")
|
||||
disposable.apply {
|
||||
clear()
|
||||
@ -67,20 +89,6 @@ class MessageTabPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onMessageItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item is MessageItem) {
|
||||
Timber.i("Select message ${item.message.realId} item")
|
||||
view?.run {
|
||||
openMessage(item.message.realId)
|
||||
if (item.message.unread) {
|
||||
item.message.unread = false
|
||||
updateItem(item)
|
||||
updateMessage(item.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMessage(message: Message) {
|
||||
Timber.i("Attempt to update message ${message.realId}")
|
||||
disposable.add(messageRepository.updateMessage(message)
|
||||
|
@ -10,8 +10,8 @@ class MorePresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresen
|
||||
|
||||
override fun onAttachView(view: MoreView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("More view is attached")
|
||||
view.initView()
|
||||
Timber.i("More view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ class NotePresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: NoteView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Note view is attached")
|
||||
view.initView()
|
||||
Timber.i("Note view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.settings
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.takisoft.preferencex.PreferenceFragmentCompat
|
||||
import dagger.android.support.AndroidSupportInjection
|
||||
import io.github.wulkanowy.BuildConfig.DEBUG
|
||||
@ -44,8 +43,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
presenter.onSharedPreferenceChanged(key)
|
||||
}
|
||||
|
||||
override fun setTheme(theme: Int) {
|
||||
AppCompatDelegate.setDefaultNightMode(theme)
|
||||
override fun recreateView() {
|
||||
activity?.recreate()
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ class SettingsPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: SettingsView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Settings view is attached")
|
||||
Timber.i("Settings view was initialized")
|
||||
view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays)
|
||||
}
|
||||
|
||||
@ -31,8 +31,8 @@ class SettingsPresenter @Inject constructor(
|
||||
when (key) {
|
||||
serviceEnableKey -> syncManager.run { if (isServiceEnabled) startSyncWorker() else stopSyncWorker() }
|
||||
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true)
|
||||
currentThemeKey -> view?.setTheme(currentTheme)
|
||||
isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable)
|
||||
appThemeKey -> view?.recreateView()
|
||||
}
|
||||
}
|
||||
analytics.logEvent("setting_changed", "name" to key)
|
||||
|
@ -4,7 +4,7 @@ import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface SettingsView : BaseView {
|
||||
|
||||
fun setTheme(theme: Int)
|
||||
fun recreateView()
|
||||
|
||||
fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean)
|
||||
}
|
||||
|
@ -80,12 +80,12 @@ class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChi
|
||||
timetableNextButton.setOnClickListener { presenter.onNextDay() }
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_timetable, menu)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_timetable, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,8 @@ class TimetablePresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: TimetableView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Timetable is attached")
|
||||
view.initView()
|
||||
Timber.i("Timetable was initialized")
|
||||
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import android.widget.Toast.LENGTH_LONG
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.activity_widget_configure.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableWidgetConfigureActivity : BaseActivity(), TimetableWidgetConfigureView {
|
||||
|
||||
@Inject
|
||||
lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: TimetableWidgetConfigurePresenter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setResult(RESULT_CANCELED)
|
||||
setContentView(R.layout.activity_widget_configure)
|
||||
|
||||
intent.extras.let {
|
||||
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID), it?.getBoolean(EXTRA_FROM_PROVIDER))
|
||||
}
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
widgetConfigureRecycler.apply {
|
||||
adapter = configureAdapter
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
}
|
||||
configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<TimetableWidgetConfigureItem>) {
|
||||
configureAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun updateTimetableWidget(widgetId: Int) {
|
||||
sendBroadcast(Intent(this, TimetableWidgetProvider::class.java)
|
||||
.apply {
|
||||
action = ACTION_APPWIDGET_UPDATE
|
||||
putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
|
||||
})
|
||||
}
|
||||
|
||||
override fun setSuccessResult(widgetId: Int) {
|
||||
setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) })
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
Toast.makeText(this, text, LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun finishView() {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun openLoginView() {
|
||||
startActivity(LoginActivity.getStartIntent(this))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
presenter.onDetachView()
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_account.*
|
||||
|
||||
class TimetableWidgetConfigureItem(val student: Student, private val isCurrent: Boolean) :
|
||||
AbstractFlexibleItem<TimetableWidgetConfigureItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_account
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
accountItemImage.setBackgroundResource(if (isCurrent) R.drawable.ic_account_circular_border else 0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TimetableWidgetConfigureItem
|
||||
|
||||
if (student != other.student) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = student.hashCode()
|
||||
result = 31 * result + student.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableWidgetConfigurePresenter @Inject constructor(
|
||||
private val errorHandler: ErrorHandler,
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val sharedPref: SharedPrefHelper
|
||||
) : BasePresenter<TimetableWidgetConfigureView>(errorHandler) {
|
||||
|
||||
private var appWidgetId: Int? = null
|
||||
|
||||
private var isFromProvider = false
|
||||
|
||||
fun onAttachView(view: TimetableWidgetConfigureView, appWidgetId: Int?, isFromProvider: Boolean?) {
|
||||
super.onAttachView(view)
|
||||
this.appWidgetId = appWidgetId
|
||||
this.isFromProvider = isFromProvider ?: false
|
||||
view.initView()
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelect(item: AbstractFlexibleItem<*>) {
|
||||
if (item is TimetableWidgetConfigureItem) {
|
||||
registerStudent(item.student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(studentRepository.getSavedStudents(false)
|
||||
.map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } }
|
||||
.map { (students, currentStudentId) ->
|
||||
students.map { student -> TimetableWidgetConfigureItem(student, student.id == currentStudentId) }
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({
|
||||
when {
|
||||
it.isEmpty() -> view?.openLoginView()
|
||||
it.size == 1 && !isFromProvider -> registerStudent(it.single().student)
|
||||
else -> view?.updateData(it)
|
||||
}
|
||||
}, { errorHandler.dispatch(it) }))
|
||||
}
|
||||
|
||||
private fun registerStudent(student: Student) {
|
||||
appWidgetId?.also {
|
||||
sharedPref.putLong(getStudentWidgetKey(it), student.id)
|
||||
view?.apply {
|
||||
updateTimetableWidget(it)
|
||||
setSuccessResult(it)
|
||||
}
|
||||
}
|
||||
view?.finishView()
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface TimetableWidgetConfigureView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<TimetableWidgetConfigureItem>)
|
||||
|
||||
fun updateTimetableWidget(widgetId: Int)
|
||||
|
||||
fun setSuccessResult(widgetId: Int)
|
||||
|
||||
fun finishView()
|
||||
|
||||
fun openLoginView()
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.widgets.timetable
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Paint.ANTI_ALIAS_FLAG
|
||||
@ -15,9 +16,11 @@ import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
import timber.log.Timber
|
||||
|
||||
@ -48,28 +51,37 @@ class TimetableWidgetFactory(
|
||||
override fun onDestroy() {}
|
||||
|
||||
override fun onDataSetChanged() {
|
||||
intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) }
|
||||
?.let { date ->
|
||||
try {
|
||||
lessons = studentRepository.isStudentSaved()
|
||||
.flatMap { isSaved ->
|
||||
if (isSaved) {
|
||||
studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||
.flatMap { timetableRepository.getTimetable(it, date, date) }
|
||||
} else Single.just(emptyList())
|
||||
}
|
||||
.map { item -> item.sortedBy { it.number } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred while downloading data for the widget")
|
||||
}
|
||||
intent?.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId ->
|
||||
val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0))
|
||||
val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0)
|
||||
|
||||
lessons = try {
|
||||
studentRepository.isStudentSaved()
|
||||
.filter { true }
|
||||
.flatMap { studentRepository.getSavedStudents().toMaybe() }
|
||||
.flatMap {
|
||||
if (studentId == 0L) throw IllegalArgumentException("Student id is 0")
|
||||
|
||||
it.singleOrNull { student -> student.id == studentId }
|
||||
.let { student ->
|
||||
if (student != null) Maybe.just(student)
|
||||
else Maybe.empty()
|
||||
}
|
||||
}
|
||||
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
|
||||
.flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() }
|
||||
.map { item -> item.sortedBy { it.number } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet(emptyList())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred in timetable widget factory")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getViewAt(position: Int): RemoteViews? {
|
||||
if (position == INVALID_POSITION || lessons.getOrNull(position) === null) return null
|
||||
if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null
|
||||
|
||||
return RemoteViews(context.packageName, R.layout.item_widget_timetable).apply {
|
||||
lessons[position].let {
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.ui.widgets.timetable
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
@ -10,21 +10,28 @@ import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.widget.RemoteViews
|
||||
import dagger.android.AndroidInjection
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.services.widgets.TimetableWidgetService
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||
import io.github.wulkanowy.utils.nextSchoolDay
|
||||
import io.github.wulkanowy.utils.previousSchoolDay
|
||||
import io.github.wulkanowy.utils.shortcutWeekDayName
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.weekDayName
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
@ -32,13 +39,21 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
@Inject
|
||||
lateinit var appWidgetManager: AppWidgetManager
|
||||
|
||||
@Inject
|
||||
lateinit var studentRepository: StudentRepository
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPref: SharedPrefHelper
|
||||
|
||||
@Inject
|
||||
lateinit var schedulers: SchedulersProvider
|
||||
|
||||
@Inject
|
||||
lateinit var analytics: FirebaseAnalyticsHelper
|
||||
|
||||
companion object {
|
||||
const val EXTRA_FROM_PROVIDER = "extraFromProvider"
|
||||
|
||||
const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget"
|
||||
|
||||
const val EXTRA_BUTTON_TYPE = "extraButtonType"
|
||||
@ -49,7 +64,9 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
|
||||
const val BUTTON_RESET = "buttonReset"
|
||||
|
||||
fun createWidgetKey(appWidgetId: Int) = "timetable_widget_$appWidgetId"
|
||||
fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId"
|
||||
|
||||
fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId"
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
@ -63,12 +80,14 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
private fun onUpdate(context: Context, intent: Intent) {
|
||||
if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) {
|
||||
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
|
||||
updateWidget(context, appWidgetId, now().nextOrSameSchoolDay)
|
||||
val student = getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
|
||||
updateWidget(context, appWidgetId, now().nextOrSameSchoolDay, student)
|
||||
}
|
||||
} else {
|
||||
val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE)
|
||||
val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0)
|
||||
val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(createWidgetKey(toggledWidgetId), 0))
|
||||
val student = getStudent(sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), toggledWidgetId)
|
||||
val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0))
|
||||
val date = when (buttonType) {
|
||||
BUTTON_RESET -> now().nextOrSameSchoolDay
|
||||
BUTTON_NEXT -> savedDate.nextSchoolDay
|
||||
@ -76,35 +95,45 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
else -> now().nextOrSameSchoolDay
|
||||
}
|
||||
if (!buttonType.isNullOrBlank()) analytics.logEvent("changed_timetable_widget_day", "button" to buttonType)
|
||||
updateWidget(context, toggledWidgetId, date)
|
||||
updateWidget(context, toggledWidgetId, date, student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDelete(intent: Intent) {
|
||||
intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let {
|
||||
if (it != 0) sharedPref.delete(createWidgetKey(it))
|
||||
if (it != 0) {
|
||||
sharedPref.apply {
|
||||
delete(getStudentWidgetKey(it))
|
||||
delete(getDateWidgetKey(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate) {
|
||||
private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate, student: Student?) {
|
||||
RemoteViews(context.packageName, R.layout.widget_timetable).apply {
|
||||
setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty)
|
||||
setTextViewText(R.id.timetableWidgetDay, date.weekDayName.capitalize())
|
||||
setTextViewText(R.id.timetableWidgetDate, date.toFormattedString())
|
||||
setTextViewText(R.id.timetableWidgetDate, "${date.shortcutWeekDayName.capitalize()} ${date.toFormattedString()}")
|
||||
setTextViewText(R.id.timetableWidgetName, student?.studentName ?: context.getString(R.string.all_no_data))
|
||||
setRemoteAdapter(R.id.timetableWidgetList, Intent(context, TimetableWidgetService::class.java)
|
||||
.apply { action = createWidgetKey(appWidgetId) })
|
||||
.apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId) })
|
||||
setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT))
|
||||
setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV))
|
||||
createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).also {
|
||||
createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).let {
|
||||
setOnClickPendingIntent(R.id.timetableWidgetDate, it)
|
||||
setOnClickPendingIntent(R.id.timetableWidgetDay, it)
|
||||
setOnClickPendingIntent(R.id.timetableWidgetName, it)
|
||||
}
|
||||
setPendingIntentTemplate(R.id.timetableWidgetList,
|
||||
PendingIntent.getActivity(context, 1, MainActivity.getStartIntent(context).apply {
|
||||
putExtra(EXTRA_START_MENU_INDEX, 3)
|
||||
setOnClickPendingIntent(R.id.timetableWidgetAccount, PendingIntent.getActivity(context, -Int.MAX_VALUE + appWidgetId,
|
||||
Intent(context, TimetableWidgetConfigureActivity::class.java).apply {
|
||||
addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
|
||||
putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
|
||||
putExtra(EXTRA_FROM_PROVIDER, true)
|
||||
}, FLAG_UPDATE_CURRENT))
|
||||
setPendingIntentTemplate(R.id.timetableWidgetList,
|
||||
PendingIntent.getActivity(context, MenuView.TIMETABLE.id,
|
||||
MainActivity.getStartIntent(context, MenuView.TIMETABLE, true), FLAG_UPDATE_CURRENT))
|
||||
}.also {
|
||||
sharedPref.putLong(createWidgetKey(appWidgetId), date.toEpochDay(), true)
|
||||
sharedPref.putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true)
|
||||
appWidgetManager.apply {
|
||||
notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList)
|
||||
updateAppWidget(appWidgetId, it)
|
||||
@ -120,4 +149,31 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId)
|
||||
}, FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
private fun getStudent(studentId: Long, appWidgetId: Int): Student? {
|
||||
return try {
|
||||
studentRepository.isStudentSaved()
|
||||
.filter { true }
|
||||
.flatMap { studentRepository.getSavedStudents(false).toMaybe() }
|
||||
.flatMap { students ->
|
||||
students.singleOrNull { student -> student.id == studentId }
|
||||
.let { student ->
|
||||
when {
|
||||
student != null -> Maybe.just(student)
|
||||
studentId != 0L -> {
|
||||
studentRepository.getCurrentStudent(false)
|
||||
.toMaybe()
|
||||
.doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) }
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred in timetable widget provider")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,11 @@ package io.github.wulkanowy.utils
|
||||
|
||||
import android.os.Bundle
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class FirebaseAnalyticsHelper(private val analytics: FirebaseAnalytics) {
|
||||
class FirebaseAnalyticsHelper @Inject constructor(private val analytics: FirebaseAnalytics) {
|
||||
|
||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
||||
Bundle().apply {
|
||||
|
@ -0,0 +1,9 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
infix fun <T> List<T>.uniqueSubtract(other: List<T>): List<T> {
|
||||
val list = toMutableList()
|
||||
other.forEach {
|
||||
list.remove(it)
|
||||
}
|
||||
return list.toList()
|
||||
}
|
@ -1,7 +1,16 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class DebugLogTree : Timber.DebugTree() {
|
||||
|
||||
@ -20,3 +29,88 @@ class CrashlyticsTree : Timber.Tree() {
|
||||
else Crashlytics.logException(t)
|
||||
}
|
||||
}
|
||||
|
||||
class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks {
|
||||
|
||||
override fun onActivityPaused(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} PAUSED") }
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} RESUMED") }
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} STARTED") }
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} DESTROYED") }
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} SAVED INSTANCE STATE") }
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} STOPPED") }
|
||||
}
|
||||
|
||||
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}") }
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLifecycleCallbacks() {
|
||||
|
||||
override fun onFragmentViewCreated(fm: FragmentManager, f: Fragment, v: View, savedInstanceState: Bundle?) {
|
||||
Timber.d("${f::class.java.simpleName} VIEW CREATED ${checkSavedState(savedInstanceState)}")
|
||||
}
|
||||
|
||||
override fun onFragmentStopped(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} STOPPED")
|
||||
}
|
||||
|
||||
override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
|
||||
Timber.d("${f::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}")
|
||||
}
|
||||
|
||||
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} RESUMED")
|
||||
}
|
||||
|
||||
override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) {
|
||||
Timber.d("${f::class.java.simpleName} ATTACHED")
|
||||
}
|
||||
|
||||
override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} DESTROYED")
|
||||
}
|
||||
|
||||
override fun onFragmentSaveInstanceState(fm: FragmentManager, f: Fragment, outState: Bundle) {
|
||||
Timber.d("${f::class.java.simpleName} SAVED INSTANCE STATE")
|
||||
}
|
||||
|
||||
override fun onFragmentStarted(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} STARTED")
|
||||
}
|
||||
|
||||
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} VIEW DESTROYED")
|
||||
}
|
||||
|
||||
override fun onFragmentActivityCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
|
||||
Timber.d("${f::class.java.simpleName} ACTIVITY CREATED ${checkSavedState(savedInstanceState)}")
|
||||
}
|
||||
|
||||
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} PAUSED")
|
||||
}
|
||||
|
||||
override fun onFragmentDetached(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} DETACHED")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSavedState(savedInstanceState: Bundle?) = if (savedInstanceState == null) "(STATE IS NULL)" else ""
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user