diff --git a/README.md b/README.md index 11b392d3..3609c49e 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ In addition to this, other useful features are also available: * analysis of collected data (for visual acuity test); * import and export of the journal to a file; * setting the correct image scaling during tests; +* light and dark themes with auto/manual selection; * support for Russian and English languages. ## Screenshots icon icon icon @@ -54,4 +55,4 @@ This project uses: * [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) (charts) * [ViewBindingPropertyDelegate](https://github.com/kirich1409/ViewBindingPropertyDelegate) (view binding) * [ObjectBox](https://github.com/objectbox/objectbox-java) (database) -* [JUnit 5](https://junit.org/junit5/) (testing) \ No newline at end of file +* [JUnit 5](https://github.com/junit-team/junit5) + [Mockito](https://github.com/mockito/mockito) (testing) \ No newline at end of file diff --git a/README.ru.md b/README.ru.md index 099e6aa2..70b0a134 100644 --- a/README.ru.md +++ b/README.ru.md @@ -28,6 +28,7 @@ * анализ собранных данных (для теста остроты зрения); * импорт и экспорт журнала в файл; * настройка правильного масштабирования изображения во время тестов; +* светлая и темная темы с авто/ручным выбором; * поддержка русского и английского языков. ## Скриншоты icon icon icon @@ -54,4 +55,4 @@ * [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) (графики) * [ViewBindingPropertyDelegate](https://github.com/kirich1409/ViewBindingPropertyDelegate) (view binding) * [ObjectBox](https://github.com/objectbox/objectbox-java) (база данных) -* [JUnit 5](https://junit.org/junit5/) (тестирование) \ No newline at end of file +* [JUnit 5](https://github.com/junit-team/junit5) + [Mockito](https://github.com/mockito/mockito) (тестирование) \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 058d5f72..381bd2ec 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -110,6 +110,12 @@ android { abi.enableSplit = false language.enableSplit = false } + @Suppress("UnstableApiUsage") + testOptions { + unitTests.all { + it.useJUnitPlatform() + } + } } dependencies { @@ -125,7 +131,7 @@ dependencies { implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") implementation("androidx.preference:preference-ktx:1.2.1") implementation("androidx.viewpager2:viewpager2:1.1.0-beta02") @@ -136,6 +142,7 @@ dependencies { // Coroutines implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:" + rootProject.extra["coroutinesVersion"]) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:" + rootProject.extra["coroutinesVersion"]) + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:" + rootProject.extra["coroutinesVersion"]) // Material implementation("com.google.android.material:material:1.11.0") @@ -148,6 +155,8 @@ dependencies { // https://github.com/InsertKoinIO/koin implementation("io.insert-koin:koin-core:" + rootProject.extra["koinVersion"]) implementation("io.insert-koin:koin-android:" + rootProject.extra["koinVersion"]) + testImplementation("io.insert-koin:koin-test:" + rootProject.extra["koinVersion"]) + testImplementation("io.insert-koin:koin-test-junit5:" + rootProject.extra["koinVersion"]) // Moxy MVP // https://github.com/moxy-community/Moxy @@ -191,4 +200,16 @@ dependencies { // FlexboxLayoutManager // https://github.com/google/flexbox-layout implementation("com.google.android.flexbox:flexbox:3.0.0") + + // Testing + // https://github.com/junit-team/junit5/ + testImplementation("org.junit.jupiter:junit-jupiter:" + rootProject.extra["junitVersion"]) + + // Mocks for testing + // https://github.com/mockito/mockito + val mockitoVersion = "5.9.0" + testImplementation("org.mockito:mockito-core:$mockitoVersion") + testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion") + // https://github.com/mockito/mockito-kotlin + testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1") } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 970de771..11338254 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -66,7 +66,7 @@ android:resource="@drawable/ic_notification"/> + android:resource="@color/globalColorAccent"/> diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/Screens.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/Screens.kt index 80355f65..4098290e 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/Screens.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/Screens.kt @@ -45,9 +45,9 @@ import ru.rznnike.eyehealthmanager.app.ui.fragment.settings.testing.TestingSetti import ru.rznnike.eyehealthmanager.app.ui.fragment.splash.SplashFlowFragment import ru.rznnike.eyehealthmanager.app.ui.fragment.splash.SplashFragment import ru.rznnike.eyehealthmanager.domain.model.AcuityTestResult -import ru.rznnike.eyehealthmanager.domain.model.AnalysisParameters import ru.rznnike.eyehealthmanager.domain.model.AnalysisResult import ru.rznnike.eyehealthmanager.domain.model.enums.AstigmatismAnswerType +import ru.rznnike.eyehealthmanager.domain.model.enums.DaltonismAnomalyType import ru.rznnike.eyehealthmanager.domain.model.enums.DayPart import ru.rznnike.eyehealthmanager.domain.model.enums.NearFarAnswerType @@ -119,7 +119,7 @@ object Screens { fun daltonismResult( errorsCount: Int, - resultType: String + resultType: DaltonismAnomalyType ) = DaltonismResultFragment::class.getFragmentScreen( DaltonismResultFragment.ERRORS_COUNT to errorsCount, DaltonismResultFragment.RESULT_TYPE to resultType diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/AppModule.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/AppModule.kt index e048c598..69b1b2dd 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/AppModule.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/AppModule.kt @@ -15,6 +15,7 @@ import ru.rznnike.eyehealthmanager.app.observer.AppLifeCycleObserver import ru.rznnike.eyehealthmanager.device.notification.Notificator import ru.rznnike.eyehealthmanager.domain.global.CoroutineProvider import ru.rznnike.eyehealthmanager.domain.global.DispatcherProvider +import java.time.Clock val appModule = module { factory { androidContext().resources } @@ -22,10 +23,11 @@ val appModule = module { factory { AppLifeCycleObserver() } single { Notifier(get()) } single { ErrorHandler(get(), get()) } - single { EventDispatcher() } + single { EventDispatcher(get()) } single { ExternalIntentDispatcher(get()) } single { Notificator(androidContext()) } single { CrashlyticsProviderImpl() } + single { Clock.systemUTC() } single { object : CoroutineProvider { diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/DatabaseModule.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/DatabaseModule.kt index a6ad8343..03c0ca79 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/DatabaseModule.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/DatabaseModule.kt @@ -2,11 +2,25 @@ package ru.rznnike.eyehealthmanager.app.di import org.koin.android.ext.koin.androidContext import org.koin.dsl.module +import ru.rznnike.eyehealthmanager.data.storage.dao.AcuityTestDAO +import ru.rznnike.eyehealthmanager.data.storage.dao.AstigmatismTestDAO +import ru.rznnike.eyehealthmanager.data.storage.dao.ColorPerceptionTestDAO +import ru.rznnike.eyehealthmanager.data.storage.dao.ContrastTestDAO +import ru.rznnike.eyehealthmanager.data.storage.dao.DaltonismTestDAO +import ru.rznnike.eyehealthmanager.data.storage.dao.NearFarTestDAO +import ru.rznnike.eyehealthmanager.data.storage.dao.TestDAO import ru.rznnike.eyehealthmanager.data.storage.entity.MyObjectBox import ru.rznnike.eyehealthmanager.domain.storage.repository.TestRepository import ru.rznnike.eyehealthmanager.data.storage.repository.TestRepositoryImpl val databaseModule = module { single { MyObjectBox.builder().androidContext(androidContext()).build() } - single { TestRepositoryImpl(get()) } + single { TestDAO(get()) } + single { AcuityTestDAO(get()) } + single { AstigmatismTestDAO(get()) } + single { ColorPerceptionTestDAO(get()) } + single { ContrastTestDAO(get()) } + single { DaltonismTestDAO(get()) } + single { NearFarTestDAO(get()) } + single { TestRepositoryImpl(get(), get(), get(), get(), get(), get(), get(), get()) } } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/GatewayModule.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/GatewayModule.kt index 338b5ef2..13b7162a 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/GatewayModule.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/GatewayModule.kt @@ -15,7 +15,7 @@ import ru.rznnike.eyehealthmanager.domain.gateway.UserGateway val gatewayModule = module { single { UserGatewayImpl(get()) } single { NotificationGatewayImpl() } - single { TestGatewayImpl(get(), get()) } - single { AnalysisGatewayImpl(get()) } - single { DevGatewayImpl(get()) } + single { TestGatewayImpl(get(), get(), get()) } + single { AnalysisGatewayImpl(get(), get()) } + single { DevGatewayImpl(get(), get()) } } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/InteractorModule.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/InteractorModule.kt index 79e26754..8394e3cc 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/InteractorModule.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/di/InteractorModule.kt @@ -8,6 +8,7 @@ import ru.rznnike.eyehealthmanager.domain.interactor.notification.ObserveCancelN import ru.rznnike.eyehealthmanager.domain.interactor.notification.ObserveShowNotificationUseCase import ru.rznnike.eyehealthmanager.domain.interactor.test.* import ru.rznnike.eyehealthmanager.domain.interactor.user.GetAcuityTestingSettingsUseCase +import ru.rznnike.eyehealthmanager.domain.interactor.user.GetAppThemeUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.GetApplyDynamicCorrectionsUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.SetUserLanguageUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.GetDisplayedChangelogVersionUseCase @@ -15,6 +16,7 @@ import ru.rznnike.eyehealthmanager.domain.interactor.user.GetTestingSettingsUseC import ru.rznnike.eyehealthmanager.domain.interactor.user.GetUserLanguageUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.GetWelcomeDialogShowedUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.SetAcuityTestingSettingsUseCase +import ru.rznnike.eyehealthmanager.domain.interactor.user.SetAppThemeUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.SetApplyDynamicCorrectionsUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.SetDisplayedChangelogVersionUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.SetTestingSettingsUseCase @@ -33,6 +35,8 @@ val interactorModule = module { single { SetAcuityTestingSettingsUseCase(get(), get()) } single { GetApplyDynamicCorrectionsUseCase(get(), get()) } single { SetApplyDynamicCorrectionsUseCase(get(), get()) } + single { GetAppThemeUseCase(get(), get()) } + single { SetAppThemeUseCase(get(), get()) } single { GetTestResultsUseCase(get(), get()) } single { AddTestResultUseCase(get(), get()) } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/dialog/DialogUtils.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/dialog/DialogUtils.kt index df1a2211..a0d3df3a 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/dialog/DialogUtils.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/dialog/DialogUtils.kt @@ -4,7 +4,11 @@ import android.app.DatePickerDialog import android.app.TimePickerDialog import android.content.Context import android.os.Build -import android.view.* +import android.view.Gravity +import android.view.View +import android.view.Window +import android.view.WindowInsetsController +import android.view.WindowManager import android.widget.EditText import android.widget.TextView import androidx.appcompat.app.AlertDialog @@ -29,8 +33,12 @@ import ru.rznnike.eyehealthmanager.app.dialog.bottom.BottomDialogParameters import ru.rznnike.eyehealthmanager.app.ui.view.EmptyDividerDecoration import ru.rznnike.eyehealthmanager.app.utils.extensions.addSystemWindowInsetToPadding import ru.rznnike.eyehealthmanager.app.utils.extensions.deviceSize +import ru.rznnike.eyehealthmanager.app.utils.extensions.isNightModeEnabled import ru.rznnike.eyehealthmanager.app.utils.extensions.toHtmlSpanned -import java.util.* +import ru.rznnike.eyehealthmanager.domain.utils.currentTimeMillis +import ru.rznnike.eyehealthmanager.domain.utils.millis +import ru.rznnike.eyehealthmanager.domain.utils.toDateTime +import ru.rznnike.eyehealthmanager.domain.utils.toLocalDate fun Context.showAlertDialog( parameters: AlertDialogParameters, @@ -222,43 +230,37 @@ fun Context.showDatePicker( onCancel: (() -> Unit)? = null, onSuccess: (date: Long) -> Unit ) { - val currentCalendar = Calendar.getInstance().apply { - timeInMillis = preselectedDate ?: System.currentTimeMillis() - } + val currentDate = (preselectedDate ?: currentTimeMillis()).toLocalDate() DatePickerDialog( this, - android.R.style.ThemeOverlay_Material_Dialog, + R.style.AppTheme_Dialog_DatePicker, { _, year, month, dayOfMonth -> if (enableTimePicker) { showTimePicker( preselectedTime = preselectedDate, onCancel = onCancel, - onSuccess = { time -> - val selectedTime = Calendar.getInstance().apply { - timeInMillis = time - set(Calendar.YEAR, year) - set(Calendar.MONTH, month) - set(Calendar.DAY_OF_MONTH, dayOfMonth) - }.timeInMillis + onSuccess = { timestamp -> + val selectedTime = timestamp.toDateTime() + .withYear(year) + .withMonth(month + 1) + .withDayOfMonth(dayOfMonth) + .millis() onSuccess(selectedTime) } ) } else { - val selectedTime = Calendar.getInstance().apply { - set(Calendar.YEAR, year) - set(Calendar.MONTH, month) - set(Calendar.DAY_OF_MONTH, dayOfMonth) - set(Calendar.HOUR_OF_DAY, 0) - set(Calendar.MINUTE, 0) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - }.timeInMillis + val selectedTime = currentTimeMillis().toDateTime().toLocalDate() + .withYear(year) + .withMonth(month + 1) + .withDayOfMonth(dayOfMonth) + .atStartOfDay() + .millis() onSuccess(selectedTime) } }, - currentCalendar.get(Calendar.YEAR), - currentCalendar.get(Calendar.MONTH), - currentCalendar.get(Calendar.DAY_OF_MONTH) + currentDate.year, + currentDate.monthValue - 1, + currentDate.dayOfMonth ).apply { maxDate?.let { datePicker.maxDate = it } minDate?.let { datePicker.minDate = it } @@ -270,25 +272,23 @@ fun Context.showDatePicker( fun Context.showTimePicker( preselectedTime: Long? = null, onCancel: (() -> Unit)? = null, - onSuccess: (date: Long) -> Unit + onSuccess: (timestamp: Long) -> Unit ) { - val currentCalendar = Calendar.getInstance().apply { - timeInMillis = preselectedTime ?: System.currentTimeMillis() - } + val currentDate = (preselectedTime ?: currentTimeMillis()).toDateTime() TimePickerDialog( this, - android.R.style.ThemeOverlay_Material_Dialog, + R.style.AppTheme_Dialog_DatePicker, { _, hourOfDay, minute -> - val selectedTime = Calendar.getInstance().apply { - set(Calendar.HOUR_OF_DAY, hourOfDay) - set(Calendar.MINUTE, minute) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - }.timeInMillis + val selectedTime = currentTimeMillis().toDateTime() + .withHour(hourOfDay) + .withMinute(minute) + .withSecond(0) + .withNano(0) + .millis() onSuccess(selectedTime) }, - currentCalendar.get(Calendar.HOUR_OF_DAY), - currentCalendar.get(Calendar.MINUTE), + currentDate.hour, + currentDate.minute, true ).apply { setOnCancelListener { onCancel?.invoke() } @@ -365,6 +365,7 @@ fun Fragment.showCustomBottomDialog( } private fun Window.setLightNavigationBar() = when { + context.isNightModeEnabled -> Unit // disable for dark theme Build.VERSION.SDK_INT >= 30 -> { insetsController?.setSystemBarsAppearance( WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS, diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/event/EventDispatcher.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/event/EventDispatcher.kt index 782c5c2f..bb7e0510 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/event/EventDispatcher.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/event/EventDispatcher.kt @@ -1,12 +1,13 @@ package ru.rznnike.eyehealthmanager.app.dispatcher.event -import android.os.Handler -import android.os.Looper +import kotlinx.coroutines.launch +import ru.rznnike.eyehealthmanager.domain.global.CoroutineProvider import java.util.* -import kotlin.collections.ArrayList import kotlin.reflect.KClass -class EventDispatcher { +class EventDispatcher( + private val coroutineProvider: CoroutineProvider +) { private val eventListeners = HashMap>() fun addEventListener(appEventClass: KClass, listener: EventListener): EventListener { @@ -44,10 +45,8 @@ class EventDispatcher { .filter { it.key == key && it.value.size > 0 } .forEach { it.value.forEach { listener -> - Handler(Looper.getMainLooper()).post { - listener.onEvent( - appEvent - ) + coroutineProvider.scopeMain.launch { + listener.onEvent(appEvent) } } } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/external/ExternalIntentDispatcher.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/external/ExternalIntentDispatcher.kt index 6b835fba..fd79aef3 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/external/ExternalIntentDispatcher.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/external/ExternalIntentDispatcher.kt @@ -1,6 +1,5 @@ package ru.rznnike.eyehealthmanager.app.dispatcher.external -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @@ -14,9 +13,7 @@ class ExternalIntentDispatcher( ExternalIntentData.App().apply { processed = true } ) - fun subscribe(): Flow { - return eventsFlow.asStateFlow() - } + fun subscribe() = eventsFlow.asStateFlow() fun send(data: ExternalIntentData) { coroutineProvider.scopeIo.launch { diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/notifier/Notifier.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/notifier/Notifier.kt index 49a495eb..2891cd1b 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/notifier/Notifier.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/dispatcher/notifier/Notifier.kt @@ -17,89 +17,77 @@ class Notifier( text: String, level: SystemMessage.Level = SystemMessage.Level.NORMAL, showOnTop: Boolean = false - ) { - val msg = SystemMessage( + ) = emitMessage( + SystemMessage( text = text, type = SystemMessage.Type.BAR, showOnTop = showOnTop, level = level ) - coroutineProvider.scopeIo.launch { - notifierFlow.emit(msg) - } - } + ) fun sendMessage( @StringRes textRes: Int, level: SystemMessage.Level = SystemMessage.Level.NORMAL, showOnTop: Boolean = false - ) { - val msg = SystemMessage( + ) = emitMessage( + SystemMessage( textRes = textRes, type = SystemMessage.Type.BAR, showOnTop = showOnTop, level = level ) - coroutineProvider.scopeIo.launch { - notifierFlow.emit(msg) - } - } + ) - fun sendAlert(text: String) { - val msg = SystemMessage( + fun sendAlert(text: String) = emitMessage( + SystemMessage( text = text, type = SystemMessage.Type.ALERT, showOnTop = false ) - coroutineProvider.scopeIo.launch { - notifierFlow.emit(msg) - } - } + ) - fun sendAlert(@StringRes textRes: Int) { - val msg = SystemMessage( + fun sendAlert(@StringRes textRes: Int) = emitMessage( + SystemMessage( textRes = textRes, type = SystemMessage.Type.ALERT, showOnTop = false ) - coroutineProvider.scopeIo.launch { - notifierFlow.emit(msg) - } - } + ) fun sendActionMessage( @StringRes textRes: Int, @StringRes actionTextRes: Int, showOnTop: Boolean = false, actionCallback: () -> Unit - ) { - val msg = SystemMessage( + ) = emitMessage( + SystemMessage( textRes = textRes, actionTextRes = actionTextRes, actionCallback = actionCallback, type = SystemMessage.Type.BAR, showOnTop = showOnTop ) - coroutineProvider.scopeIo.launch { - notifierFlow.emit(msg) - } - } + ) fun sendActionMessage( text: String, actionText: String, showOnTop: Boolean = false, actionCallback: () -> Unit - ) { - val msg = SystemMessage( + ) = emitMessage( + SystemMessage( text = text, actionText = actionText, actionCallback = actionCallback, type = SystemMessage.Type.BAR, showOnTop = showOnTop ) + ) + + private fun emitMessage(message: SystemMessage) { coroutineProvider.scopeIo.launch { - notifierFlow.emit(msg) + notifierFlow.emit(message) } } } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/global/ui/activity/BaseActivity.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/global/ui/activity/BaseActivity.kt index 5482a0ca..8f356983 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/global/ui/activity/BaseActivity.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/global/ui/activity/BaseActivity.kt @@ -11,7 +11,9 @@ import org.koin.android.ext.android.inject import ru.rznnike.eyehealthmanager.R import ru.rznnike.eyehealthmanager.app.global.presentation.ErrorHandler import ru.rznnike.eyehealthmanager.app.global.ui.fragment.BaseFragment +import ru.rznnike.eyehealthmanager.app.utils.extensions.applyTheme import ru.rznnike.eyehealthmanager.data.preference.PreferencesWrapper +import ru.rznnike.eyehealthmanager.domain.model.enums.AppTheme import ru.rznnike.eyehealthmanager.domain.model.enums.Language abstract class BaseActivity(@LayoutRes layoutRes: Int) : MvpAppCompatActivity(layoutRes) { @@ -23,6 +25,7 @@ abstract class BaseActivity(@LayoutRes layoutRes: Int) : MvpAppCompatActivity(la override fun onCreate(savedInstanceState: Bundle?) { setLanguage() + applyTheme(AppTheme[preferences.appTheme.get()]) onBackPressedDispatcher.addCallback(this) { currentFragment?.onBackPressed() } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/global/ui/fragment/BaseFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/global/ui/fragment/BaseFragment.kt index a054827a..447aa087 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/global/ui/fragment/BaseFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/global/ui/fragment/BaseFragment.kt @@ -10,13 +10,16 @@ import moxy.MvpAppCompatFragment import org.koin.androidx.viewmodel.ext.android.getViewModel import ru.rznnike.eyehealthmanager.app.navigation.FlowNavigationViewModel import ru.rznnike.eyehealthmanager.app.utils.extensions.hideKeyboard +import ru.rznnike.eyehealthmanager.app.utils.extensions.isNightModeEnabled import java.util.* import kotlin.concurrent.schedule abstract class BaseFragment(@LayoutRes layoutRes: Int) : MvpAppCompatFragment(layoutRes) { open var isLightStatusBar: Boolean = true + get() = !(context?.isNightModeEnabled ?: false) protected set open var isLightNavigationBar: Boolean = true + get() = !(context?.isNightModeEnabled ?: false) protected set open var progressDelayMs: Long = DEFAULT_PROGRESS_DELAY_MS protected set diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/navigation/FragmentScreenWithArguments.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/navigation/FragmentScreenWithArguments.kt new file mode 100644 index 00000000..473b0254 --- /dev/null +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/navigation/FragmentScreenWithArguments.kt @@ -0,0 +1,15 @@ +package ru.rznnike.eyehealthmanager.app.navigation + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentFactory +import com.github.terrakok.cicerone.androidx.Creator +import com.github.terrakok.cicerone.androidx.FragmentScreen + +class FragmentScreenWithArguments( + val arguments: Map, + val fragmentCreator: Creator, + override val screenKey: String, + override val clearContainer: Boolean = true +) : FragmentScreen { + override fun createFragment(factory: FragmentFactory) = fragmentCreator.create(factory) +} diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/navigation/NavigationUtils.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/navigation/NavigationUtils.kt index e2ce2d76..3ddf3cd4 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/navigation/NavigationUtils.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/navigation/NavigationUtils.kt @@ -2,9 +2,12 @@ package ru.rznnike.eyehealthmanager.app.navigation import androidx.core.os.bundleOf import androidx.fragment.app.Fragment -import com.github.terrakok.cicerone.* +import com.github.terrakok.cicerone.BackTo +import com.github.terrakok.cicerone.Command +import com.github.terrakok.cicerone.Forward +import com.github.terrakok.cicerone.Navigator +import com.github.terrakok.cicerone.Replace import com.github.terrakok.cicerone.Screen -import com.github.terrakok.cicerone.androidx.FragmentScreen import kotlin.reflect.KClass import kotlin.reflect.full.createInstance @@ -24,13 +27,14 @@ fun Navigator.setLaunchScreenChain(vararg screens: Screen) { } fun KClass.getFragmentScreen( - vararg args: Pair, + vararg arguments: Pair, clearContainer: Boolean = true -) = FragmentScreen( - key = java.name, +) = FragmentScreenWithArguments( + arguments = arguments.toMap(), + screenKey = java.name, fragmentCreator = { - this.createInstance().apply { - arguments = bundleOf(*args) + createInstance().apply { + this.arguments = bundleOf(*arguments) } }, clearContainer = clearContainer diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/observer/AppLifecycleObserver.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/observer/AppLifeCycleObserver.kt similarity index 100% rename from app/src/main/java/ru/rznnike/eyehealthmanager/app/observer/AppLifecycleObserver.kt rename to app/src/main/java/ru/rznnike/eyehealthmanager/app/observer/AppLifeCycleObserver.kt diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/pagination/Paginator.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/pagination/Paginator.kt index a34ea9f9..a2043919 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/pagination/Paginator.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/pagination/Paginator.kt @@ -34,7 +34,7 @@ class Paginator( fun refresh(forceRefresh: Boolean = false) { if (forceRefresh || (state != State.LOADING)) { load( - limit = if (data.isEmpty()) limit else data.size.coerceAtLeast(DEFAULT_PAGE_LIMIT), + limit = if (data.isEmpty()) limit else data.size.coerceAtLeast(limit), isRefresh = true ) } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/doctor/AcuityDoctorResultPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/doctor/AcuityDoctorResultPresenter.kt index ea83899a..83f14a81 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/doctor/AcuityDoctorResultPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/doctor/AcuityDoctorResultPresenter.kt @@ -16,9 +16,11 @@ import ru.rznnike.eyehealthmanager.domain.model.enums.AcuityTestSymbolsType import ru.rznnike.eyehealthmanager.domain.model.enums.DayPart import ru.rznnike.eyehealthmanager.domain.model.enums.TestEyesType import ru.rznnike.eyehealthmanager.domain.utils.toFloatOrNullSmart +import java.time.Clock @InjectViewState class AcuityDoctorResultPresenter : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -61,7 +63,7 @@ class AcuityDoctorResultPresenter : BasePresenter() { else -> { viewState.setProgress(true) val testResult = AcuityTestResult( - timestamp = date ?: System.currentTimeMillis(), + timestamp = date ?: clock.millis(), symbolsType = AcuityTestSymbolsType.LETTERS_RU, testEyesType = when { leftEyeFloat == null -> TestEyesType.RIGHT diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/info/AcuityInfoPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/info/AcuityInfoPresenter.kt index a73bf854..a793c73d 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/info/AcuityInfoPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/info/AcuityInfoPresenter.kt @@ -17,9 +17,11 @@ import ru.rznnike.eyehealthmanager.domain.model.enums.AcuityTestSymbolsType import ru.rznnike.eyehealthmanager.domain.model.enums.DayPart import ru.rznnike.eyehealthmanager.domain.model.enums.TestEyesType import ru.rznnike.eyehealthmanager.domain.utils.getDayTime +import java.time.Clock @InjectViewState class AcuityInfoPresenter : BasePresenter(), EventDispatcher.EventListener { + private val clock: Clock by inject() private val eventDispatcher: EventDispatcher by inject() private val getTestingSettingsUseCase: GetTestingSettingsUseCase by inject() private val getAcuityTestingSettingsUseCase: GetAcuityTestingSettingsUseCase by inject() @@ -93,7 +95,7 @@ class AcuityInfoPresenter : BasePresenter(), EventDispatcher.Eve } private fun autoSelectDayPart() = - when (val currentDayTime = System.currentTimeMillis().getDayTime()) { + when (val currentDayTime = clock.millis().getDayTime()) { generalSettings.timeToDayBeginning -> DayPart.BEGINNING generalSettings.timeToDayMiddle -> DayPart.MIDDLE generalSettings.timeToDayEnd -> DayPart.END diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/result/AcuityResultPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/result/AcuityResultPresenter.kt index 0242cf7d..537c5eed 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/result/AcuityResultPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/result/AcuityResultPresenter.kt @@ -19,13 +19,15 @@ import ru.rznnike.eyehealthmanager.domain.model.AnalysisResult import ru.rznnike.eyehealthmanager.domain.model.enums.AnalysisType import ru.rznnike.eyehealthmanager.domain.model.exception.NotEnoughDataException import ru.rznnike.eyehealthmanager.domain.utils.atEndOfDay -import ru.rznnike.eyehealthmanager.domain.utils.getTodayCalendar -import java.util.Calendar +import ru.rznnike.eyehealthmanager.domain.utils.millis +import ru.rznnike.eyehealthmanager.domain.utils.toLocalDate +import java.time.Clock @InjectViewState class AcuityResultPresenter( private val testResult: AcuityTestResult ) : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -40,11 +42,10 @@ class AcuityResultPresenter( presenterScope.launch { viewState.setProgress(true) applyDynamicCorrections = getApplyDynamicCorrectionsUseCase().data ?: false + val dateNow = clock.millis().toLocalDate() val parameters = AnalysisParameters( - dateFrom = getTodayCalendar().apply { - add(Calendar.MONTH, -1) - }.timeInMillis, - dateTo = Calendar.getInstance().atEndOfDay().timeInMillis, + dateFrom = dateNow.minusMonths(1).atStartOfDay().millis(), + dateTo = dateNow.atEndOfDay().millis(), applyDynamicCorrections = applyDynamicCorrections, analysisType = AnalysisType.ACUITY_ONLY ) @@ -57,8 +58,8 @@ class AcuityResultPresenter( if (error !is NotEnoughDataException) { notifier.sendMessage(it) } - populateData() } + populateData() } ) viewState.setProgress(false) diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/test/AcuityTestPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/test/AcuityTestPresenter.kt index 9490e8b0..c99ffbc5 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/test/AcuityTestPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/acuity/test/AcuityTestPresenter.kt @@ -15,6 +15,7 @@ import ru.rznnike.eyehealthmanager.domain.interactor.user.GetAcuityTestingSettin import ru.rznnike.eyehealthmanager.domain.interactor.user.GetTestingSettingsUseCase import ru.rznnike.eyehealthmanager.domain.model.* import ru.rznnike.eyehealthmanager.domain.model.enums.* +import java.time.Clock import kotlin.math.pow import kotlin.math.sqrt @@ -28,6 +29,7 @@ private const val MAX_ANSWERS = 3 class AcuityTestPresenter( private var dayPart: DayPart ) : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -145,7 +147,7 @@ class AcuityTestPresenter( } else { viewState.setProgress(true) val testResult = AcuityTestResult( - timestamp = System.currentTimeMillis(), + timestamp = clock.millis(), symbolsType = acuitySettings.symbolsType, testEyesType = acuitySettings.eyesType, dayPart = dayPart, diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/analysis/parameters/AnalysisParametersPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/analysis/parameters/AnalysisParametersPresenter.kt index dfdd5119..7235e480 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/analysis/parameters/AnalysisParametersPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/analysis/parameters/AnalysisParametersPresenter.kt @@ -16,15 +16,14 @@ import ru.rznnike.eyehealthmanager.domain.model.enums.AnalysisType import ru.rznnike.eyehealthmanager.domain.model.exception.NotEnoughDataException import ru.rznnike.eyehealthmanager.domain.utils.GlobalConstants import ru.rznnike.eyehealthmanager.domain.utils.atEndOfDay -import ru.rznnike.eyehealthmanager.domain.utils.atStartOfDay -import ru.rznnike.eyehealthmanager.domain.utils.getTodayCalendar -import ru.rznnike.eyehealthmanager.domain.utils.toCalendar +import ru.rznnike.eyehealthmanager.domain.utils.millis import ru.rznnike.eyehealthmanager.domain.utils.toLocalDate +import java.time.Clock import java.time.temporal.ChronoUnit -import java.util.* @InjectViewState class AnalysisParametersPresenter : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val getApplyDynamicCorrectionsUseCase: GetApplyDynamicCorrectionsUseCase by inject() @@ -39,11 +38,10 @@ class AnalysisParametersPresenter : BasePresenter() { private fun initData() { presenterScope.launch { + val dateNow = clock.millis().toLocalDate() parameters.apply { - dateFrom = getTodayCalendar().apply { - add(Calendar.DAY_OF_MONTH, -GlobalConstants.ANALYSIS_MAX_RANGE_DAYS) - }.timeInMillis - dateTo = Calendar.getInstance().atEndOfDay().timeInMillis + dateFrom = dateNow.minusDays(GlobalConstants.ANALYSIS_MAX_RANGE_DAYS - 1).atStartOfDay().millis() + dateTo = dateNow.atEndOfDay().millis() applyDynamicCorrections = getApplyDynamicCorrectionsUseCase().data ?: false } viewState.populateData(parameters) @@ -51,48 +49,40 @@ class AnalysisParametersPresenter : BasePresenter() { } fun onDateFromValueChanged(newValue: Long) { - parameters.dateFrom = newValue.toCalendar().atStartOfDay().timeInMillis + parameters.dateFrom = newValue.toLocalDate().atStartOfDay().millis() val deltaDays = ChronoUnit.DAYS.between(parameters.dateFrom.toLocalDate(), parameters.dateTo.toLocalDate()) when { - deltaDays < GlobalConstants.ANALYSIS_MIN_RANGE_DAYS -> { - parameters.dateTo = newValue.toCalendar() - .apply { - add(Calendar.DAY_OF_MONTH, GlobalConstants.ANALYSIS_MIN_RANGE_DAYS) - } + deltaDays < (GlobalConstants.ANALYSIS_MIN_RANGE_DAYS - 1) -> { + parameters.dateTo = newValue.toLocalDate() + .plusDays(GlobalConstants.ANALYSIS_MIN_RANGE_DAYS - 1) .atEndOfDay() - .timeInMillis + .millis() } - deltaDays > GlobalConstants.ANALYSIS_MAX_RANGE_DAYS -> { - parameters.dateTo = newValue.toCalendar() - .apply { - add(Calendar.DAY_OF_MONTH, GlobalConstants.ANALYSIS_MAX_RANGE_DAYS) - } + deltaDays > (GlobalConstants.ANALYSIS_MAX_RANGE_DAYS - 1) -> { + parameters.dateTo = newValue.toLocalDate() + .plusDays(GlobalConstants.ANALYSIS_MAX_RANGE_DAYS - 1) .atEndOfDay() - .timeInMillis + .millis() } } viewState.populateData(parameters) } fun onDateToValueChanged(newValue: Long) { - parameters.dateTo = newValue.toCalendar().atEndOfDay().timeInMillis + parameters.dateTo = newValue.toLocalDate().atEndOfDay().millis() val deltaDays = ChronoUnit.DAYS.between(parameters.dateFrom.toLocalDate(), parameters.dateTo.toLocalDate()) when { - deltaDays < GlobalConstants.ANALYSIS_MIN_RANGE_DAYS -> { - parameters.dateFrom = newValue.toCalendar() - .apply { - add(Calendar.DAY_OF_MONTH, -GlobalConstants.ANALYSIS_MIN_RANGE_DAYS) - } + deltaDays < (GlobalConstants.ANALYSIS_MIN_RANGE_DAYS - 1) -> { + parameters.dateFrom = newValue.toLocalDate() + .minusDays(GlobalConstants.ANALYSIS_MIN_RANGE_DAYS - 1) .atStartOfDay() - .timeInMillis + .millis() } - deltaDays > GlobalConstants.ANALYSIS_MAX_RANGE_DAYS -> { - parameters.dateFrom = newValue.toCalendar() - .apply { - add(Calendar.DAY_OF_MONTH, -GlobalConstants.ANALYSIS_MAX_RANGE_DAYS) - } + deltaDays > (GlobalConstants.ANALYSIS_MAX_RANGE_DAYS - 1) -> { + parameters.dateFrom = newValue.toLocalDate() + .minusDays(GlobalConstants.ANALYSIS_MAX_RANGE_DAYS - 1) .atStartOfDay() - .timeInMillis + .millis() } } viewState.populateData(parameters) diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/analysis/result/AnalysisResultView.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/analysis/result/AnalysisResultView.kt index d35c710d..3bc949c6 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/analysis/result/AnalysisResultView.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/analysis/result/AnalysisResultView.kt @@ -1,7 +1,6 @@ package ru.rznnike.eyehealthmanager.app.presentation.analysis.result import moxy.viewstate.strategy.alias.AddToEndSingle -import moxy.viewstate.strategy.alias.OneExecution import ru.rznnike.eyehealthmanager.app.global.presentation.NavigationMvpView import ru.rznnike.eyehealthmanager.domain.model.AnalysisResult diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/astigmatism/answer/AstigmatismAnswerPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/astigmatism/answer/AstigmatismAnswerPresenter.kt index 6dc580ff..4d184e55 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/astigmatism/answer/AstigmatismAnswerPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/astigmatism/answer/AstigmatismAnswerPresenter.kt @@ -13,9 +13,11 @@ import ru.rznnike.eyehealthmanager.app.global.presentation.ErrorHandler import ru.rznnike.eyehealthmanager.domain.interactor.test.AddTestResultUseCase import ru.rznnike.eyehealthmanager.domain.model.AstigmatismTestResult import ru.rznnike.eyehealthmanager.domain.model.enums.AstigmatismAnswerType +import java.time.Clock @InjectViewState class AstigmatismAnswerPresenter : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -28,7 +30,7 @@ class AstigmatismAnswerPresenter : BasePresenter() { val typeRightEye = if (answerRightEye == 0) AstigmatismAnswerType.OK else AstigmatismAnswerType.ANOMALY val testResult = AstigmatismTestResult( - timestamp = System.currentTimeMillis(), + timestamp = clock.millis(), resultLeftEye = typeLeftEye, resultRightEye = typeRightEye ) @@ -42,6 +44,7 @@ class AstigmatismAnswerPresenter : BasePresenter() { } ) eventDispatcher.sendEvent(AppEvent.JournalChanged) + viewState.setProgress(false) } } } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/colorperception/test/ColorPerceptionTestPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/colorperception/test/ColorPerceptionTestPresenter.kt index 118969a4..5fade5c3 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/colorperception/test/ColorPerceptionTestPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/colorperception/test/ColorPerceptionTestPresenter.kt @@ -13,9 +13,11 @@ import ru.rznnike.eyehealthmanager.app.global.presentation.ErrorHandler import ru.rznnike.eyehealthmanager.domain.interactor.test.AddTestResultUseCase import ru.rznnike.eyehealthmanager.domain.model.ColorPerceptionTestData import ru.rznnike.eyehealthmanager.domain.model.ColorPerceptionTestResult +import java.time.Clock @InjectViewState class ColorPerceptionTestPresenter : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -57,7 +59,7 @@ class ColorPerceptionTestPresenter : BasePresenter() { presenterScope.launch { viewState.setProgress(true) val testResult = ColorPerceptionTestResult( - timestamp = System.currentTimeMillis(), + timestamp = clock.millis(), recognizedColorsCount = recognizedColorsCount, allColorsCount = ColorPerceptionTestData.colors.size ) diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/contrast/test/ContrastTestPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/contrast/test/ContrastTestPresenter.kt index 0e833a57..6e913f12 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/contrast/test/ContrastTestPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/contrast/test/ContrastTestPresenter.kt @@ -13,6 +13,7 @@ import ru.rznnike.eyehealthmanager.app.global.presentation.ErrorHandler import ru.rznnike.eyehealthmanager.domain.interactor.test.AddTestResultUseCase import ru.rznnike.eyehealthmanager.domain.model.ContrastTestResult import ru.rznnike.eyehealthmanager.domain.model.enums.Direction +import java.time.Clock import kotlin.random.Random private const val START_VALUE = 100 @@ -25,6 +26,7 @@ private const val MAX_ANSWERS = 3 @InjectViewState class ContrastTestPresenter : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -38,9 +40,9 @@ class ContrastTestPresenter : BasePresenter() { private var maxBaseSteps = 0 private var currentBaseStep = 0 - init { + override fun onFirstViewAttach() { initData() - goToNextStep() + nextStep() } private fun initData() { @@ -52,15 +54,15 @@ class ContrastTestPresenter : BasePresenter() { } } - fun onAnswer(direction: Direction) { + fun answer(direction: Direction) { answersCount++ if (direction == currentDirection) { correctAnswersCount++ } - goToNextStep() + nextStep() } - private fun goToNextStep() = when { + private fun nextStep() = when { correctAnswersCount >= MIN_CORRECT_ANSWERS -> { recognizedDelta = currentDelta if (recognizedDelta <= END_VALUE) { @@ -101,7 +103,7 @@ class ContrastTestPresenter : BasePresenter() { presenterScope.launch { viewState.setProgress(true) val testResult = ContrastTestResult( - timestamp = System.currentTimeMillis(), + timestamp = clock.millis(), recognizedContrast = recognizedDelta ) addTestResultUseCase(testResult).process( diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/daltonism/test/DaltonismTestPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/daltonism/test/DaltonismTestPresenter.kt index 0ad6d8a5..7c76ba7a 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/daltonism/test/DaltonismTestPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/daltonism/test/DaltonismTestPresenter.kt @@ -14,11 +14,13 @@ import ru.rznnike.eyehealthmanager.domain.interactor.test.AddTestResultUseCase import ru.rznnike.eyehealthmanager.domain.model.DaltonismTestData import ru.rznnike.eyehealthmanager.domain.model.DaltonismTestResult import ru.rznnike.eyehealthmanager.domain.model.enums.DaltonismAnomalyType +import java.time.Clock private const val NORMAL_BORDER = 2 @InjectViewState class DaltonismTestPresenter : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -29,11 +31,11 @@ class DaltonismTestPresenter : BasePresenter() { private val userAnswers: MutableList = mutableListOf() private val answerDeltas: MutableMap = mutableMapOf() - init { + override fun onFirstViewAttach() { nextStep() } - fun onAnswer(selection: Int) { + fun answer(selection: Int) { userAnswers.add(answersOrder[selection]) nextStep() } @@ -89,7 +91,7 @@ class DaltonismTestPresenter : BasePresenter() { } val testResult = DaltonismTestResult( - timestamp = System.currentTimeMillis(), + timestamp = clock.millis(), errorsCount = errorsCount, anomalyType = anomalyType ) @@ -98,7 +100,7 @@ class DaltonismTestPresenter : BasePresenter() { viewState.routerNewRootScreen( Screens.Screen.daltonismResult( errorsCount = errorsCount, - resultType = anomalyType.toString() + resultType = anomalyType ) ) }, { error -> diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/journal/backup/ExportJournalPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/journal/backup/ExportJournalPresenter.kt index e33113ea..e9ae0911 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/journal/backup/ExportJournalPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/journal/backup/ExportJournalPresenter.kt @@ -20,13 +20,13 @@ import ru.rznnike.eyehealthmanager.domain.model.TestResultFilter import ru.rznnike.eyehealthmanager.domain.model.enums.TestType import ru.rznnike.eyehealthmanager.domain.utils.GlobalConstants import ru.rznnike.eyehealthmanager.domain.utils.atEndOfDay -import ru.rznnike.eyehealthmanager.domain.utils.atStartOfDay -import ru.rznnike.eyehealthmanager.domain.utils.getTodayCalendar -import ru.rznnike.eyehealthmanager.domain.utils.toCalendar -import java.util.Calendar +import ru.rznnike.eyehealthmanager.domain.utils.millis +import ru.rznnike.eyehealthmanager.domain.utils.toLocalDate +import java.time.Clock @InjectViewState class ExportJournalPresenter : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -37,24 +37,18 @@ class ExportJournalPresenter : BasePresenter() { private var startExportAutomatically = false override fun onFirstViewAttach() { - initFilters() + clearFilters() } - private fun initFilters() { + fun clearFilters() { + val dateNow = clock.millis().toLocalDate() filter = TestResultFilter( - dateFrom = getTodayCalendar().apply { - add(Calendar.MONTH, -1) - }.timeInMillis, - dateTo = Calendar.getInstance().atEndOfDay().timeInMillis + dateFrom = dateNow.minusMonths(1).atStartOfDay().millis(), + dateTo = dateNow.atEndOfDay().millis() ) populateData() } - fun onClearFilters() { - initFilters() - populateData() - } - fun onFilterTestTypeClick(testType: TestType) { if (filter.selectedTestTypes.contains(testType)) { filter.selectedTestTypes.remove(testType) @@ -76,18 +70,18 @@ class ExportJournalPresenter : BasePresenter() { } fun onFilterDateFromSelected(timestamp: Long) { - filter.dateFrom = timestamp.toCalendar().atStartOfDay().timeInMillis + filter.dateFrom = timestamp.toLocalDate().atStartOfDay().millis() if (filter.dateTo <= filter.dateFrom) { - filter.dateTo = timestamp.toCalendar().atEndOfDay().timeInMillis + filter.dateTo = timestamp.toLocalDate().atEndOfDay().millis() } filter.filterByDate = true populateData() } fun onFilterDateToSelected(timestamp: Long) { - filter.dateTo = timestamp.toCalendar().atEndOfDay().timeInMillis + filter.dateTo = timestamp.toLocalDate().atEndOfDay().millis() if (filter.dateTo <= filter.dateFrom) { - filter.dateFrom = timestamp.toCalendar().atStartOfDay().timeInMillis + filter.dateFrom = timestamp.toLocalDate().atStartOfDay().millis() } filter.filterByDate = true populateData() diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/MainPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/MainPresenter.kt index 72a02943..f56d923f 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/MainPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/MainPresenter.kt @@ -79,7 +79,7 @@ class MainPresenter : BasePresenter(), EventDispatcher.EventListener { presenterScope.launch { val currentVersionCode = BuildConfig.VERSION_CODE val displayedVersionCode = getDisplayedChangelogVersionUseCase().data ?: currentVersionCode - if (displayedVersionCode != currentVersionCode) { + if (displayedVersionCode < currentVersionCode) { setDisplayedChangelogVersionUseCase(currentVersionCode) if (displayedVersionCode > 0) { viewState.showChangelogDialog() @@ -96,7 +96,6 @@ class MainPresenter : BasePresenter(), EventDispatcher.EventListener { notifier.sendAlert(R.string.duplicates_successfully_deleted) }, ::onError ) - eventDispatcher.sendEvent(AppEvent.JournalChanged) viewState.setProgress(false) } @@ -107,10 +106,9 @@ class MainPresenter : BasePresenter(), EventDispatcher.EventListener { viewState.setProgress(true) deleteAllTestResultsUseCase().process( { - notifier.sendMessage(R.string.clear_journal_success) + notifier.sendAlert(R.string.clear_journal_success) }, ::onError ) - eventDispatcher.sendEvent(AppEvent.JournalChanged) viewState.setProgress(false) } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/journal/JournalPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/journal/JournalPresenter.kt index 626d3815..f8279d96 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/journal/JournalPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/journal/JournalPresenter.kt @@ -14,13 +14,23 @@ import ru.rznnike.eyehealthmanager.app.global.presentation.ErrorHandler import ru.rznnike.eyehealthmanager.app.pagination.Paginator import ru.rznnike.eyehealthmanager.domain.interactor.test.DeleteTestResultUseCase import ru.rznnike.eyehealthmanager.domain.interactor.test.GetTestResultsUseCase -import ru.rznnike.eyehealthmanager.domain.model.* +import ru.rznnike.eyehealthmanager.domain.model.AcuityTestResult +import ru.rznnike.eyehealthmanager.domain.model.AstigmatismTestResult +import ru.rznnike.eyehealthmanager.domain.model.ColorPerceptionTestResult +import ru.rznnike.eyehealthmanager.domain.model.ContrastTestResult +import ru.rznnike.eyehealthmanager.domain.model.DaltonismTestResult +import ru.rznnike.eyehealthmanager.domain.model.NearFarTestResult +import ru.rznnike.eyehealthmanager.domain.model.TestResult +import ru.rznnike.eyehealthmanager.domain.model.TestResultFilter +import ru.rznnike.eyehealthmanager.domain.model.TestResultPagingParameters import ru.rznnike.eyehealthmanager.domain.utils.atEndOfDay -import ru.rznnike.eyehealthmanager.domain.utils.getTodayCalendar -import java.util.* +import ru.rznnike.eyehealthmanager.domain.utils.millis +import ru.rznnike.eyehealthmanager.domain.utils.toLocalDate +import java.time.Clock @InjectViewState class JournalPresenter : BasePresenter(), EventDispatcher.EventListener { + private val clock: Clock by inject() private val eventDispatcher: EventDispatcher by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() @@ -153,11 +163,10 @@ class JournalPresenter : BasePresenter(), EventDispatcher.EventList } private fun setDefaultFilter() { + val dateNow = clock.millis().toLocalDate() filter = TestResultFilter( - dateFrom = getTodayCalendar().apply { - add(Calendar.MONTH, -1) - }.timeInMillis, - dateTo = Calendar.getInstance().atEndOfDay().timeInMillis + dateFrom = dateNow.minusMonths(1).atStartOfDay().millis(), + dateTo = dateNow.atEndOfDay().millis() ) } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/settings/SettingsPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/settings/SettingsPresenter.kt index 2812203d..0ab9a73f 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/settings/SettingsPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/settings/SettingsPresenter.kt @@ -11,9 +11,13 @@ import ru.rznnike.eyehealthmanager.app.dispatcher.event.EventDispatcher import ru.rznnike.eyehealthmanager.app.dispatcher.notifier.Notifier import ru.rznnike.eyehealthmanager.app.global.presentation.BasePresenter import ru.rznnike.eyehealthmanager.app.global.presentation.ErrorHandler +import ru.rznnike.eyehealthmanager.app.utils.extensions.applyTheme import ru.rznnike.eyehealthmanager.domain.interactor.dev.GenerateDataUseCase +import ru.rznnike.eyehealthmanager.domain.interactor.user.GetAppThemeUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.GetUserLanguageUseCase +import ru.rznnike.eyehealthmanager.domain.interactor.user.SetAppThemeUseCase import ru.rznnike.eyehealthmanager.domain.interactor.user.SetUserLanguageUseCase +import ru.rznnike.eyehealthmanager.domain.model.enums.AppTheme import ru.rznnike.eyehealthmanager.domain.model.enums.DataGenerationType import ru.rznnike.eyehealthmanager.domain.model.enums.Language @@ -24,18 +28,39 @@ class SettingsPresenter : BasePresenter() { private val eventDispatcher: EventDispatcher by inject() private val getUserLanguageUseCase: GetUserLanguageUseCase by inject() private val setUserLanguageUseCase: SetUserLanguageUseCase by inject() + private val getAppThemeUseCase: GetAppThemeUseCase by inject() + private val setAppThemeUseCase: SetAppThemeUseCase by inject() private val generateDataUseCase: GenerateDataUseCase by inject() - fun onResume() { + private var language = Language.EN + private var theme = AppTheme.SYSTEM + + override fun onFirstViewAttach() { + initData() + } + + private fun initData() { presenterScope.launch { getUserLanguageUseCase().process( - { - viewState.populateData(it) - } + { result -> + language = result + }, ::onError + ) + getAppThemeUseCase().process( + { result -> + theme = result + }, ::onError ) + populateData() } } + private fun populateData() = + viewState.populateData( + language = language, + theme = theme + ) + fun changeLanguage(language: Language) { presenterScope.launch { viewState.setProgress(true) @@ -48,6 +73,18 @@ class SettingsPresenter : BasePresenter() { } } + fun changeTheme(newTheme: AppTheme) { + presenterScope.launch { + setAppThemeUseCase(newTheme).process( + { + theme = newTheme + populateData() + }, ::onError + ) + applyTheme(newTheme) + } + } + fun openTestingSettings() = viewState.routerNavigateTo(Screens.Screen.testingSettings()) fun openAnalysis() = viewState.routerStartFlow(Screens.Flow.analysis()) diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/settings/SettingsView.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/settings/SettingsView.kt index 4d3dc4d6..0996cd2e 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/settings/SettingsView.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/main/settings/SettingsView.kt @@ -3,6 +3,7 @@ package ru.rznnike.eyehealthmanager.app.presentation.main.settings import moxy.viewstate.strategy.alias.AddToEndSingle import moxy.viewstate.strategy.alias.OneExecution import ru.rznnike.eyehealthmanager.app.global.presentation.NavigationMvpView +import ru.rznnike.eyehealthmanager.domain.model.enums.AppTheme import ru.rznnike.eyehealthmanager.domain.model.enums.Language interface SettingsView : NavigationMvpView { @@ -10,7 +11,7 @@ interface SettingsView : NavigationMvpView { fun setProgress(show: Boolean, immediately: Boolean = true) @AddToEndSingle - fun populateData(currentLanguage: Language) + fun populateData(language: Language, theme: AppTheme) @OneExecution fun updateUiLanguage() diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/answer/NearFarAnswerPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/answer/NearFarAnswerPresenter.kt index a70e3b84..18ec5e09 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/answer/NearFarAnswerPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/answer/NearFarAnswerPresenter.kt @@ -13,9 +13,11 @@ import ru.rznnike.eyehealthmanager.app.global.presentation.ErrorHandler import ru.rznnike.eyehealthmanager.domain.interactor.test.AddTestResultUseCase import ru.rznnike.eyehealthmanager.domain.model.NearFarTestResult import ru.rznnike.eyehealthmanager.domain.model.enums.NearFarAnswerType +import java.time.Clock @InjectViewState class NearFarAnswerPresenter : BasePresenter() { + private val clock: Clock by inject() private val errorHandler: ErrorHandler by inject() private val notifier: Notifier by inject() private val eventDispatcher: EventDispatcher by inject() @@ -28,7 +30,7 @@ class NearFarAnswerPresenter : BasePresenter() { val typeRightEye = NearFarAnswerType.entries[answerRightEye] val testResult = NearFarTestResult( - timestamp = System.currentTimeMillis(), + timestamp = clock.millis(), resultRightEye = typeRightEye, resultLeftEye = typeLeftEye ) diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/info/NearFarInfoPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/info/NearFarInfoPresenter.kt index 2b8a57bb..e69dd4af 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/info/NearFarInfoPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/info/NearFarInfoPresenter.kt @@ -6,7 +6,5 @@ import ru.rznnike.eyehealthmanager.app.global.presentation.BasePresenter @InjectViewState class NearFarInfoPresenter : BasePresenter() { - fun startTest() { - viewState.routerNewRootScreen(Screens.Screen.nearFarTest()) - } + fun startTest() = viewState.routerNewRootScreen(Screens.Screen.nearFarTest()) } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/test/NearFarTestPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/test/NearFarTestPresenter.kt index 6b8fd51f..85805802 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/test/NearFarTestPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/nearfar/test/NearFarTestPresenter.kt @@ -6,7 +6,5 @@ import ru.rznnike.eyehealthmanager.app.global.presentation.BasePresenter @InjectViewState class NearFarTestPresenter : BasePresenter() { - fun openAnswerForm() { - viewState.routerNavigateTo(Screens.Screen.nearFarAnswer()) - } + fun openAnswerForm() = viewState.routerNavigateTo(Screens.Screen.nearFarAnswer()) } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/settings/testing/TestingSettingsPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/settings/testing/TestingSettingsPresenter.kt index caa03cb2..10894f25 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/settings/testing/TestingSettingsPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/settings/testing/TestingSettingsPresenter.kt @@ -16,8 +16,6 @@ import ru.rznnike.eyehealthmanager.domain.model.TestingSettings import ru.rznnike.eyehealthmanager.domain.utils.GlobalConstants import ru.rznnike.eyehealthmanager.domain.utils.getDayTime -private const val MIN_DELTA_IN_MS = 60 * 1000L // 1m - @InjectViewState class TestingSettingsPresenter : BasePresenter() { private val errorHandler: ErrorHandler by inject() @@ -52,8 +50,8 @@ class TestingSettingsPresenter : BasePresenter() { }, { error -> errorHandler.proceed(error) { notifier.sendMessage(it) - viewState.routerExit() } + viewState.routerExit() } ) } @@ -118,19 +116,19 @@ class TestingSettingsPresenter : BasePresenter() { if (!isTimeOrderCorrect(settings)) { when (period) { TimePeriod.BEGINNING -> { - settings.timeToDayBeginning = settings.timeToDayEnd + MIN_DELTA_IN_MS + settings.timeToDayBeginning = settings.timeToDayEnd + GlobalConstants.MINUTE_MS if (settings.timeToDayBeginning >= GlobalConstants.DAY_MS) { settings.timeToDayBeginning -= GlobalConstants.DAY_MS } } TimePeriod.MIDDLE -> { - settings.timeToDayMiddle = settings.timeToDayBeginning + MIN_DELTA_IN_MS + settings.timeToDayMiddle = settings.timeToDayBeginning + GlobalConstants.MINUTE_MS if (settings.timeToDayMiddle >= GlobalConstants.DAY_MS) { settings.timeToDayMiddle -= GlobalConstants.DAY_MS } } TimePeriod.END -> { - settings.timeToDayEnd = settings.timeToDayMiddle + MIN_DELTA_IN_MS + settings.timeToDayEnd = settings.timeToDayMiddle + GlobalConstants.MINUTE_MS if (settings.timeToDayEnd >= GlobalConstants.DAY_MS) { settings.timeToDayEnd -= GlobalConstants.DAY_MS } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/splash/SplashPresenter.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/splash/SplashPresenter.kt index f0fa4067..65d66100 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/splash/SplashPresenter.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/presentation/splash/SplashPresenter.kt @@ -6,7 +6,5 @@ import ru.rznnike.eyehealthmanager.app.global.presentation.BasePresenter @InjectViewState class SplashPresenter : BasePresenter() { - fun onAnimationEnd() { - viewState.routerNewRootFlow(Screens.Flow.main()) - } + fun onAnimationEnd() = viewState.routerNewRootFlow(Screens.Flow.main()) } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/analysis/result/AnalysisResultFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/analysis/result/AnalysisResultFragment.kt index f192f06e..b5058722 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/analysis/result/AnalysisResultFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/analysis/result/AnalysisResultFragment.kt @@ -217,6 +217,7 @@ class AnalysisResultFragment : BaseFragment(R.layout.fragment_analysis_result), color = context.getColor(baseColorResId) setCircleColor(context.getColor(R.color.colorTransparent)) circleHoleColor = context.getColor(baseColorResId) + valueTextColor = context.getColor(R.color.colorText) } chartVisionDynamic.data.addDataSet(baseDataSet) @@ -240,6 +241,7 @@ class AnalysisResultFragment : BaseFragment(R.layout.fragment_analysis_result), color = context.getColor(extrapolationColorResId) setCircleColor(context.getColor(R.color.colorTransparent)) circleHoleColor = context.getColor(extrapolationColorResId) + valueTextColor = context.getColor(R.color.colorText) } chartVisionDynamic.data.addDataSet(extrapolationDataSet) @@ -258,6 +260,7 @@ class AnalysisResultFragment : BaseFragment(R.layout.fragment_analysis_result), axisLeft.apply { axisMinimum = 0f axisMaximum = yAxisMaximum + 0.05f + textColor = context.getColor(R.color.colorTextDark) } xAxis.apply { axisMinimum = xAxisMinimum @@ -270,8 +273,12 @@ class AnalysisResultFragment : BaseFragment(R.layout.fragment_analysis_result), return value.toLong().toDate() } } + textColor = context.getColor(R.color.colorTextDark) + } + legend.apply { + isWordWrapEnabled = true + textColor = context.getColor(R.color.colorText) } - legend.isWordWrapEnabled = true } companion object { diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/contrast/test/ContrastTestFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/contrast/test/ContrastTestFragment.kt index 820c3749..95ccb3c5 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/contrast/test/ContrastTestFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/contrast/test/ContrastTestFragment.kt @@ -74,16 +74,16 @@ class ContrastTestFragment : BaseFragment(R.layout.fragment_contrast_test), Cont private fun initOnClickListeners() = binding.apply { buttonUp.setOnClickListener { - presenter.onAnswer(Direction.UP) + presenter.answer(Direction.UP) } buttonDown.setOnClickListener { - presenter.onAnswer(Direction.DOWN) + presenter.answer(Direction.DOWN) } buttonLeft.setOnClickListener { - presenter.onAnswer(Direction.LEFT) + presenter.answer(Direction.LEFT) } buttonRight.setOnClickListener { - presenter.onAnswer(Direction.RIGHT) + presenter.answer(Direction.RIGHT) } } @@ -107,7 +107,7 @@ class ContrastTestFragment : BaseFragment(R.layout.fragment_contrast_test), Cont it.isEnabled = false } - layoutTest.animate() + layoutTestFigure.animate() .alpha(0f) .setStartDelay(0) .setDuration(FADE_ANIMATION_MS) @@ -122,8 +122,8 @@ class ContrastTestFragment : BaseFragment(R.layout.fragment_contrast_test), Cont } imageViewForeground.setImageResource(imageRes) - layoutTest.setVisible() - layoutTest.animate() + layoutTestFigure.setVisible() + layoutTestFigure.animate() .alpha(1f) .setStartDelay(0) .setDuration(FADE_ANIMATION_MS) diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/daltonism/result/DaltonismResultFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/daltonism/result/DaltonismResultFragment.kt index 91911065..26a35d1e 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/daltonism/result/DaltonismResultFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/daltonism/result/DaltonismResultFragment.kt @@ -23,7 +23,7 @@ class DaltonismResultFragment : BaseFragment(R.layout.fragment_daltonism_result) @ProvidePresenter fun providePresenter() = DaltonismResultPresenter( errorsCount = getIntArg(ERRORS_COUNT), - resultType = DaltonismAnomalyType[getStringArg(RESULT_TYPE)] ?: DaltonismAnomalyType.NONE + resultType = getParcelableArg(RESULT_TYPE) ?: DaltonismAnomalyType.NONE ) private val binding by viewBinding(FragmentDaltonismResultBinding::bind) diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/daltonism/test/DaltonismTestFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/daltonism/test/DaltonismTestFragment.kt index 8acbce52..3f658b9a 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/daltonism/test/DaltonismTestFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/daltonism/test/DaltonismTestFragment.kt @@ -66,7 +66,7 @@ class DaltonismTestFragment : BaseFragment(R.layout.fragment_daltonism_test), Da buttonVariant4 ).forEachIndexed { index, button -> button.setOnClickListener { - presenter.onAnswer(index) + presenter.answer(index) } } } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/journal/backup/ExportJournalFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/journal/backup/ExportJournalFragment.kt index 04a9c0c7..ca34789f 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/journal/backup/ExportJournalFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/journal/backup/ExportJournalFragment.kt @@ -120,7 +120,7 @@ class ExportJournalFragment : BaseFragment(R.layout.fragment_export_journal), Ex private fun initOnClickListeners() = binding.apply { buttonClearFilters.setOnClickListener { - presenter.onClearFilters() + presenter.clearFilters() } checkBoxFilterByDate.setOnClickListener { presenter.onFilterByDateValueChanged(checkBoxFilterByDate.isChecked) diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/main/journal/JournalFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/main/journal/JournalFragment.kt index e4b34346..ba6c90a1 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/main/journal/JournalFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/main/journal/JournalFragment.kt @@ -37,9 +37,9 @@ import ru.rznnike.eyehealthmanager.domain.model.TestResultFilter import ru.rznnike.eyehealthmanager.domain.model.enums.TestType import ru.rznnike.eyehealthmanager.domain.utils.GlobalConstants import ru.rznnike.eyehealthmanager.domain.utils.atEndOfDay -import ru.rznnike.eyehealthmanager.domain.utils.atStartOfDay -import ru.rznnike.eyehealthmanager.domain.utils.toCalendar +import ru.rznnike.eyehealthmanager.domain.utils.millis import ru.rznnike.eyehealthmanager.domain.utils.toDate +import ru.rznnike.eyehealthmanager.domain.utils.toLocalDate class JournalFragment : BaseFragment(R.layout.fragment_journal), JournalView { @InjectPresenter @@ -294,9 +294,9 @@ class JournalFragment : BaseFragment(R.layout.fragment_journal), JournalView { showDatePicker( preselectedDate = newFilter.dateFrom ) { timestamp -> - newFilter.dateFrom = timestamp.toCalendar().atStartOfDay().timeInMillis + newFilter.dateFrom = timestamp.toLocalDate().atStartOfDay().millis() if (newFilter.dateTo <= newFilter.dateFrom) { - newFilter.dateTo = timestamp.toCalendar().atEndOfDay().timeInMillis + newFilter.dateTo = timestamp.toLocalDate().atEndOfDay().millis() } newFilter.filterByDate = true updateDates() @@ -306,9 +306,9 @@ class JournalFragment : BaseFragment(R.layout.fragment_journal), JournalView { showDatePicker( preselectedDate = newFilter.dateTo ) { timestamp -> - newFilter.dateTo = timestamp.toCalendar().atEndOfDay().timeInMillis + newFilter.dateTo = timestamp.toLocalDate().atEndOfDay().millis() if (newFilter.dateTo <= newFilter.dateFrom) { - newFilter.dateFrom = timestamp.toCalendar().atStartOfDay().timeInMillis + newFilter.dateFrom = timestamp.toLocalDate().atStartOfDay().millis() } newFilter.filterByDate = true updateDates() diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/main/settings/SettingsFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/main/settings/SettingsFragment.kt index f52bff97..91bfdb85 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/main/settings/SettingsFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/main/settings/SettingsFragment.kt @@ -24,6 +24,7 @@ import ru.rznnike.eyehealthmanager.databinding.DialogAboutAppBinding import ru.rznnike.eyehealthmanager.databinding.DialogChangelogBinding import ru.rznnike.eyehealthmanager.databinding.DialogDevMenuBinding import ru.rznnike.eyehealthmanager.databinding.FragmentSettingsBinding +import ru.rznnike.eyehealthmanager.domain.model.enums.AppTheme import ru.rznnike.eyehealthmanager.domain.model.enums.DataGenerationType import ru.rznnike.eyehealthmanager.domain.model.enums.Language import ru.rznnike.eyehealthmanager.domain.utils.GlobalConstants @@ -49,11 +50,6 @@ class SettingsFragment : BaseFragment(R.layout.fragment_settings), SettingsView initOnClickListeners() } - override fun onResume() { - super.onResume() - presenter.onResume() - } - private fun initViews() = binding.apply { listOf( buttonTestingSettings, @@ -63,6 +59,7 @@ class SettingsFragment : BaseFragment(R.layout.fragment_settings), SettingsView buttonDeleteDuplicates, buttonClearJournal, buttonLanguage, + buttonTheme, buttonAboutApp, buttonChangelog, buttonDevMenu @@ -103,11 +100,15 @@ class SettingsFragment : BaseFragment(R.layout.fragment_settings), SettingsView } } - override fun populateData(currentLanguage: Language) { + override fun populateData(language: Language, theme: AppTheme) { binding.apply { - textViewCurrentLanguage.text = currentLanguage.localizedName + textViewCurrentLanguage.text = language.localizedName buttonLanguage.setOnClickListener { - showLanguageSelectionBottomDialog(currentLanguage) + showLanguageSelectionBottomDialog(language) + } + textViewCurrentTheme.setText(theme.nameResId) + buttonTheme.setOnClickListener { + showThemeSelectionBottomDialog(theme) } } } @@ -129,6 +130,21 @@ class SettingsFragment : BaseFragment(R.layout.fragment_settings), SettingsView override fun updateUiLanguage() = requireActivity().restartApp() + private fun showThemeSelectionBottomDialog(currentTheme: AppTheme) { + showBottomDialog( + header = getString(R.string.choose_theme), + actions = AppTheme.entries.map { theme -> + BottomDialogAction( + text = getString(theme.nameResId), + selected = theme == currentTheme + ) { + it.dismiss() + presenter.changeTheme(theme) + } + } + ) + } + private fun showClearJournalDialog() { showAlertDialog( parameters = AlertDialogParameters.HORIZONTAL_2_OPTIONS_LEFT_ACCENT, diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/settings/testing/TestingSettingsFragment.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/settings/testing/TestingSettingsFragment.kt index e382c687..78e8f3ab 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/settings/testing/TestingSettingsFragment.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/fragment/settings/testing/TestingSettingsFragment.kt @@ -104,15 +104,18 @@ class TestingSettingsFragment : BaseFragment(R.layout.fragment_settings_testing) viewTimeControlsDisabler.setVisible(!settings.enableAutoDayPart) - val calendar = Calendar.getInstance() - val timeToDayBeginningWithOffset = settings.timeToDayBeginning - calendar.timeZone.rawOffset - val timeToDayBeginningText = timeToDayBeginningWithOffset.toDate(GlobalConstants.DATE_PATTERN_CLOCK) - - val timeToDayMiddleWithOffset = settings.timeToDayMiddle - calendar.timeZone.rawOffset - val timeToDayMiddleText = timeToDayMiddleWithOffset.toDate(GlobalConstants.DATE_PATTERN_CLOCK) - - val timeToDayEndWithOffset = settings.timeToDayEnd - calendar.timeZone.rawOffset - val timeToDayEndText = timeToDayEndWithOffset.toDate(GlobalConstants.DATE_PATTERN_CLOCK) + val timeToDayBeginningText = settings.timeToDayBeginning.toDate( + pattern = GlobalConstants.DATE_PATTERN_CLOCK, + zeroTimeZone = true + ) + val timeToDayMiddleText = settings.timeToDayMiddle.toDate( + pattern = GlobalConstants.DATE_PATTERN_CLOCK, + zeroTimeZone = true + ) + val timeToDayEndText = settings.timeToDayEnd.toDate( + pattern = GlobalConstants.DATE_PATTERN_CLOCK, + zeroTimeZone = true + ) buttonTimeToBeginning1.text = timeToDayBeginningText buttonTimeToBeginning2.text = timeToDayBeginningText @@ -121,10 +124,11 @@ class TestingSettingsFragment : BaseFragment(R.layout.fragment_settings_testing) buttonTimeToEnd1.text = timeToDayEndText buttonTimeToEnd2.text = timeToDayEndText + val zoneOffset = TimeZone.getDefault().rawOffset listOf(buttonTimeToBeginning1, buttonTimeToBeginning2).forEach { it.setOnClickListener { showTimePicker( - preselectedTime = timeToDayBeginningWithOffset, + preselectedTime = settings.timeToDayBeginning - zoneOffset, onSuccess = presenter::onTimeToDayBeginningValueChanged ) } @@ -132,7 +136,7 @@ class TestingSettingsFragment : BaseFragment(R.layout.fragment_settings_testing) listOf(buttonTimeToMiddle1, buttonTimeToMiddle2).forEach { it.setOnClickListener { showTimePicker( - preselectedTime = timeToDayMiddleWithOffset, + preselectedTime = settings.timeToDayMiddle - zoneOffset, onSuccess = presenter::onTimeToDayMiddleValueChanged ) } @@ -140,7 +144,7 @@ class TestingSettingsFragment : BaseFragment(R.layout.fragment_settings_testing) listOf(buttonTimeToEnd1, buttonTimeToEnd2).forEach { it.setOnClickListener { showTimePicker( - preselectedTime = timeToDayEndWithOffset, + preselectedTime = settings.timeToDayEnd - zoneOffset, onSuccess = presenter::onTimeToDayEndValueChanged ) } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/view/PercentProgressView.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/view/PercentProgressView.kt index c9707255..9a89796f 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/view/PercentProgressView.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/ui/view/PercentProgressView.kt @@ -37,12 +37,12 @@ class PercentProgressView @JvmOverloads constructor( } private fun updateMax() = binding.apply { - progressBar.max = max * PROGRESS_MULTIPLIER + percentProgressBar.max = max * PROGRESS_MULTIPLIER } private fun updateProgress() = binding.apply { textViewProgress.text = "%d%%".format(progress) - ObjectAnimator.ofInt(progressBar, "progress", progress * PROGRESS_MULTIPLIER) + ObjectAnimator.ofInt(percentProgressBar, "progress", progress * PROGRESS_MULTIPLIER) .setDuration(PROGRESS_ANIMATION_MS) .start() } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/ActivityUtils.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/ActivityUtils.kt index 6a1126c2..a1d1d535 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/ActivityUtils.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/ActivityUtils.kt @@ -2,6 +2,8 @@ package ru.rznnike.eyehealthmanager.app.utils.extensions import android.app.Activity import android.content.Intent +import androidx.appcompat.app.AppCompatDelegate +import ru.rznnike.eyehealthmanager.domain.model.enums.AppTheme import kotlin.system.exitProcess fun Activity.restartApp() { @@ -10,3 +12,12 @@ fun Activity.restartApp() { startActivity(restartIntent) exitProcess(0) } + +fun applyTheme(theme: AppTheme) { + val flag = when (theme) { + AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO + AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES + AppTheme.SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + } + AppCompatDelegate.setDefaultNightMode(flag) +} diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/ContextUtils.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/ContextUtils.kt index 3b7cb19e..06cc4830 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/ContextUtils.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/ContextUtils.kt @@ -2,6 +2,7 @@ package ru.rznnike.eyehealthmanager.app.utils.extensions import android.app.Activity import android.content.Context +import android.content.res.Configuration import android.graphics.Rect import android.util.DisplayMetrics import android.util.TypedValue @@ -40,3 +41,9 @@ val Activity.deviceSize: Rect get() = WindowMetricsCalculator.getOrCreate() .computeCurrentWindowMetrics(this) .bounds + +val Context.isNightModeEnabled: Boolean + get() { + val uiModeFlag = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + return uiModeFlag == Configuration.UI_MODE_NIGHT_YES + } diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/EditTextUtils.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/EditTextUtils.kt index ee330c2e..36c769ab 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/EditTextUtils.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/EditTextUtils.kt @@ -49,7 +49,7 @@ fun EditText?.removeFocusAndSpan() { fun EditText.setOnKeyboardActionListener(keyId: Int, action: () -> Unit) { setOnEditorActionListener { _, actionId, _ -> if (actionId == keyId) { - action.invoke() + action() true } else { false diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/StringUtils.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/StringUtils.kt index df5b8e7d..2be9fa12 100644 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/StringUtils.kt +++ b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/StringUtils.kt @@ -1,76 +1,12 @@ package ru.rznnike.eyehealthmanager.app.utils.extensions -import android.text.SpannableStringBuilder -import android.text.Spanned -import android.text.TextPaint -import android.text.style.ClickableSpan -import android.view.View import androidx.core.text.HtmlCompat -import androidx.core.text.inSpans -import java.util.* +import java.util.Locale fun String.toHtmlSpanned() = HtmlCompat.fromHtml(this, HtmlCompat.FROM_HTML_MODE_LEGACY) -fun getHexFromColor(color: Int) = String.format("#%06X", 0xFFFFFF and color) - fun String.capitalize() = replaceFirstChar { if (it.isLowerCase()) it.titlecase( Locale.getDefault() ) else it.toString() -} - -fun createStringWithClickableSpans( - template: String, - clickableTexts: List Unit>> -): Spanned { - fun getClickableSpan(onClickListener: () -> Unit) = object : ClickableSpan() { - override fun onClick(textView: View) = onClickListener() - - override fun updateDrawState(ds: TextPaint) { - super.updateDrawState(ds) - ds.isUnderlineText = false - } - } - - val stringBuilder = SpannableStringBuilder() - template.split("%s").forEachIndexed { index, part -> - if (index > 0) { - val spanIndex = index - 1 - clickableTexts.getOrNull(spanIndex)?.let { spanData -> - stringBuilder.inSpans(getClickableSpan(spanData.second)) { - append(spanData.first.toHtmlSpanned()) - } - } - } - "^ +".toRegex().find(part)?.let { match -> - stringBuilder.append(match.value) - } - stringBuilder.append( - part.replace("\n".toRegex(), "
") - .toHtmlSpanned() - ) - } - - return stringBuilder -} - -fun createStringWithHighlights( - text: String, - partsToHighlight: List, - highlightColor: Int -): Spanned { - val highlightColorHex = getHexFromColor(highlightColor) - - var resultText = text - partsToHighlight.forEach { part -> - resultText = resultText.replaceFirst( - part, - "%s".format( - highlightColorHex, - part - ) - ) - } - - return resultText.replace("\n".toRegex(), "
").toHtmlSpanned() } \ No newline at end of file diff --git a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/TextViewUtils.kt b/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/TextViewUtils.kt deleted file mode 100644 index 0f8c851e..00000000 --- a/app/src/main/java/ru/rznnike/eyehealthmanager/app/utils/extensions/TextViewUtils.kt +++ /dev/null @@ -1,48 +0,0 @@ -package ru.rznnike.eyehealthmanager.app.utils.extensions - -import android.text.SpannableString -import android.text.TextPaint -import android.text.style.ClickableSpan -import android.text.style.URLSpan -import android.view.View -import android.widget.TextView - -fun TextView.removeLinkUnderlines(clickCallback: ((String) -> Unit)? = null) { - val spannable = SpannableString(text) - val spans = spannable.getSpans(0, spannable.length, URLSpan::class.java) - for (span in spans) { - val start = spannable.getSpanStart(span) - val end = spannable.getSpanEnd(span) - val newSpan = clickCallback?.let { - ClickableSpanNoUnderline(span.url, clickCallback) - } ?: run { - URLSpanNoUnderline(span.url) - } - spannable.removeSpan(span) - spannable.setSpan(newSpan, start, end, 0) - } - text = spannable -} - -private class URLSpanNoUnderline( - url: String -) : URLSpan(url) { - override fun updateDrawState(textPaint: TextPaint) { - super.updateDrawState(textPaint) - textPaint.isUnderlineText = false - } -} - -private class ClickableSpanNoUnderline( - val url: String, - val clickCallback: ((String) -> Unit) -) : ClickableSpan() { - override fun updateDrawState(textPaint: TextPaint) { - super.updateDrawState(textPaint) - textPaint.isUnderlineText = false - } - - override fun onClick(widget: View) { - clickCallback.invoke(url) - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_about_app.xml b/app/src/main/res/layout/dialog_about_app.xml index a5cb6b02..5fd3fb87 100644 --- a/app/src/main/res/layout/dialog_about_app.xml +++ b/app/src/main/res/layout/dialog_about_app.xml @@ -1,6 +1,5 @@ + android:layout_height="16dp" /> - + android:layout_height="0dp" + android:layout_weight="1" + android:layout_marginHorizontal="16dp" + android:background="@drawable/bg_rounded_8" + android:backgroundTint="@color/colorBackgroundTestWhite"> + + + + + android:layout_height="16dp" /> @@ -114,30 +128,50 @@ tools:visibility="visible"> + android:layout_width="match_parent" + android:layout_height="16dp" /> - + android:background="@drawable/bg_rounded_8" + android:backgroundTint="@color/colorBackgroundTestWhite"> + + + + + + - + android:layout_height="16dp" /> diff --git a/app/src/main/res/layout/fragment_astigmatism_test.xml b/app/src/main/res/layout/fragment_astigmatism_test.xml index 85c39f03..2d18865e 100644 --- a/app/src/main/res/layout/fragment_astigmatism_test.xml +++ b/app/src/main/res/layout/fragment_astigmatism_test.xml @@ -1,9 +1,9 @@ - - + android:layout_marginHorizontal="16dp" + android:background="@drawable/bg_rounded_8" + android:backgroundTint="@color/colorBackgroundTestWhite"> - + - + - + - - - - - + android:layout_margin="100dp"> + + + + + + + - + - + - + - + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_cant_answer.xml b/app/src/main/res/layout/item_cant_answer.xml index 4e44f2bd..bf55791f 100644 --- a/app/src/main/res/layout/item_cant_answer.xml +++ b/app/src/main/res/layout/item_cant_answer.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_rounded_8" - android:backgroundTint="@color/colorBackground" + android:backgroundTint="@color/colorBackgroundInvariant" android:orientation="horizontal"> diff --git a/app/src/main/res/layout/item_symbol.xml b/app/src/main/res/layout/item_symbol.xml index 9bc67575..c5ac897c 100644 --- a/app/src/main/res/layout/item_symbol.xml +++ b/app/src/main/res/layout/item_symbol.xml @@ -2,10 +2,10 @@ + tools:src="@drawable/ic_letters_en_f"/> \ No newline at end of file diff --git a/app/src/main/res/layout/view_percent_progress.xml b/app/src/main/res/layout/view_percent_progress.xml index 6a309c8f..07d139d0 100644 --- a/app/src/main/res/layout/view_percent_progress.xml +++ b/app/src/main/res/layout/view_percent_progress.xml @@ -7,7 +7,7 @@ android:gravity="center"> - + +