Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
df0a1e59cc | |||
dbbc8069b1 | |||
f84040109c | |||
baf1420193 | |||
4a36d78709 | |||
4464812651 | |||
72a35481e5 | |||
017c200115 | |||
2bf7755157 | |||
269af4b7ba | |||
7431738366 | |||
034b99c7ab | |||
74e98e4430 | |||
cbf3215dd1 | |||
c18877466f | |||
aa6dcaff94 | |||
2bff468e56 | |||
f2855d598d | |||
1ebc296bfe | |||
a2a18e5652 | |||
cd1ceea860 |
@ -7,11 +7,11 @@ references:
|
||||
|
||||
container_config: &container_config
|
||||
docker:
|
||||
- image: circleci/android:api-28-alpha
|
||||
- image: circleci/android:api-28
|
||||
working_directory: *workspace_root
|
||||
environment:
|
||||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
_JAVA_OPTS: -Xmx3072m
|
||||
|
||||
attach_workspace: &attach_workspace
|
||||
attach_workspace:
|
||||
|
25
.idea/codeStyles/Project.xml
generated
@ -21,31 +21,6 @@
|
||||
<option name="CONTINUATION_INDENT_IN_ELVIS" value="false" />
|
||||
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
||||
</JetCodeStyleSettings>
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
<XML>
|
||||
<option name="XML_KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
|
||||
|
@ -16,13 +16,13 @@ android {
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 28
|
||||
versionCode 32
|
||||
versionName "0.7.6"
|
||||
versionCode 33
|
||||
versionName "0.8.0"
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
manifestPlaceholders = [
|
||||
fabric_api_key: System.getenv("FABRIC_API_KEY") ?: "null",
|
||||
fabric_api_key : System.getenv("FABRIC_API_KEY") ?: "null",
|
||||
crashlytics_enabled: project.hasProperty("enableCrashlytics")
|
||||
]
|
||||
javaCompileOptions {
|
||||
@ -85,48 +85,49 @@ play {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation('io.github.wulkanowy:api:0.7.6') { exclude module: "threetenbp" }
|
||||
implementation 'io.github.wulkanowy:api:0.8.0'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
||||
implementation "androidx.appcompat:appcompat:1.0.2"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation "com.google.android.material:material:1.0.0"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
|
||||
implementation "android.arch.work:work-runtime:1.0.0"
|
||||
implementation "android.arch.work:work-rxjava2:1.0.0"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation "com.google.android.material:material:1.1.0-alpha05"
|
||||
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
|
||||
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||
|
||||
implementation "androidx.room:room-runtime:2.1.0-alpha06"
|
||||
implementation "androidx.room:room-rxjava2:2.1.0-alpha06"
|
||||
kapt "androidx.room:room-compiler:2.1.0-alpha06"
|
||||
implementation "androidx.work:work-runtime:2.0.1"
|
||||
implementation "androidx.work:work-rxjava2:2.0.1"
|
||||
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
|
||||
implementation "androidx.room:room-runtime:2.1.0-alpha07"
|
||||
implementation "androidx.room:room-rxjava2:2.1.0-alpha07"
|
||||
kapt "androidx.room:room-compiler:2.1.0-alpha07"
|
||||
|
||||
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.3'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.3'
|
||||
|
||||
implementation "com.google.dagger:dagger-android-support:2.21"
|
||||
kapt "com.google.dagger:dagger-compiler:2.21"
|
||||
kapt "com.google.dagger:dagger-android-processor:2.21"
|
||||
implementation "com.google.dagger:dagger-android-support:2.22.1"
|
||||
kapt "com.google.dagger:dagger-compiler:2.22.1"
|
||||
kapt "com.google.dagger:dagger-android-processor:2.22.1"
|
||||
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
|
||||
|
||||
implementation "eu.davidea:flexible-adapter:5.1.0"
|
||||
implementation "eu.davidea:flexible-adapter-ui:1.0.0"
|
||||
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
|
||||
implementation 'com.ncapdevi:frag-nav:3.2.0'
|
||||
|
||||
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
|
||||
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||
|
||||
implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2'
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.7"
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.8"
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.0"
|
||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:3.12.1"
|
||||
|
||||
implementation "com.mikepenz:aboutlibraries:6.2.3"
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
|
||||
|
||||
implementation 'com.google.firebase:firebase-core:16.0.8'
|
||||
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'
|
||||
@ -138,15 +139,15 @@ dependencies {
|
||||
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation "io.mockk:mockk:1.9.2"
|
||||
testImplementation "org.mockito:mockito-inline:2.25.1"
|
||||
testImplementation "org.mockito:mockito-inline:2.27.0"
|
||||
testImplementation 'org.threeten:threetenbp:1.3.8'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.1.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
||||
androidTestImplementation "io.mockk:mockk-android:1.9.2"
|
||||
androidTestImplementation 'org.mockito:mockito-android:2.25.1'
|
||||
androidTestImplementation "androidx.room:room-testing:2.1.0-alpha06"
|
||||
androidTestImplementation 'org.mockito:mockito-android:2.27.0'
|
||||
androidTestImplementation "androidx.room:room-testing:2.1.0-alpha07"
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
}
|
||||
|
||||
|
@ -31,17 +31,17 @@ task jacocoTestReport(type: JacocoReport) {
|
||||
'**/*_Provide*Factory*.*',
|
||||
'**/*_Factory.*']
|
||||
|
||||
classDirectories = fileTree(
|
||||
classDirectories.setFrom(fileTree(
|
||||
dir: "$buildDir/intermediates/classes/debug",
|
||||
excludes: excludes
|
||||
) + fileTree(
|
||||
dir: "$buildDir/tmp/kotlin-classes/debug",
|
||||
excludes: excludes
|
||||
)
|
||||
))
|
||||
|
||||
sourceDirectories = files("$project.projectDir/src/main/java")
|
||||
executionData = fileTree(
|
||||
sourceDirectories.setFrom(files("$project.projectDir/src/main/java"))
|
||||
executionData.setFrom(fileTree(
|
||||
dir: project.projectDir,
|
||||
includes: ["**/*.exec", "**/*.ec"]
|
||||
)
|
||||
))
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class StudentLocalTest {
|
||||
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = ""))
|
||||
studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "")))
|
||||
.blockingGet()
|
||||
|
||||
val student = studentLocal.getCurrentStudent(true).blockingGet()
|
||||
|
@ -34,6 +34,7 @@
|
||||
android:name=".ui.modules.login.LoginActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/login_title"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.main.MainActivity"
|
||||
@ -45,13 +46,31 @@
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/send_message_title"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
android:noHistory="true"
|
||||
android:excludeFromRecents="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:noHistory="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".services.widgets.TimetableWidgetService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
|
||||
<receiver
|
||||
android:name=".ui.widgets.timetable.TimetableWidgetProvider"
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
||||
android:exported="true"
|
||||
android:label="@string/timetable_title">
|
||||
<intent-filter>
|
||||
@ -62,6 +81,17 @@
|
||||
android:resource="@xml/provider_widget_timetable" />
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
|
||||
android:label="@string/lucky_number_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/provider_widget_lucky_number" />
|
||||
</receiver>
|
||||
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
|
@ -1,7 +1,6 @@
|
||||
package io.github.wulkanowy
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.multidex.MultiDex
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.WorkManager
|
||||
@ -13,24 +12,21 @@ import dagger.android.support.DaggerApplication
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.utils.Log
|
||||
import io.fabric.sdk.android.Fabric
|
||||
import io.github.wulkanowy.BuildConfig.CRASHLYTICS_ENABLED
|
||||
import io.github.wulkanowy.BuildConfig.DEBUG
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.di.DaggerAppComponent
|
||||
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||
import io.github.wulkanowy.utils.CrashlyticsTree
|
||||
import io.github.wulkanowy.utils.DebugLogTree
|
||||
import io.reactivex.exceptions.UndeliverableException
|
||||
import io.reactivex.plugins.RxJavaPlugins
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.lang.Exception
|
||||
import javax.inject.Inject
|
||||
|
||||
class WulkanowyApp : DaggerApplication() {
|
||||
|
||||
@Inject
|
||||
lateinit var prefRepository: PreferencesRepository
|
||||
|
||||
@Inject
|
||||
lateinit var workerFactory: SyncWorkerFactory
|
||||
|
||||
@ -42,32 +38,36 @@ class WulkanowyApp : DaggerApplication() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
AndroidThreeTen.init(this)
|
||||
initializeFabric()
|
||||
if (DEBUG) enableDebugLog()
|
||||
AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme)
|
||||
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build())
|
||||
RxJavaPlugins.setErrorHandler(::onError)
|
||||
|
||||
initCrashlytics()
|
||||
initLogging()
|
||||
}
|
||||
|
||||
private fun enableDebugLog() {
|
||||
Timber.plant(DebugLogTree())
|
||||
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
||||
private fun initLogging() {
|
||||
if (DEBUG) {
|
||||
Timber.plant(DebugLogTree())
|
||||
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
||||
} else {
|
||||
Timber.plant(CrashlyticsTree())
|
||||
}
|
||||
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
||||
}
|
||||
|
||||
private fun initializeFabric() {
|
||||
private fun initCrashlytics() {
|
||||
Fabric.with(Fabric.Builder(this).kits(
|
||||
Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!BuildConfig.CRASHLYTICS_ENABLED).build()).build()
|
||||
).debuggable(BuildConfig.DEBUG).build())
|
||||
Timber.plant(CrashlyticsTree())
|
||||
Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!CRASHLYTICS_ENABLED).build()).build()
|
||||
).debuggable(DEBUG).build())
|
||||
}
|
||||
|
||||
private fun onError(t: Throwable) {
|
||||
if (t is UndeliverableException && t.cause is IOException || t.cause is InterruptedException) {
|
||||
Timber.e(t.cause, "An undeliverable error occurred")
|
||||
} else throw t
|
||||
private fun onError(error: Throwable) {
|
||||
if (error is UndeliverableException && error.cause is IOException || error.cause is InterruptedException) {
|
||||
Timber.e(error.cause, "An undeliverable error occurred")
|
||||
} else throw error
|
||||
}
|
||||
|
||||
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
|
||||
return DaggerAppComponent.builder().create(this)
|
||||
return DaggerAppComponent.factory().create(this)
|
||||
}
|
||||
}
|
||||
|
@ -6,18 +6,16 @@ import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@SuppressLint("ApplySharedPref")
|
||||
class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) {
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
fun putLong(key: String, value: Long, sync: Boolean = false) {
|
||||
sharedPref.edit().putLong(key, value).apply {
|
||||
if (sync) commit() else apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getLong(key: String, defaultValue: Long): Long {
|
||||
return sharedPref.getLong(key, defaultValue)
|
||||
}
|
||||
fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue)
|
||||
|
||||
fun delete(key: String) {
|
||||
sharedPref.edit().remove(key).apply()
|
||||
|
@ -14,7 +14,7 @@ import javax.inject.Singleton
|
||||
interface StudentDao {
|
||||
|
||||
@Insert(onConflict = ABORT)
|
||||
fun insert(student: Student): Long
|
||||
fun insertAll(student: List<Student>): List<Long>
|
||||
|
||||
@Delete
|
||||
fun delete(student: Student)
|
||||
|
@ -8,7 +8,7 @@ class Migration11 : Migration(10, 11) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("""
|
||||
CREATE TABLE IF NOT EXISTS Grades_temp (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
is_read INTEGER NOT NULL,
|
||||
is_notified INTEGER NOT NULL,
|
||||
semester_id INTEGER NOT NULL,
|
||||
|
@ -59,4 +59,8 @@ class MessageRemote @Inject constructor(private val api: Api) {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteMessage(message: Message): Single<Boolean> {
|
||||
return api.deleteMessages(listOf(Pair(message.realId, message.folderId)))
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -89,4 +90,20 @@ class MessageRepository @Inject constructor(
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteMessage(message: Message): Maybe<Boolean> {
|
||||
return ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMap {
|
||||
if (it) remote.deleteMessage(message)
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
.filter { it }
|
||||
.doOnSuccess {
|
||||
if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply {
|
||||
id = message.id
|
||||
content = message.content
|
||||
}))
|
||||
else local.deleteMessages(listOf(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,15 @@ class PreferencesRepository @Inject constructor(
|
||||
val isShowPresent: Boolean
|
||||
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true)
|
||||
|
||||
val gradeAverageMode: String
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester"
|
||||
|
||||
val isGradeExpandable: Boolean
|
||||
get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false)
|
||||
|
||||
val currentThemeKey: String = context.getString(R.string.pref_key_theme)
|
||||
val currentTheme: Int
|
||||
get() = sharedPref.getString(currentThemeKey, "1")?.toIntOrNull() ?: 1
|
||||
val appThemeKey: String = context.getString(R.string.pref_key_app_theme)
|
||||
val appTheme: String
|
||||
get() = sharedPref.getString(appThemeKey, "light") ?: "light"
|
||||
|
||||
val gradeColorTheme: String
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan"
|
||||
@ -50,8 +53,7 @@ class PreferencesRepository @Inject constructor(
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0
|
||||
|
||||
val gradeMinusModifier: Double
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble()
|
||||
?: 0.0
|
||||
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0
|
||||
|
||||
val fillMessageContent: Boolean
|
||||
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true)
|
||||
|
@ -17,8 +17,8 @@ class StudentLocal @Inject constructor(
|
||||
private val context: Context
|
||||
) {
|
||||
|
||||
fun saveStudent(student: Student): Single<Long> {
|
||||
return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) }
|
||||
fun saveStudents(students: List<Student>): Single<List<Long>> {
|
||||
return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) }
|
||||
}
|
||||
|
||||
fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {
|
||||
|
@ -41,8 +41,8 @@ class StudentRepository @Inject constructor(
|
||||
.toSingle()
|
||||
}
|
||||
|
||||
fun saveStudent(student: Student): Single<Long> {
|
||||
return local.saveStudent(student)
|
||||
fun saveStudents(students: List<Student>): Single<List<Long>> {
|
||||
return local.saveStudents(students)
|
||||
}
|
||||
|
||||
fun switchStudent(student: Student): Completable {
|
||||
|
@ -17,6 +17,6 @@ import javax.inject.Singleton
|
||||
BuilderModule::class])
|
||||
interface AppComponent : AndroidInjector<WulkanowyApp> {
|
||||
|
||||
@Component.Builder
|
||||
abstract class Builder : AndroidInjector.Builder<WulkanowyApp>()
|
||||
@Component.Factory
|
||||
interface Factory : AndroidInjector.Factory<WulkanowyApp>
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.BuildConfig.DEBUG
|
||||
import io.github.wulkanowy.WulkanowyApp
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
@ -30,11 +29,11 @@ internal class AppModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideFirebaseAnalyticsHelper(context: Context) = FirebaseAnalyticsHelper(FirebaseAnalytics.getInstance(context))
|
||||
fun provideFirebaseAnalytics(context: Context) = FirebaseAnalytics.getInstance(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAppWidgetManager(context: Context) = AppWidgetManager.getInstance(context)
|
||||
fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context)
|
||||
|
||||
@Singleton
|
||||
@Named("isDebug")
|
||||
|
@ -5,11 +5,14 @@ import dagger.android.ContributesAndroidInjector
|
||||
import io.github.wulkanowy.di.scopes.PerActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginModule
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainModule
|
||||
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
|
||||
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
||||
import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
|
||||
|
||||
@Module
|
||||
internal abstract class BuilderModule {
|
||||
@ -29,6 +32,15 @@ internal abstract class BuilderModule {
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindMessageSendActivity(): SendMessageActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindTimetableWidgetAccountActivity(): TimetableWidgetConfigureActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLuckyNumberWidgetAccountActivity(): LuckyNumberWidgetConfigureActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -48,7 +49,7 @@ class GradeWork @Inject constructor(
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 0), FLAG_UPDATE_CURRENT))
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU, MainView.MenuView.GRADE), FLAG_UPDATE_CURRENT))
|
||||
.setStyle(NotificationCompat.InboxStyle().run {
|
||||
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size))
|
||||
grades.forEach { addLine("${it.subject}: ${it.entry}") }
|
||||
|
@ -15,7 +15,8 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -47,9 +48,9 @@ class LuckyNumberWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
PendingIntent.getActivity(context, MainView.MenuView.MESSAGE.id,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU, MainView.MenuView.LUCKY_NUMBER)
|
||||
, FLAG_UPDATE_CURRENT))
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ import io.github.wulkanowy.data.repositories.message.MessageRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -48,8 +49,8 @@ class MessageWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
PendingIntent.getActivity(context, MainView.MenuView.MESSAGE.id, MainActivity.getStartIntent(context)
|
||||
.putExtra(EXTRA_START_MENU, MainView.MenuView.MESSAGE), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
.setStyle(NotificationCompat.InboxStyle().run {
|
||||
setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size))
|
||||
|
@ -15,7 +15,8 @@ import io.github.wulkanowy.data.repositories.note.NoteRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.reactivex.Completable
|
||||
import javax.inject.Inject
|
||||
@ -47,9 +48,9 @@ class NoteWork @Inject constructor(
|
||||
.setPriority(PRIORITY_HIGH)
|
||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(context, 0,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
PendingIntent.getActivity(context, MainView.MenuView.NOTE.id,
|
||||
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU, MainView.MenuView.NOTE)
|
||||
, FLAG_UPDATE_CURRENT))
|
||||
.setStyle(NotificationCompat.InboxStyle().run {
|
||||
setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size))
|
||||
notes.forEach { addLine("${it.teacher}: ${it.category}") }
|
||||
|
@ -7,7 +7,7 @@ import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
||||
import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -2,18 +2,36 @@ package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
||||
import dagger.android.support.DaggerAppCompatActivity
|
||||
import dagger.android.AndroidInjection
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.support.HasSupportFragmentInjector
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.utils.FragmentLifecycleLogger
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
|
||||
abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentInjector {
|
||||
|
||||
@Inject
|
||||
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
|
||||
|
||||
@Inject
|
||||
lateinit var fragmentLifecycleLogger: FragmentLifecycleLogger
|
||||
|
||||
@Inject
|
||||
lateinit var themeManager: ThemeManager
|
||||
|
||||
protected lateinit var messageContainer: View
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
AndroidInjection.inject(this)
|
||||
themeManager.applyTheme(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
}
|
||||
|
||||
@ -31,4 +49,6 @@ abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
|
||||
super.onDestroy()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun supportFragmentInjector() = supportFragmentInjector
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.content.pm.PackageManager.GET_ACTIVITIES
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class ThemeManager @Inject constructor(private val preferencesRepository: PreferencesRepository) {
|
||||
|
||||
fun applyTheme(activity: AppCompatActivity) {
|
||||
if (isThemeApplicable(activity)) {
|
||||
activity.delegate.apply {
|
||||
when (preferencesRepository.appTheme) {
|
||||
"light" -> setLocalNightMode(MODE_NIGHT_NO)
|
||||
"dark" -> setLocalNightMode(MODE_NIGHT_YES)
|
||||
"black" -> {
|
||||
setLocalNightMode(MODE_NIGHT_YES)
|
||||
activity.setTheme(R.style.WulkanowyTheme_Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isThemeApplicable(activity: AppCompatActivity): Boolean {
|
||||
return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
||||
.activities.singleOrNull { it.name == activity.localClassName }?.theme
|
||||
.let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar }
|
||||
}
|
||||
}
|
@ -44,7 +44,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
.withFields(R.string::class.java.fields)
|
||||
.withCheckCachedDetection(false)
|
||||
.withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio",
|
||||
"OkHttp", "Butterknife", "CircleImageView")
|
||||
"Butterknife", "CircleImageView")
|
||||
.withOnExtraListener { presenter.onExtraSelect(it) })
|
||||
}.let {
|
||||
fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it)
|
||||
|
@ -17,7 +17,7 @@ class AboutPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: AboutView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("About view is attached")
|
||||
Timber.i("About view was initialized")
|
||||
}
|
||||
|
||||
fun onExtraSelect(type: Libs.SpecialButton?) {
|
||||
|
@ -19,8 +19,8 @@ class AccountPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: AccountView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Account dialog is attached")
|
||||
view.initView()
|
||||
Timber.i("Account dialog view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -77,12 +77,12 @@ class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainC
|
||||
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_attendance, menu)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_attendance, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,8 @@ class AttendancePresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: AttendanceView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Attendance view is attached")
|
||||
view.initView()
|
||||
Timber.i("Attendance view was initialized")
|
||||
loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ class AttendanceSummaryPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Attendance summary view is attached with subject id ${subjectId ?: -1}")
|
||||
view.initView()
|
||||
Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}")
|
||||
loadData(subjectId ?: -1)
|
||||
loadSubjects()
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ class ExamPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: ExamView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Exam view is attached")
|
||||
view.initView()
|
||||
Timber.i("Exam view was initialized")
|
||||
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
package io.github.wulkanowy.ui.modules.grade
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
|
||||
class GradeAverageProvider @Inject constructor(
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val gradeRepository: GradeRepository
|
||||
) {
|
||||
fun getGradeAverage(student: Student, semesters: List<Semester>, selectedSemesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
return when (preferencesRepository.gradeAverageMode) {
|
||||
"all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh)
|
||||
"only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh)
|
||||
else -> throw IllegalArgumentException("Incorrect grade average mode: ${preferencesRepository.gradeAverageMode} ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
val plusModifier = preferencesRepository.gradePlusModifier
|
||||
val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
return gradeRepository.getGrades(student, selectedSemester, forceRefresh)
|
||||
.flatMap { firstGrades ->
|
||||
if (selectedSemester == firstSemester) Single.just(firstGrades)
|
||||
else gradeRepository.getGrades(student, firstSemester)
|
||||
.map { secondGrades -> secondGrades + firstGrades }
|
||||
}.map { grades ->
|
||||
grades.map { it.changeModifier(plusModifier, minusModifier) }
|
||||
.groupBy { it.subject }
|
||||
.mapValues { it.value.calcAverage() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOnlyOneSemesterAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val plusModifier = preferencesRepository.gradePlusModifier
|
||||
val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
return gradeRepository.getGrades(student, selectedSemester, forceRefresh)
|
||||
.map { grades ->
|
||||
grades.map { it.changeModifier(plusModifier, minusModifier) }
|
||||
.groupBy { it.subject }
|
||||
.mapValues { it.value.calcAverage() }
|
||||
}
|
||||
}
|
||||
}
|
@ -57,9 +57,9 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
|
||||
presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_grade, menu)
|
||||
semesterSwitchMenu = menu?.findItem(R.id.gradeMenuSemester)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_grade, menu)
|
||||
semesterSwitchMenu = menu.findItem(R.id.gradeMenuSemester)
|
||||
presenter.onCreateMenu()
|
||||
}
|
||||
|
||||
@ -82,8 +82,8 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
|
||||
gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,12 @@ class GradePresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: GradeView, savedIndex: Int?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Grade view is attached")
|
||||
selectedIndex = savedIndex ?: 0
|
||||
view.run {
|
||||
initView()
|
||||
enableSwipe(false)
|
||||
}
|
||||
Timber.i("Grade view was initialized with $selectedIndex index")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -67,8 +67,8 @@ class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_grade_details, menu)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_grade_details, menu)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
@ -88,8 +88,8 @@ class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.
|
||||
gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,9 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
|
||||
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import io.github.wulkanowy.utils.getBackgroundColor
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
@ -23,6 +22,7 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
private val studentRepository: StudentRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val averageProvider: GradeAverageProvider,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BaseSessionPresenter<GradeDetailsView>(errorHandler) {
|
||||
|
||||
@ -109,11 +109,16 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
|
||||
Timber.i("Loading grade details data started")
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } }
|
||||
.flatMap { gradeRepository.getGrades(it.second, it.first.first { item -> item.semesterId == semesterId }, forceRefresh) }
|
||||
.map { it.sortedByDescending { grade -> grade.date } }
|
||||
.map { it.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } }
|
||||
.map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } }
|
||||
.flatMap { (student, semesters) ->
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
|
||||
.flatMap { averages ->
|
||||
gradeRepository.getGrades(student, semesters.first { semester -> semester.semesterId == semesterId })
|
||||
.map { it.sortedByDescending { grade -> grade.date } }
|
||||
.map { it.groupBy { grade -> grade.subject }.toSortedMap() }
|
||||
.map { createGradeItems(it, averages) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
@ -139,32 +144,36 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
})
|
||||
}
|
||||
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>): List<GradeDetailsHeader> {
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>, averages: Map<String, Double>): List<GradeDetailsHeader> {
|
||||
val isGradeExpandable = preferencesRepository.isGradeExpandable
|
||||
val gradeColorTheme = preferencesRepository.gradeColorTheme
|
||||
|
||||
val noDescriptionString = view?.noDescriptionString.orEmpty()
|
||||
val weightString = view?.weightString.orEmpty()
|
||||
|
||||
return items.map {
|
||||
it.value.calcAverage().let { average ->
|
||||
GradeDetailsHeader(
|
||||
subject = it.key,
|
||||
average = formatAverage(average),
|
||||
number = view?.getGradeNumberString(it.value.size).orEmpty(),
|
||||
newGrades = it.value.filter { grade -> !grade.isRead }.size,
|
||||
isExpandable = preferencesRepository.isGradeExpandable
|
||||
).apply {
|
||||
subItems = it.value.map { item ->
|
||||
GradeDetailsItem(
|
||||
grade = item,
|
||||
valueBgColor = item.getBackgroundColor(preferencesRepository.gradeColorTheme),
|
||||
weightString = view?.weightString.orEmpty(),
|
||||
noDescriptionString = view?.noDescriptionString.orEmpty()
|
||||
)
|
||||
}
|
||||
GradeDetailsHeader(
|
||||
subject = it.key,
|
||||
average = formatAverage(averages[it.key]),
|
||||
number = view?.getGradeNumberString(it.value.size).orEmpty(),
|
||||
newGrades = it.value.filter { grade -> !grade.isRead }.size,
|
||||
isExpandable = isGradeExpandable
|
||||
).apply {
|
||||
subItems = it.value.map { item ->
|
||||
GradeDetailsItem(
|
||||
grade = item,
|
||||
valueBgColor = item.getBackgroundColor(gradeColorTheme),
|
||||
weightString = weightString,
|
||||
noDescriptionString = noDescriptionString
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatAverage(average: Double): String {
|
||||
private fun formatAverage(average: Double?): String {
|
||||
return view?.run {
|
||||
if (average == 0.0) emptyAverageString
|
||||
if (average == null || average == .0) emptyAverageString
|
||||
else averageString.format(average)
|
||||
}.orEmpty()
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
package io.github.wulkanowy.ui.modules.grade.summary
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
|
||||
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.calcAverage
|
||||
import io.github.wulkanowy.utils.changeModifier
|
||||
import timber.log.Timber
|
||||
import java.lang.String.format
|
||||
import java.util.Locale.FRANCE
|
||||
@ -20,10 +18,9 @@ import javax.inject.Inject
|
||||
class GradeSummaryPresenter @Inject constructor(
|
||||
private val errorHandler: SessionErrorHandler,
|
||||
private val gradeSummaryRepository: GradeSummaryRepository,
|
||||
private val gradeRepository: GradeRepository,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val averageProvider: GradeAverageProvider,
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BaseSessionPresenter<GradeSummaryView>(errorHandler) {
|
||||
@ -36,25 +33,13 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
|
||||
Timber.i("Loading grade summary data started")
|
||||
disposable.add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } }
|
||||
.map { pair -> pair.first.first { it.semesterId == semesterId } to pair.second }
|
||||
.flatMap {
|
||||
gradeSummaryRepository.getGradesSummary(it.first, forceRefresh)
|
||||
.flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } }
|
||||
.flatMap { (student, semesters) ->
|
||||
gradeSummaryRepository.getGradesSummary(semesters.first { it.semesterId == semesterId }, forceRefresh)
|
||||
.map { it.sortedBy { subject -> subject.subject } }
|
||||
.flatMap { gradesSummary ->
|
||||
gradeRepository.getGrades(it.second, it.first, forceRefresh)
|
||||
.map { grades ->
|
||||
grades.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) }
|
||||
.groupBy { grade -> grade.subject }
|
||||
.mapValues { entry -> entry.value.calcAverage() }
|
||||
.filterValues { value -> value != 0.0 }
|
||||
.let { averages ->
|
||||
createGradeSummaryItems(gradesSummary, averages) to
|
||||
GradeSummaryScrollableHeader(
|
||||
formatAverage(gradesSummary.calcAverage()),
|
||||
formatAverage(averages.values.average())
|
||||
)
|
||||
}
|
||||
}
|
||||
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
|
||||
.map { averages -> createGradeSummaryItemsAndHeader(gradesSummary, averages) }
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
@ -66,14 +51,14 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
enableSwipe(true)
|
||||
notifyParentDataLoaded(semesterId)
|
||||
}
|
||||
}.subscribe({
|
||||
}.subscribe({ (gradeSummaryItems, gradeSummaryHeader) ->
|
||||
Timber.i("Loading grade summary result: Success")
|
||||
view?.run {
|
||||
showEmpty(it.first.isEmpty())
|
||||
showContent(it.first.isNotEmpty())
|
||||
updateData(it.first, it.second)
|
||||
showEmpty(gradeSummaryItems.isEmpty())
|
||||
showContent(gradeSummaryItems.isNotEmpty())
|
||||
updateData(gradeSummaryItems, gradeSummaryHeader)
|
||||
}
|
||||
analytics.logEvent("load_grade_summary", "items" to it.first.size, "force_refresh" to forceRefresh)
|
||||
analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh)
|
||||
}) {
|
||||
Timber.i("Loading grade summary result: An exception occurred")
|
||||
view?.run { showEmpty(isViewEmpty) }
|
||||
@ -104,16 +89,24 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
disposable.clear()
|
||||
}
|
||||
|
||||
private fun createGradeSummaryItems(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
|
||||
: List<GradeSummaryItem> {
|
||||
return gradesSummary.filter { !checkEmpty(it, averages) }.map { it ->
|
||||
GradeSummaryItem(
|
||||
title = it.subject,
|
||||
average = formatAverage(averages.getOrElse(it.subject) { 0.0 }, ""),
|
||||
predictedGrade = it.predictedGrade,
|
||||
finalGrade = it.finalGrade
|
||||
)
|
||||
}
|
||||
private fun createGradeSummaryItemsAndHeader(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
|
||||
: Pair<List<GradeSummaryItem>, GradeSummaryScrollableHeader> {
|
||||
return averages.filterValues { value -> value != 0.0 }
|
||||
.let { filteredAverages ->
|
||||
gradesSummary.filter { !checkEmpty(it, filteredAverages) }
|
||||
.map {
|
||||
GradeSummaryItem(
|
||||
title = it.subject,
|
||||
average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""),
|
||||
predictedGrade = it.predictedGrade,
|
||||
finalGrade = it.finalGrade
|
||||
)
|
||||
}.let {
|
||||
it to GradeSummaryScrollableHeader(
|
||||
formatAverage(gradesSummary.calcAverage()),
|
||||
formatAverage(filteredAverages.values.average()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkEmpty(gradeSummary: GradeSummary, averages: Map<String, Double>): Boolean {
|
||||
|
@ -34,8 +34,8 @@ class HomeworkPresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: HomeworkView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Homework view is attached")
|
||||
view.initView()
|
||||
Timber.i("Homework view was initialized")
|
||||
loadData(LocalDate.ofEpochDay(date ?: LocalDate.now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePrese
|
||||
initAdapter()
|
||||
showActionBar(false)
|
||||
}
|
||||
Timber.i("Login view is attached")
|
||||
Timber.i("Login view was initialized")
|
||||
}
|
||||
|
||||
fun onFormViewAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package io.github.wulkanowy.ui.modules.login.form
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
@ -54,8 +54,8 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() }
|
||||
loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() }
|
||||
loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() }
|
||||
loginFormPrivacyPolicyLink.movementMethod = LinkMovementMethod.getInstance()
|
||||
loginFormSignIn.setOnClickListener { presenter.attemptLogin() }
|
||||
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
|
||||
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
|
||||
|
||||
loginFormPass.setOnEditorActionListener { _, id, _ ->
|
||||
if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false
|
||||
@ -132,6 +132,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
}
|
||||
}
|
||||
|
||||
override fun showPrivacyPolicy() {
|
||||
loginFormPrivacyLink.visibility = VISIBLE
|
||||
}
|
||||
|
||||
override fun notifyParentAccountLogged(students: List<Student>) {
|
||||
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
|
||||
loginFormName.text.toString(),
|
||||
@ -140,6 +144,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
||||
))
|
||||
}
|
||||
|
||||
override fun openPrivacyPolicyPage() {
|
||||
startActivity(Intent.parseUri("https://wulkanowy.github.io/polityka-prywatnosci.html", 0))
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
presenter.onDetachView()
|
||||
|
@ -22,7 +22,8 @@ class LoginFormPresenter @Inject constructor(
|
||||
super.onAttachView(view)
|
||||
view.run {
|
||||
initView()
|
||||
if (isDebug) showVersion()
|
||||
if (isDebug) showVersion() else showPrivacyPolicy()
|
||||
|
||||
errorHandler.onBadCredentials = {
|
||||
setErrorPassIncorrect()
|
||||
showSoftKeyboard()
|
||||
@ -31,6 +32,10 @@ class LoginFormPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onPrivacyLinkClick() {
|
||||
view?.openPrivacyPolicyPage()
|
||||
}
|
||||
|
||||
fun onHostSelected() {
|
||||
view?.apply {
|
||||
clearPassError()
|
||||
@ -47,7 +52,7 @@ class LoginFormPresenter @Inject constructor(
|
||||
view?.clearNameError()
|
||||
}
|
||||
|
||||
fun attemptLogin() {
|
||||
fun onSignInClick() {
|
||||
val email = view?.formNameValue.orEmpty()
|
||||
val password = view?.formPassValue.orEmpty()
|
||||
val endpoint = view?.formHostValue.orEmpty()
|
||||
|
@ -37,5 +37,9 @@ interface LoginFormView : BaseView {
|
||||
|
||||
fun showVersion()
|
||||
|
||||
fun showPrivacyPolicy()
|
||||
|
||||
fun notifyParentAccountLogged(students: List<Student>)
|
||||
|
||||
fun openPrivacyPolicyPage()
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
@ -45,6 +44,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() }
|
||||
loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } }
|
||||
|
||||
loginStudentSelectRecycler.apply {
|
||||
@ -54,7 +54,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LoginStudentSelectItem>) {
|
||||
loginAdapter.updateDataSet(data, true)
|
||||
loginAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun openMainView() {
|
||||
@ -69,11 +69,11 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
loginStudentSelectRecycler.visibility = if (show) VISIBLE else GONE
|
||||
loginStudentSelectContent.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showActionBar(show: Boolean) {
|
||||
(activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() }
|
||||
override fun enableSignIn(enable: Boolean) {
|
||||
loginStudentSelectSignIn.isEnabled = enable
|
||||
}
|
||||
|
||||
fun onParentInitStudentSelectFragment(students: List<Student>) {
|
||||
|
@ -13,15 +13,15 @@ import kotlinx.android.synthetic.main.item_login_student_select.*
|
||||
|
||||
class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
|
||||
|
||||
override fun getLayoutRes(): Int = R.layout.item_login_student_select
|
||||
override fun getLayoutRes() = R.layout.item_login_student_select
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ItemViewHolder {
|
||||
return ItemViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
holder.run {
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
loginItemName.text = "${student.studentName} ${student.className}"
|
||||
loginItemSchool.text = student.schoolName
|
||||
}
|
||||
@ -43,7 +43,17 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
|
||||
}
|
||||
|
||||
class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
|
||||
override val containerView: View
|
||||
get() = itemView
|
||||
|
||||
init {
|
||||
loginItemCheck.setOnClickListener { super.onClick(loginItemContainer) }
|
||||
}
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
super.onClick(view)
|
||||
loginItemCheck.apply { isChecked = !isChecked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.io.Serializable
|
||||
import javax.inject.Inject
|
||||
@ -22,10 +21,13 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
|
||||
var students = emptyList<Student>()
|
||||
|
||||
var selectedStudents = mutableListOf<Student>()
|
||||
|
||||
fun onAttachView(view: LoginStudentSelectView, students: Serializable?) {
|
||||
super.onAttachView(view)
|
||||
view.run {
|
||||
initView()
|
||||
enableSignIn(false)
|
||||
errorHandler.onStudentDuplicate = {
|
||||
showMessage(it)
|
||||
Timber.i("The student already registered in the app was selected")
|
||||
@ -37,13 +39,21 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onSignIn() {
|
||||
registerStudents(selectedStudents)
|
||||
}
|
||||
|
||||
fun onParentInitStudentSelectView(students: List<Student>) {
|
||||
loadData(students)
|
||||
if (students.size == 1) registerStudents(students)
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is LoginStudentSelectItem) {
|
||||
registerStudent(item.student)
|
||||
selectedStudents.removeAll { it == item.student }
|
||||
.let { if (!it) selectedStudents.add(item.student) }
|
||||
|
||||
view?.enableSignIn(selectedStudents.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,33 +64,30 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerStudent(student: Student) {
|
||||
disposable.add(studentRepository.saveStudent(student)
|
||||
.map { student.apply { id = it } }
|
||||
.onErrorResumeNext { studentRepository.logoutStudent(student).andThen(Single.error(it)) }
|
||||
.flatMapCompletable { studentRepository.switchStudent(student) }
|
||||
private fun registerStudents(students: List<Student>) {
|
||||
disposable.add(studentRepository.saveStudents(students)
|
||||
.map { students.first().apply { id = it.first() } }
|
||||
.flatMapCompletable { studentRepository.switchStudent(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
view?.apply {
|
||||
showProgress(true)
|
||||
showContent(false)
|
||||
showActionBar(false)
|
||||
}
|
||||
Timber.i("Registration started")
|
||||
}
|
||||
.subscribe({
|
||||
analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to "No error")
|
||||
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") }
|
||||
Timber.i("Registration result: Success")
|
||||
view?.openMainView()
|
||||
}, {
|
||||
analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to it.localizedMessage)
|
||||
}, { error ->
|
||||
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.localizedMessage) }
|
||||
Timber.i("Registration result: An exception occurred ")
|
||||
errorHandler.dispatch(it)
|
||||
errorHandler.dispatch(error)
|
||||
view?.apply {
|
||||
showProgress(false)
|
||||
showContent(true)
|
||||
showActionBar(true)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
@ -14,5 +14,5 @@ interface LoginStudentSelectView : BaseView {
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showActionBar(show: Boolean)
|
||||
fun enableSignIn(enable: Boolean)
|
||||
}
|
||||
|
@ -21,12 +21,12 @@ class LuckyNumberPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: LuckyNumberView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Lucky number view is attached")
|
||||
view.run {
|
||||
initView()
|
||||
showContent(false)
|
||||
enableSwipe(false)
|
||||
}
|
||||
Timber.i("Lucky number view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,77 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.activity_widget_configure.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetConfigureActivity : BaseActivity(), LuckyNumberWidgetConfigureView {
|
||||
|
||||
@Inject
|
||||
lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: LuckyNumberWidgetConfigurePresenter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setResult(RESULT_CANCELED)
|
||||
setContentView(R.layout.activity_widget_configure)
|
||||
|
||||
intent.extras.let {
|
||||
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID))
|
||||
}
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
widgetConfigureRecycler.apply {
|
||||
adapter = configureAdapter
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
}
|
||||
configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<LuckyNumberWidgetConfigureItem>) {
|
||||
configureAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun updateLuckyNumberWidget(widgetId: Int) {
|
||||
sendBroadcast(Intent(this, LuckyNumberWidgetProvider::class.java)
|
||||
.apply {
|
||||
action = ACTION_APPWIDGET_UPDATE
|
||||
putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
|
||||
})
|
||||
}
|
||||
|
||||
override fun setSuccessResult(widgetId: Int) {
|
||||
setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) })
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
Toast.makeText(this, text, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun finishView() {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun openLoginView() {
|
||||
startActivity(LoginActivity.getStartIntent(this))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
presenter.onDetachView()
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_account.*
|
||||
|
||||
class LuckyNumberWidgetConfigureItem(var student: Student, val isCurrent: Boolean) :
|
||||
AbstractFlexibleItem<LuckyNumberWidgetConfigureItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_account
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
accountItemImage.setBackgroundResource(if (isCurrent) R.drawable.ic_account_circular_border else 0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TimetableWidgetConfigureItem
|
||||
|
||||
if (student != other.student) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = student.hashCode()
|
||||
result = 31 * result + student.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class LuckyNumberWidgetConfigurePresenter @Inject constructor(
|
||||
private val errorHandler: ErrorHandler,
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val sharedPref: SharedPrefHelper
|
||||
) : BasePresenter<LuckyNumberWidgetConfigureView>(errorHandler) {
|
||||
|
||||
private var appWidgetId: Int? = null
|
||||
|
||||
fun onAttachView(view: LuckyNumberWidgetConfigureView, appWidgetId: Int?) {
|
||||
super.onAttachView(view)
|
||||
this.appWidgetId = appWidgetId
|
||||
view.initView()
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelect(item: AbstractFlexibleItem<*>) {
|
||||
if (item is LuckyNumberWidgetConfigureItem) {
|
||||
registerStudent(item.student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(studentRepository.getSavedStudents(false)
|
||||
.map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } }
|
||||
.map { (students, currentStudentId) ->
|
||||
students.map { student -> LuckyNumberWidgetConfigureItem(student, student.id == currentStudentId) }
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({
|
||||
when {
|
||||
it.isEmpty() -> view?.openLoginView()
|
||||
it.size == 1 -> registerStudent(it.single().student)
|
||||
else -> view?.updateData(it)
|
||||
}
|
||||
}, { errorHandler.dispatch(it) }))
|
||||
}
|
||||
|
||||
private fun registerStudent(student: Student) {
|
||||
appWidgetId?.also {
|
||||
sharedPref.putLong(getStudentWidgetKey(it), student.id)
|
||||
view?.apply {
|
||||
updateLuckyNumberWidget(it)
|
||||
setSuccessResult(it)
|
||||
}
|
||||
}
|
||||
view?.finishView()
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
|
||||
|
||||
interface LuckyNumberWidgetConfigureView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<LuckyNumberWidgetConfigureItem>)
|
||||
|
||||
fun updateLuckyNumberWidget(widgetId: Int)
|
||||
|
||||
fun setSuccessResult(widgetId: Int)
|
||||
|
||||
fun finishView()
|
||||
|
||||
fun openLoginView()
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.PendingIntent
|
||||
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.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
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, MainView.MenuView.LUCKY_NUMBER.id, MainActivity.getStartIntent(context).apply {
|
||||
putExtra(EXTRA_START_MENU, MainView.MenuView.LUCKY_NUMBER)
|
||||
}, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
}.also {
|
||||
setStyles(it, intent)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDelete(intent: Intent) {
|
||||
intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let {
|
||||
if (it != 0) sharedPref.delete(getStudentWidgetKey(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLuckyNumber(studentId: Long, appWidgetId: Int): LuckyNumber? {
|
||||
return try {
|
||||
studentRepository.isStudentSaved()
|
||||
.filter { true }
|
||||
.flatMap { studentRepository.getSavedStudents().toMaybe() }
|
||||
.flatMap { students ->
|
||||
students.singleOrNull { student -> student.id == studentId }
|
||||
.let { student ->
|
||||
when {
|
||||
student != null -> Maybe.just(student)
|
||||
studentId != 0L -> {
|
||||
studentRepository.getCurrentStudent(false)
|
||||
.toMaybe()
|
||||
.doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) }
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
|
||||
.flatMap { luckyNumberRepository.getLuckyNumber(it) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred in lucky number provider")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun onOptionsChange(context: Context, intent: Intent) {
|
||||
intent.extras?.let { extras ->
|
||||
RemoteViews(context.packageName, R.layout.widget_luckynumber).apply {
|
||||
setStyles(this, intent)
|
||||
appWidgetManager.updateAppWidget(extras.getInt(EXTRA_APPWIDGET_ID), this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
private fun setStyles(views: RemoteViews, intent: Intent) {
|
||||
val options = intent.extras?.getBundle(EXTRA_APPWIDGET_OPTIONS)
|
||||
|
||||
val maxWidth = options?.getInt(OPTION_APPWIDGET_MAX_WIDTH) ?: 150
|
||||
val maxHeight = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: 40
|
||||
|
||||
Timber.d("New lucky number widget measurement: %dx%d", maxWidth, maxHeight)
|
||||
|
||||
when {
|
||||
// 1x1
|
||||
maxWidth < 150 && maxHeight < 110 -> {
|
||||
Timber.d("Lucky number widget size: 1x1")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 1x2
|
||||
maxWidth < 150 && maxHeight > 110 -> {
|
||||
Timber.d("Lucky number widget size: 1x2")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 2x1
|
||||
maxWidth >= 150 && maxHeight <= 110 -> {
|
||||
Timber.d("Lucky number widget size: 2x1")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
// 2x2 and bigger
|
||||
else -> {
|
||||
Timber.d("Lucky number widget size: 2x2 and bigger")
|
||||
views.run {
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetTitle, VISIBLE)
|
||||
setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,8 +22,12 @@ import io.github.wulkanowy.ui.modules.account.AccountDialog
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.more.MoreFragment
|
||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.safelyPopFragment
|
||||
@ -40,7 +44,7 @@ class MainActivity : BaseActivity(), MainView {
|
||||
lateinit var navController: FragNavController
|
||||
|
||||
companion object {
|
||||
const val EXTRA_START_MENU_INDEX = "extraStartMenuIndex"
|
||||
const val EXTRA_START_MENU = "extraStartMenu"
|
||||
|
||||
fun getStartIntent(context: Context) = Intent(context, MainActivity::class.java)
|
||||
}
|
||||
@ -56,14 +60,27 @@ class MainActivity : BaseActivity(), MainView {
|
||||
|
||||
override var startMenuIndex = 0
|
||||
|
||||
override var startMenuMoreIndex = -1
|
||||
|
||||
private val moreMenuFragments = listOf<Fragment>(
|
||||
MessageFragment.newInstance(),
|
||||
HomeworkFragment.newInstance(),
|
||||
NoteFragment.newInstance(),
|
||||
LuckyNumberFragment.newInstance()
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(mainToolbar)
|
||||
messageContainer = mainFragmentContainer
|
||||
|
||||
presenter.onAttachView(this, intent.getIntExtra(EXTRA_START_MENU_INDEX, -1))
|
||||
navController.initialize(startMenuIndex, savedInstanceState)
|
||||
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_START_MENU) as? MainView.MenuView)
|
||||
|
||||
navController.run {
|
||||
initialize(startMenuIndex, savedInstanceState)
|
||||
pushFragment(moreMenuFragments.getOrNull(startMenuMoreIndex))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
@ -168,7 +185,7 @@ class MainActivity : BaseActivity(), MainView {
|
||||
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle?) {
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
navController.onSaveInstanceState(outState)
|
||||
}
|
||||
|
@ -22,22 +22,19 @@ class MainPresenter @Inject constructor(
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BasePresenter<MainView>(errorHandler) {
|
||||
|
||||
fun onAttachView(view: MainView, initMenuIndex: Int) {
|
||||
fun onAttachView(view: MainView, initMenu: MainView.MenuView?) {
|
||||
super.onAttachView(view)
|
||||
view.run {
|
||||
startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex
|
||||
Timber.i("Main view is attached with $startMenuIndex menu index")
|
||||
view.apply {
|
||||
getProperViewIndexes(initMenu).let { (main, more) ->
|
||||
startMenuIndex = main
|
||||
startMenuMoreIndex = more
|
||||
}
|
||||
initView()
|
||||
Timber.i("Main view was initialized with $startMenuIndex menu index and $startMenuMoreIndex more index")
|
||||
}
|
||||
|
||||
syncManager.startSyncWorker()
|
||||
|
||||
analytics.logEvent(APP_OPEN, DESTINATION to when (initMenuIndex) {
|
||||
1 -> "Grades"
|
||||
3 -> "Timetable"
|
||||
4 -> "More"
|
||||
else -> "User action"
|
||||
})
|
||||
analytics.logEvent(APP_OPEN, DESTINATION to initMenu?.name)
|
||||
}
|
||||
|
||||
fun onViewChange() {
|
||||
@ -104,4 +101,12 @@ class MainPresenter @Inject constructor(
|
||||
errorHandler.dispatch(it)
|
||||
}))
|
||||
}
|
||||
|
||||
private fun getProperViewIndexes(initMenu: MainView.MenuView?): Pair<Int, Int> {
|
||||
return when {
|
||||
initMenu?.id in 0..3 -> initMenu!!.id to -1
|
||||
(initMenu?.id ?: 0) > 3 -> 4 to initMenu!!.id - 4
|
||||
else -> prefRepository.startMenuIndex to -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ interface MainView : BaseView {
|
||||
|
||||
var startMenuIndex: Int
|
||||
|
||||
var startMenuMoreIndex: Int
|
||||
|
||||
val isRootView: Boolean
|
||||
|
||||
val currentViewTitle: String?
|
||||
@ -37,4 +39,15 @@ interface MainView : BaseView {
|
||||
|
||||
val titleStringId: Int
|
||||
}
|
||||
|
||||
enum class MenuView(val id: Int) {
|
||||
GRADE(0),
|
||||
ATTENDANCE(1),
|
||||
EXAM(2),
|
||||
TIMETABLE(3),
|
||||
MESSAGE(4),
|
||||
HOMEWORK(5),
|
||||
NOTE(6),
|
||||
LUCKY_NUMBER(7),
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
|
||||
@ -75,12 +76,20 @@ class MessageFragment : BaseFragment(), MessageView, MainView.TitledView {
|
||||
messageProgress.visibility = if (show) VISIBLE else INVISIBLE
|
||||
}
|
||||
|
||||
fun onDeleteMessage(message: Message) {
|
||||
presenter.onDeleteMessage(message)
|
||||
}
|
||||
|
||||
fun onChildFragmentLoaded() {
|
||||
presenter.onChildViewLoaded()
|
||||
}
|
||||
|
||||
override fun notifyChildMessageDeleted(tabId: Int) {
|
||||
(pagerAdapter.getFragmentInstance(tabId) as? MessageTabFragment)?.onParentDeleteMessage()
|
||||
}
|
||||
|
||||
override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) {
|
||||
(pagerAdapter.getFragmentInstance(index) as? MessageView.MessageChildView)?.onParentLoadData(forceRefresh)
|
||||
(pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)?.onParentLoadData(forceRefresh)
|
||||
}
|
||||
|
||||
override fun openSendMessage() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.message
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
@ -15,10 +16,10 @@ class MessagePresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: MessageView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Message view is attached")
|
||||
disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread)
|
||||
.subscribe {
|
||||
view.initView()
|
||||
Timber.i("Message view was initialized")
|
||||
loadData()
|
||||
})
|
||||
}
|
||||
@ -43,6 +44,15 @@ class MessagePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onDeleteMessage(message: Message) {
|
||||
view?.notifyChildMessageDeleted(
|
||||
when (message.removed) {
|
||||
true -> 2
|
||||
else -> message.folderId - 1
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun onSendMessageButtonClicked() {
|
||||
view?.openSendMessage()
|
||||
}
|
||||
|
@ -14,10 +14,7 @@ interface MessageView : BaseView {
|
||||
|
||||
fun notifyChildLoadData(index: Int, forceRefresh: Boolean)
|
||||
|
||||
fun notifyChildMessageDeleted(tabId: Int)
|
||||
|
||||
fun openSendMessage()
|
||||
|
||||
interface MessageChildView {
|
||||
|
||||
fun onParentLoadData(forceRefresh: Boolean)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,9 @@ import android.view.ViewGroup
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
|
||||
import kotlinx.android.synthetic.main.fragment_message_preview.*
|
||||
import javax.inject.Inject
|
||||
@ -25,6 +27,8 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
lateinit var presenter: MessagePreviewPresenter
|
||||
|
||||
private var menuReplyButton: MenuItem? = null
|
||||
private var menuForwardButton: MenuItem? = null
|
||||
private var menuDeleteButton: MenuItem? = null
|
||||
|
||||
override val titleStringId: Int
|
||||
get() = R.string.message_title
|
||||
@ -32,6 +36,9 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
override val noSubjectString: String
|
||||
get() = getString(R.string.message_no_subject)
|
||||
|
||||
override val deleteMessageSuccessString: String
|
||||
get() = getString(R.string.message_delete_success)
|
||||
|
||||
companion object {
|
||||
const val MESSAGE_ID_KEY = "message_id"
|
||||
|
||||
@ -57,15 +64,21 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getInt(MESSAGE_ID_KEY) ?: 0)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_message_preview, menu)
|
||||
menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_message_preview, menu)
|
||||
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply)
|
||||
menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward)
|
||||
menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete)
|
||||
presenter.onCreateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.messagePreviewMenuReply) presenter.onReply()
|
||||
else false
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.messagePreviewMenuReply -> presenter.onReply()
|
||||
R.id.messagePreviewMenuForward -> presenter.onForward()
|
||||
R.id.messagePreviewMenuDelete -> presenter.onMessageDelete()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun setSubject(subject: String) {
|
||||
@ -92,8 +105,22 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
messagePreviewProgress.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showReplyButton(show: Boolean) {
|
||||
override fun showContent(show: Boolean) {
|
||||
messagePreviewContentContainer.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showOptions(show: Boolean) {
|
||||
menuReplyButton?.isVisible = show
|
||||
menuForwardButton?.isVisible = show
|
||||
menuDeleteButton?.isVisible = show
|
||||
}
|
||||
|
||||
override fun setDeletedOptionsLabels() {
|
||||
menuDeleteButton?.setTitle(R.string.message_delete_forever)
|
||||
}
|
||||
|
||||
override fun setNotDeletedOptionsLabels() {
|
||||
menuDeleteButton?.setTitle(R.string.message_move_to_bin)
|
||||
}
|
||||
|
||||
override fun showMessageError() {
|
||||
@ -101,9 +128,21 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
|
||||
}
|
||||
|
||||
override fun openMessageReply(message: Message?) {
|
||||
context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message, true)) }
|
||||
}
|
||||
|
||||
override fun openMessageForward(message: Message?) {
|
||||
context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) }
|
||||
}
|
||||
|
||||
override fun popView() {
|
||||
(activity as MainActivity).popView()
|
||||
}
|
||||
|
||||
override fun notifyParentMessageDeleted(message: Message) {
|
||||
fragmentManager?.fragments?.forEach { if (it is MessageFragment) it.onDeleteMessage(message) }
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt(MESSAGE_ID_KEY, presenter.messageId)
|
||||
|
@ -22,7 +22,7 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
|
||||
var messageId: Int = 0
|
||||
|
||||
private var replyMessage: Message? = null
|
||||
private var message: Message? = null
|
||||
|
||||
fun onAttachView(view: MessagePreviewView, id: Int) {
|
||||
super.onAttachView(view)
|
||||
@ -41,13 +41,13 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
.doFinally { view?.showProgress(false) }
|
||||
.subscribe({ message ->
|
||||
Timber.i("Loading message $id preview result: Success ")
|
||||
replyMessage = message
|
||||
this@MessagePreviewPresenter.message = message
|
||||
view?.run {
|
||||
message.let {
|
||||
setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString)
|
||||
setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
|
||||
setContent(it.content.orEmpty())
|
||||
showReplyButton(true)
|
||||
initOptions()
|
||||
|
||||
if (it.recipient.isNotBlank()) setRecipient(it.recipient)
|
||||
else setSender(it.sender)
|
||||
@ -63,13 +63,69 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onReply(): Boolean {
|
||||
return if (replyMessage != null) {
|
||||
view?.openMessageReply(replyMessage)
|
||||
return if (message != null) {
|
||||
view?.openMessageReply(message)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
fun onForward(): Boolean {
|
||||
return if (message != null) {
|
||||
view?.openMessageForward(message)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
private fun deleteMessage() {
|
||||
message?.let { message ->
|
||||
disposable.add(messageRepository.deleteMessage(message)
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
view?.run {
|
||||
showContent(false)
|
||||
showProgress(true)
|
||||
showOptions(false)
|
||||
}
|
||||
}
|
||||
.doFinally {
|
||||
view?.showProgress(false)
|
||||
}
|
||||
.subscribe({
|
||||
view?.run {
|
||||
notifyParentMessageDeleted(message)
|
||||
showMessage(deleteMessageSuccessString)
|
||||
popView()
|
||||
}
|
||||
}, { error ->
|
||||
view?.showMessageError()
|
||||
errorHandler.dispatch(error)
|
||||
}, {
|
||||
view?.showMessageError()
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onMessageDelete(): Boolean {
|
||||
deleteMessage()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun initOptions() {
|
||||
view?.apply {
|
||||
showOptions(message != null)
|
||||
message?.let {
|
||||
when (it.removed) {
|
||||
true -> setDeletedOptionsLabels()
|
||||
false -> setNotDeletedOptionsLabels()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateOptionsMenu() {
|
||||
view?.showReplyButton(replyMessage != null)
|
||||
initOptions()
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ interface MessagePreviewView : BaseSessionView {
|
||||
|
||||
val noSubjectString: String
|
||||
|
||||
val deleteMessageSuccessString: String
|
||||
|
||||
fun setSubject(subject: String)
|
||||
|
||||
fun setRecipient(recipient: String)
|
||||
@ -19,9 +21,21 @@ interface MessagePreviewView : BaseSessionView {
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
fun showReplyButton(show: Boolean)
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showOptions(show: Boolean)
|
||||
|
||||
fun setDeletedOptionsLabels()
|
||||
|
||||
fun setNotDeletedOptionsLabels()
|
||||
|
||||
fun showMessageError()
|
||||
|
||||
fun openMessageReply(message: Message?)
|
||||
|
||||
fun openMessageForward(message: Message?)
|
||||
|
||||
fun popView()
|
||||
|
||||
fun notifyParentMessageDeleted(message: Message)
|
||||
}
|
||||
|
@ -26,11 +26,14 @@ class SendMessageActivity : BaseActivity(), SendMessageView {
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_MESSAGE = "EXTRA_MESSAGE"
|
||||
private const val EXTRA_REPLY = "EXTRA_REPLY"
|
||||
|
||||
fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java)
|
||||
|
||||
fun getStartIntent(context: Context, message: Message?): Intent {
|
||||
return getStartIntent(context).putExtra(EXTRA_MESSAGE, message)
|
||||
fun getStartIntent(context: Context, message: Message?, reply: Boolean = false): Intent {
|
||||
return getStartIntent(context)
|
||||
.putExtra(EXTRA_MESSAGE, message)
|
||||
.putExtra(EXTRA_REPLY, reply)
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +62,7 @@ class SendMessageActivity : BaseActivity(), SendMessageView {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
messageContainer = sendMessageContainer
|
||||
|
||||
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message)
|
||||
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
|
@ -30,18 +30,25 @@ class SendMessagePresenter @Inject constructor(
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BasePresenter<SendMessageView>(errorHandler) {
|
||||
|
||||
fun onAttachView(view: SendMessageView, message: Message?) {
|
||||
fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Send message view is attached")
|
||||
loadData(message)
|
||||
Timber.i("Send message view was initialized")
|
||||
loadData(message, reply)
|
||||
view.apply {
|
||||
message?.let {
|
||||
setSubject("RE: ${message.subject}")
|
||||
if (preferencesRepository.fillMessageContent) {
|
||||
setContent(when (message.sender.isNotEmpty()) {
|
||||
true -> "\n\nOd: ${message.sender}\n"
|
||||
false -> "\n\nDo: ${message.recipient}\n"
|
||||
} + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}")
|
||||
setSubject(when (reply) {
|
||||
true -> "RE: "
|
||||
else -> "FW: "
|
||||
} + message.subject)
|
||||
if (preferencesRepository.fillMessageContent || reply != true) {
|
||||
setContent(
|
||||
when (reply) {
|
||||
true -> "\n\n"
|
||||
else -> ""
|
||||
} + when (message.sender.isNotEmpty()) {
|
||||
true -> "Od: ${message.sender}\n"
|
||||
false -> "Do: ${message.recipient}\n"
|
||||
} + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,7 +59,7 @@ class SendMessagePresenter @Inject constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun loadData(message: Message?) {
|
||||
private fun loadData(message: Message?, reply: Boolean?) {
|
||||
var reportingUnit: ReportingUnit? = null
|
||||
var recipients: List<Recipient> = emptyList()
|
||||
var selectedRecipient: List<Recipient> = emptyList()
|
||||
@ -69,7 +76,7 @@ class SendMessagePresenter @Inject constructor(
|
||||
recipients = it
|
||||
}
|
||||
.flatMapCompletable {
|
||||
if (message == null) Completable.complete()
|
||||
if (message == null || reply != true) Completable.complete()
|
||||
else recipientRepository.getMessageRecipients(student, message)
|
||||
.doOnSuccess {
|
||||
Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size)
|
||||
|
@ -17,13 +17,12 @@ import io.github.wulkanowy.ui.base.session.BaseSessionFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.message.MessageItem
|
||||
import io.github.wulkanowy.ui.modules.message.MessageView
|
||||
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_message_tab.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.MessageChildView {
|
||||
class MessageTabFragment : BaseSessionFragment(), MessageTabView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: MessageTabPresenter
|
||||
@ -76,7 +75,7 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.Me
|
||||
}
|
||||
|
||||
override fun updateData(data: List<MessageItem>) {
|
||||
tabAdapter.updateDataSet(data, true)
|
||||
tabAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun updateItem(item: AbstractFlexibleItem<*>) {
|
||||
@ -115,10 +114,14 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.Me
|
||||
(parentFragment as? MessageFragment)?.onChildFragmentLoaded()
|
||||
}
|
||||
|
||||
override fun onParentLoadData(forceRefresh: Boolean) {
|
||||
fun onParentLoadData(forceRefresh: Boolean) {
|
||||
presenter.onParentViewLoadData(forceRefresh)
|
||||
}
|
||||
|
||||
fun onParentDeleteMessage() {
|
||||
presenter.onDeleteMessage()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID, presenter.folder.name)
|
||||
|
@ -34,7 +34,29 @@ class MessageTabPresenter @Inject constructor(
|
||||
onParentViewLoadData(true)
|
||||
}
|
||||
|
||||
fun onDeleteMessage() {
|
||||
loadData(false)
|
||||
}
|
||||
|
||||
fun onParentViewLoadData(forceRefresh: Boolean) {
|
||||
loadData(forceRefresh)
|
||||
}
|
||||
|
||||
fun onMessageItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item is MessageItem) {
|
||||
Timber.i("Select message ${item.message.realId} item")
|
||||
view?.run {
|
||||
openMessage(item.message.realId)
|
||||
if (item.message.unread) {
|
||||
item.message.unread = false
|
||||
updateItem(item)
|
||||
updateMessage(item.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData(forceRefresh: Boolean) {
|
||||
Timber.i("Loading $folder message data started")
|
||||
disposable.apply {
|
||||
clear()
|
||||
@ -67,20 +89,6 @@ class MessageTabPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onMessageItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item is MessageItem) {
|
||||
Timber.i("Select message ${item.message.realId} item")
|
||||
view?.run {
|
||||
openMessage(item.message.realId)
|
||||
if (item.message.unread) {
|
||||
item.message.unread = false
|
||||
updateItem(item)
|
||||
updateMessage(item.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMessage(message: Message) {
|
||||
Timber.i("Attempt to update message ${message.realId}")
|
||||
disposable.add(messageRepository.updateMessage(message)
|
||||
|
@ -10,8 +10,8 @@ class MorePresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresen
|
||||
|
||||
override fun onAttachView(view: MoreView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("More view is attached")
|
||||
view.initView()
|
||||
Timber.i("More view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ class NotePresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: NoteView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Note view is attached")
|
||||
view.initView()
|
||||
Timber.i("Note view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.settings
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.takisoft.preferencex.PreferenceFragmentCompat
|
||||
import dagger.android.support.AndroidSupportInjection
|
||||
import io.github.wulkanowy.BuildConfig.DEBUG
|
||||
@ -44,8 +43,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
presenter.onSharedPreferenceChanged(key)
|
||||
}
|
||||
|
||||
override fun setTheme(theme: Int) {
|
||||
AppCompatDelegate.setDefaultNightMode(theme)
|
||||
override fun recreateView() {
|
||||
activity?.recreate()
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ class SettingsPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: SettingsView) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Settings view is attached")
|
||||
Timber.i("Settings view was initialized")
|
||||
view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays)
|
||||
}
|
||||
|
||||
@ -31,8 +31,8 @@ class SettingsPresenter @Inject constructor(
|
||||
when (key) {
|
||||
serviceEnableKey -> syncManager.run { if (isServiceEnabled) startSyncWorker() else stopSyncWorker() }
|
||||
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true)
|
||||
currentThemeKey -> view?.setTheme(currentTheme)
|
||||
isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable)
|
||||
appThemeKey -> view?.recreateView()
|
||||
}
|
||||
}
|
||||
analytics.logEvent("setting_changed", "name" to key)
|
||||
|
@ -4,7 +4,7 @@ import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface SettingsView : BaseView {
|
||||
|
||||
fun setTheme(theme: Int)
|
||||
fun recreateView()
|
||||
|
||||
fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean)
|
||||
}
|
||||
|
@ -80,12 +80,12 @@ class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChi
|
||||
timetableNextButton.setOnClickListener { presenter.onNextDay() }
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_timetable, menu)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_timetable, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected()
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,8 @@ class TimetablePresenter @Inject constructor(
|
||||
|
||||
fun onAttachView(view: TimetableView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Timetable is attached")
|
||||
view.initView()
|
||||
Timber.i("Timetable was initialized")
|
||||
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import android.widget.Toast.LENGTH_LONG
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.activity_widget_configure.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableWidgetConfigureActivity : BaseActivity(), TimetableWidgetConfigureView {
|
||||
|
||||
@Inject
|
||||
lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: TimetableWidgetConfigurePresenter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setResult(RESULT_CANCELED)
|
||||
setContentView(R.layout.activity_widget_configure)
|
||||
|
||||
intent.extras.let {
|
||||
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID), it?.getBoolean(EXTRA_FROM_PROVIDER))
|
||||
}
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
widgetConfigureRecycler.apply {
|
||||
adapter = configureAdapter
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
}
|
||||
configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<TimetableWidgetConfigureItem>) {
|
||||
configureAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun updateTimetableWidget(widgetId: Int) {
|
||||
sendBroadcast(Intent(this, TimetableWidgetProvider::class.java)
|
||||
.apply {
|
||||
action = ACTION_APPWIDGET_UPDATE
|
||||
putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
|
||||
})
|
||||
}
|
||||
|
||||
override fun setSuccessResult(widgetId: Int) {
|
||||
setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) })
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
Toast.makeText(this, text, LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun finishView() {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun openLoginView() {
|
||||
startActivity(LoginActivity.getStartIntent(this))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
presenter.onDetachView()
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_account.*
|
||||
|
||||
class TimetableWidgetConfigureItem(val student: Student, private val isCurrent: Boolean) :
|
||||
AbstractFlexibleItem<TimetableWidgetConfigureItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_account
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.apply {
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
accountItemImage.setBackgroundResource(if (isCurrent) R.drawable.ic_account_circular_border else 0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TimetableWidgetConfigureItem
|
||||
|
||||
if (student != other.student) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = student.hashCode()
|
||||
result = 31 * result + student.id.toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableWidgetConfigurePresenter @Inject constructor(
|
||||
private val errorHandler: ErrorHandler,
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val sharedPref: SharedPrefHelper
|
||||
) : BasePresenter<TimetableWidgetConfigureView>(errorHandler) {
|
||||
|
||||
private var appWidgetId: Int? = null
|
||||
|
||||
private var isFromProvider = false
|
||||
|
||||
fun onAttachView(view: TimetableWidgetConfigureView, appWidgetId: Int?, isFromProvider: Boolean?) {
|
||||
super.onAttachView(view)
|
||||
this.appWidgetId = appWidgetId
|
||||
this.isFromProvider = isFromProvider ?: false
|
||||
view.initView()
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelect(item: AbstractFlexibleItem<*>) {
|
||||
if (item is TimetableWidgetConfigureItem) {
|
||||
registerStudent(item.student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(studentRepository.getSavedStudents(false)
|
||||
.map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } }
|
||||
.map { (students, currentStudentId) ->
|
||||
students.map { student -> TimetableWidgetConfigureItem(student, student.id == currentStudentId) }
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.subscribe({
|
||||
when {
|
||||
it.isEmpty() -> view?.openLoginView()
|
||||
it.size == 1 && !isFromProvider -> registerStudent(it.single().student)
|
||||
else -> view?.updateData(it)
|
||||
}
|
||||
}, { errorHandler.dispatch(it) }))
|
||||
}
|
||||
|
||||
private fun registerStudent(student: Student) {
|
||||
appWidgetId?.also {
|
||||
sharedPref.putLong(getStudentWidgetKey(it), student.id)
|
||||
view?.apply {
|
||||
updateTimetableWidget(it)
|
||||
setSuccessResult(it)
|
||||
}
|
||||
}
|
||||
view?.finishView()
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface TimetableWidgetConfigureView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<TimetableWidgetConfigureItem>)
|
||||
|
||||
fun updateTimetableWidget(widgetId: Int)
|
||||
|
||||
fun setSuccessResult(widgetId: Int)
|
||||
|
||||
fun finishView()
|
||||
|
||||
fun openLoginView()
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.widgets.timetable
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Paint.ANTI_ALIAS_FLAG
|
||||
@ -15,9 +16,11 @@ import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
import timber.log.Timber
|
||||
|
||||
@ -48,28 +51,37 @@ class TimetableWidgetFactory(
|
||||
override fun onDestroy() {}
|
||||
|
||||
override fun onDataSetChanged() {
|
||||
intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) }
|
||||
?.let { date ->
|
||||
try {
|
||||
lessons = studentRepository.isStudentSaved()
|
||||
.flatMap { isSaved ->
|
||||
if (isSaved) {
|
||||
studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||
.flatMap { timetableRepository.getTimetable(it, date, date) }
|
||||
} else Single.just(emptyList())
|
||||
}
|
||||
.map { item -> item.sortedBy { it.number } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred while downloading data for the widget")
|
||||
}
|
||||
intent?.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId ->
|
||||
val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0))
|
||||
val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0)
|
||||
|
||||
lessons = try {
|
||||
studentRepository.isStudentSaved()
|
||||
.filter { true }
|
||||
.flatMap { studentRepository.getSavedStudents().toMaybe() }
|
||||
.flatMap {
|
||||
if (studentId == 0L) throw IllegalArgumentException("Student id is 0")
|
||||
|
||||
it.singleOrNull { student -> student.id == studentId }
|
||||
.let { student ->
|
||||
if (student != null) Maybe.just(student)
|
||||
else Maybe.empty()
|
||||
}
|
||||
}
|
||||
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
|
||||
.flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() }
|
||||
.map { item -> item.sortedBy { it.number } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet(emptyList())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred in timetable widget factory")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getViewAt(position: Int): RemoteViews? {
|
||||
if (position == INVALID_POSITION || lessons.getOrNull(position) === null) return null
|
||||
if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null
|
||||
|
||||
return RemoteViews(context.packageName, R.layout.item_widget_timetable).apply {
|
||||
lessons[position].let {
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.ui.widgets.timetable
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
@ -10,21 +10,29 @@ import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.widget.RemoteViews
|
||||
import dagger.android.AndroidInjection
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.SharedPrefHelper
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.services.widgets.TimetableWidgetService
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||
import io.github.wulkanowy.utils.nextSchoolDay
|
||||
import io.github.wulkanowy.utils.previousSchoolDay
|
||||
import io.github.wulkanowy.utils.shortcutWeekDayName
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.weekDayName
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
@ -32,13 +40,21 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
@Inject
|
||||
lateinit var appWidgetManager: AppWidgetManager
|
||||
|
||||
@Inject
|
||||
lateinit var studentRepository: StudentRepository
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPref: SharedPrefHelper
|
||||
|
||||
@Inject
|
||||
lateinit var schedulers: SchedulersProvider
|
||||
|
||||
@Inject
|
||||
lateinit var analytics: FirebaseAnalyticsHelper
|
||||
|
||||
companion object {
|
||||
const val EXTRA_FROM_PROVIDER = "extraFromProvider"
|
||||
|
||||
const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget"
|
||||
|
||||
const val EXTRA_BUTTON_TYPE = "extraButtonType"
|
||||
@ -49,7 +65,9 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
|
||||
const val BUTTON_RESET = "buttonReset"
|
||||
|
||||
fun createWidgetKey(appWidgetId: Int) = "timetable_widget_$appWidgetId"
|
||||
fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId"
|
||||
|
||||
fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId"
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
@ -63,12 +81,14 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
private fun onUpdate(context: Context, intent: Intent) {
|
||||
if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) {
|
||||
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
|
||||
updateWidget(context, appWidgetId, now().nextOrSameSchoolDay)
|
||||
val student = getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
|
||||
updateWidget(context, appWidgetId, now().nextOrSameSchoolDay, student)
|
||||
}
|
||||
} else {
|
||||
val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE)
|
||||
val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0)
|
||||
val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(createWidgetKey(toggledWidgetId), 0))
|
||||
val student = getStudent(sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), toggledWidgetId)
|
||||
val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0))
|
||||
val date = when (buttonType) {
|
||||
BUTTON_RESET -> now().nextOrSameSchoolDay
|
||||
BUTTON_NEXT -> savedDate.nextSchoolDay
|
||||
@ -76,35 +96,46 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
else -> now().nextOrSameSchoolDay
|
||||
}
|
||||
if (!buttonType.isNullOrBlank()) analytics.logEvent("changed_timetable_widget_day", "button" to buttonType)
|
||||
updateWidget(context, toggledWidgetId, date)
|
||||
updateWidget(context, toggledWidgetId, date, student)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDelete(intent: Intent) {
|
||||
intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let {
|
||||
if (it != 0) sharedPref.delete(createWidgetKey(it))
|
||||
if (it != 0) {
|
||||
sharedPref.apply {
|
||||
delete(getStudentWidgetKey(it))
|
||||
delete(getDateWidgetKey(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate) {
|
||||
private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate, student: Student?) {
|
||||
RemoteViews(context.packageName, R.layout.widget_timetable).apply {
|
||||
setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty)
|
||||
setTextViewText(R.id.timetableWidgetDay, date.weekDayName.capitalize())
|
||||
setTextViewText(R.id.timetableWidgetDate, date.toFormattedString())
|
||||
setTextViewText(R.id.timetableWidgetDate, "${date.shortcutWeekDayName.capitalize()} ${date.toFormattedString()}")
|
||||
setTextViewText(R.id.timetableWidgetName, student?.studentName ?: context.getString(R.string.all_no_data))
|
||||
setRemoteAdapter(R.id.timetableWidgetList, Intent(context, TimetableWidgetService::class.java)
|
||||
.apply { action = createWidgetKey(appWidgetId) })
|
||||
.apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId) })
|
||||
setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT))
|
||||
setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV))
|
||||
createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).also {
|
||||
createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).let {
|
||||
setOnClickPendingIntent(R.id.timetableWidgetDate, it)
|
||||
setOnClickPendingIntent(R.id.timetableWidgetDay, it)
|
||||
setOnClickPendingIntent(R.id.timetableWidgetName, it)
|
||||
}
|
||||
setOnClickPendingIntent(R.id.timetableWidgetAccount, PendingIntent.getActivity(context, -Int.MAX_VALUE + appWidgetId,
|
||||
Intent(context, TimetableWidgetConfigureActivity::class.java).apply {
|
||||
addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
|
||||
putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
|
||||
putExtra(EXTRA_FROM_PROVIDER, true)
|
||||
}, FLAG_UPDATE_CURRENT))
|
||||
setPendingIntentTemplate(R.id.timetableWidgetList,
|
||||
PendingIntent.getActivity(context, 1, MainActivity.getStartIntent(context).apply {
|
||||
putExtra(EXTRA_START_MENU_INDEX, 3)
|
||||
putExtra(EXTRA_START_MENU, MainView.MenuView.TIMETABLE)
|
||||
}, FLAG_UPDATE_CURRENT))
|
||||
}.also {
|
||||
sharedPref.putLong(createWidgetKey(appWidgetId), date.toEpochDay(), true)
|
||||
sharedPref.putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true)
|
||||
appWidgetManager.apply {
|
||||
notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList)
|
||||
updateAppWidget(appWidgetId, it)
|
||||
@ -120,4 +151,31 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId)
|
||||
}, FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
private fun getStudent(studentId: Long, appWidgetId: Int): Student? {
|
||||
return try {
|
||||
studentRepository.isStudentSaved()
|
||||
.filter { true }
|
||||
.flatMap { studentRepository.getSavedStudents(false).toMaybe() }
|
||||
.flatMap { students ->
|
||||
students.singleOrNull { student -> student.id == studentId }
|
||||
.let { student ->
|
||||
when {
|
||||
student != null -> Maybe.just(student)
|
||||
studentId != 0L -> {
|
||||
studentRepository.getCurrentStudent(false)
|
||||
.toMaybe()
|
||||
.doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) }
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.blockingGet()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "An error has occurred in timetable widget provider")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,11 @@ package io.github.wulkanowy.utils
|
||||
|
||||
import android.os.Bundle
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class FirebaseAnalyticsHelper(private val analytics: FirebaseAnalytics) {
|
||||
class FirebaseAnalyticsHelper @Inject constructor(private val analytics: FirebaseAnalytics) {
|
||||
|
||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
||||
Bundle().apply {
|
||||
|
@ -1,7 +1,16 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class DebugLogTree : Timber.DebugTree() {
|
||||
|
||||
@ -20,3 +29,88 @@ class CrashlyticsTree : Timber.Tree() {
|
||||
else Crashlytics.logException(t)
|
||||
}
|
||||
}
|
||||
|
||||
class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks {
|
||||
|
||||
override fun onActivityPaused(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} PAUSED") }
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} RESUMED") }
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} STARTED") }
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} DESTROYED") }
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} SAVED INSTANCE STATE") }
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} STOPPED") }
|
||||
}
|
||||
|
||||
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
|
||||
activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}") }
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLifecycleCallbacks() {
|
||||
|
||||
override fun onFragmentViewCreated(fm: FragmentManager, f: Fragment, v: View, savedInstanceState: Bundle?) {
|
||||
Timber.d("${f::class.java.simpleName} VIEW CREATED ${checkSavedState(savedInstanceState)}")
|
||||
}
|
||||
|
||||
override fun onFragmentStopped(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} STOPPED")
|
||||
}
|
||||
|
||||
override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
|
||||
Timber.d("${f::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}")
|
||||
}
|
||||
|
||||
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} RESUMED")
|
||||
}
|
||||
|
||||
override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) {
|
||||
Timber.d("${f::class.java.simpleName} ATTACHED")
|
||||
}
|
||||
|
||||
override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} DESTROYED")
|
||||
}
|
||||
|
||||
override fun onFragmentSaveInstanceState(fm: FragmentManager, f: Fragment, outState: Bundle) {
|
||||
Timber.d("${f::class.java.simpleName} SAVED INSTANCE STATE")
|
||||
}
|
||||
|
||||
override fun onFragmentStarted(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} STARTED")
|
||||
}
|
||||
|
||||
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} VIEW DESTROYED")
|
||||
}
|
||||
|
||||
override fun onFragmentActivityCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
|
||||
Timber.d("${f::class.java.simpleName} ACTIVITY CREATED ${checkSavedState(savedInstanceState)}")
|
||||
}
|
||||
|
||||
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} PAUSED")
|
||||
}
|
||||
|
||||
override fun onFragmentDetached(fm: FragmentManager, f: Fragment) {
|
||||
Timber.d("${f::class.java.simpleName} DETACHED")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSavedState(savedInstanceState: Bundle?) = if (savedInstanceState == null) "(STATE IS NULL)" else ""
|
||||
|
@ -99,6 +99,9 @@ inline val LocalDate.previousOrSameSchoolDay: LocalDate
|
||||
inline val LocalDate.weekDayName: String
|
||||
get() = this.format(ofPattern("EEEE", Locale.getDefault()))
|
||||
|
||||
inline val LocalDate.shortcutWeekDayName: String
|
||||
get() = this.format(ofPattern("EEE", Locale.getDefault()))
|
||||
|
||||
inline val LocalDate.monday: LocalDate
|
||||
get() = this.with(MONDAY)
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
Wersja 0.7.5
|
||||
Wersja 0.8.0
|
||||
|
||||
Uwaga! Jeżeli w aplikacji przestały wyświetlać się oceny, prosimy o wylogowanie i zalogowanie się ponownie!
|
||||
Uwaga! Po tej aktualizacji wymagane jest ponowne przypięcie widżetu planu lekcji!
|
||||
|
||||
Naprawiliśmy:
|
||||
- problem z brakiem aktywnego semestru
|
||||
- logowanie w niestandardowych dziennikach na vulcan.net.pl
|
||||
- oznaczanie lekcji w planie jako odwołanej, jeśli brak opisu tej zmiany
|
||||
- ładowanie wiadomości, jeśli byli zalogowani jednocześnie uczeń i rodzic
|
||||
Dodaliśmy:
|
||||
- możliwość przesyłania dalej i usuwania wiadomości
|
||||
- możliwość zmiany ucznia w widżecie
|
||||
- opcję liczenia średniej ocen z całego roku
|
||||
- tryb AMOLED
|
||||
- logowanie wielu uczniów jednocześnie
|
||||
- widżet szczęśliwego numerka
|
||||
|
||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.5
|
||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.8.0
|
||||
|
BIN
app/src/main/res/drawable-hdpi/ic_widget_account.png
Normal file
After Width: | Height: | Size: 461 B |
BIN
app/src/main/res/drawable-mdpi/ic_widget_account.png
Normal file
After Width: | Height: | Size: 319 B |
BIN
app/src/main/res/drawable-xhdpi/ic_widget_account.png
Normal file
After Width: | Height: | Size: 596 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_widget_account.png
Normal file
After Width: | Height: | Size: 922 B |
6
app/src/main/res/drawable/background_rounded_corner.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#ffffffff" />
|
||||
<corners android:radius="5dp" />
|
||||
</shape>
|
@ -2,7 +2,7 @@
|
||||
<solid android:color="@null" />
|
||||
<stroke
|
||||
android:width="1dip"
|
||||
android:color="#61000000" />
|
||||
android:color="@color/spinner_stroke" />
|
||||
<corners android:radius="4dip" />
|
||||
<padding
|
||||
android:bottom="0dip"
|
||||
|
10
app/src/main/res/drawable/ic_message_delete_24dp.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_message_forward_24dp.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,8V4l8,8 -8,8v-4H4V8z" />
|
||||
</vector>
|
BIN
app/src/main/res/drawable/ic_widget_clover_24dp.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/drawable/img_luckynumber_widget_preview.png
Normal file
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 5.2 KiB |
31
app/src/main/res/layout/activity_widget_configure.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="280dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/widgetConfigureTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:text="@string/account_title"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:firstBaselineToTopHeight="40dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/widgetConfigureRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/widgetConfigureTitle"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:overScrollMode="never"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/item_account" />
|
||||
</RelativeLayout>
|
@ -30,7 +30,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginLeft="32dp"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginRight="32dp"
|
||||
android:gravity="center_horizontal"
|
||||
@ -39,7 +39,6 @@
|
||||
app:fontFamily="sans-serif-light"
|
||||
app:layout_constraintBottom_toTopOf="@+id/loginFormNameLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
@ -59,7 +58,6 @@
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/loginFormPassLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginFormHeader">
|
||||
|
||||
@ -87,7 +85,6 @@
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/loginFormHostLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginFormNameLayout"
|
||||
app:passwordToggleEnabled="true">
|
||||
@ -117,7 +114,6 @@
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@+id/loginFormSignIn"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginFormPassLayout">
|
||||
|
||||
@ -134,7 +130,7 @@
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginLeft="7dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:background="?android:colorBackground"
|
||||
android:background="?android:windowBackground"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:text="@string/login_host_hint"
|
||||
@ -145,15 +141,6 @@
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/loginFormHostLayout" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginFormPrivacyPolicyLink"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/login_privacy_policy"
|
||||
app:layout_constraintStart_toStartOf="@+id/loginFormHostLayout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginFormSignIn"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
@ -163,7 +150,7 @@
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/login_sign_in"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:textColor="@android:color/white"
|
||||
@ -171,14 +158,12 @@
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginFormVersion"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
@ -186,11 +171,31 @@
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@id/loginFormSignIn"
|
||||
app:layout_constraintEnd_toStartOf="@+id/loginFormSignIn"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/loginFormSignIn"
|
||||
tools:text="Version 1.0.0" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginFormPrivacyLink"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:text="@string/login_privacy_policy"
|
||||
android:textAppearance="?android:textAppearance"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:visibility="invisible"
|
||||
app:backgroundTint="?android:windowBackground"
|
||||
app:fontFamily="sans-serif-medium"
|
||||
app:layout_constraintBottom_toBottomOf="@id/loginFormSignIn"
|
||||
app:layout_constraintEnd_toStartOf="@+id/loginFormSignIn"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="@id/loginFormHostLayout"
|
||||
app:layout_constraintTop_toTopOf="@+id/loginFormSignIn"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</FrameLayout>
|
||||
|
@ -1,4 +1,5 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -12,10 +13,60 @@
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/loginStudentSelectRecycler"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/loginStudentSelectContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:itemCount="5"
|
||||
tools:listitem="@layout/item_login_student_select" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginStudentSelectHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginLeft="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginRight="32dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/login_select_student"
|
||||
android:textSize="16sp"
|
||||
app:fontFamily="sans-serif-light"
|
||||
app:layout_constraintBottom_toTopOf="@id/loginStudentSelectRecycler"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/loginStudentSelectRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="144dp"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/loginStudentSelectSignIn"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_max="432dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/loginStudentSelectHeader"
|
||||
tools:itemCount="6"
|
||||
tools:listitem="@layout/item_login_student_select" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginStudentSelectSignIn"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:text="@string/login_sign_in"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:textColor="@android:color/white"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/loginStudentSelectRecycler" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
||||
|
@ -29,7 +29,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginLeft="32dp"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginRight="32dp"
|
||||
android:gravity="center_horizontal"
|
||||
@ -79,11 +79,10 @@
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/login_sign_in"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:textColor="@android:color/white"
|
||||
@ -91,8 +90,7 @@
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginSymbolNameLayout"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginSymbolNameLayout" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</FrameLayout>
|
||||
|
@ -10,6 +10,7 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/messagePreviewContentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
|
@ -33,7 +33,8 @@
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
tools:text="@tools:sample/lorem/random"
|
||||
android:textColor="?android:textColorSecondary"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/accountItemSchool"
|
||||
@ -47,6 +48,7 @@
|
||||
android:layout_toRightOf="@id/accountItemImage"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
</RelativeLayout>
|
||||
|