1
0
Fork 1

Compare commits

..

No commits in common. "0.8.3" and "0.7.5" have entirely different histories.
0.8.3 ... 0.7.5

158 changed files with 654 additions and 2307 deletions

View file

@ -7,11 +7,11 @@ references:
container_config: &container_config container_config: &container_config
docker: docker:
- image: circleci/android:api-28 - image: circleci/android:api-28-alpha
working_directory: *workspace_root working_directory: *workspace_root
environment: environment:
environment: environment:
_JAVA_OPTS: -Xmx3072m JVM_OPTS: -Xmx3200m
attach_workspace: &attach_workspace attach_workspace: &attach_workspace
attach_workspace: attach_workspace:

View file

@ -21,6 +21,31 @@
<option name="CONTINUATION_INDENT_IN_ELVIS" value="false" /> <option name="CONTINUATION_INDENT_IN_ELVIS" value="false" />
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" /> <option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
</JetCodeStyleSettings> </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> <XML>
<option name="XML_KEEP_LINE_BREAKS" value="false" /> <option name="XML_KEEP_LINE_BREAKS" value="false" />
<option name="XML_ALIGN_ATTRIBUTES" value="false" /> <option name="XML_ALIGN_ATTRIBUTES" value="false" />

View file

@ -14,7 +14,7 @@ cache:
#branches: #branches:
# only: # only:
# - master # - master
# - 0.8.x # - 0.7.x
android: android:
licenses: licenses:

View file

@ -16,8 +16,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 28 targetSdkVersion 28
versionCode 36 versionCode 31
versionName "0.8.3" versionName "0.7.5"
multiDexEnabled true multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -80,54 +80,53 @@ androidExtensions {
play { play {
serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf" serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf"
serviceAccountCredentials = file('key.p12') serviceAccountCredentials = file('key.p12')
defaultToAppBundles = false defaultToAppBundles = true
track = 'alpha' track = 'alpha'
} }
dependencies { dependencies {
implementation 'io.github.wulkanowy:api:0.8.3'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation('io.github.wulkanowy:api:0.7.5') { exclude module: "threetenbp" }
implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.0.2" 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 'androidx.multidex:multidex:2.0.1'
implementation "androidx.cardview:cardview:1.0.0" implementation "android.arch.work:work-runtime:1.0.0"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation "android.arch.work:work-rxjava2:1.0.0"
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.work:work-runtime:2.0.1" implementation "androidx.room:room-runtime:2.1.0-alpha06"
implementation "androidx.work:work-rxjava2:2.0.1" implementation "androidx.room:room-rxjava2:2.1.0-alpha06"
kapt "androidx.room:room-compiler:2.1.0-alpha06"
implementation "androidx.room:room-runtime:2.1.0-alpha07" implementation 'com.takisoft.preferencex:preferencex:1.0.0'
implementation "androidx.room:room-rxjava2:2.1.0-alpha07"
kapt "androidx.room:room-compiler:2.1.0-alpha07"
implementation "com.google.dagger:dagger-android-support:2.22.1" implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.3'
kapt "com.google.dagger:dagger-compiler:2.22.1" kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.3'
kapt "com.google.dagger:dagger-android-processor:2.22.1"
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0' implementation "com.google.dagger:dagger-android-support:2.21"
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0' kapt "com.google.dagger:dagger-compiler:2.21"
kapt "com.google.dagger:dagger-android-processor:2.21"
implementation "eu.davidea:flexible-adapter:5.1.0" implementation "eu.davidea:flexible-adapter:5.1.0"
implementation "eu.davidea:flexible-adapter-ui:1.0.0" implementation "eu.davidea:flexible-adapter-ui:1.0.0"
implementation "com.aurelhubert:ahbottomnavigation:2.3.4" implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
implementation 'com.ncapdevi:frag-nav:3.2.0' 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 'com.github.pwittchen:reactivenetwork-rx2:3.0.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "io.reactivex.rxjava2:rxjava:2.2.8" implementation "io.reactivex.rxjava2:rxjava:2.2.7"
implementation 'com.google.code.gson:gson:2.8.5'
implementation "com.jakewharton.threetenabp:threetenabp:1.2.0" implementation "com.jakewharton.threetenabp:threetenabp:1.2.0"
implementation "com.jakewharton.timber:timber:4.7.1" implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.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.mikepenz:aboutlibraries:6.2.3"
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
implementation 'com.google.firebase:firebase-core:16.0.8' implementation 'com.google.firebase:firebase-core:16.0.8'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9' implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'
@ -139,15 +138,15 @@ dependencies {
testImplementation "junit:junit:4.12" testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:1.9.2" testImplementation "io.mockk:mockk:1.9.2"
testImplementation "org.mockito:mockito-inline:2.27.0" testImplementation "org.mockito:mockito-inline:2.25.1"
testImplementation 'org.threeten:threetenbp:1.3.8' testImplementation 'org.threeten:threetenbp:1.3.8'
androidTestImplementation 'androidx.test:core:1.1.0' androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation "io.mockk:mockk-android:1.9.2" androidTestImplementation "io.mockk:mockk-android:1.9.2"
androidTestImplementation 'org.mockito:mockito-android:2.27.0' androidTestImplementation 'org.mockito:mockito-android:2.25.1'
androidTestImplementation "androidx.room:room-testing:2.1.0-alpha07" androidTestImplementation "androidx.room:room-testing:2.1.0-alpha06"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
} }

View file

@ -31,17 +31,17 @@ task jacocoTestReport(type: JacocoReport) {
'**/*_Provide*Factory*.*', '**/*_Provide*Factory*.*',
'**/*_Factory.*'] '**/*_Factory.*']
classDirectories.setFrom(fileTree( classDirectories = fileTree(
dir: "$buildDir/intermediates/classes/debug", dir: "$buildDir/intermediates/classes/debug",
excludes: excludes excludes: excludes
) + fileTree( ) + fileTree(
dir: "$buildDir/tmp/kotlin-classes/debug", dir: "$buildDir/tmp/kotlin-classes/debug",
excludes: excludes excludes: excludes
)) )
sourceDirectories.setFrom(files("$project.projectDir/src/main/java")) sourceDirectories = files("$project.projectDir/src/main/java")
executionData.setFrom(fileTree( executionData = fileTree(
dir: project.projectDir, dir: project.projectDir,
includes: ["**/*.exec", "**/*.ec"] includes: ["**/*.exec", "**/*.ec"]
)) )
} }

View file

@ -22,7 +22,6 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate.of import org.threeten.bp.LocalDate.of
import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime
import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
import io.github.wulkanowy.api.grades.Grade as GradeApi import io.github.wulkanowy.api.grades.Grade as GradeApi
@ -110,73 +109,4 @@ class GradeRepositoryTest {
assertTrue { grades[2].isRead } assertTrue { grades[2].isRead }
assertTrue { grades[3].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)
}
} }

View file

@ -39,7 +39,7 @@ class StudentLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
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 = ""))) 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 = ""))
.blockingGet() .blockingGet()
val student = studentLocal.getCurrentStudent(true).blockingGet() val student = studentLocal.getCurrentStudent(true).blockingGet()

View file

@ -7,7 +7,7 @@ import org.threeten.bp.LocalDateTime.now
import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote
import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = "", teacher: String = ""): TimetableLocal { fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = ""): TimetableLocal {
return TimetableLocal( return TimetableLocal(
studentId = 1, studentId = 1,
diaryId = 2, diaryId = 2,
@ -20,7 +20,7 @@ fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", s
group = "", group = "",
room = room, room = room,
roomOld = "", roomOld = "",
teacher = teacher, teacher = "",
teacherOld = "", teacherOld = "",
info = "", info = "",
changes = false, changes = false,
@ -28,7 +28,7 @@ fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", s
) )
} }
fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = "", teacher: String = ""): TimetableRemote { fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = ""): TimetableRemote {
return TimetableRemote( return TimetableRemote(
number = number, number = number,
start = start.toDate(), start = start.toDate(),
@ -37,7 +37,7 @@ fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subje
subject = subject, subject = subject,
group = "", group = "",
room = room, room = room,
teacher = teacher, teacher = "",
info = "", info = "",
changes = false, changes = false,
canceled = false canceled = false

View file

@ -63,27 +63,23 @@ class TimetableRepositoryTest {
fun copyDetailsToCompletedFromPrevious() { fun copyDetailsToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf( timetableLocal.saveTimetable(listOf(
createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"), createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"),
createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia"), createTimetableLocal(1, of(2019, 3, 5, 8, 50), "321", "Religia"),
createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F"), createTimetableLocal(1, of(2019, 3, 5, 9, 40), "213", "W-F")
createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "Jan Kowalski")
)) ))
every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf( every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda"), createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda"),
createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia"), createTimetableRemote(1, of(2019, 3, 5, 8, 50), "", "Religia"),
createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F"), createTimetableRemote(1, of(2019, 3, 5, 9, 40), "", "W-F")
createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F")
)) ))
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)
.getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true) .getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true)
.blockingGet() .blockingGet()
assertEquals(4, lessons.size) assertEquals(3, lessons.size)
assertEquals("123", lessons[0].room) assertEquals("123", lessons[0].room)
assertEquals("321", lessons[1].room) assertEquals("321", lessons[1].room)
assertEquals("213", lessons[2].room) assertEquals("213", lessons[2].room)
assertEquals("", lessons[3].teacher)
} }
} }

View file

@ -34,7 +34,6 @@
android:name=".ui.modules.login.LoginActivity" android:name=".ui.modules.login.LoginActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/login_title" android:label="@string/login_title"
android:theme="@style/WulkanowyTheme.NoActionBar"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name=".ui.modules.main.MainActivity" android:name=".ui.modules.main.MainActivity"
@ -46,31 +45,13 @@
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/send_message_title" android:label="@string/send_message_title"
android:theme="@style/WulkanowyTheme.NoActionBar" /> 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 <service
android:name=".services.widgets.TimetableWidgetService" android:name=".services.widgets.TimetableWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" /> android:permission="android.permission.BIND_REMOTEVIEWS" />
<receiver <receiver
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider" android:name=".ui.widgets.timetable.TimetableWidgetProvider"
android:exported="true" android:exported="true"
android:label="@string/timetable_title"> android:label="@string/timetable_title">
<intent-filter> <intent-filter>
@ -81,17 +62,6 @@
android:resource="@xml/provider_widget_timetable" /> android:resource="@xml/provider_widget_timetable" />
</receiver> </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 <provider
android:name="androidx.work.impl.WorkManagerInitializer" android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init" android:authorities="${applicationId}.workmanager-init"

View file

