From 757074dd3f412fd46f333b26cb67a0186d2c3e59 Mon Sep 17 00:00:00 2001 From: razeeman Date: Thu, 21 Mar 2024 21:44:43 +0300 Subject: [PATCH] add wear compact list --- .../SettingsDataUpdateInteractor.kt | 23 +++ .../viewModel/SettingsViewModel.kt | 10 ++ .../WearCommunicationInteractor.kt | 11 ++ .../feature_wear/WearRPCServer.kt | 8 + .../feature_wear/src/main/res/values/wear.xml | 1 + resources/src/main/res/values-ar/strings.xml | 2 + resources/src/main/res/values-ca/strings.xml | 2 + resources/src/main/res/values-de/strings.xml | 2 + resources/src/main/res/values-es/strings.xml | 2 + resources/src/main/res/values-fa/strings.xml | 2 + resources/src/main/res/values-fr/strings.xml | 2 + resources/src/main/res/values-hi/strings.xml | 2 + resources/src/main/res/values-in/strings.xml | 2 + resources/src/main/res/values-it/strings.xml | 2 + resources/src/main/res/values-ja/strings.xml | 2 + resources/src/main/res/values-ko/strings.xml | 2 + resources/src/main/res/values-nl/strings.xml | 2 + .../src/main/res/values-pt-rPT/strings.xml | 2 + resources/src/main/res/values-pt/strings.xml | 2 + resources/src/main/res/values-ru/strings.xml | 2 + resources/src/main/res/values-sv/strings.xml | 2 + resources/src/main/res/values-tr/strings.xml | 2 + resources/src/main/res/values-uk/strings.xml | 2 + resources/src/main/res/values-vi/strings.xml | 2 + .../src/main/res/values-zh-rTW/strings.xml | 2 + resources/src/main/res/values-zh/strings.xml | 2 + resources/src/main/res/values/strings.xml | 2 + .../complication/WearComplicationService.kt | 2 +- .../complication/WearIconView.kt | 2 +- .../simpletimetracker/data/WearDataModule.kt | 36 +++++ .../simpletimetracker/data/WearDataRepo.kt | 4 + .../simpletimetracker/data/WearIconMapper.kt | 2 +- .../data/WearPrefsRepoImpl.kt | 27 ++++ .../simpletimetracker/data/WearRPCClient.kt | 4 + .../data/WearResourceRepo.kt | 20 +++ ...rCheckNotificationsPermissionInteractor.kt | 2 +- .../domain/interactor/WearPrefsInteractor.kt | 24 +++ .../CurrentActivitiesMediator.kt | 2 +- .../{ => mediator}/StartActivityMediator.kt | 2 +- .../domain/{ => model}/WearActivityIcon.kt | 2 +- .../domain/repo/WearPrefsRepo.kt | 11 ++ .../navigation/WearNavigator.kt | 8 + .../notification/WearNotificationManager.kt | 2 +- .../components/PresentationConsts.kt | 4 - .../screens/activities/ActivitiesScreen.kt | 4 +- .../activities/ActivitiesViewDataMapper.kt | 6 +- .../screens/activities/ActivitiesViewModel.kt | 11 +- .../screens/settings/SettingsItemType.kt | 12 ++ .../screens/settings/SettingsScreen.kt | 25 +++ .../settings/SettingsViewDataMapper.kt | 47 ++++++ .../screens/settings/SettingsViewModel.kt | 90 +++++++++++ .../screens/tagsSelection/TagsScreen.kt | 2 +- .../tagsSelection/TagsViewDataMapper.kt | 4 +- .../screens/tagsSelection/TagsViewModel.kt | 4 +- .../presentation/theme/Color.kt | 1 + .../{ => ui}/components/ActivitiesList.kt | 147 ++++++++++++++++-- .../{ => ui}/components/ActivityChip.kt | 33 +--- .../ui/components/ActivityChipCompact.kt | 143 +++++++++++++++++ .../{ => ui}/components/ActivityIcon.kt | 8 +- .../{ => ui}/components/OpenOnPhoneButton.kt | 4 +- .../ui/components/PresentationConsts.kt | 5 + .../{ => ui}/components/README.md | 0 .../{ => ui}/components/RefreshButton.kt | 4 +- .../ui/components/SettingsButton.kt | 70 +++++++++ .../ui/components/SettingsItems.kt | 131 ++++++++++++++++ .../ui/components/SettingsList.kt | 143 +++++++++++++++++ .../{ => ui}/components/TagChip.kt | 2 +- .../{ => ui}/components/TagList.kt | 10 +- .../{ => ui}/components/TagSelectionButton.kt | 2 +- .../presentation/{ => ui}/layout/README.md | 0 .../layout/ScaffoldedScrollingColumn.kt | 13 +- .../{ => ui}/layout/Scaffolding.kt | 2 +- .../{ => ui}/layout/ScrollingColumn.kt | 5 +- .../{ => ui}/remember/AnimationRotation.kt | 2 +- .../{ => ui}/remember/DurationSince.kt | 2 +- .../presentation/{ => ui}/remember/README.md | 0 .../utils/WearComposableUtils.kt | 44 ++++++ .../util/simpletimetracker/utils/WearUtils.kt | 42 +++++ ...on_error.xml => wear_connection_error.xml} | 0 ...en_on_phone.xml => wear_open_on_phone.xml} | 0 wear/src/main/res/drawable/wear_settings.xml | 9 ++ .../wear/StartActivityMediatorTest.kt | 4 +- .../wear_api/WearCommunicationAPI.kt | 7 + .../wear_api/WearRequests.kt | 1 + 84 files changed, 1198 insertions(+), 92 deletions(-) create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/SettingsDataUpdateInteractor.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/data/WearDataModule.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/data/WearPrefsRepoImpl.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/data/WearResourceRepo.kt rename wear/src/main/java/com/example/util/simpletimetracker/domain/{ => interactor}/WearCheckNotificationsPermissionInteractor.kt (96%) create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/domain/interactor/WearPrefsInteractor.kt rename wear/src/main/java/com/example/util/simpletimetracker/domain/{ => mediator}/CurrentActivitiesMediator.kt (96%) rename wear/src/main/java/com/example/util/simpletimetracker/domain/{ => mediator}/StartActivityMediator.kt (97%) rename wear/src/main/java/com/example/util/simpletimetracker/domain/{ => model}/WearActivityIcon.kt (88%) create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/domain/repo/WearPrefsRepo.kt delete mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/components/PresentationConsts.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsItemType.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsScreen.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewDataMapper.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewModel.kt rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/ActivitiesList.kt (52%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/ActivityChip.kt (82%) create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipCompact.kt rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/ActivityIcon.kt (88%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/OpenOnPhoneButton.kt (90%) create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/PresentationConsts.kt rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/README.md (100%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/RefreshButton.kt (90%) create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsButton.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsItems.kt create mode 100644 wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsList.kt rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/TagChip.kt (98%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/TagList.kt (95%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/components/TagSelectionButton.kt (96%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/layout/README.md (100%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/layout/ScaffoldedScrollingColumn.kt (60%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/layout/Scaffolding.kt (94%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/layout/ScrollingColumn.kt (94%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/remember/AnimationRotation.kt (90%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/remember/DurationSince.kt (93%) rename wear/src/main/java/com/example/util/simpletimetracker/presentation/{ => ui}/remember/README.md (100%) rename wear/src/main/res/drawable/{connection_error.xml => wear_connection_error.xml} (100%) rename wear/src/main/res/drawable/{open_on_phone.xml => wear_open_on_phone.xml} (100%) create mode 100644 wear/src/main/res/drawable/wear_settings.xml diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/SettingsDataUpdateInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/SettingsDataUpdateInteractor.kt new file mode 100644 index 000000000..fb2fcd306 --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/interactor/SettingsDataUpdateInteractor.kt @@ -0,0 +1,23 @@ +package com.example.util.simpletimetracker.domain.interactor + +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SettingsDataUpdateInteractor @Inject constructor() { + + val dataUpdated: SharedFlow get() = _dataUpdated.asSharedFlow() + + private val _dataUpdated = MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST, + ) + + suspend fun send() { + _dataUpdated.emit(Unit) + } +} \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt index 7c36502b8..32dcacdf6 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt @@ -9,6 +9,7 @@ import com.example.util.simpletimetracker.core.extension.set import com.example.util.simpletimetracker.core.model.NavigationTab import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.core.viewData.SettingsBlock +import com.example.util.simpletimetracker.domain.interactor.SettingsDataUpdateInteractor import com.example.util.simpletimetracker.feature_settings.mapper.SettingsMapper import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsAdditionalViewModelDelegate import com.example.util.simpletimetracker.feature_settings.viewModel.delegate.SettingsBackupViewModelDelegate @@ -24,6 +25,7 @@ import com.example.util.simpletimetracker.navigation.Router import com.example.util.simpletimetracker.navigation.params.screen.DateTimeDialogParams import com.example.util.simpletimetracker.navigation.params.screen.DateTimeDialogType import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import javax.inject.Inject @@ -40,6 +42,7 @@ class SettingsViewModel @Inject constructor( private val exportDelegate: SettingsExportViewModelDelegate, private val translatorsDelegate: SettingsTranslatorsViewModelDelegate, private val contributorsDelegate: SettingsContributorsViewModelDelegate, + private val settingsDataUpdateInteractor: SettingsDataUpdateInteractor, ) : BaseViewModel(), SettingsParent { val content: LiveData> by lazySuspend { loadContent() } @@ -52,6 +55,7 @@ class SettingsViewModel @Inject constructor( additionalDelegate.init(this) backupDelegate.init(this) exportDelegate.init(this) + subscribeToUpdates() } override fun onCleared() { @@ -267,6 +271,12 @@ class SettingsViewModel @Inject constructor( content.set(loadContent()) } + private fun subscribeToUpdates() = viewModelScope.launch { + settingsDataUpdateInteractor.dataUpdated.collect { + updateContent() + } + } + private suspend fun loadContent(): List { val result = mutableListOf() result += mainDelegate.getViewData() diff --git a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearCommunicationInteractor.kt b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearCommunicationInteractor.kt index 6bb1a3891..f78858770 100644 --- a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearCommunicationInteractor.kt +++ b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearCommunicationInteractor.kt @@ -12,10 +12,13 @@ import com.example.util.simpletimetracker.domain.interactor.RecordTagInteractor import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor import com.example.util.simpletimetracker.domain.interactor.RemoveRunningRecordMediator import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor +import com.example.util.simpletimetracker.domain.interactor.SettingsDataUpdateInteractor +import com.example.util.simpletimetracker.domain.interactor.WidgetInteractor import com.example.util.simpletimetracker.domain.mapper.AppColorMapper import com.example.util.simpletimetracker.domain.model.AppColor import com.example.util.simpletimetracker.domain.model.RecordTag import com.example.util.simpletimetracker.domain.model.RunningRecord +import com.example.util.simpletimetracker.domain.model.WidgetType import com.example.util.simpletimetracker.navigation.Router import com.example.util.simpletimetracker.wear_api.WearActivity import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI @@ -34,6 +37,8 @@ class WearCommunicationInteractor @Inject constructor( private val addRunningRecordMediator: Lazy, private val appColorMapper: AppColorMapper, private val router: Router, + private val widgetInteractor: WidgetInteractor, + private val settingsDataUpdateInteractor: SettingsDataUpdateInteractor, ) : WearCommunicationAPI { override suspend fun queryActivities(): List { @@ -99,6 +104,12 @@ class WearCommunicationInteractor @Inject constructor( ) } + override suspend fun setSettings(settings: WearSettings) { + prefsInteractor.setAllowMultitasking(settings.allowMultitasking) + widgetInteractor.updateWidgets(listOf(WidgetType.QUICK_SETTINGS)) + settingsDataUpdateInteractor.send() + } + override suspend fun openPhoneApp() { router.startApp() } diff --git a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt index a447ed142..67506aaf1 100644 --- a/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt +++ b/features/feature_wear/src/main/java/com/example/util/simpletimetracker/feature_wear/WearRPCServer.kt @@ -9,6 +9,7 @@ import android.content.Context import com.example.util.simpletimetracker.wear_api.WearCommunicationAPI import com.example.util.simpletimetracker.wear_api.WearCurrentActivity import com.example.util.simpletimetracker.wear_api.WearRequests +import com.example.util.simpletimetracker.wear_api.WearSettings import com.google.android.gms.tasks.Tasks import com.google.android.gms.wearable.Wearable import com.google.gson.Gson @@ -34,6 +35,7 @@ class WearRPCServer @Inject constructor( WearRequests.SET_CURRENT_ACTIVITIES -> onSetCurrentActivities(request) WearRequests.QUERY_TAGS_FOR_ACTIVITY -> onQueryTagsForActivity(request) WearRequests.QUERY_SETTINGS -> onQuerySettings() + WearRequests.SET_SETTINGS -> onSetSettings(request) WearRequests.OPEN_PHONE_APP -> onOpenPhoneApp() else -> { Timber.d("$path is an invalid RPC call") @@ -90,6 +92,12 @@ class WearRPCServer @Inject constructor( return mapToBytes(api.querySettings()) } + private suspend fun onSetSettings(request: ByteArray): ByteArray? { + val settings: WearSettings = mapFromBytes(request) ?: return null + api.setSettings(settings) + return ByteArray(0) + } + private suspend fun onOpenPhoneApp(): ByteArray? { api.openPhoneApp() return null diff --git a/features/feature_wear/src/main/res/values/wear.xml b/features/feature_wear/src/main/res/values/wear.xml index cb677b16b..b358b5648 100644 --- a/features/feature_wear/src/main/res/values/wear.xml +++ b/features/feature_wear/src/main/res/values/wear.xml @@ -10,6 +10,7 @@ /stt/SET_CURRENT_ACTIVITIES /stt/QUERY_TAGS_FOR_ACTIVITY /stt/QUERY_SETTINGS + /stt/SET_SETTINGS /stt/OPEN_PHONE_APP \ No newline at end of file diff --git a/resources/src/main/res/values-ar/strings.xml b/resources/src/main/res/values-ar/strings.xml index b53eb12d5..590f39ea6 100644 --- a/resources/src/main/res/values-ar/strings.xml +++ b/resources/src/main/res/values-ar/strings.xml @@ -489,5 +489,7 @@ تحقق من اتصالك وحاول مرة أخرى فتح على الهاتف + إعدادات + عرض القائمة المدمجة \ No newline at end of file diff --git a/resources/src/main/res/values-ca/strings.xml b/resources/src/main/res/values-ca/strings.xml index 9591e39e8..f9878e31a 100644 --- a/resources/src/main/res/values-ca/strings.xml +++ b/resources/src/main/res/values-ca/strings.xml @@ -489,5 +489,7 @@ Exemple:
Comprova la teva connexió i torna-ho a provar Obre al telèfon + Configuració + Mostra la llista compacta \ No newline at end of file diff --git a/resources/src/main/res/values-de/strings.xml b/resources/src/main/res/values-de/strings.xml index 70c6e6c11..183de19c3 100644 --- a/resources/src/main/res/values-de/strings.xml +++ b/resources/src/main/res/values-de/strings.xml @@ -489,5 +489,7 @@ Beispiel:
Überprüfen Sie Ihre Verbindung und versuchen Sie es erneut Am Telefon geöffnet + Einstellungen + Kompakte Liste anzeigen \ No newline at end of file diff --git a/resources/src/main/res/values-es/strings.xml b/resources/src/main/res/values-es/strings.xml index 453297bf8..6a9c383c3 100644 --- a/resources/src/main/res/values-es/strings.xml +++ b/resources/src/main/res/values-es/strings.xml @@ -489,5 +489,7 @@ Ejemplo:
Comprueba tu conexión y vuelve a intentarlo. Abierto en el teléfono + Ajustes + Mostrar lista compacta \ No newline at end of file diff --git a/resources/src/main/res/values-fa/strings.xml b/resources/src/main/res/values-fa/strings.xml index d135c1766..4d33465cd 100644 --- a/resources/src/main/res/values-fa/strings.xml +++ b/resources/src/main/res/values-fa/strings.xml @@ -489,5 +489,7 @@ اتصال خود را بررسی کنید و دوباره امتحان کنید روی گوشی باز کنید + تنظیمات + نمایش لیست فشرده \ No newline at end of file diff --git a/resources/src/main/res/values-fr/strings.xml b/resources/src/main/res/values-fr/strings.xml index 6026303c8..5e673fb07 100644 --- a/resources/src/main/res/values-fr/strings.xml +++ b/resources/src/main/res/values-fr/strings.xml @@ -489,5 +489,7 @@ Exemple:
Vérifiez votre connexion et réessayez Ouvrir sur téléphone + Paramètres + Afficher la liste compacte \ No newline at end of file diff --git a/resources/src/main/res/values-hi/strings.xml b/resources/src/main/res/values-hi/strings.xml index 01ede34f0..0070e1b97 100644 --- a/resources/src/main/res/values-hi/strings.xml +++ b/resources/src/main/res/values-hi/strings.xml @@ -489,5 +489,7 @@ csv फ़ाइल में कॉमा से अलग किए गए य अपना कनेक्शन जांचें और पुनः प्रयास करें फ़ोन पर खोलें + समायोजन + संक्षिप्त सूची दिखाएँ \ No newline at end of file diff --git a/resources/src/main/res/values-in/strings.xml b/resources/src/main/res/values-in/strings.xml index 36bef3a07..8a92264f7 100644 --- a/resources/src/main/res/values-in/strings.xml +++ b/resources/src/main/res/values-in/strings.xml @@ -489,5 +489,7 @@ Contoh:
Periksa koneksi Anda dan coba lagi Buka di ponsel + Pengaturan + Tampilkan daftar ringkas \ No newline at end of file diff --git a/resources/src/main/res/values-it/strings.xml b/resources/src/main/res/values-it/strings.xml index c9237a58b..7d95920da 100644 --- a/resources/src/main/res/values-it/strings.xml +++ b/resources/src/main/res/values-it/strings.xml @@ -489,5 +489,7 @@ Esempio:
Controlla la connessione e riprova Apri sul telefono + Impostazioni + Mostra elenco compatto \ No newline at end of file diff --git a/resources/src/main/res/values-ja/strings.xml b/resources/src/main/res/values-ja/strings.xml index 7b8f2ca7c..8afcc2c7f 100644 --- a/resources/src/main/res/values-ja/strings.xml +++ b/resources/src/main/res/values-ja/strings.xml @@ -489,5 +489,7 @@ CSV ファイルには、カンマで区切られた次の列が含まれてい 接続を確認して再試行してください 電話で開く + 設定 + コンパクトなリストを表示 \ No newline at end of file diff --git a/resources/src/main/res/values-ko/strings.xml b/resources/src/main/res/values-ko/strings.xml index 9de4c42b4..72dab71a5 100644 --- a/resources/src/main/res/values-ko/strings.xml +++ b/resources/src/main/res/values-ko/strings.xml @@ -489,5 +489,7 @@ csv 파일은 다음과 같은 열(column)들을 가져야합니다:
연결을 확인하고 다시 시도하세요. 휴대전화에서 열기 + 설정 + 간략한 목록 표시 \ No newline at end of file diff --git a/resources/src/main/res/values-nl/strings.xml b/resources/src/main/res/values-nl/strings.xml index c03f21fb5..7d925802e 100644 --- a/resources/src/main/res/values-nl/strings.xml +++ b/resources/src/main/res/values-nl/strings.xml @@ -489,5 +489,7 @@ Voorbeeld:
Controleer uw verbinding en probeer het opnieuw Openen op telefoon + Instellingen + Toon compacte lijst \ No newline at end of file diff --git a/resources/src/main/res/values-pt-rPT/strings.xml b/resources/src/main/res/values-pt-rPT/strings.xml index fcdd350db..ff20ddc8b 100644 --- a/resources/src/main/res/values-pt-rPT/strings.xml +++ b/resources/src/main/res/values-pt-rPT/strings.xml @@ -489,5 +489,7 @@ Exemplo:
Verifique sua conexão e tente novamente Abra no telefone + Configurações + Mostrar lista compacta \ No newline at end of file diff --git a/resources/src/main/res/values-pt/strings.xml b/resources/src/main/res/values-pt/strings.xml index 2e43ab701..da03dc9cc 100644 --- a/resources/src/main/res/values-pt/strings.xml +++ b/resources/src/main/res/values-pt/strings.xml @@ -489,5 +489,7 @@ Exemplo:
Verifique sua conexão e tente novamente Abra no telefone + Configurações + Mostrar lista compacta \ No newline at end of file diff --git a/resources/src/main/res/values-ru/strings.xml b/resources/src/main/res/values-ru/strings.xml index 67763ae52..f4ced2984 100644 --- a/resources/src/main/res/values-ru/strings.xml +++ b/resources/src/main/res/values-ru/strings.xml @@ -489,5 +489,7 @@ CSV-файл должен содержать следующие столбцы, Проверьте подключение и повторите попытку. Открыть на телефоне + Настройки + Показать компактный список \ No newline at end of file diff --git a/resources/src/main/res/values-sv/strings.xml b/resources/src/main/res/values-sv/strings.xml index 51d93eec3..c334d63dc 100644 --- a/resources/src/main/res/values-sv/strings.xml +++ b/resources/src/main/res/values-sv/strings.xml @@ -489,5 +489,7 @@ Exempel:
Kontrollera din anslutning och försök igen Öppna på telefonen + inställningar + Visa kompakt lista \ No newline at end of file diff --git a/resources/src/main/res/values-tr/strings.xml b/resources/src/main/res/values-tr/strings.xml index 24e11463f..46e83d034 100644 --- a/resources/src/main/res/values-tr/strings.xml +++ b/resources/src/main/res/values-tr/strings.xml @@ -489,5 +489,7 @@ CSV dosyası virgülle ayrılmış şu sütunları içermelidir:
Bağlantınızı kontrol edip tekrar deneyin Telefonda aç + Ayarlar + Kompakt listeyi göster \ No newline at end of file diff --git a/resources/src/main/res/values-uk/strings.xml b/resources/src/main/res/values-uk/strings.xml index 850ee71a3..6b746632f 100644 --- a/resources/src/main/res/values-uk/strings.xml +++ b/resources/src/main/res/values-uk/strings.xml @@ -489,5 +489,7 @@ Перевірте підключення та повторіть спробу Відкрити на телефоні + Налаштування + Показати компактний список \ No newline at end of file diff --git a/resources/src/main/res/values-vi/strings.xml b/resources/src/main/res/values-vi/strings.xml index 4bae5c983..9c76fe876 100644 --- a/resources/src/main/res/values-vi/strings.xml +++ b/resources/src/main/res/values-vi/strings.xml @@ -489,5 +489,7 @@ Ví dụ:
Kiểm tra kết nối của bạn và thử lại Mở trên điện thoại + Cài đặt + Hiển thị danh sách thu gọn diff --git a/resources/src/main/res/values-zh-rTW/strings.xml b/resources/src/main/res/values-zh-rTW/strings.xml index 310ba5179..43e002a9e 100644 --- a/resources/src/main/res/values-zh-rTW/strings.xml +++ b/resources/src/main/res/values-zh-rTW/strings.xml @@ -489,5 +489,7 @@ csv 文件必須包含以逗號分隔的這些列:
檢查您的連線並重試 在手機上打開 + 設定 + 顯示緊湊列表 \ No newline at end of file diff --git a/resources/src/main/res/values-zh/strings.xml b/resources/src/main/res/values-zh/strings.xml index 30a00114e..0a4d831fc 100644 --- a/resources/src/main/res/values-zh/strings.xml +++ b/resources/src/main/res/values-zh/strings.xml @@ -489,5 +489,7 @@ csv 文件必须包含以逗号分隔的这些列:
检查您的连接并重试 在手机上打开 + 设置 + 显示紧凑列表 \ No newline at end of file diff --git a/resources/src/main/res/values/strings.xml b/resources/src/main/res/values/strings.xml index e5be2736f..2e40baad0 100644 --- a/resources/src/main/res/values/strings.xml +++ b/resources/src/main/res/values/strings.xml @@ -489,5 +489,7 @@ Example:
Check your connection and try again Open on phone + Settings + Show compact list \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/complication/WearComplicationService.kt b/wear/src/main/java/com/example/util/simpletimetracker/complication/WearComplicationService.kt index 3f0fb930a..2f24228a3 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/complication/WearComplicationService.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/complication/WearComplicationService.kt @@ -23,7 +23,7 @@ import androidx.wear.watchface.complications.datasource.SuspendingComplicationDa import com.example.util.simpletimetracker.R import com.example.util.simpletimetracker.data.WearDataRepo import com.example.util.simpletimetracker.data.WearIconMapper -import com.example.util.simpletimetracker.domain.WearActivityIcon +import com.example.util.simpletimetracker.domain.model.WearActivityIcon import com.example.util.simpletimetracker.utils.getMainStartIntent import dagger.hilt.android.AndroidEntryPoint import java.time.Instant diff --git a/wear/src/main/java/com/example/util/simpletimetracker/complication/WearIconView.kt b/wear/src/main/java/com/example/util/simpletimetracker/complication/WearIconView.kt index b2b036576..b43201739 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/complication/WearIconView.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/complication/WearIconView.kt @@ -11,7 +11,7 @@ import android.view.LayoutInflater import android.widget.FrameLayout import androidx.core.view.isVisible import com.example.util.simpletimetracker.databinding.WearIconViewLayoutBinding -import com.example.util.simpletimetracker.domain.WearActivityIcon +import com.example.util.simpletimetracker.domain.model.WearActivityIcon class WearIconView @JvmOverloads constructor( context: Context, diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataModule.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataModule.kt new file mode 100644 index 000000000..53ee85484 --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataModule.kt @@ -0,0 +1,36 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.data + +import android.content.Context +import android.content.SharedPreferences +import com.example.util.simpletimetracker.domain.repo.WearPrefsRepo +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +interface WearDataModule { + + @Binds + @Singleton + fun WearPrefsRepoImpl.bindPrefsRepo(): WearPrefsRepo + + companion object { + private const val PREFS_NAME = "prefs_simple_time_tracker_wear" + + @Provides + @Singleton + fun getSharedPrefs(@ApplicationContext context: Context): SharedPreferences { + return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + } + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt index 6bd2374fa..40c652dd9 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearDataRepo.kt @@ -89,6 +89,10 @@ class WearDataRepo @Inject constructor( return runCatching { wearRPCClient.querySettings() } } + suspend fun setSettings(settings: WearSettings): Result = mutex.withLock { + return runCatching { wearRPCClient.setSettings(settings) } + } + suspend fun openAppPhone(): Result = mutex.withLock { return runCatching { wearRPCClient.openPhoneApp() } } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearIconMapper.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearIconMapper.kt index 9da55313c..88d70a589 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/data/WearIconMapper.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearIconMapper.kt @@ -6,7 +6,7 @@ package com.example.util.simpletimetracker.data import android.content.Context -import com.example.util.simpletimetracker.domain.WearActivityIcon +import com.example.util.simpletimetracker.domain.model.WearActivityIcon import com.example.util.simpletimetracker.resources.CommonActivityIcon import com.example.util.simpletimetracker.resources.IconMapperUtils import dagger.hilt.android.qualifiers.ApplicationContext diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearPrefsRepoImpl.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearPrefsRepoImpl.kt new file mode 100644 index 000000000..9a9fad6ea --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearPrefsRepoImpl.kt @@ -0,0 +1,27 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.data + +import android.content.SharedPreferences +import com.example.util.simpletimetracker.domain.repo.WearPrefsRepo +import com.example.util.simpletimetracker.utils.delegate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class WearPrefsRepoImpl @Inject constructor( + prefs: SharedPreferences, +) : WearPrefsRepo { + + override var wearShowCompactList: Boolean by prefs.delegate( + KEY_WEAR_SHOW_COMPACT_LIST, false, + ) + + @Suppress("unused") + companion object { + private const val KEY_WEAR_SHOW_COMPACT_LIST = "wearShowCompactList" + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearRPCClient.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearRPCClient.kt index 4616b2a81..5d2d5a312 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/data/WearRPCClient.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearRPCClient.kt @@ -59,6 +59,10 @@ class WearRPCClient @Inject constructor( return response ?: throw WearRPCException } + override suspend fun setSettings(settings: WearSettings) { + messenger.send(WearRequests.SET_SETTINGS, mapToBytes(settings)) + } + override suspend fun openPhoneApp() { messenger.send(WearRequests.OPEN_PHONE_APP) } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/data/WearResourceRepo.kt b/wear/src/main/java/com/example/util/simpletimetracker/data/WearResourceRepo.kt new file mode 100644 index 000000000..b0f0df019 --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/data/WearResourceRepo.kt @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.data + +import android.content.Context +import androidx.annotation.StringRes +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +class WearResourceRepo @Inject constructor( + @ApplicationContext private val context: Context, +) { + + fun getString(@StringRes stringResId: Int): String { + return context.getString(stringResId) + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/WearCheckNotificationsPermissionInteractor.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/interactor/WearCheckNotificationsPermissionInteractor.kt similarity index 96% rename from wear/src/main/java/com/example/util/simpletimetracker/domain/WearCheckNotificationsPermissionInteractor.kt rename to wear/src/main/java/com/example/util/simpletimetracker/domain/interactor/WearCheckNotificationsPermissionInteractor.kt index fbb2873b5..d9920b54f 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/domain/WearCheckNotificationsPermissionInteractor.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/interactor/WearCheckNotificationsPermissionInteractor.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.domain +package com.example.util.simpletimetracker.domain.interactor import android.Manifest import android.os.Build diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/interactor/WearPrefsInteractor.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/interactor/WearPrefsInteractor.kt new file mode 100644 index 000000000..873f98a7f --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/interactor/WearPrefsInteractor.kt @@ -0,0 +1,24 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.domain.interactor + +import com.example.util.simpletimetracker.domain.repo.WearPrefsRepo +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import javax.inject.Inject + +class WearPrefsInteractor @Inject constructor( + private val wearPrefsRepo: WearPrefsRepo, +) { + + suspend fun getWearShowCompactList(): Boolean = withContext(Dispatchers.IO) { + wearPrefsRepo.wearShowCompactList + } + + suspend fun setWearShowCompactList(enabled: Boolean) = withContext(Dispatchers.IO) { + wearPrefsRepo.wearShowCompactList = enabled + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/CurrentActivitiesMediator.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/CurrentActivitiesMediator.kt similarity index 96% rename from wear/src/main/java/com/example/util/simpletimetracker/domain/CurrentActivitiesMediator.kt rename to wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/CurrentActivitiesMediator.kt index 77d4f6cd5..fc4f052b4 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/domain/CurrentActivitiesMediator.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/CurrentActivitiesMediator.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.domain +package com.example.util.simpletimetracker.domain.mediator import com.example.util.simpletimetracker.data.WearDataRepo import com.example.util.simpletimetracker.data.WearRPCException diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/StartActivityMediator.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/StartActivityMediator.kt similarity index 97% rename from wear/src/main/java/com/example/util/simpletimetracker/domain/StartActivityMediator.kt rename to wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/StartActivityMediator.kt index 79386c023..efdf8eddc 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/domain/StartActivityMediator.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/mediator/StartActivityMediator.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.domain +package com.example.util.simpletimetracker.domain.mediator import com.example.util.simpletimetracker.data.WearDataRepo import com.example.util.simpletimetracker.data.WearRPCException diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/WearActivityIcon.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearActivityIcon.kt similarity index 88% rename from wear/src/main/java/com/example/util/simpletimetracker/domain/WearActivityIcon.kt rename to wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearActivityIcon.kt index 588579d2e..36bd07338 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/domain/WearActivityIcon.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/model/WearActivityIcon.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.domain +package com.example.util.simpletimetracker.domain.model import androidx.annotation.DrawableRes diff --git a/wear/src/main/java/com/example/util/simpletimetracker/domain/repo/WearPrefsRepo.kt b/wear/src/main/java/com/example/util/simpletimetracker/domain/repo/WearPrefsRepo.kt new file mode 100644 index 000000000..efcaf3707 --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/domain/repo/WearPrefsRepo.kt @@ -0,0 +1,11 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.domain.repo + +interface WearPrefsRepo { + + var wearShowCompactList: Boolean +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/navigation/WearNavigator.kt b/wear/src/main/java/com/example/util/simpletimetracker/navigation/WearNavigator.kt index 83561d273..e135a3251 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/navigation/WearNavigator.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/navigation/WearNavigator.kt @@ -10,11 +10,13 @@ import androidx.wear.compose.navigation.SwipeDismissableNavHost import androidx.wear.compose.navigation.composable import androidx.wear.compose.navigation.rememberSwipeDismissableNavController import com.example.util.simpletimetracker.presentation.screens.activities.ActivitiesScreen +import com.example.util.simpletimetracker.presentation.screens.settings.SettingsScreen import com.example.util.simpletimetracker.presentation.screens.tagsSelection.TagsScreen object Route { const val ACTIVITIES = "activities" const val TAGS = "activities/{id}/tags" + const val SETTINGS = "settings" } @Composable @@ -30,6 +32,9 @@ fun WearNavigator() { val route = Route.TAGS.replace("{id}", it.toString()) navigation.navigate(route) }, + onSettingsClick = { + navigation.navigate(Route.SETTINGS) + } ) } composable(Route.TAGS) { @@ -45,5 +50,8 @@ fun WearNavigator() { }, ) } + composable(Route.SETTINGS) { + SettingsScreen() + } } } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/notification/WearNotificationManager.kt b/wear/src/main/java/com/example/util/simpletimetracker/notification/WearNotificationManager.kt index b73ddc10f..1a96b601e 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/notification/WearNotificationManager.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/notification/WearNotificationManager.kt @@ -17,7 +17,7 @@ import com.example.util.simpletimetracker.R import com.example.util.simpletimetracker.data.WearDataRepo import com.example.util.simpletimetracker.data.WearIconMapper import com.example.util.simpletimetracker.data.WearPermissionRepo -import com.example.util.simpletimetracker.domain.WearActivityIcon +import com.example.util.simpletimetracker.domain.model.WearActivityIcon import com.example.util.simpletimetracker.utils.getMainStartIntent import com.example.util.simpletimetracker.wear_api.WearActivity import com.example.util.simpletimetracker.wear_api.WearCurrentActivity diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/PresentationConsts.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/PresentationConsts.kt deleted file mode 100644 index 7eadf0010..000000000 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/PresentationConsts.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.util.simpletimetracker.presentation.components - -internal const val ACTIVITY_VIEW_HEIGHT = 44 -internal const val ACTIVITY_RUNNING_VIEW_HEIGHT = 56 \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesScreen.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesScreen.kt index 8feec1d3c..f5672fed7 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesScreen.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesScreen.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.example.util.simpletimetracker.presentation.components.ActivitiesList +import com.example.util.simpletimetracker.presentation.ui.components.ActivitiesList import com.example.util.simpletimetracker.presentation.screens.activities.ActivitiesViewModel.Effect import com.example.util.simpletimetracker.utils.OnLifecycle import com.example.util.simpletimetracker.utils.collectEffects @@ -17,6 +17,7 @@ import com.example.util.simpletimetracker.utils.collectEffects @Composable fun ActivitiesScreen( onRequestTagSelection: (activityId: Long) -> Unit, + onSettingsClick: () -> Unit, ) { val viewModel = hiltViewModel() viewModel.init() @@ -36,5 +37,6 @@ fun ActivitiesScreen( onStop = viewModel::stopActivity, onRefresh = viewModel::onRefresh, onOpenOnPhone = viewModel::onOpenOnPhone, + onSettingsClick = onSettingsClick, ) } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewDataMapper.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewDataMapper.kt index 0c1638edb..1d49c6021 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewDataMapper.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewDataMapper.kt @@ -7,8 +7,8 @@ package com.example.util.simpletimetracker.presentation.screens.activities import com.example.util.simpletimetracker.R import com.example.util.simpletimetracker.data.WearIconMapper -import com.example.util.simpletimetracker.presentation.components.ActivitiesListState -import com.example.util.simpletimetracker.presentation.components.ActivityChipState +import com.example.util.simpletimetracker.presentation.ui.components.ActivitiesListState +import com.example.util.simpletimetracker.presentation.ui.components.ActivityChipState import com.example.util.simpletimetracker.wear_api.WearActivity import com.example.util.simpletimetracker.wear_api.WearCurrentActivity import javax.inject.Inject @@ -28,6 +28,7 @@ class ActivitiesViewDataMapper @Inject constructor( fun mapContentState( activities: List, currentActivities: List, + showCompactList: Boolean, ): ActivitiesListState.Content { val currentActivitiesMap = currentActivities.associateBy { it.id } val items = activities.map { activity -> @@ -38,6 +39,7 @@ class ActivitiesViewDataMapper @Inject constructor( } return ActivitiesListState.Content( + isCompact = showCompactList, items = items, ) } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewModel.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewModel.kt index 3e6b76c33..29a386620 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewModel.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/activities/ActivitiesViewModel.kt @@ -9,11 +9,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.util.simpletimetracker.complication.WearComplicationManager import com.example.util.simpletimetracker.data.WearDataRepo -import com.example.util.simpletimetracker.domain.CurrentActivitiesMediator -import com.example.util.simpletimetracker.domain.StartActivityMediator -import com.example.util.simpletimetracker.domain.WearCheckNotificationsPermissionInteractor +import com.example.util.simpletimetracker.domain.interactor.WearCheckNotificationsPermissionInteractor +import com.example.util.simpletimetracker.domain.interactor.WearPrefsInteractor +import com.example.util.simpletimetracker.domain.mediator.CurrentActivitiesMediator +import com.example.util.simpletimetracker.domain.mediator.StartActivityMediator import com.example.util.simpletimetracker.notification.WearNotificationManager -import com.example.util.simpletimetracker.presentation.components.ActivitiesListState +import com.example.util.simpletimetracker.presentation.ui.components.ActivitiesListState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow @@ -33,6 +34,7 @@ class ActivitiesViewModel @Inject constructor( private val startActivitiesMediator: StartActivityMediator, private val currentActivitiesMediator: CurrentActivitiesMediator, private val activitiesViewDataMapper: ActivitiesViewDataMapper, + private val wearPrefsInteractor: WearPrefsInteractor, private val wearCheckNotificationsPermissionInteractor: WearCheckNotificationsPermissionInteractor, ) : ViewModel() { @@ -100,6 +102,7 @@ class ActivitiesViewModel @Inject constructor( _state.value = activitiesViewDataMapper.mapContentState( activities = activities.getOrNull().orEmpty(), currentActivities = currentActivities.getOrNull().orEmpty(), + showCompactList = wearPrefsInteractor.getWearShowCompactList(), ) } } diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsItemType.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsItemType.kt new file mode 100644 index 000000000..796249875 --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsItemType.kt @@ -0,0 +1,12 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.screens.settings + +interface SettingsItemType { + + object AllowMultitasking: SettingsItemType + object ShowCompactList: SettingsItemType +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsScreen.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsScreen.kt new file mode 100644 index 000000000..16a04f0eb --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsScreen.kt @@ -0,0 +1,25 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.screens.settings + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.example.util.simpletimetracker.presentation.ui.components.SettingsList + +@Composable +fun SettingsScreen() { + val viewModel = hiltViewModel() + viewModel.init() + val state by viewModel.state.collectAsStateWithLifecycle() + + SettingsList( + state = state, + onRefresh = viewModel::onRefresh, + onSettingClick = viewModel::onSettingClick, + ) +} diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewDataMapper.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewDataMapper.kt new file mode 100644 index 000000000..6c42d6c1a --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewDataMapper.kt @@ -0,0 +1,47 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.screens.settings + +import com.example.util.simpletimetracker.R +import com.example.util.simpletimetracker.data.WearResourceRepo +import com.example.util.simpletimetracker.presentation.ui.components.SettingsItem +import com.example.util.simpletimetracker.presentation.ui.components.SettingsListState +import com.example.util.simpletimetracker.wear_api.WearSettings +import javax.inject.Inject + +class SettingsViewDataMapper @Inject constructor( + private val resourceRepo: WearResourceRepo, +) { + + fun mapErrorState(): SettingsListState.Error { + return SettingsListState.Error(R.string.wear_loading_error) + } + + fun mapContentState( + showCompactList: Boolean, + wearSettings: WearSettings, + ): SettingsListState.Content { + val items = mutableListOf() + + items += SettingsItem.CheckBox( + type = SettingsItemType.AllowMultitasking, + text = resourceRepo.getString(R.string.settings_allow_multitasking), + hint = resourceRepo.getString(R.string.settings_allow_multitasking_hint), + checked = wearSettings.allowMultitasking, + ) + + items += SettingsItem.CheckBox( + type = SettingsItemType.ShowCompactList, + text = resourceRepo.getString(R.string.wear_settings_title_show_compact_list), + hint = "", + checked = showCompactList, + ) + + return SettingsListState.Content( + items = items, + ) + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewModel.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewModel.kt new file mode 100644 index 000000000..9f9a8ca19 --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/settings/SettingsViewModel.kt @@ -0,0 +1,90 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.screens.settings + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.util.simpletimetracker.data.WearDataRepo +import com.example.util.simpletimetracker.domain.interactor.WearPrefsInteractor +import com.example.util.simpletimetracker.presentation.ui.components.SettingsListState +import com.example.util.simpletimetracker.wear_api.WearSettings +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SettingsViewModel @Inject constructor( + private val settingsViewDataMapper: SettingsViewDataMapper, + private val wearPrefsInteractor: WearPrefsInteractor, + private val wearDataRepo: WearDataRepo, +) : ViewModel() { + + val state: StateFlow get() = _state.asStateFlow() + private val _state: MutableStateFlow = MutableStateFlow(SettingsListState.Loading) + + private var isInitialized = false + private var settings: WearSettings? = null + + fun init() { + if (isInitialized) return + viewModelScope.launch { loadData() } + subscribeToDataUpdates() + isInitialized = true + } + + fun onRefresh() = viewModelScope.launch { + loadData() + } + + fun onSettingClick(itemType: SettingsItemType) = viewModelScope.launch { + when (itemType) { + is SettingsItemType.AllowMultitasking -> { + val settings = this@SettingsViewModel.settings ?: return@launch + wearDataRepo.setSettings( + settings.copy( + allowMultitasking = !settings.allowMultitasking, + ), + ) + } + is SettingsItemType.ShowCompactList -> { + val value = wearPrefsInteractor.getWearShowCompactList() + wearPrefsInteractor.setWearShowCompactList(!value) + } + } + loadData() + } + + private suspend fun loadData() { + val showCompactList = wearPrefsInteractor.getWearShowCompactList() + val settings = wearDataRepo.loadSettings() + + when { + settings.isFailure -> { + showError() + } + else -> { + this.settings = settings.getOrNull() + _state.value = settingsViewDataMapper.mapContentState( + showCompactList = showCompactList, + wearSettings = this.settings ?: return, + ) + } + } + } + + private fun showError() { + _state.value = settingsViewDataMapper.mapErrorState() + } + + private fun subscribeToDataUpdates() { + viewModelScope.launch { + wearDataRepo.dataUpdated.collect { loadData() } + } + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsScreen.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsScreen.kt index 9d41c0445..bd00732c3 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsScreen.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsScreen.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.example.util.simpletimetracker.presentation.components.TagList +import com.example.util.simpletimetracker.presentation.ui.components.TagList import com.example.util.simpletimetracker.presentation.screens.tagsSelection.TagsViewModel.Effect import com.example.util.simpletimetracker.utils.collectEffects diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewDataMapper.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewDataMapper.kt index 8079c0355..b0da4f809 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewDataMapper.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewDataMapper.kt @@ -6,8 +6,8 @@ package com.example.util.simpletimetracker.presentation.screens.tagsSelection import com.example.util.simpletimetracker.R -import com.example.util.simpletimetracker.presentation.components.TagChipState -import com.example.util.simpletimetracker.presentation.components.TagListState +import com.example.util.simpletimetracker.presentation.ui.components.TagChipState +import com.example.util.simpletimetracker.presentation.ui.components.TagListState import com.example.util.simpletimetracker.presentation.theme.ColorActive import com.example.util.simpletimetracker.presentation.theme.ColorInactive import com.example.util.simpletimetracker.wear_api.WearSettings diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewModel.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewModel.kt index de00276e7..334f749aa 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewModel.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/screens/tagsSelection/TagsViewModel.kt @@ -8,8 +8,8 @@ package com.example.util.simpletimetracker.presentation.screens.tagsSelection import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.util.simpletimetracker.data.WearDataRepo -import com.example.util.simpletimetracker.presentation.components.TagListState -import com.example.util.simpletimetracker.domain.CurrentActivitiesMediator +import com.example.util.simpletimetracker.presentation.ui.components.TagListState +import com.example.util.simpletimetracker.domain.mediator.CurrentActivitiesMediator import com.example.util.simpletimetracker.wear_api.WearSettings import com.example.util.simpletimetracker.wear_api.WearTag import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/theme/Color.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/theme/Color.kt index 627d8a4e3..eacfa7556 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/theme/Color.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/theme/Color.kt @@ -10,5 +10,6 @@ import androidx.wear.compose.material.Colors val ColorActive = Color(0xFF263238) val ColorInactive = Color(0xFF455A64) +val ColorAccent = Color(0xFFFF4081) internal val wearColors: Colors = Colors() diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivitiesList.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivitiesList.kt similarity index 52% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivitiesList.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivitiesList.kt index 90ac3fdb3..aa527d074 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivitiesList.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivitiesList.kt @@ -3,10 +3,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.components +package com.example.util.simpletimetracker.presentation.ui.components import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable @@ -23,9 +29,12 @@ import androidx.wear.compose.material.ScalingLazyListScope import androidx.wear.compose.material.Text import androidx.wear.tooling.preview.devices.WearDevices import com.example.util.simpletimetracker.R -import com.example.util.simpletimetracker.domain.WearActivityIcon -import com.example.util.simpletimetracker.presentation.layout.ScaffoldedScrollingColumn +import com.example.util.simpletimetracker.domain.model.WearActivityIcon +import com.example.util.simpletimetracker.presentation.ui.layout.ScaffoldedScrollingColumn import com.example.util.simpletimetracker.utils.getString +import com.example.util.simpletimetracker.utils.orZero +import java.time.Instant +import java.util.UUID sealed interface ActivitiesListState { object Loading : ActivitiesListState @@ -39,6 +48,7 @@ sealed interface ActivitiesListState { ) : ActivitiesListState data class Content( + val isCompact: Boolean, val items: List, ) : ActivitiesListState } @@ -50,8 +60,11 @@ fun ActivitiesList( onStop: (activityId: Long) -> Unit = {}, onRefresh: () -> Unit = {}, onOpenOnPhone: () -> Unit = {}, + onSettingsClick: () -> Unit = {}, ) { - ScaffoldedScrollingColumn { + ScaffoldedScrollingColumn( + startItemIndex = 1, + ) { when (state) { is ActivitiesListState.Loading -> item { RenderLoading() @@ -67,6 +80,7 @@ fun ActivitiesList( state = state, onStart = onStart, onStop = onStop, + onSettingsClick = onSettingsClick, ) item { RefreshButton(onRefresh) } } @@ -90,7 +104,7 @@ private fun RenderError( horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( - painter = painterResource(R.drawable.connection_error), + painter = painterResource(R.drawable.wear_connection_error), contentDescription = null, ) Text( @@ -123,6 +137,30 @@ private fun ScalingLazyListScope.renderContent( state: ActivitiesListState.Content, onStart: (activityId: Long) -> Unit, onStop: (activityId: Long) -> Unit, + onSettingsClick: () -> Unit, +) { + item { + SettingsButton(onSettingsClick) + } + if (state.isCompact) { + renderContentCompact( + state = state, + onStart = onStart, + onStop = onStop, + ) + } else { + renderContentFull( + state = state, + onStart = onStart, + onStop = onStop, + ) + } +} + +private fun ScalingLazyListScope.renderContentFull( + state: ActivitiesListState.Content, + onStart: (activityId: Long) -> Unit, + onStop: (activityId: Long) -> Unit, ) { for (itemState in state.items) { item(key = itemState.id) { @@ -144,6 +182,67 @@ private fun ScalingLazyListScope.renderContent( } } +private fun ScalingLazyListScope.renderContentCompact( + state: ActivitiesListState.Content, + onStart: (activityId: Long) -> Unit, + onStop: (activityId: Long) -> Unit, +) { + state.items + .withIndex() + .groupBy { it.index / ACTIVITY_LIST_COMPACT_CHIP_COUNT } + .map { it.value.map { part -> part.value } } + .forEach { part -> + item(key = part.firstOrNull()?.id.orZero()) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + CompactChipPlaceHolder(part.size) + part.forEach { itemState -> + val isRunning = itemState.startedAt != null + val onClick = remember(itemState) { + { + if (isRunning) { + onStop(itemState.id) + } else { + onStart(itemState.id) + } + } + } + ActivityChipCompact( + modifier = Modifier + .fillMaxSize() + .aspectRatio(1f) + .weight(1f), + state = ActivityChipCompatState( + id = itemState.id, + icon = itemState.icon, + color = itemState.color, + startedAt = itemState.startedAt, + ), + onClick = onClick, + ) + } + CompactChipPlaceHolder(part.size) + } + } + } +} + +@Composable +private fun RowScope.CompactChipPlaceHolder( + partSize: Int, +) { + if (partSize < ACTIVITY_LIST_COMPACT_CHIP_COUNT) { + val weight = if (partSize == 1) 1f else 0.5f + Box( + modifier = Modifier + .fillMaxSize() + .aspectRatio(1f) + .weight(weight), + ) + } +} + @Preview(device = WearDevices.LARGE_ROUND) @Composable private fun Loading() { @@ -170,25 +269,41 @@ private fun NoActivities() { @Preview(device = WearDevices.LARGE_ROUND) @Composable -private fun Preview() { - val items = listOf( +private fun ContentFull() { + val items = List(5) { ActivityChipState( - id = 1234, - name = "Chores", - icon = WearActivityIcon.Text("🧹"), - color = 0xFFFA0000, - ), - ActivityChipState( - id = 4321, + id = UUID.randomUUID().hashCode().toLong(), name = "Sleep", - icon = WearActivityIcon.Text("🛏️"), + icon = WearActivityIcon.Image(R.drawable.ic_hotel_24px), color = 0xFF0000FA, - startedAt = 1708241427000L, + startedAt = Instant.now().toEpochMilli() - 36500000, tagString = "", + ) + } + ActivitiesList( + state = ActivitiesListState.Content( + isCompact = false, + items = items, ), ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun ContentCompact() { + val items = List(5) { + ActivityChipState( + id = UUID.randomUUID().hashCode().toLong(), + name = "Sleep", + icon = WearActivityIcon.Image(R.drawable.ic_hotel_24px), + color = 0xFF0000FA, + startedAt = Instant.now().toEpochMilli() - 36500000, + tagString = "", + ) + } ActivitiesList( state = ActivitiesListState.Content( + isCompact = true, items = items, ), ) diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityChip.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChip.kt similarity index 82% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityChip.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChip.kt index 5387332d5..ec40655b6 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityChip.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChip.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.components +package com.example.util.simpletimetracker.presentation.ui.components import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize @@ -23,10 +23,9 @@ import androidx.wear.compose.material.ChipDefaults import androidx.wear.compose.material.Text import androidx.wear.tooling.preview.devices.WearDevices import com.example.util.simpletimetracker.R -import com.example.util.simpletimetracker.domain.WearActivityIcon -import com.example.util.simpletimetracker.presentation.remember.rememberDurationSince -import com.example.util.simpletimetracker.utils.getString -import java.time.Duration +import com.example.util.simpletimetracker.domain.model.WearActivityIcon +import com.example.util.simpletimetracker.presentation.ui.remember.rememberDurationSince +import com.example.util.simpletimetracker.utils.durationToLabel import java.time.Instant @Immutable @@ -99,30 +98,6 @@ fun ActivityChip( ) } -// Copy from TimeMapper.formatInterval -@Composable -fun durationToLabel(duration: Duration): String { - val hourString = getString(R.string.time_hour) - val minuteString = getString(R.string.time_minute) - val secondString = getString(R.string.time_second) - - val hr = duration.toHours() - val min = duration.toMinutes() % 60 - val sec = duration.seconds % 60 - - val willShowHours = hr != 0L - val willShowMinutes = willShowHours || min != 0L - val willShowSeconds = true - - var res = "" - if (willShowHours) res += "$hr$hourString " - if (willShowMinutes) res += "$min$minuteString" - if (willShowMinutes && willShowSeconds) res += " " - if (willShowSeconds) res += "$sec$secondString" - - return res -} - @Preview(device = WearDevices.LARGE_ROUND) @Composable fun SampleCooking() { diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipCompact.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipCompact.kt new file mode 100644 index 000000000..93f1153cb --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityChipCompact.kt @@ -0,0 +1,143 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.wear.compose.material.Button +import androidx.wear.compose.material.ButtonDefaults +import androidx.wear.compose.material.Text +import androidx.wear.tooling.preview.devices.WearDevices +import com.example.util.simpletimetracker.R +import com.example.util.simpletimetracker.domain.model.WearActivityIcon +import com.example.util.simpletimetracker.presentation.ui.remember.rememberDurationSince +import com.example.util.simpletimetracker.utils.durationToLabelShort +import java.time.Instant + +@Immutable +data class ActivityChipCompatState( + val id: Long, + val icon: WearActivityIcon, + val color: Long, + val startedAt: Long? = null, +) + +@Composable +fun ActivityChipCompact( + modifier: Modifier = Modifier, + state: ActivityChipCompatState, + onClick: () -> Unit = {}, +) { + Box( + modifier = modifier, + ) { + Button( + modifier = Modifier.fillMaxSize(), + content = { + ActivityIcon( + activityIcon = state.icon, + modifier = Modifier.fillMaxSize(0.5f), + ) + }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color(state.color), + ), + onClick = onClick, + ) + if (state.startedAt != null) { + val startedDiff = rememberDurationSince(state.startedAt) + val text = durationToLabelShort(startedDiff) + Box( + modifier = Modifier + .align(Alignment.TopCenter) + .clip(CircleShape) + .background(color = Color.Black.copy(alpha = .7F)), + ) { + Text( + modifier = Modifier + .padding(horizontal = 2.dp), + text = text, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontSize = 10.sp, + letterSpacing = (-0.3).sp, + fontWeight = FontWeight.Bold, + ) + } + } + } +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun Preview() { + ActivityChipCompact( + modifier = Modifier.size(48.dp), + state = ActivityChipCompatState( + id = 0, + icon = WearActivityIcon.Text("🎉"), + color = 0xFF123456, + startedAt = null, + ), + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun PreviewText() { + ActivityChipCompact( + modifier = Modifier.size(48.dp), + state = ActivityChipCompatState( + id = 0, + icon = WearActivityIcon.Text("Zzzz"), + color = 0xFFABCDEF, + startedAt = null, + ), + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun PreviewIcon() { + ActivityChipCompact( + modifier = Modifier.size(48.dp), + state = ActivityChipCompatState( + id = 0, + icon = WearActivityIcon.Image(R.drawable.ic_hotel_24px), + color = 0xFFABCDEF, + startedAt = null, + ), + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun PreviewRunning() { + ActivityChipCompact( + modifier = Modifier.size(48.dp), + state = ActivityChipCompatState( + id = 0, + icon = WearActivityIcon.Text("🎉"), + color = 0xFF123456, + startedAt = Instant.now().toEpochMilli() - 36500000, + ), + ) +} diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityIcon.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityIcon.kt similarity index 88% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityIcon.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityIcon.kt index cbd72599c..487a245f8 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/ActivityIcon.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/ActivityIcon.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.components +package com.example.util.simpletimetracker.presentation.ui.components import android.util.TypedValue import android.view.Gravity @@ -19,7 +19,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.core.widget.TextViewCompat import androidx.wear.compose.material.Icon import androidx.wear.compose.material.LocalContentColor -import com.example.util.simpletimetracker.domain.WearActivityIcon +import com.example.util.simpletimetracker.domain.model.WearActivityIcon @Composable fun ActivityIcon( @@ -31,7 +31,9 @@ fun ActivityIcon( Icon( painter = painterResource(activityIcon.iconId), contentDescription = null, - modifier = modifier, + modifier = modifier + .aspectRatio(1f) + .width(0.dp), ) } is WearActivityIcon.Text -> { diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/OpenOnPhoneButton.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/OpenOnPhoneButton.kt similarity index 90% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/OpenOnPhoneButton.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/OpenOnPhoneButton.kt index 9b34a9c72..78ccff6ad 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/OpenOnPhoneButton.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/OpenOnPhoneButton.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.components +package com.example.util.simpletimetracker.presentation.ui.components import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -28,7 +28,7 @@ fun OpenOnPhoneButton( icon = { Icon( modifier = Modifier.padding(4.dp), - painter = painterResource(R.drawable.open_on_phone), + painter = painterResource(R.drawable.wear_open_on_phone), contentDescription = null, ) }, diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/PresentationConsts.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/PresentationConsts.kt new file mode 100644 index 000000000..049c8abdd --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/PresentationConsts.kt @@ -0,0 +1,5 @@ +package com.example.util.simpletimetracker.presentation.ui.components + +internal const val ACTIVITY_VIEW_HEIGHT = 44 +internal const val ACTIVITY_RUNNING_VIEW_HEIGHT = 56 +internal const val ACTIVITY_LIST_COMPACT_CHIP_COUNT = 3 \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/README.md b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/README.md similarity index 100% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/README.md rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/README.md diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/RefreshButton.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/RefreshButton.kt similarity index 90% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/RefreshButton.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/RefreshButton.kt index ada6d0429..a5260c67a 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/RefreshButton.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/RefreshButton.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.components +package com.example.util.simpletimetracker.presentation.ui.components import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons @@ -19,7 +19,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.wear.compose.material.Icon import androidx.wear.compose.material.OutlinedButton -import com.example.util.simpletimetracker.presentation.remember.rememberAnimationRotation +import com.example.util.simpletimetracker.presentation.ui.remember.rememberAnimationRotation @Composable fun RefreshButton( diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsButton.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsButton.kt new file mode 100644 index 000000000..62691e3fd --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsButton.kt @@ -0,0 +1,70 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.ui.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material.Chip +import androidx.wear.compose.material.ChipDefaults +import androidx.wear.compose.material.Icon +import androidx.wear.compose.material.Text +import com.example.util.simpletimetracker.R +import com.example.util.simpletimetracker.presentation.theme.ColorInactive +import com.example.util.simpletimetracker.utils.getString + +@Composable +fun SettingsButton( + onClick: () -> Unit = {}, +) { + Chip( + modifier = Modifier + .height(ACTIVITY_VIEW_HEIGHT.dp) + .fillMaxWidth(), + onClick = onClick, + label = { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + Icon( + painter = painterResource(id = R.drawable.wear_settings), + contentDescription = null, + ) + Spacer( + modifier = Modifier.width(4.dp), + ) + Text( + text = getString(stringResId = R.string.wear_settings_title), + maxLines = 1, + textAlign = TextAlign.Center, + overflow = TextOverflow.Ellipsis, + ) + } + }, + colors = ChipDefaults.chipColors( + backgroundColor = ColorInactive, + ), + ) +} + +@Preview +@Composable +private fun Preview() { + SettingsButton() +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsItems.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsItems.kt new file mode 100644 index 000000000..e0aea1fbd --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsItems.kt @@ -0,0 +1,131 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.wear.compose.material.Checkbox +import androidx.wear.compose.material.CheckboxDefaults +import androidx.wear.compose.material.Text +import androidx.wear.tooling.preview.devices.WearDevices +import com.example.util.simpletimetracker.presentation.screens.settings.SettingsItemType +import com.example.util.simpletimetracker.presentation.theme.ColorAccent + +sealed interface SettingsItem { + val type: SettingsItemType + + data class CheckBox( + override val type: SettingsItemType, + val text: String, + val hint: String, + val checked: Boolean, + ) : SettingsItem +} + +@Composable +fun SettingsCheckbox( + state: SettingsItem.CheckBox, + onClick: () -> Unit = {}, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onClick), + ) { + Column( + Modifier + .padding(vertical = 3.dp) + .padding(horizontal = 4.dp) + .fillMaxWidth() + .weight(1f), + ) { + Text( + text = state.text, + fontWeight = FontWeight.Medium, + ) + if (state.hint.isNotEmpty()) { + Text( + text = state.hint, + fontWeight = FontWeight.Light, + fontSize = 11.sp, + lineHeight = 11.sp, + ) + } + } + Checkbox( + modifier = Modifier, + checked = state.checked, + colors = CheckboxDefaults.colors( + checkedBoxColor = ColorAccent, + checkedCheckmarkColor = ColorAccent, + uncheckedBoxColor = Color.White, + uncheckedCheckmarkColor = Color.White, + ), + ) + } +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun SettingsCheckboxPreview() { + SettingsCheckbox( + state = SettingsItem.CheckBox( + type = SettingsItemType.ShowCompactList, + text = "Check box", + hint = "", + checked = false, + ), + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun SettingsCheckboxCheckedPreview() { + SettingsCheckbox( + state = SettingsItem.CheckBox( + type = SettingsItemType.ShowCompactList, + text = "Check box", + hint = "Check box hint", + checked = true, + ), + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun SettingsCheckboxHintPreview() { + SettingsCheckbox( + state = SettingsItem.CheckBox( + type = SettingsItemType.ShowCompactList, + text = "Check box", + hint = "Check box hint", + checked = false, + ), + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun SettingsCheckboxLongPreview() { + SettingsCheckbox( + state = SettingsItem.CheckBox( + type = SettingsItemType.ShowCompactList, + text = "Check box Check box Check box Check box Check box Check box Check box Check box ", + hint = "Check box hint Check box hint Check box hint Check box hint Check box hint ", + checked = false, + ), + ) +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsList.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsList.kt new file mode 100644 index 000000000..4ec8b665f --- /dev/null +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/SettingsList.kt @@ -0,0 +1,143 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package com.example.util.simpletimetracker.presentation.ui.components + +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material.CircularProgressIndicator +import androidx.wear.compose.material.Icon +import androidx.wear.compose.material.ScalingLazyListScope +import androidx.wear.compose.material.Text +import androidx.wear.tooling.preview.devices.WearDevices +import com.example.util.simpletimetracker.R +import com.example.util.simpletimetracker.presentation.ui.layout.ScaffoldedScrollingColumn +import com.example.util.simpletimetracker.presentation.screens.settings.SettingsItemType +import com.example.util.simpletimetracker.utils.getString + +sealed interface SettingsListState { + object Loading : SettingsListState + + data class Error( + @StringRes val messageResId: Int, + ) : SettingsListState + + data class Content( + val items: List, + ) : SettingsListState +} + +@Composable +fun SettingsList( + state: SettingsListState, + onRefresh: () -> Unit = {}, + onSettingClick: (SettingsItemType) -> Unit = {}, +) { + ScaffoldedScrollingColumn( + startItemIndex = 0, + ) { + when (state) { + is SettingsListState.Loading -> item { + RenderLoading() + } + is SettingsListState.Error -> item { + RenderError(state, onRefresh) + } + is SettingsListState.Content -> { + renderContent( + state = state, + onSettingClick = onSettingClick, + ) + } + } + } +} + +// TODO move to outer element, replace in other screens. +// TODO same for error. +@Composable +private fun RenderLoading() { + CircularProgressIndicator( + modifier = Modifier.width(64.dp), + ) +} + +@Composable +private fun RenderError( + state: SettingsListState.Error, + onRefresh: () -> Unit, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + painter = painterResource(R.drawable.wear_connection_error), + contentDescription = null, + ) + Text( + text = getString(stringResId = state.messageResId), + modifier = Modifier.padding(8.dp), + textAlign = TextAlign.Center, + ) + RefreshButton(onRefresh) + } +} + +private fun ScalingLazyListScope.renderContent( + state: SettingsListState.Content, + onSettingClick: (SettingsItemType) -> Unit, +) { + for (item in state.items) { + item { + val onClick = remember(item) { + { onSettingClick(item.type) } + } + when (item) { + is SettingsItem.CheckBox -> { + SettingsCheckbox( + state = item, + onClick = onClick, + ) + } + } + } + } +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun Loading() { + SettingsList( + state = SettingsListState.Loading, + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun Content() { + val items = listOf( + SettingsItem.CheckBox( + type = SettingsItemType.ShowCompactList, + text = "Setting", + hint = "", + checked = true, + ), + ) + SettingsList( + state = SettingsListState.Content( + items = items, + ), + ) +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagChip.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagChip.kt similarity index 98% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagChip.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagChip.kt index 80a1e42ca..ccd1c8b9d 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagChip.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagChip.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.components +package com.example.util.simpletimetracker.presentation.ui.components import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagList.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagList.kt similarity index 95% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagList.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagList.kt index 07cdb8bb3..384c65ffa 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagList.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagList.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.components +package com.example.util.simpletimetracker.presentation.ui.components import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column @@ -24,7 +24,7 @@ import androidx.wear.compose.material.ScalingLazyListScope import androidx.wear.compose.material.Text import androidx.wear.tooling.preview.devices.WearDevices import com.example.util.simpletimetracker.R -import com.example.util.simpletimetracker.presentation.layout.ScaffoldedScrollingColumn +import com.example.util.simpletimetracker.presentation.ui.layout.ScaffoldedScrollingColumn import com.example.util.simpletimetracker.utils.getString sealed interface TagListState { @@ -68,7 +68,9 @@ fun TagList( onToggleClick: (Long) -> Unit = {}, onRefresh: () -> Unit = {}, ) { - ScaffoldedScrollingColumn { + ScaffoldedScrollingColumn( + startItemIndex = 0, + ) { when (state) { is TagListState.Loading -> item { RenderLoadingState() @@ -106,7 +108,7 @@ private fun RenderErrorState( horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( - painter = painterResource(R.drawable.connection_error), + painter = painterResource(R.drawable.wear_connection_error), contentDescription = null, ) Text( diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagSelectionButton.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagSelectionButton.kt similarity index 96% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagSelectionButton.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagSelectionButton.kt index 043d3f775..860e3c409 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/components/TagSelectionButton.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/components/TagSelectionButton.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.components +package com.example.util.simpletimetracker.presentation.ui.components import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/README.md b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/README.md similarity index 100% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/README.md rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/README.md diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/ScaffoldedScrollingColumn.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScaffoldedScrollingColumn.kt similarity index 60% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/ScaffoldedScrollingColumn.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScaffoldedScrollingColumn.kt index 75fc9ea89..9fae892f1 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/ScaffoldedScrollingColumn.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScaffoldedScrollingColumn.kt @@ -3,16 +3,23 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.layout +package com.example.util.simpletimetracker.presentation.ui.layout import androidx.compose.runtime.Composable import androidx.wear.compose.material.ScalingLazyListScope import androidx.wear.compose.material.rememberScalingLazyListState @Composable -fun ScaffoldedScrollingColumn(content: ScalingLazyListScope.() -> Unit) { +fun ScaffoldedScrollingColumn( + startItemIndex: Int, + content: ScalingLazyListScope.() -> Unit +) { val scrollState = rememberScalingLazyListState() Scaffolding(scrollState) { - ScrollingColumn(scrollState, content) + ScrollingColumn( + startItemIndex = startItemIndex, + scrollState = scrollState, + content = content, + ) } } \ No newline at end of file diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/Scaffolding.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/Scaffolding.kt similarity index 94% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/Scaffolding.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/Scaffolding.kt index 6cc169f59..19de39638 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/Scaffolding.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/Scaffolding.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.layout +package com.example.util.simpletimetracker.presentation.ui.layout import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/ScrollingColumn.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScrollingColumn.kt similarity index 94% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/ScrollingColumn.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScrollingColumn.kt index a29818fc0..9660ad497 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/layout/ScrollingColumn.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/layout/ScrollingColumn.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.layout +package com.example.util.simpletimetracker.presentation.ui.layout import androidx.compose.animation.core.exponentialDecay import androidx.compose.foundation.background @@ -28,6 +28,7 @@ import com.google.android.horologist.compose.rotaryinput.rotaryWithScroll @OptIn(ExperimentalHorologistComposeLayoutApi::class) @Composable fun ScrollingColumn( + startItemIndex: Int, scrollState: ScalingLazyListState = rememberScalingLazyListState(), content: ScalingLazyListScope.() -> Unit, ) { @@ -44,7 +45,7 @@ fun ScrollingColumn( decay = exponentialDecay(frictionMultiplier = 0.75f), ), autoCentering = AutoCenteringParams( - itemIndex = 0, + itemIndex = startItemIndex, ), verticalArrangement = Arrangement.spacedBy(10.dp), state = scrollState, diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/AnimationRotation.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/remember/AnimationRotation.kt similarity index 90% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/AnimationRotation.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/remember/AnimationRotation.kt index d04a2f0e4..0bfab1e0f 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/AnimationRotation.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/remember/AnimationRotation.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.remember +package com.example.util.simpletimetracker.presentation.ui.remember import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/DurationSince.kt b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/remember/DurationSince.kt similarity index 93% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/DurationSince.kt rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/remember/DurationSince.kt index c8693559f..005877c5e 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/DurationSince.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/remember/DurationSince.kt @@ -3,7 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -package com.example.util.simpletimetracker.presentation.remember +package com.example.util.simpletimetracker.presentation.ui.remember import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect diff --git a/wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/README.md b/wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/remember/README.md similarity index 100% rename from wear/src/main/java/com/example/util/simpletimetracker/presentation/remember/README.md rename to wear/src/main/java/com/example/util/simpletimetracker/presentation/ui/remember/README.md diff --git a/wear/src/main/java/com/example/util/simpletimetracker/utils/WearComposableUtils.kt b/wear/src/main/java/com/example/util/simpletimetracker/utils/WearComposableUtils.kt index 8dddb2150..08b46251a 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/utils/WearComposableUtils.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/utils/WearComposableUtils.kt @@ -16,9 +16,11 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver +import com.example.util.simpletimetracker.R import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach +import java.time.Duration @SuppressLint("ComposableNaming") @Composable @@ -67,3 +69,45 @@ fun OnLifecycleEvent(onEvent: (Lifecycle.Event) -> Unit) { } } } + +// Copy from TimeMapper.formatInterval +@Composable +fun durationToLabel(duration: Duration): String { + val hourString = getString(R.string.time_hour) + val minuteString = getString(R.string.time_minute) + val secondString = getString(R.string.time_second) + + val hr = duration.toHours() + val min = duration.toMinutes() % 60 + val sec = duration.seconds % 60 + + val willShowHours = hr != 0L + val willShowMinutes = willShowHours || min != 0L + val willShowSeconds = true + + var res = "" + if (willShowHours) res += "$hr$hourString " + if (willShowMinutes) res += "$min$minuteString" + if (willShowMinutes && willShowSeconds) res += " " + if (willShowSeconds) res += "$sec$secondString" + + return res +} + +@Composable +fun durationToLabelShort(duration: Duration): String { + val hr = duration.toHours() + val min = duration.toMinutes() % 60 + val sec = duration.seconds % 60 + + val willShowHours = hr != 0L + val willShowMinutes = true + val willShowSeconds = true + + var res = "" + if (willShowHours) res += "${hr.toString().padDuration()}:" + if (willShowMinutes) res += "${min.toString().padDuration()}:" + if (willShowSeconds) res += sec.toString().padDuration() + + return res +} diff --git a/wear/src/main/java/com/example/util/simpletimetracker/utils/WearUtils.kt b/wear/src/main/java/com/example/util/simpletimetracker/utils/WearUtils.kt index 5b55432ed..940bffc76 100644 --- a/wear/src/main/java/com/example/util/simpletimetracker/utils/WearUtils.kt +++ b/wear/src/main/java/com/example/util/simpletimetracker/utils/WearUtils.kt @@ -9,8 +9,11 @@ import android.annotation.SuppressLint import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.content.SharedPreferences import android.os.Build import com.example.util.simpletimetracker.presentation.MainActivity +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty @SuppressLint("WearRecents") fun getMainStartIntent(context: Context): PendingIntent { @@ -26,6 +29,45 @@ fun getMainStartIntent(context: Context): PendingIntent { ) } +fun String.padDuration(): String = this.padStart(2, '0') + +fun Long?.orZero(): Long = this ?: 0 + +@Suppress("UNCHECKED_CAST") +internal inline fun SharedPreferences.delegate( + key: String, + default: T, +) = object : ReadWriteProperty { + + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + val data = when (default) { + is Boolean -> (getBoolean(key, default) as? T) ?: default + is Int -> (getInt(key, default) as? T) ?: default + is Long -> (getLong(key, default) as? T) ?: default + is String -> (getString(key, default) as? T) ?: default + is Set<*> -> (getStringSet(key, default as? Set)?.toSet() as? T) ?: default + else -> throw IllegalArgumentException( + "Prefs delegate not implemented for class ${(default as Any?)?.javaClass}", + ) + } + return data + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = with(edit()) { + when (value) { + is Boolean -> putBoolean(key, value) + is Int -> putInt(key, value) + is Long -> putLong(key, value) + is String -> putString(key, value) + is Set<*> -> putStringSet(key, value as? Set) + else -> throw IllegalArgumentException( + "Prefs delegate not implemented for class ${(default as Any?)?.javaClass}", + ) + } + apply() + } +} + private fun getPendingIntentFlags(): Int { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE diff --git a/wear/src/main/res/drawable/connection_error.xml b/wear/src/main/res/drawable/wear_connection_error.xml similarity index 100% rename from wear/src/main/res/drawable/connection_error.xml rename to wear/src/main/res/drawable/wear_connection_error.xml diff --git a/wear/src/main/res/drawable/open_on_phone.xml b/wear/src/main/res/drawable/wear_open_on_phone.xml similarity index 100% rename from wear/src/main/res/drawable/open_on_phone.xml rename to wear/src/main/res/drawable/wear_open_on_phone.xml diff --git a/wear/src/main/res/drawable/wear_settings.xml b/wear/src/main/res/drawable/wear_settings.xml new file mode 100644 index 000000000..a277a99d5 --- /dev/null +++ b/wear/src/main/res/drawable/wear_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/wear/src/test/java/com/example/util/simpletimetracker/wear/StartActivityMediatorTest.kt b/wear/src/test/java/com/example/util/simpletimetracker/wear/StartActivityMediatorTest.kt index 586658d44..c4b3b95bb 100644 --- a/wear/src/test/java/com/example/util/simpletimetracker/wear/StartActivityMediatorTest.kt +++ b/wear/src/test/java/com/example/util/simpletimetracker/wear/StartActivityMediatorTest.kt @@ -6,8 +6,8 @@ package com.example.util.simpletimetracker.wear import com.example.util.simpletimetracker.data.WearDataRepo -import com.example.util.simpletimetracker.domain.CurrentActivitiesMediator -import com.example.util.simpletimetracker.domain.StartActivityMediator +import com.example.util.simpletimetracker.domain.mediator.CurrentActivitiesMediator +import com.example.util.simpletimetracker.domain.mediator.StartActivityMediator import com.example.util.simpletimetracker.wear_api.WearActivity import com.example.util.simpletimetracker.wear_api.WearSettings import com.example.util.simpletimetracker.wear_api.WearTag diff --git a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearCommunicationAPI.kt b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearCommunicationAPI.kt index 5f083ef5a..e3e86d159 100644 --- a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearCommunicationAPI.kt +++ b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearCommunicationAPI.kt @@ -41,6 +41,13 @@ interface WearCommunicationAPI { */ suspend fun querySettings(): WearSettings + /** + * [WearRequests.SET_SETTINGS] + * + * Set app settings from wear. + */ + suspend fun setSettings(settings: WearSettings) + /** * [WearRequests.OPEN_PHONE_APP] * diff --git a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearRequests.kt b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearRequests.kt index faa577f23..0c95cbdd9 100644 --- a/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearRequests.kt +++ b/wear_api/src/main/java/com/example/util/simpletimetracker/wear_api/WearRequests.kt @@ -15,6 +15,7 @@ object WearRequests { const val SET_CURRENT_ACTIVITIES = "$PATH/SET_CURRENT_ACTIVITIES" const val QUERY_TAGS_FOR_ACTIVITY = "$PATH/QUERY_TAGS_FOR_ACTIVITY" const val QUERY_SETTINGS = "$PATH/QUERY_SETTINGS" + const val SET_SETTINGS = "$PATH/SET_SETTINGS" const val OPEN_PHONE_APP = "$PATH/OPEN_PHONE_APP" // From app to wear.