@ -1,6 +1,7 @@
package io.github.wulkanowy package io.github.wulkanowy
import android.content.Context import android.content.Context
import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDex import androidx.multidex.MultiDex
import androidx.work.Configuration import androidx.work.Configuration
import androidx.work.WorkManager import androidx.work.WorkManager
@ -12,21 +13,24 @@ import dagger.android.support.DaggerApplication
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.utils.Log import eu.davidea.flexibleadapter.utils.Log
import io.fabric.sdk.android.Fabric import io.fabric.sdk.android.Fabric
import io.github.wulkanowy.BuildConfig.CRASHLYTICS_ENABLED
import io.github.wulkanowy.BuildConfig.DEBUG import io.github.wulkanowy.BuildConfig.DEBUG
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.di.DaggerAppComponent import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory import io.github.wulkanowy.services.sync.SyncWorkerFactory
import io.github.wulkanowy.utils.ActivityLifecycleLogger
import io.github.wulkanowy.utils.CrashlyticsTree import io.github.wulkanowy.utils.CrashlyticsTree
import io.github.wulkanowy.utils.DebugLogTree import io.github.wulkanowy.utils.DebugLogTree
import io.reactivex.exceptions.UndeliverableException import io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber import timber.log.Timber
import java.io.IOException import java.io.IOException
import java.lang.Exception
import javax.inject.Inject import javax.inject.Inject
class WulkanowyApp : DaggerApplication() { class WulkanowyApp : DaggerApplication() {
@Inject
lateinit var prefRepository: PreferencesRepository
@Inject @Inject
lateinit var workerFactory: SyncWorkerFactory lateinit var workerFactory: SyncWorkerFactory
@ -38,36 +42,32 @@ class WulkanowyApp : DaggerApplication() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
AndroidThreeTen.init(this) AndroidThreeTen.init(this)
initializeFabric()
if (DEBUG) enableDebugLog()
AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme)
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build()) WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build())
RxJavaPlugins.setErrorHandler(::onError) RxJavaPlugins.setErrorHandler(::onError)
initCrashlytics()
initLogging()
} }
private fun initLogging() { private fun enableDebugLog() {
if (DEBUG) {
Timber.plant(DebugLogTree()) Timber.plant(DebugLogTree())
FlexibleAdapter.enableLogs(Log.Level.DEBUG) FlexibleAdapter.enableLogs(Log.Level.DEBUG)
} else { }
private fun initializeFabric() {
Fabric.with(Fabric.Builder(this).kits(
Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!BuildConfig.CRASHLYTICS_ENABLED).build()).build()
).debuggable(BuildConfig.DEBUG).build())
Timber.plant(CrashlyticsTree()) Timber.plant(CrashlyticsTree())
} }
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
}
private fun initCrashlytics() { private fun onError(t: Throwable) {
Fabric.with(Fabric.Builder(this).kits( if (t is UndeliverableException && t.cause is IOException || t.cause is InterruptedException) {
Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!CRASHLYTICS_ENABLED).build()).build() Timber.e(t.cause, "An undeliverable error occurred")
).debuggable(DEBUG).build()) } 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> { override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.factory().create(this) return DaggerAppComponent.builder().create(this)
} }
} }

View file

@ -6,16 +6,18 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@SuppressLint("ApplySharedPref")
class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) { class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) {
@SuppressLint("ApplySharedPref")
fun putLong(key: String, value: Long, sync: Boolean = false) { fun putLong(key: String, value: Long, sync: Boolean = false) {
sharedPref.edit().putLong(key, value).apply { sharedPref.edit().putLong(key, value).apply {
if (sync) commit() else apply() if (sync) commit() else apply()
} }
} }
fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue) fun getLong(key: String, defaultValue: Long): Long {
return sharedPref.getLong(key, defaultValue)
}
fun delete(key: String) { fun delete(key: String) {
sharedPref.edit().remove(key).apply() sharedPref.edit().remove(key).apply()

View file

@ -23,8 +23,8 @@ interface MessagesDao {
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC") @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC")
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>> fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>
@Query("SELECT * FROM Messages WHERE id = :id") @Query("SELECT * FROM Messages WHERE student_id = :studentId AND real_id = :id")
fun load(id: Long): Maybe<Message> fun load(studentId: Int, id: Int): Maybe<Message>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC") @Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
fun loadDeleted(studentId: Int): Maybe<List<Message>> fun loadDeleted(studentId: Int): Maybe<List<Message>>

View file

@ -14,7 +14,7 @@ import javax.inject.Singleton
interface StudentDao { interface StudentDao {
@Insert(onConflict = ABORT) @Insert(onConflict = ABORT)
fun insertAll(student: List<Student>): List<Long> fun insert(student: Student): Long
@Delete @Delete
fun delete(student: Student) fun delete(student: Student)

View file

@ -8,7 +8,7 @@ class Migration11 : Migration(10, 11) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(""" database.execSQL("""
CREATE TABLE IF NOT EXISTS Grades_temp ( CREATE TABLE IF NOT EXISTS Grades_temp (
id INTEGER PRIMARY KEY NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
is_read INTEGER NOT NULL, is_read INTEGER NOT NULL,
is_notified INTEGER NOT NULL, is_notified INTEGER NOT NULL,
semester_id INTEGER NOT NULL, semester_id INTEGER NOT NULL,

View file

@ -6,7 +6,6 @@ import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.net.UnknownHostException import java.net.UnknownHostException
@ -32,8 +31,8 @@ class AttendanceRepository @Inject constructor(
local.getAttendance(semester, dates.first, dates.second) local.getAttendance(semester, dates.first, dates.second)
.toSingle(emptyList()) .toSingle(emptyList())
.doOnSuccess { oldAttendance -> .doOnSuccess { oldAttendance ->
local.deleteAttendance(oldAttendance.uniqueSubtract(newAttendance)) local.deleteAttendance(oldAttendance - newAttendance)
local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance)) local.saveAttendance(newAttendance - oldAttendance)
} }
}.flatMap { }.flatMap {
local.getAttendance(semester, dates.first, dates.second) local.getAttendance(semester, dates.first, dates.second)

View file

@ -4,7 +4,6 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@ -26,8 +25,8 @@ class AttendanceSummaryRepository @Inject constructor(
}.flatMap { new -> }.flatMap { new ->
local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) local.getAttendanceSummary(semester, subjectId).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteAttendanceSummary(old.uniqueSubtract(new)) local.deleteAttendanceSummary(old - new)
local.saveAttendanceSummary(new.uniqueSubtract(old)) local.saveAttendanceSummary(new - old)
} }
}.flatMap { local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) }) }.flatMap { local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) })
} }

View file

@ -6,7 +6,6 @@ import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.net.UnknownHostException import java.net.UnknownHostException
@ -32,8 +31,8 @@ class CompletedLessonsRepository @Inject constructor(
local.getCompletedLessons(semester, dates.first, dates.second) local.getCompletedLessons(semester, dates.first, dates.second)
.toSingle(emptyList()) .toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteCompleteLessons(old.uniqueSubtract(new)) local.deleteCompleteLessons(old - new)
local.saveCompletedLessons(new.uniqueSubtract(old)) local.saveCompletedLessons(new - old)
} }
}.flatMap { }.flatMap {
local.getCompletedLessons(semester, dates.first, dates.second) local.getCompletedLessons(semester, dates.first, dates.second)

View file

@ -6,7 +6,6 @@ import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.net.UnknownHostException import java.net.UnknownHostException
@ -28,12 +27,12 @@ class ExamRepository @Inject constructor(
.flatMap { .flatMap {
if (it) remote.getExams(semester, dates.first, dates.second) if (it) remote.getExams(semester, dates.first, dates.second)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { newExams ->
local.getExams(semester, dates.first, dates.second) local.getExams(semester, dates.first, dates.second)
.toSingle(emptyList()) .toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { oldExams ->
local.deleteExams(old.uniqueSubtract(new)) local.deleteExams(oldExams - newExams)
local.saveExams(new.uniqueSubtract(old)) local.saveExams(newExams - oldExams)
} }
}.flatMap { }.flatMap {
local.getExams(semester, dates.first, dates.second) local.getExams(semester, dates.first, dates.second)

View file

@ -5,7 +5,6 @@ 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.Grade
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
@ -25,12 +24,13 @@ class GradeRepository @Inject constructor(
.flatMap { .flatMap {
if (it) remote.getGrades(semester) if (it) remote.getGrades(semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { newGrades ->
local.getGrades(semester).toSingle(emptyList()) local.getGrades(semester).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { oldGrades ->
val notifyBreakDate = old.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate() val notifyBreakDate = oldGrades.maxBy { it.date }?.date
local.deleteGrades(old.uniqueSubtract(new)) ?: student.registrationDate.toLocalDate()
local.saveGrades(new.uniqueSubtract(old) local.deleteGrades(oldGrades - newGrades)
local.saveGrades((newGrades - oldGrades)
.onEach { .onEach {
if (it.date >= notifyBreakDate) it.apply { if (it.date >= notifyBreakDate) it.apply {
isRead = false isRead = false

View file

@ -4,7 +4,6 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@ -23,11 +22,11 @@ class GradeSummaryRepository @Inject constructor(
.flatMap { .flatMap {
if (it) remote.getGradeSummary(semester) if (it) remote.getGradeSummary(semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { newGradesSummary ->
local.getGradesSummary(semester).toSingle(emptyList()) local.getGradesSummary(semester).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { oldGradesSummary ->
local.deleteGradesSummary(old.uniqueSubtract(new)) local.deleteGradesSummary(oldGradesSummary - newGradesSummary)
local.saveGradesSummary(new.uniqueSubtract(old)) local.saveGradesSummary(newGradesSummary - oldGradesSummary)
} }
}.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) }) }.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) })
} }

View file

@ -4,7 +4,6 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@ -23,11 +22,11 @@ class GradeStatisticsRepository @Inject constructor(
.flatMap { .flatMap {
if (it) remote.getGradeStatistics(semester, isSemester) if (it) remote.getGradeStatistics(semester, isSemester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { newGradesStats ->
local.getGradesStatistics(semester, isSemester).toSingle(emptyList()) local.getGradesStatistics(semester, isSemester).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { oldGradesStats ->
local.deleteGradesStatistics(old.uniqueSubtract(new)) local.deleteGradesStatistics(oldGradesStats - newGradesStats)
local.saveGradesStatistics(new.uniqueSubtract(old)) local.saveGradesStatistics(newGradesStats - oldGradesStats)
} }
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) }) }.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
} }

View file

@ -6,7 +6,6 @@ import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.net.UnknownHostException import java.net.UnknownHostException
@ -27,11 +26,11 @@ class HomeworkRepository @Inject constructor(
.flatMap { .flatMap {
if (it) remote.getHomework(semester, monday, friday) if (it) remote.getHomework(semester, monday, friday)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { newGrades ->
local.getHomework(semester, monday, friday).toSingle(emptyList()) local.getHomework(semester, monday, friday).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { oldGrades ->
local.deleteHomework(old.uniqueSubtract(new)) local.deleteHomework(oldGrades - newGrades)
local.saveHomework(new.uniqueSubtract(old)) local.saveHomework(newGrades - oldGrades)
} }
}.flatMap { local.getHomework(semester, monday, friday).toSingle(emptyList()) }) }.flatMap { local.getHomework(semester, monday, friday).toSingle(emptyList()) })
} }

View file

@ -23,8 +23,8 @@ class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) {
messagesDb.deleteAll(messages) messagesDb.deleteAll(messages)
} }
fun getMessage(id: Long): Maybe<Message> { fun getMessage(student: Student, id: Int): Maybe<Message> {
return messagesDb.load(id) return messagesDb.load(student.id.toInt(), id)
} }
fun getMessages(student: Student, folder: MessageFolder): Maybe<List<Message>> { fun getMessages(student: Student, folder: MessageFolder): Maybe<List<Message>> {

View file

@ -59,8 +59,4 @@ class MessageRemote @Inject constructor(private val api: Api) {
} }
) )
} }
fun deleteMessage(message: Message): Single<Boolean> {
return api.deleteMessages(listOf(Pair(message.realId, message.folderId)))
}
} }

View file

@ -8,9 +8,7 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@ -35,8 +33,8 @@ class MessageRepository @Inject constructor(
}.flatMap { new -> }.flatMap { new ->
local.getMessages(student, folder).toSingle(emptyList()) local.getMessages(student, folder).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteMessages(old.uniqueSubtract(new)) local.deleteMessages(old - new)
local.saveMessages(new.uniqueSubtract(old) local.saveMessages((new - old)
.onEach { .onEach {
it.isNotified = !notify it.isNotified = !notify
}) })
@ -46,14 +44,14 @@ class MessageRepository @Inject constructor(
} }
} }
fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single<Message> { fun getMessage(student: Student, messageId: Int, markAsRead: Boolean = false): Single<Message> {
return Single.just(apiHelper.initApi(student)) return Single.just(apiHelper.initApi(student))
.flatMap { _ -> .flatMap { _ ->
local.getMessage(messageDbId) local.getMessage(student, messageId)
.filter { !it.content.isNullOrEmpty() } .filter { !it.content.isNullOrEmpty() }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) local.getMessage(messageDbId).toSingle() if (it) local.getMessage(student, messageId).toSingle()
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
} }
.flatMap { dbMessage -> .flatMap { dbMessage ->
@ -64,7 +62,7 @@ class MessageRepository @Inject constructor(
})) }))
} }
}.flatMap { }.flatMap {
local.getMessage(messageDbId).toSingle() local.getMessage(student, messageId).toSingle()
} }
) )
} }
@ -91,20 +89,4 @@ class MessageRepository @Inject constructor(
else Single.error(UnknownHostException()) 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))
}
}
} }

View file

@ -5,7 +5,6 @@ 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.Note
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
@ -28,8 +27,8 @@ class NoteRepository @Inject constructor(
}.flatMap { new -> }.flatMap { new ->
local.getNotes(student).toSingle(emptyList()) local.getNotes(student).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteNotes(old.uniqueSubtract(new)) local.deleteNotes(old - new)
local.saveNotes(new.uniqueSubtract(old) local.saveNotes((new - old)
.onEach { .onEach {
if (it.date >= student.registrationDate.toLocalDate()) it.apply { if (it.date >= student.registrationDate.toLocalDate()) it.apply {
isRead = false isRead = false

View file

@ -17,15 +17,12 @@ class PreferencesRepository @Inject constructor(
val isShowPresent: Boolean val isShowPresent: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true) 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 val isGradeExpandable: Boolean
get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false) get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false)
val appThemeKey: String = context.getString(R.string.pref_key_app_theme) val currentThemeKey: String = context.getString(R.string.pref_key_theme)
val appTheme: String val currentTheme: Int
get() = sharedPref.getString(appThemeKey, "light") ?: "light" get() = sharedPref.getString(currentThemeKey, "1")?.toIntOrNull() ?: 1
val gradeColorTheme: String val gradeColorTheme: String
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan" get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan"
@ -53,7 +50,8 @@ class PreferencesRepository @Inject constructor(
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0 get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0
val gradeMinusModifier: Double 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 val fillMessageContent: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true) get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true)

View file

@ -7,7 +7,6 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@ -32,8 +31,8 @@ class RecipientRepository @Inject constructor(
}.flatMap { new -> }.flatMap { new ->
local.getRecipients(student, role, unit).toSingle(emptyList()) local.getRecipients(student, role, unit).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteRecipients(old.uniqueSubtract(new)) local.deleteRecipients(old - new)
local.saveRecipients(new.uniqueSubtract(old)) local.saveRecipients(new - old)
} }
}.flatMap { }.flatMap {
local.getRecipients(student, role, unit).toSingle(emptyList()) local.getRecipients(student, role, unit).toSingle(emptyList())

View file

@ -5,7 +5,6 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
import io.github.wulkanowy.data.ApiHelper import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
@ -31,8 +30,8 @@ class ReportingUnitRepository @Inject constructor(
}.flatMap { new -> }.flatMap { new ->
local.getReportingUnits(student).toSingle(emptyList()) local.getReportingUnits(student).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteReportingUnits(old.uniqueSubtract(new)) local.deleteReportingUnits(old - new)
local.saveReportingUnits(new.uniqueSubtract(old)) local.saveReportingUnits(new - old)
} }
}.flatMap { local.getReportingUnits(student).toSingle(emptyList()) } }.flatMap { local.getReportingUnits(student).toSingle(emptyList()) }
) )

View file

@ -5,7 +5,6 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
import io.github.wulkanowy.data.ApiHelper import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
import timber.log.Timber import timber.log.Timber
@ -32,8 +31,8 @@ class SemesterRepository @Inject constructor(
if (currentSemesters.size == 1) { if (currentSemesters.size == 1) {
local.getSemesters(student).toSingle(emptyList()) local.getSemesters(student).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteSemesters(old.uniqueSubtract(new)) local.deleteSemesters(old - new)
local.saveSemesters(new.uniqueSubtract(old)) local.saveSemesters(new - old)
} }
} else { } else {
Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}") Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}")

View file

@ -17,8 +17,8 @@ class StudentLocal @Inject constructor(
private val context: Context private val context: Context
) { ) {
fun saveStudents(students: List<Student>): Single<List<Long>> { fun saveStudent(student: Student): Single<Long> {
return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) } return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) }
} }
fun getStudents(decryptPass: Boolean): Maybe<List<Student>> { fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {

View file

@ -22,8 +22,6 @@ class StudentRepository @Inject constructor(
fun isStudentSaved(): Single<Boolean> = local.getStudents(false).isEmpty.map { !it } fun isStudentSaved(): Single<Boolean> = local.getStudents(false).isEmpty.map { !it }
fun isCurrentStudentSet(): Single<Boolean> = local.getCurrentStudent(false).isEmpty.map { !it }
fun getStudents(email: String, password: String, endpoint: String, symbol: String = ""): Single<List<Student>> { fun getStudents(email: String, password: String, endpoint: String, symbol: String = ""): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings) return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
@ -43,8 +41,8 @@ class StudentRepository @Inject constructor(
.toSingle() .toSingle()
} }
fun saveStudents(students: List<Student>): Single<List<Long>> { fun saveStudent(student: Student): Single<Long> {
return local.saveStudents(students) return local.saveStudent(student)
} }
fun switchStudent(student: Student): Completable { fun switchStudent(student: Student): Completable {

View file

@ -4,7 +4,6 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.Semester
import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@ -27,8 +26,8 @@ class SubjectRepository @Inject constructor(
local.getSubjects(semester) local.getSubjects(semester)
.toSingle(emptyList()) .toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteSubjects(old.uniqueSubtract(new)) local.deleteSubjects(old - new)
local.saveSubjects(new.uniqueSubtract(old)) local.saveSubjects(new - old)
} }
}.flatMap { }.flatMap {
local.getSubjects(semester).toSingle(emptyList()) local.getSubjects(semester).toSingle(emptyList())

View file

@ -6,7 +6,6 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.net.UnknownHostException import java.net.UnknownHostException
@ -26,16 +25,17 @@ class TimetableRepository @Inject constructor(
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getTimetable(semester, monday, friday) if (it) remote.getTimetable(semester, monday, friday)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { newTimetable ->
local.getTimetable(semester, monday, friday) local.getTimetable(semester, monday, friday)
.toSingle(emptyList()) .toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { oldTimetable ->
local.deleteTimetable(old.uniqueSubtract(new)) local.deleteTimetable(oldTimetable - newTimetable)
local.saveTimetable(new.uniqueSubtract(old).map { item -> local.saveTimetable((newTimetable - oldTimetable).map { item ->
item.apply { item.apply {
old.singleOrNull { this.start == it.start }?.let { oldTimetable.singleOrNull { this.start == it.start }?.let {
return@map copy( return@map copy(
room = if (room.isEmpty()) it.room else room room = if (room.isEmpty()) it.room else room,
teacher = if (teacher.isEmpty()) it.teacher else teacher
) )
} }
} }

View file

@ -17,6 +17,6 @@ import javax.inject.Singleton
BuilderModule::class]) BuilderModule::class])
interface AppComponent : AndroidInjector<WulkanowyApp> { interface AppComponent : AndroidInjector<WulkanowyApp> {
@Component.Factory @Component.Builder
interface Factory : AndroidInjector.Factory<WulkanowyApp> abstract class Builder : AndroidInjector.Builder<WulkanowyApp>()
} }

View file

@ -9,6 +9,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.BuildConfig.DEBUG import io.github.wulkanowy.BuildConfig.DEBUG
import io.github.wulkanowy.WulkanowyApp import io.github.wulkanowy.WulkanowyApp
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import javax.inject.Named import javax.inject.Named
import javax.inject.Singleton import javax.inject.Singleton
@ -29,11 +30,11 @@ internal class AppModule {
@Singleton @Singleton
@Provides @Provides
fun provideFirebaseAnalytics(context: Context) = FirebaseAnalytics.getInstance(context) fun provideFirebaseAnalyticsHelper(context: Context) = FirebaseAnalyticsHelper(FirebaseAnalytics.getInstance(context))
@Singleton @Singleton
@Provides @Provides
fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context) fun provideAppWidgetManager(context: Context) = AppWidgetManager.getInstance(context)
@Singleton @Singleton
@Named("isDebug") @Named("isDebug")

View file

@ -5,14 +5,11 @@ import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.di.scopes.PerActivity import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule 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.MainActivity
import io.github.wulkanowy.ui.modules.main.MainModule import io.github.wulkanowy.ui.modules.main.MainModule
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.modules.splash.SplashActivity import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
@Module @Module
internal abstract class BuilderModule { internal abstract class BuilderModule {
@ -32,15 +29,6 @@ internal abstract class BuilderModule {
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindMessageSendActivity(): SendMessageActivity abstract fun bindMessageSendActivity(): SendMessageActivity
@ContributesAndroidInjector
abstract fun bindTimetableWidgetAccountActivity(): TimetableWidgetConfigureActivity
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider
@ContributesAndroidInjector
abstract fun bindLuckyNumberWidgetAccountActivity(): LuckyNumberWidgetConfigureActivity
@ContributesAndroidInjector
abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider
} }

View file

@ -35,7 +35,7 @@ class SyncWorker @AssistedInject constructor(
override fun createWork(): Single<Result> { override fun createWork(): Single<Result> {
Timber.i("SyncWorker is starting") Timber.i("SyncWorker is starting")
return studentRepository.isCurrentStudentSet() return studentRepository.isStudentSaved()
.filter { true } .filter { true }
.flatMap { studentRepository.getCurrentStudent().toMaybe() } .flatMap { studentRepository.getCurrentStudent().toMaybe() }
.flatMapCompletable { student -> .flatMapCompletable { student ->

View file

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import javax.inject.Inject import javax.inject.Inject
@ -47,8 +47,8 @@ class GradeWork @Inject constructor(
.setDefaults(DEFAULT_ALL) .setDefaults(DEFAULT_ALL)
.setColor(context.getCompatColor(R.color.colorPrimary)) .setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent( .setContentIntent(
PendingIntent.getActivity(context, MenuView.GRADE.id, PendingIntent.getActivity(context, 0,
MainActivity.getStartIntent(context, MenuView.GRADE, true), FLAG_UPDATE_CURRENT)) MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 0), FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.InboxStyle().run { .setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size)) setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size))
grades.forEach { addLine("${it.subject}: ${it.entry}") } grades.forEach { addLine("${it.subject}: ${it.entry}") }

View file

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import javax.inject.Inject import javax.inject.Inject
@ -47,8 +47,9 @@ class LuckyNumberWork @Inject constructor(
.setPriority(PRIORITY_HIGH) .setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary)) .setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent( .setContentIntent(
PendingIntent.getActivity(context, MenuView.MESSAGE.id, PendingIntent.getActivity(context, 0,
MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT)) MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
)
.build()) .build())
} }
} }

View file

@ -16,7 +16,7 @@ import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import javax.inject.Inject import javax.inject.Inject
@ -48,8 +48,8 @@ class MessageWork @Inject constructor(
.setPriority(PRIORITY_HIGH) .setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary)) .setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent( .setContentIntent(
PendingIntent.getActivity(context, MenuView.MESSAGE.id, PendingIntent.getActivity(context, 0,
MainActivity.getStartIntent(context, MenuView.MESSAGE, true), FLAG_UPDATE_CURRENT) MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
) )
.setStyle(NotificationCompat.InboxStyle().run { .setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size)) setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size))

View file

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.note.NoteRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import javax.inject.Inject import javax.inject.Inject
@ -47,8 +47,9 @@ class NoteWork @Inject constructor(
.setPriority(PRIORITY_HIGH) .setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary)) .setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent( .setContentIntent(
PendingIntent.getActivity(context, MenuView.NOTE.id, PendingIntent.getActivity(context, 0,
MainActivity.getStartIntent(context, MenuView.NOTE, true), FLAG_UPDATE_CURRENT)) MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
)
.setStyle(NotificationCompat.InboxStyle().run { .setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size)) setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size))
notes.forEach { addLine("${it.teacher}: ${it.category}") } notes.forEach { addLine("${it.teacher}: ${it.category}") }

View file

@ -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.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import javax.inject.Inject import javax.inject.Inject

View file

@ -2,59 +2,33 @@ package io.github.wulkanowy.ui.base
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.fragment.app.Fragment
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
import dagger.android.AndroidInjection import dagger.android.support.DaggerAppCompatActivity
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.utils.FragmentLifecycleLogger
import javax.inject.Inject
abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentInjector { abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
@Inject protected lateinit var messageContainer: View
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
@Inject
lateinit var fragmentLifecycleLogger: FragmentLifecycleLogger
@Inject
lateinit var themeManager: ThemeManager
protected var messageContainer: View? = null
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
themeManager.applyTheme(this)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
} }
override fun showError(text: String, error: Throwable) { override fun showError(text: String, error: Throwable) {
if (messageContainer != null) { Snackbar.make(messageContainer, text, LENGTH_LONG).setAction(R.string.all_details) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
.setAction(R.string.all_details) {
ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString())
} }.show()
.show()
} else showMessage(text)
} }
override fun showMessage(text: String) { override fun showMessage(text: String) {
if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() Snackbar.make(messageContainer, text, LENGTH_LONG).show()
else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
invalidateOptionsMenu() invalidateOptionsMenu()
} }
override fun supportFragmentInjector() = supportFragmentInjector
} }

View file

@ -2,7 +2,6 @@ package io.github.wulkanowy.ui.base
import android.view.View import android.view.View
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import io.github.wulkanowy.R import io.github.wulkanowy.R
@ -11,22 +10,16 @@ abstract class BaseFragment : DaggerFragment(), BaseView {
protected var messageContainer: View? = null protected var messageContainer: View? = null
override fun showError(text: String, error: Throwable) { override fun showError(text: String, error: Throwable) {
if (messageContainer != null) { if (messageContainer == null) (activity as? BaseActivity)?.showError(text, error)
Snackbar.make(messageContainer!!, text, LENGTH_LONG) else messageContainer?.also {
.setAction(R.string.all_details) { Snackbar.make(it, text, Snackbar.LENGTH_LONG).setAction(R.string.all_details) {
if (isAdded) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
} }.show()
.show()
} else {
(activity as? BaseActivity)?.showError(text, error)
} }
} }
override fun showMessage(text: String) { override fun showMessage(text: String) {
if (messageContainer != null) { if (messageContainer == null) (activity as? BaseActivity)?.showMessage(text)
Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() else messageContainer?.also { Snackbar.make(it, text, Snackbar.LENGTH_LONG).show() }
} else {
(activity as? BaseActivity)?.showMessage(text)
}
} }
} }

View file

@ -1,33 +0,0 @@
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 }
}
}

View file

@ -12,7 +12,6 @@ import io.github.wulkanowy.BuildConfig
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.withOnExtraListener import io.github.wulkanowy.utils.withOnExtraListener
import javax.inject.Inject import javax.inject.Inject
@ -45,7 +44,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
.withFields(R.string::class.java.fields) .withFields(R.string::class.java.fields)
.withCheckCachedDetection(false) .withCheckCachedDetection(false)
.withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio", .withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio",
"Butterknife", "CircleImageView") "OkHttp", "Butterknife", "CircleImageView")
.withOnExtraListener { presenter.onExtraSelect(it) }) .withOnExtraListener { presenter.onExtraSelect(it) })
}.let { }.let {
fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it) fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it)
@ -58,11 +57,11 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
} }
override fun openDiscordInviteView() { override fun openDiscordInviteView() {
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) startActivity(Intent.parseUri("https://discord.gg/vccAQBr", 0))
} }
override fun openHomepageWebView() { override fun openHomepageWebView() {
context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage) startActivity(Intent.parseUri("https://wulkanowy.github.io/", 0))
} }
override fun openEmailClientView() { override fun openEmailClientView() {
@ -81,7 +80,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
if (intent.resolveActivity(it.packageManager) != null) { if (intent.resolveActivity(it.packageManager) != null) {
startActivity(Intent.createChooser(intent, getString(R.string.about_feedback))) startActivity(Intent.createChooser(intent, getString(R.string.about_feedback)))
} else { } else {
it.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage) startActivity(Intent.parseUri("https://github.com/wulkanowy/wulkanowy/issues", 0))
} }
} }
} }

View file

@ -17,7 +17,7 @@ class AboutPresenter @Inject constructor(
override fun onAttachView(view: AboutView) { override fun onAttachView(view: AboutView) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("About view was initialized") Timber.i("About view is attached")
} }
fun onExtraSelect(type: Libs.SpecialButton?) { fun onExtraSelect(type: Libs.SpecialButton?) {

View file

@ -15,6 +15,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.utils.setOnItemClickListener import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.dialog_account.* import kotlinx.android.synthetic.main.dialog_account.*
import javax.inject.Inject import javax.inject.Inject
@ -96,8 +97,11 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
} }
} }
override fun recreateMainView() { override fun recreateView() {
activity?.recreate() activity?.also {
startActivity(MainActivity.getStartIntent(it))
it.finish()
}
} }
override fun onDestroy() { override fun onDestroy() {

View file

@ -19,8 +19,8 @@ class AccountPresenter @Inject constructor(
override fun onAttachView(view: AccountView) { override fun onAttachView(view: AccountView) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Account dialog is attached")
view.initView() view.initView()
Timber.i("Account dialog view was initialized")
loadData() loadData()
} }
@ -54,7 +54,7 @@ class AccountPresenter @Inject constructor(
openClearLoginView() openClearLoginView()
} else { } else {
Timber.i("Logout result: Switch to another student") Timber.i("Logout result: Switch to another student")
recreateMainView() recreateView()
} }
} }
}, { }, {
@ -73,10 +73,9 @@ class AccountPresenter @Inject constructor(
disposable.add(studentRepository.switchStudent(item.student) disposable.add(studentRepository.switchStudent(item.student)
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doFinally { view?.dismissView() }
.subscribe({ .subscribe({
Timber.i("Change a student result: Success") Timber.i("Change a student result: Success")
view?.recreateMainView() view?.recreateView()
}, { }, {
Timber.i("Change a student result: An exception occurred") Timber.i("Change a student result: An exception occurred")
errorHandler.dispatch(it) errorHandler.dispatch(it)

View file

@ -16,6 +16,6 @@ interface AccountView : BaseView {
fun openClearLoginView() fun openClearLoginView()
fun recreateMainView() fun recreateView()
} }

View file

@ -77,12 +77,12 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
attendanceNextButton.setOnClickListener { presenter.onNextDay() } attendanceNextButton.setOnClickListener { presenter.onNextDay() }
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater.inflate(R.menu.action_menu_attendance, menu) inflater?.inflate(R.menu.action_menu_attendance, menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected() return if (item?.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
else false else false
} }
@ -103,7 +103,7 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
} }
override fun onFragmentReselected() { override fun onFragmentReselected() {
if (::presenter.isInitialized) presenter.onViewReselected() presenter.onViewReselected()
} }
override fun popView() { override fun popView() {

View file

@ -37,8 +37,8 @@ class AttendancePresenter @Inject constructor(
fun onAttachView(view: AttendanceView, date: Long?) { fun onAttachView(view: AttendanceView, date: Long?) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Attendance view is attached")
view.initView() view.initView()
Timber.i("Attendance view was initialized")
loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay())) loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay()))
reloadView() reloadView()
} }

View file

@ -35,8 +35,8 @@ class AttendanceSummaryPresenter @Inject constructor(
fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) { fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Attendance summary view is attached with subject id ${subjectId ?: -1}")
view.initView() view.initView()
Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}")
loadData(subjectId ?: -1) loadData(subjectId ?: -1)
loadSubjects() loadSubjects()
} }

View file

@ -88,7 +88,7 @@ class ExamFragment : BaseSessionFragment(), ExamView, MainView.MainChildView, Ma
} }
override fun onFragmentReselected() { override fun onFragmentReselected() {
if (::presenter.isInitialized) presenter.onViewReselected() presenter.onViewReselected()
} }
override fun showEmpty(show: Boolean) { override fun showEmpty(show: Boolean) {

View file

@ -36,8 +36,8 @@ class ExamPresenter @Inject constructor(
fun onAttachView(view: ExamView, date: Long?) { fun onAttachView(view: ExamView, date: Long?) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Exam view is attached")
view.initView() view.initView()
Timber.i("Exam view was initialized")
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
reloadView() reloadView()
} }

View file

@ -1,54 +0,0 @@
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() }
}
}
}

View file

@ -57,9 +57,9 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY)) presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY))
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater.inflate(R.menu.action_menu_grade, menu) inflater?.inflate(R.menu.action_menu_grade, menu)
semesterSwitchMenu = menu.findItem(R.id.gradeMenuSemester) semesterSwitchMenu = menu?.findItem(R.id.gradeMenuSemester)
presenter.onCreateMenu() presenter.onCreateMenu()
} }
@ -82,13 +82,13 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return if (item.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch() return if (item?.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch()
else false else false
} }
override fun onFragmentReselected() { override fun onFragmentReselected() {
if (::presenter.isInitialized) presenter.onViewReselected() presenter.onViewReselected()
} }
override fun showContent(show: Boolean) { override fun showContent(show: Boolean) {

View file

@ -27,12 +27,12 @@ class GradePresenter @Inject constructor(
fun onAttachView(view: GradeView, savedIndex: Int?) { fun onAttachView(view: GradeView, savedIndex: Int?) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Grade view is attached")
selectedIndex = savedIndex ?: 0 selectedIndex = savedIndex ?: 0
view.run { view.run {
initView() initView()
enableSwipe(false) enableSwipe(false)
} }
Timber.i("Grade view was initialized with $selectedIndex index")
loadData() loadData()
} }

View file

@ -67,8 +67,8 @@ class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.
presenter.onAttachView(this) presenter.onAttachView(this)
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater.inflate(R.menu.action_menu_grade_details, menu) inflater?.inflate(R.menu.action_menu_grade_details, menu)
} }
override fun initView() { override fun initView() {
@ -88,8 +88,8 @@ class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.
gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return if (item.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected() return if (item?.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected()
else false else false
} }

View file

@ -8,9 +8,10 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler 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.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider 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 io.github.wulkanowy.utils.getBackgroundColor
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -22,7 +23,6 @@ class GradeDetailsPresenter @Inject constructor(
private val studentRepository: StudentRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val averageProvider: GradeAverageProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeDetailsView>(errorHandler) { ) : BaseSessionPresenter<GradeDetailsView>(errorHandler) {
@ -109,16 +109,11 @@ class GradeDetailsPresenter @Inject constructor(
private fun loadData(semesterId: Int, forceRefresh: Boolean) { private fun loadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade details data started") Timber.i("Loading grade details data started")
disposable.add(studentRepository.getCurrentStudent() disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } } .flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } }
.flatMap { (student, semesters) -> .flatMap { gradeRepository.getGrades(it.second, it.first.first { item -> item.semesterId == semesterId }, forceRefresh) }
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.sortedByDescending { grade -> grade.date } }
.map { it.groupBy { grade -> grade.subject }.toSortedMap() } .map { it.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } }
.map { createGradeItems(it, averages) } .map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }
}
}
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doFinally { .doFinally {
@ -144,36 +139,32 @@ class GradeDetailsPresenter @Inject constructor(
}) })
} }
private fun createGradeItems(items: Map<String, List<Grade>>, averages: Map<String, Double>): List<GradeDetailsHeader> { private fun createGradeItems(items: Map<String, List<Grade>>): List<GradeDetailsHeader> {
val isGradeExpandable = preferencesRepository.isGradeExpandable
val gradeColorTheme = preferencesRepository.gradeColorTheme
val noDescriptionString = view?.noDescriptionString.orEmpty()
val weightString = view?.weightString.orEmpty()
return items.map { return items.map {
it.value.calcAverage().let { average ->
GradeDetailsHeader( GradeDetailsHeader(
subject = it.key, subject = it.key,
average = formatAverage(averages[it.key]), average = formatAverage(average),
number = view?.getGradeNumberString(it.value.size).orEmpty(), number = view?.getGradeNumberString(it.value.size).orEmpty(),
newGrades = it.value.filter { grade -> !grade.isRead }.size, newGrades = it.value.filter { grade -> !grade.isRead }.size,
isExpandable = isGradeExpandable isExpandable = preferencesRepository.isGradeExpandable
).apply { ).apply {
subItems = it.value.map { item -> subItems = it.value.map { item ->
GradeDetailsItem( GradeDetailsItem(
grade = item, grade = item,
valueBgColor = item.getBackgroundColor(gradeColorTheme), valueBgColor = item.getBackgroundColor(preferencesRepository.gradeColorTheme),
weightString = weightString, weightString = view?.weightString.orEmpty(),
noDescriptionString = noDescriptionString noDescriptionString = view?.noDescriptionString.orEmpty()
) )
} }
} }
} }
} }
}
private fun formatAverage(average: Double?): String { private fun formatAverage(average: Double): String {
return view?.run { return view?.run {
if (average == null || average == .0) emptyAverageString if (average == 0.0) emptyAverageString
else averageString.format(average) else averageString.format(average)
}.orEmpty() }.orEmpty()
} }

View file

@ -1,15 +1,17 @@
package io.github.wulkanowy.ui.modules.grade.summary package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.data.db.entities.GradeSummary 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.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler 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.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier
import timber.log.Timber import timber.log.Timber
import java.lang.String.format import java.lang.String.format
import java.util.Locale.FRANCE import java.util.Locale.FRANCE
@ -18,9 +20,10 @@ import javax.inject.Inject
class GradeSummaryPresenter @Inject constructor( class GradeSummaryPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, private val errorHandler: SessionErrorHandler,
private val gradeSummaryRepository: GradeSummaryRepository, private val gradeSummaryRepository: GradeSummaryRepository,
private val gradeRepository: GradeRepository,
private val studentRepository: StudentRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val averageProvider: GradeAverageProvider, private val preferencesRepository: PreferencesRepository,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeSummaryView>(errorHandler) { ) : BaseSessionPresenter<GradeSummaryView>(errorHandler) {
@ -33,13 +36,25 @@ class GradeSummaryPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade summary data started") Timber.i("Loading grade summary data started")
disposable.add(studentRepository.getCurrentStudent() disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } } .flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } }
.flatMap { (student, semesters) -> .map { pair -> pair.first.first { it.semesterId == semesterId } to pair.second }
gradeSummaryRepository.getGradesSummary(semesters.first { it.semesterId == semesterId }, forceRefresh) .flatMap {
.map { it.sortedBy { subject -> subject.subject } } gradeSummaryRepository.getGradesSummary(it.first, forceRefresh)
.flatMap { gradesSummary -> .flatMap { gradesSummary ->
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh) gradeRepository.getGrades(it.second, it.first, forceRefresh)
.map { averages -> createGradeSummaryItemsAndHeader(gradesSummary, averages) } .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())
)
}
}
} }
} }
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
@ -51,14 +66,14 @@ class GradeSummaryPresenter @Inject constructor(
enableSwipe(true) enableSwipe(true)
notifyParentDataLoaded(semesterId) notifyParentDataLoaded(semesterId)
} }
}.subscribe({ (gradeSummaryItems, gradeSummaryHeader) -> }.subscribe({
Timber.i("Loading grade summary result: Success") Timber.i("Loading grade summary result: Success")
view?.run { view?.run {
showEmpty(gradeSummaryItems.isEmpty()) showEmpty(it.first.isEmpty())
showContent(gradeSummaryItems.isNotEmpty()) showContent(it.first.isNotEmpty())
updateData(gradeSummaryItems, gradeSummaryHeader) updateData(it.first, it.second)
} }
analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh) analytics.logEvent("load_grade_summary", "items" to it.first.size, "force_refresh" to forceRefresh)
}) { }) {
Timber.i("Loading grade summary result: An exception occurred") Timber.i("Loading grade summary result: An exception occurred")
view?.run { showEmpty(isViewEmpty) } view?.run { showEmpty(isViewEmpty) }
@ -89,23 +104,15 @@ class GradeSummaryPresenter @Inject constructor(
disposable.clear() disposable.clear()
} }
private fun createGradeSummaryItemsAndHeader(gradesSummary: List<GradeSummary>, averages: Map<String, Double>) private fun createGradeSummaryItems(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
: Pair<List<GradeSummaryItem>, GradeSummaryScrollableHeader> { : List<GradeSummaryItem> {
return averages.filterValues { value -> value != 0.0 } return gradesSummary.filter { !checkEmpty(it, averages) }.map { it ->
.let { filteredAverages ->
gradesSummary.filter { !checkEmpty(it, filteredAverages) }
.map {
GradeSummaryItem( GradeSummaryItem(
title = it.subject, title = it.subject,
average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""), average = formatAverage(averages.getOrElse(it.subject) { 0.0 }, ""),
predictedGrade = it.predictedGrade, predictedGrade = it.predictedGrade,
finalGrade = it.finalGrade finalGrade = it.finalGrade
) )
}.let {
it to GradeSummaryScrollableHeader(
formatAverage(gradesSummary.calcAverage()),
formatAverage(filteredAverages.values.average()))
}
} }
} }

View file

@ -34,8 +34,8 @@ class HomeworkPresenter @Inject constructor(
fun onAttachView(view: HomeworkView, date: Long?) { fun onAttachView(view: HomeworkView, date: Long?) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Homework view is attached")
view.initView() view.initView()
Timber.i("Homework view was initialized")
loadData(LocalDate.ofEpochDay(date ?: LocalDate.now().nextOrSameSchoolDay.toEpochDay())) loadData(LocalDate.ofEpochDay(date ?: LocalDate.now().nextOrSameSchoolDay.toEpochDay()))
reloadView() reloadView()
} }

View file

@ -14,7 +14,7 @@ class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePrese
initAdapter() initAdapter()
showActionBar(false) showActionBar(false)
} }
Timber.i("Login view was initialized") Timber.i("Login view is attached")
} }
fun onFormViewAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) { fun onFormViewAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) {

View file

@ -16,7 +16,6 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.hideSoftInput import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.setOnItemSelectedListener import io.github.wulkanowy.utils.setOnItemSelectedListener
import io.github.wulkanowy.utils.setOnTextChangedListener import io.github.wulkanowy.utils.setOnTextChangedListener
import io.github.wulkanowy.utils.showSoftInput import io.github.wulkanowy.utils.showSoftInput
@ -54,8 +53,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() } loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() }
loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() } loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() }
loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() } loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() }
loginFormSignIn.setOnClickListener { presenter.onSignInClick() } loginFormSignIn.setOnClickListener { presenter.attemptLogin() }
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
loginFormPass.setOnEditorActionListener { _, id, _ -> loginFormPass.setOnEditorActionListener { _, id, _ ->
if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false
@ -132,10 +130,6 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
} }
} }
override fun showPrivacyPolicy() {
loginFormPrivacyLink.visibility = VISIBLE
}
override fun notifyParentAccountLogged(students: List<Student>) { override fun notifyParentAccountLogged(students: List<Student>) {
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple( (activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
loginFormName.text.toString(), loginFormName.text.toString(),
@ -144,10 +138,6 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
)) ))
} }
override fun openPrivacyPolicyPage() {
context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage)
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
presenter.onDetachView() presenter.onDetachView()

View file

@ -22,8 +22,7 @@ class LoginFormPresenter @Inject constructor(
super.onAttachView(view) super.onAttachView(view)
view.run { view.run {
initView() initView()
if (isDebug) showVersion() else showPrivacyPolicy() if (isDebug) showVersion()
errorHandler.onBadCredentials = { errorHandler.onBadCredentials = {
setErrorPassIncorrect() setErrorPassIncorrect()
showSoftKeyboard() showSoftKeyboard()
@ -32,10 +31,6 @@ class LoginFormPresenter @Inject constructor(
} }
} }
fun onPrivacyLinkClick() {
view?.openPrivacyPolicyPage()
}
fun onHostSelected() { fun onHostSelected() {
view?.apply { view?.apply {
clearPassError() clearPassError()
@ -52,7 +47,7 @@ class LoginFormPresenter @Inject constructor(
view?.clearNameError() view?.clearNameError()
} }
fun onSignInClick() { fun attemptLogin() {
val email = view?.formNameValue.orEmpty() val email = view?.formNameValue.orEmpty()
val password = view?.formPassValue.orEmpty() val password = view?.formPassValue.orEmpty()
val endpoint = view?.formHostValue.orEmpty() val endpoint = view?.formHostValue.orEmpty()

View file

@ -37,9 +37,5 @@ interface LoginFormView : BaseView {
fun showVersion() fun showVersion()
fun showPrivacyPolicy()
fun notifyParentAccountLogged(students: List<Student>) fun notifyParentAccountLogged(students: List<Student>)
fun openPrivacyPolicyPage()
} }

View file

@ -1,11 +1,14 @@
package io.github.wulkanowy.ui.modules.login.studentselect 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.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@ -42,7 +45,6 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
} }
override fun initView() { override fun initView() {
loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() }
loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } } loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } }
loginStudentSelectRecycler.apply { loginStudentSelectRecycler.apply {
@ -52,11 +54,14 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
} }
override fun updateData(data: List<LoginStudentSelectItem>) { override fun updateData(data: List<LoginStudentSelectItem>) {
loginAdapter.updateDataSet(data) loginAdapter.updateDataSet(data, true)
} }
override fun openMainView() { override fun openMainView() {
activity?.let { startActivity(MainActivity.getStartIntent(context = it, clear = true)) } activity?.let {
startActivity(MainActivity.getStartIntent(it)
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
}
} }
override fun showProgress(show: Boolean) { override fun showProgress(show: Boolean) {
@ -64,11 +69,11 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
} }
override fun showContent(show: Boolean) { override fun showContent(show: Boolean) {
loginStudentSelectContent.visibility = if (show) VISIBLE else GONE loginStudentSelectRecycler.visibility = if (show) VISIBLE else GONE
} }
override fun enableSignIn(enable: Boolean) { override fun showActionBar(show: Boolean) {
loginStudentSelectSignIn.isEnabled = enable (activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() }
} }
fun onParentInitStudentSelectFragment(students: List<Student>) { fun onParentInitStudentSelectFragment(students: List<Student>) {

View file

@ -13,15 +13,15 @@ import kotlinx.android.synthetic.main.item_login_student_select.*
class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() { class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
override fun getLayoutRes() = R.layout.item_login_student_select override fun getLayoutRes(): Int = R.layout.item_login_student_select
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ItemViewHolder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ItemViewHolder {
return ItemViewHolder(view, adapter) return ItemViewHolder(view, adapter)
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>) { override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply { holder.run {
loginItemName.text = "${student.studentName} ${student.className}" loginItemName.text = "${student.studentName} ${student.className}"
loginItemSchool.text = student.schoolName loginItemSchool.text = student.schoolName
} }
@ -43,17 +43,7 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
} }
class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View override val containerView: View
get() = itemView get() = itemView
init {
loginItemCheck.setOnClickListener { super.onClick(loginItemContainer) }
}
override fun onClick(view: View?) {
super.onClick(view)
loginItemCheck.apply { isChecked = !isChecked }
}
} }
} }

View file

@ -8,6 +8,7 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Single
import timber.log.Timber import timber.log.Timber
import java.io.Serializable import java.io.Serializable
import javax.inject.Inject import javax.inject.Inject
@ -21,13 +22,10 @@ class LoginStudentSelectPresenter @Inject constructor(
var students = emptyList<Student>() var students = emptyList<Student>()
var selectedStudents = mutableListOf<Student>()
fun onAttachView(view: LoginStudentSelectView, students: Serializable?) { fun onAttachView(view: LoginStudentSelectView, students: Serializable?) {
super.onAttachView(view) super.onAttachView(view)
view.run { view.run {
initView() initView()
enableSignIn(false)
errorHandler.onStudentDuplicate = { errorHandler.onStudentDuplicate = {
showMessage(it) showMessage(it)
Timber.i("The student already registered in the app was selected") Timber.i("The student already registered in the app was selected")
@ -39,21 +37,13 @@ class LoginStudentSelectPresenter @Inject constructor(
} }
} }
fun onSignIn() {
registerStudents(selectedStudents)
}
fun onParentInitStudentSelectView(students: List<Student>) { fun onParentInitStudentSelectView(students: List<Student>) {
loadData(students) loadData(students)
if (students.size == 1) registerStudents(students)
} }
fun onItemSelected(item: AbstractFlexibleItem<*>?) { fun onItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is LoginStudentSelectItem) { if (item is LoginStudentSelectItem) {
selectedStudents.removeAll { it == item.student } registerStudent(item.student)
.let { if (!it) selectedStudents.add(item.student) }
view?.enableSignIn(selectedStudents.isNotEmpty())
} }
} }
@ -64,30 +54,33 @@ class LoginStudentSelectPresenter @Inject constructor(
} }
} }
private fun registerStudents(students: List<Student>) { private fun registerStudent(student: Student) {
disposable.add(studentRepository.saveStudents(students) disposable.add(studentRepository.saveStudent(student)
.map { students.first().apply { id = it.first() } } .map { student.apply { id = it } }
.flatMapCompletable { studentRepository.switchStudent(it) } .onErrorResumeNext { studentRepository.logoutStudent(student).andThen(Single.error(it)) }
.flatMapCompletable { studentRepository.switchStudent(student) }
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doOnSubscribe { .doOnSubscribe {
view?.apply { view?.apply {
showProgress(true) showProgress(true)
showContent(false) showContent(false)
showActionBar(false)
} }
Timber.i("Registration started") Timber.i("Registration started")
} }
.subscribe({ .subscribe({
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") } analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to "No error")
Timber.i("Registration result: Success") Timber.i("Registration result: Success")
view?.openMainView() view?.openMainView()
}, { error -> }, {
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.localizedMessage) } analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to it.localizedMessage)
Timber.i("Registration result: An exception occurred ") Timber.i("Registration result: An exception occurred ")
errorHandler.dispatch(error) errorHandler.dispatch(it)
view?.apply { view?.apply {
showProgress(false) showProgress(false)
showContent(true) showContent(true)
showActionBar(true)
} }
})) }))
} }

View file

@ -14,5 +14,5 @@ interface LoginStudentSelectView : BaseView {
fun showContent(show: Boolean) fun showContent(show: Boolean)
fun enableSignIn(enable: Boolean) fun showActionBar(show: Boolean)
} }

View file

@ -21,12 +21,12 @@ class LuckyNumberPresenter @Inject constructor(
override fun onAttachView(view: LuckyNumberView) { override fun onAttachView(view: LuckyNumberView) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Lucky number view is attached")
view.run { view.run {
initView() initView()
showContent(false) showContent(false)
enableSwipe(false) enableSwipe(false)
} }
Timber.i("Lucky number view was initialized")
loadData() loadData()
} }

View file

@ -1,77 +0,0 @@
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()
}
}

View file

@ -1,54 +0,0 @@
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
}
}

View file

@ -1,62 +0,0 @@
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()
}
}

View file

@ -1,19 +0,0 @@
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()
}

View file

@ -1,183 +0,0 @@
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.isCurrentStudentSet()
.filter { true }
.flatMap { studentRepository.getCurrentStudent(false).toMaybe() }
.doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) }
}
else -> Maybe.empty()
}
}
}
.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)
}
}
}
}
}

View file

@ -5,6 +5,7 @@ import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
@ -21,12 +22,8 @@ import io.github.wulkanowy.ui.modules.account.AccountDialog
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity 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.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.safelyPopFragment import io.github.wulkanowy.utils.safelyPopFragment
@ -43,15 +40,9 @@ class MainActivity : BaseActivity(), MainView {
lateinit var navController: FragNavController lateinit var navController: FragNavController
companion object { companion object {
const val EXTRA_START_MENU = "extraStartMenu" const val EXTRA_START_MENU_INDEX = "extraStartMenuIndex"
fun getStartIntent(context: Context, startMenu: MainView.MenuView? = null, clear: Boolean = false): Intent { fun getStartIntent(context: Context) = Intent(context, MainActivity::class.java)
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 override val isRootView: Boolean
@ -65,27 +56,14 @@ class MainActivity : BaseActivity(), MainView {
override var startMenuIndex = 0 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?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
setSupportActionBar(mainToolbar) setSupportActionBar(mainToolbar)
messageContainer = mainFragmentContainer messageContainer = mainFragmentContainer
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_START_MENU) as? MainView.MenuView) presenter.onAttachView(this, intent.getIntExtra(EXTRA_START_MENU_INDEX, -1))
navController.initialize(startMenuIndex, savedInstanceState)
navController.run {
initialize(startMenuIndex, savedInstanceState)
pushFragment(moreMenuFragments.getOrNull(startMenuMoreIndex))
}
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu?): Boolean {
@ -96,7 +74,7 @@ class MainActivity : BaseActivity(), MainView {
override fun initView() { override fun initView() {
mainBottomNav.run { mainBottomNav.run {
addItems( addItems(
listOf( mutableListOf(
AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_menu_main_grade_26dp, 0), 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.attendance_title, R.drawable.ic_menu_main_attendance_24dp, 0),
AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_menu_main_exam_24dp, 0), AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_menu_main_exam_24dp, 0),
@ -164,7 +142,9 @@ class MainActivity : BaseActivity(), MainView {
} }
override fun notifyMenuViewReselected() { override fun notifyMenuViewReselected() {
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected() Handler().postDelayed({
(navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected()
}, 250)
} }
fun showDialogFragment(dialog: DialogFragment) { fun showDialogFragment(dialog: DialogFragment) {
@ -188,10 +168,9 @@ class MainActivity : BaseActivity(), MainView {
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
navController.onSaveInstanceState(outState) navController.onSaveInstanceState(outState)
intent.removeExtra(EXTRA_START_MENU)
} }
override fun onDestroy() { override fun onDestroy() {

View file

@ -22,19 +22,22 @@ class MainPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MainView>(errorHandler) { ) : BasePresenter<MainView>(errorHandler) {
fun onAttachView(view: MainView, initMenu: MainView.MenuView?) { fun onAttachView(view: MainView, initMenuIndex: Int) {
super.onAttachView(view) super.onAttachView(view)
view.apply { view.run {
getProperViewIndexes(initMenu).let { (main, more) -> startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex
startMenuIndex = main Timber.i("Main view is attached with $startMenuIndex menu index")
startMenuMoreIndex = more
}
initView() initView()
Timber.i("Main view was initialized with $startMenuIndex menu index and $startMenuMoreIndex more index")
} }
syncManager.startSyncWorker() syncManager.startSyncWorker()
analytics.logEvent(APP_OPEN, DESTINATION to initMenu?.name)
analytics.logEvent(APP_OPEN, DESTINATION to when (initMenuIndex) {
1 -> "Grades"
3 -> "Timetable"
4 -> "More"
else -> "User action"
})
} }
fun onViewChange() { fun onViewChange() {
@ -101,12 +104,4 @@ class MainPresenter @Inject constructor(
errorHandler.dispatch(it) 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
}
}
} }

View file

@ -6,8 +6,6 @@ interface MainView : BaseView {
var startMenuIndex: Int var startMenuIndex: Int
var startMenuMoreIndex: Int
val isRootView: Boolean val isRootView: Boolean
val currentViewTitle: String? val currentViewTitle: String?
@ -39,15 +37,4 @@ interface MainView : BaseView {
val titleStringId: Int 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),
}
} }

View file

@ -7,7 +7,6 @@ import android.view.View.INVISIBLE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import io.github.wulkanowy.R 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.RECEIVED
import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
@ -76,20 +75,12 @@ class MessageFragment : BaseFragment(), MessageView, MainView.TitledView {
messageProgress.visibility = if (show) VISIBLE else INVISIBLE messageProgress.visibility = if (show) VISIBLE else INVISIBLE
} }
fun onDeleteMessage(message: Message) {
presenter.onDeleteMessage(message)
}
fun onChildFragmentLoaded() { fun onChildFragmentLoaded() {
presenter.onChildViewLoaded() presenter.onChildViewLoaded()
} }
override fun notifyChildMessageDeleted(tabId: Int) {
(pagerAdapter.getFragmentInstance(tabId) as? MessageTabFragment)?.onParentDeleteMessage()
}
override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) {
(pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)?.onParentLoadData(forceRefresh) (pagerAdapter.getFragmentInstance(index) as? MessageView.MessageChildView)?.onParentLoadData(forceRefresh)
} }
override fun openSendMessage() { override fun openSendMessage() {

View file

@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.message 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.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
@ -16,10 +15,10 @@ class MessagePresenter @Inject constructor(
override fun onAttachView(view: MessageView) { override fun onAttachView(view: MessageView) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Message view is attached")
disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread) disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread)
.subscribe { .subscribe {
view.initView() view.initView()
Timber.i("Message view was initialized")
loadData() loadData()
}) })
} }
@ -44,15 +43,6 @@ class MessagePresenter @Inject constructor(
} }
} }
fun onDeleteMessage(message: Message) {
view?.notifyChildMessageDeleted(
when (message.removed) {
true -> 2
else -> message.folderId - 1
}
)
}
fun onSendMessageButtonClicked() { fun onSendMessageButtonClicked() {
view?.openSendMessage() view?.openSendMessage()
} }

View file

@ -14,7 +14,10 @@ interface MessageView : BaseView {
fun notifyChildLoadData(index: Int, forceRefresh: Boolean) fun notifyChildLoadData(index: Int, forceRefresh: Boolean)
fun notifyChildMessageDeleted(tabId: Int)
fun openSendMessage() fun openSendMessage()
interface MessageChildView {
fun onParentLoadData(forceRefresh: Boolean)
}
} }

View file

@ -13,9 +13,7 @@ import android.view.ViewGroup
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.ui.base.session.BaseSessionFragment 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.main.MainView
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import kotlinx.android.synthetic.main.fragment_message_preview.* import kotlinx.android.synthetic.main.fragment_message_preview.*
import javax.inject.Inject import javax.inject.Inject
@ -28,25 +26,18 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
private var menuReplyButton: MenuItem? = null private var menuReplyButton: MenuItem? = null
private var menuForwardButton: MenuItem? = null
private var menuDeleteButton: MenuItem? = null
override val titleStringId: Int override val titleStringId: Int
get() = R.string.message_title get() = R.string.message_title
override val noSubjectString: String override val noSubjectString: String
get() = getString(R.string.message_no_subject) get() = getString(R.string.message_no_subject)
override val deleteMessageSuccessString: String
get() = getString(R.string.message_delete_success)
companion object { companion object {
const val MESSAGE_ID_KEY = "message_id" const val MESSAGE_ID_KEY = "message_id"
fun newInstance(messageId: Long): MessagePreviewFragment { fun newInstance(messageId: Int?): MessagePreviewFragment {
return MessagePreviewFragment().apply { return MessagePreviewFragment().apply {
arguments = Bundle().apply { putLong(MESSAGE_ID_KEY, messageId) } arguments = Bundle().apply { putInt(MESSAGE_ID_KEY, messageId ?: 0) }
} }
} }
} }
@ -63,24 +54,18 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
messageContainer = messagePreviewContainer messageContainer = messagePreviewContainer
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getLong(MESSAGE_ID_KEY) ?: 0L) presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getInt(MESSAGE_ID_KEY) ?: 0)
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater.inflate(R.menu.action_menu_message_preview, menu) inflater?.inflate(R.menu.action_menu_message_preview, menu)
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply) menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply)
menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward)
menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete)
presenter.onCreateOptionsMenu() presenter.onCreateOptionsMenu()
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return when (item.itemId) { return if (item?.itemId == R.id.messagePreviewMenuReply) presenter.onReply()
R.id.messagePreviewMenuReply -> presenter.onReply() else false
R.id.messagePreviewMenuForward -> presenter.onForward()
R.id.messagePreviewMenuDelete -> presenter.onMessageDelete()
else -> false
}
} }
override fun setSubject(subject: String) { override fun setSubject(subject: String) {
@ -107,22 +92,8 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
messagePreviewProgress.visibility = if (show) VISIBLE else GONE messagePreviewProgress.visibility = if (show) VISIBLE else GONE
} }
override fun showContent(show: Boolean) { override fun showReplyButton(show: Boolean) {
messagePreviewContentContainer.visibility = if (show) VISIBLE else GONE
}
override fun showOptions(show: Boolean) {
menuReplyButton?.isVisible = show 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() { override fun showMessageError() {
@ -130,24 +101,12 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
} }
override fun openMessageReply(message: Message?) { 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)) } 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) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
outState.putLong(MESSAGE_ID_KEY, presenter.messageId) outState.putInt(MESSAGE_ID_KEY, presenter.messageId)
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

@ -20,16 +20,16 @@ class MessagePreviewPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<MessagePreviewView>(errorHandler) { ) : BaseSessionPresenter<MessagePreviewView>(errorHandler) {
var messageId = 0L var messageId: Int = 0
private var message: Message? = null private var replyMessage: Message? = null
fun onAttachView(view: MessagePreviewView, id: Long) { fun onAttachView(view: MessagePreviewView, id: Int) {
super.onAttachView(view) super.onAttachView(view)
loadData(id) loadData(id)
} }
private fun loadData(id: Long) { private fun loadData(id: Int) {
Timber.i("Loading message $id preview started") Timber.i("Loading message $id preview started")
messageId = id messageId = id
disposable.apply { disposable.apply {
@ -41,13 +41,13 @@ class MessagePreviewPresenter @Inject constructor(
.doFinally { view?.showProgress(false) } .doFinally { view?.showProgress(false) }
.subscribe({ message -> .subscribe({ message ->
Timber.i("Loading message $id preview result: Success ") Timber.i("Loading message $id preview result: Success ")
this@MessagePreviewPresenter.message = message replyMessage = message
view?.run { view?.run {
message.let { message.let {
setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString)
setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
setContent(it.content.orEmpty()) setContent(it.content.orEmpty())
initOptions() showReplyButton(true)
if (it.recipient.isNotBlank()) setRecipient(it.recipient) if (it.recipient.isNotBlank()) setRecipient(it.recipient)
else setSender(it.sender) else setSender(it.sender)
@ -63,69 +63,13 @@ class MessagePreviewPresenter @Inject constructor(
} }
fun onReply(): Boolean { fun onReply(): Boolean {
return if (message != null) { return if (replyMessage != null) {
view?.openMessageReply(message) view?.openMessageReply(replyMessage)
true true
} else false } 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() { fun onCreateOptionsMenu() {
initOptions() view?.showReplyButton(replyMessage != null)
} }
} }

View file

@ -7,8 +7,6 @@ interface MessagePreviewView : BaseSessionView {
val noSubjectString: String val noSubjectString: String
val deleteMessageSuccessString: String
fun setSubject(subject: String) fun setSubject(subject: String)
fun setRecipient(recipient: String) fun setRecipient(recipient: String)
@ -21,21 +19,9 @@ interface MessagePreviewView : BaseSessionView {
fun showProgress(show: Boolean) fun showProgress(show: Boolean)
fun showContent(show: Boolean) fun showReplyButton(show: Boolean)
fun showOptions(show: Boolean)
fun setDeletedOptionsLabels()
fun setNotDeletedOptionsLabels()
fun showMessageError() fun showMessageError()
fun openMessageReply(message: Message?) fun openMessageReply(message: Message?)
fun openMessageForward(message: Message?)
fun popView()
fun notifyParentMessageDeleted(message: Message)
} }

View file

@ -26,14 +26,11 @@ class SendMessageActivity : BaseActivity(), SendMessageView {
companion object { companion object {
private const val EXTRA_MESSAGE = "EXTRA_MESSAGE" 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) = Intent(context, SendMessageActivity::class.java)
fun getStartIntent(context: Context, message: Message?, reply: Boolean = false): Intent { fun getStartIntent(context: Context, message: Message?): Intent {
return getStartIntent(context) return getStartIntent(context).putExtra(EXTRA_MESSAGE, message)
.putExtra(EXTRA_MESSAGE, message)
.putExtra(EXTRA_REPLY, reply)
} }
} }
@ -62,7 +59,7 @@ class SendMessageActivity : BaseActivity(), SendMessageView {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
messageContainer = sendMessageContainer messageContainer = sendMessageContainer
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean) presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message)
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu?): Boolean {

View file

@ -30,24 +30,17 @@ class SendMessagePresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<SendMessageView>(errorHandler) { ) : BasePresenter<SendMessageView>(errorHandler) {
fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) { fun onAttachView(view: SendMessageView, message: Message?) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Send message view was initialized") Timber.i("Send message view is attached")
loadData(message, reply) loadData(message)
view.apply { view.apply {
message?.let { message?.let {
setSubject(when (reply) { setSubject("RE: ${message.subject}")
true -> "RE: " if (preferencesRepository.fillMessageContent) {
else -> "FW: " setContent(when (message.sender.isNotEmpty()) {
} + message.subject) true -> "\n\nOd: ${message.sender}\n"
if (preferencesRepository.fillMessageContent || reply != true) { false -> "\n\nDo: ${message.recipient}\n"
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}") } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}")
} }
} }
@ -59,7 +52,7 @@ class SendMessagePresenter @Inject constructor(
return true return true
} }
private fun loadData(message: Message?, reply: Boolean?) { private fun loadData(message: Message?) {
var reportingUnit: ReportingUnit? = null var reportingUnit: ReportingUnit? = null
var recipients: List<Recipient> = emptyList() var recipients: List<Recipient> = emptyList()
var selectedRecipient: List<Recipient> = emptyList() var selectedRecipient: List<Recipient> = emptyList()
@ -76,7 +69,7 @@ class SendMessagePresenter @Inject constructor(
recipients = it recipients = it
} }
.flatMapCompletable { .flatMapCompletable {
if (message == null || reply != true) Completable.complete() if (message == null) Completable.complete()
else recipientRepository.getMessageRecipients(student, message) else recipientRepository.getMessageRecipients(student, message)
.doOnSuccess { .doOnSuccess {
Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size) Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size)

View file

@ -17,12 +17,13 @@ import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.MessageItem 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.ui.modules.message.preview.MessagePreviewFragment
import io.github.wulkanowy.utils.setOnItemClickListener import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_message_tab.* import kotlinx.android.synthetic.main.fragment_message_tab.*
import javax.inject.Inject import javax.inject.Inject
class MessageTabFragment : BaseSessionFragment(), MessageTabView { class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.MessageChildView {
@Inject @Inject
lateinit var presenter: MessageTabPresenter lateinit var presenter: MessageTabPresenter
@ -56,7 +57,7 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
messageContainer = messageTabRecycler messageContainer = messageTabRecycler
presenter.onAttachView(this, MessageFolder.valueOf( presenter.onAttachView(this, MessageFolder.valueOf(
(savedInstanceState ?: arguments)?.getString(MESSAGE_TAB_FOLDER_ID).orEmpty() (savedInstanceState ?: arguments)?.getString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID).orEmpty()
)) ))
} }
@ -75,7 +76,7 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView {
} }
override fun updateData(data: List<MessageItem>) { override fun updateData(data: List<MessageItem>) {
tabAdapter.updateDataSet(data) tabAdapter.updateDataSet(data, true)
} }
override fun updateItem(item: AbstractFlexibleItem<*>) { override fun updateItem(item: AbstractFlexibleItem<*>) {
@ -106,7 +107,7 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView {
messageTabSwipe.isRefreshing = show messageTabSwipe.isRefreshing = show
} }
override fun openMessage(messageId: Long) { override fun openMessage(messageId: Int?) {
(activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(messageId)) (activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(messageId))
} }
@ -114,17 +115,13 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView {
(parentFragment as? MessageFragment)?.onChildFragmentLoaded() (parentFragment as? MessageFragment)?.onChildFragmentLoaded()
} }
fun onParentLoadData(forceRefresh: Boolean) { override fun onParentLoadData(forceRefresh: Boolean) {
presenter.onParentViewLoadData(forceRefresh) presenter.onParentViewLoadData(forceRefresh)
} }
fun onParentDeleteMessage() {
presenter.onDeleteMessage()
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
outState.putString(MESSAGE_TAB_FOLDER_ID, presenter.folder.name) outState.putString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID, presenter.folder.name)
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

@ -34,29 +34,7 @@ class MessageTabPresenter @Inject constructor(
onParentViewLoadData(true) onParentViewLoadData(true)
} }
fun onDeleteMessage() {
loadData(false)
}
fun onParentViewLoadData(forceRefresh: Boolean) { fun onParentViewLoadData(forceRefresh: Boolean) {
loadData(forceRefresh)
}
fun onMessageItemSelected(item: AbstractFlexibleItem<*>) {
if (item is MessageItem) {
Timber.i("Select message ${item.message.id} item")
view?.run {
openMessage(item.message.id)
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") Timber.i("Loading $folder message data started")
disposable.apply { disposable.apply {
clear() clear()
@ -89,14 +67,28 @@ 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) { private fun updateMessage(message: Message) {
Timber.i("Attempt to update message ${message.id}") Timber.i("Attempt to update message ${message.realId}")
disposable.add(messageRepository.updateMessage(message) disposable.add(messageRepository.updateMessage(message)
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.subscribe({ Timber.d("Update message ${message.id} result: Success") }) .subscribe({ Timber.d("Update message ${message.realId} result: Success") })
{ error -> { error ->
Timber.i("Update message ${message.id} result: An exception occurred") Timber.i("Update message ${message.realId} result: An exception occurred")
errorHandler.dispatch(error) errorHandler.dispatch(error)
}) })
} }

View file

@ -28,7 +28,7 @@ interface MessageTabView : BaseSessionView {
fun showRefresh(show: Boolean) fun showRefresh(show: Boolean)
fun openMessage(messageId: Long) fun openMessage(messageId: Int?)
fun notifyParentDataLoaded() fun notifyParentDataLoaded()
} }

View file

@ -104,7 +104,7 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
} }
override fun onFragmentReselected() { override fun onFragmentReselected() {
if (::presenter.isInitialized) presenter.onViewReselected() presenter.onViewReselected()
} }
override fun updateData(data: List<MoreItem>) { override fun updateData(data: List<MoreItem>) {

View file

@ -10,8 +10,8 @@ class MorePresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresen
override fun onAttachView(view: MoreView) { override fun onAttachView(view: MoreView) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("More view is attached")
view.initView() view.initView()
Timber.i("More view was initialized")
loadData() loadData()
} }

View file

@ -23,8 +23,8 @@ class NotePresenter @Inject constructor(
override fun onAttachView(view: NoteView) { override fun onAttachView(view: NoteView) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Note view is attached")
view.initView() view.initView()
Timber.i("Note view was initialized")
loadData() loadData()
} }

View file

@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.settings
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatDelegate
import com.takisoft.preferencex.PreferenceFragmentCompat import com.takisoft.preferencex.PreferenceFragmentCompat
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import io.github.wulkanowy.BuildConfig.DEBUG import io.github.wulkanowy.BuildConfig.DEBUG
@ -43,7 +44,8 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
presenter.onSharedPreferenceChanged(key) presenter.onSharedPreferenceChanged(key)
} }
override fun recreateView() { override fun setTheme(theme: Int) {
AppCompatDelegate.setDefaultNightMode(theme)
activity?.recreate() activity?.recreate()
} }

View file

@ -21,7 +21,7 @@ class SettingsPresenter @Inject constructor(
override fun onAttachView(view: SettingsView) { override fun onAttachView(view: SettingsView) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Settings view was initialized") Timber.i("Settings view is attached")
view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays) view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays)
} }
@ -31,8 +31,8 @@ class SettingsPresenter @Inject constructor(
when (key) { when (key) {
serviceEnableKey -> syncManager.run { if (isServiceEnabled) startSyncWorker() else stopSyncWorker() } serviceEnableKey -> syncManager.run { if (isServiceEnabled) startSyncWorker() else stopSyncWorker() }
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true) servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true)
currentThemeKey -> view?.setTheme(currentTheme)
isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable) isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable)
appThemeKey -> view?.recreateView()
} }
} }
analytics.logEvent("setting_changed", "name" to key) analytics.logEvent("setting_changed", "name" to key)

View file

@ -4,7 +4,7 @@ import io.github.wulkanowy.ui.base.BaseView
interface SettingsView : BaseView { interface SettingsView : BaseView {
fun recreateView() fun setTheme(theme: Int)
fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean)
} }

View file

@ -14,7 +14,7 @@ class SplashPresenter @Inject constructor(
override fun onAttachView(view: SplashView) { override fun onAttachView(view: SplashView) {
super.onAttachView(view) super.onAttachView(view)
disposable.add(studentRepository.isCurrentStudentSet() disposable.add(studentRepository.isStudentSaved()
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.subscribe({ .subscribe({

View file

@ -80,12 +80,12 @@ class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChi
timetableNextButton.setOnClickListener { presenter.onNextDay() } timetableNextButton.setOnClickListener { presenter.onNextDay() }
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater.inflate(R.menu.action_menu_timetable, menu) inflater?.inflate(R.menu.action_menu_timetable, menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return if (item.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected() return if (item?.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected()
else false else false
} }
@ -110,7 +110,7 @@ class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChi
} }
override fun onFragmentReselected() { override fun onFragmentReselected() {
if (::presenter.isInitialized) presenter.onViewReselected() presenter.onViewReselected()
} }
override fun popView() { override fun popView() {

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