From 3a6e7e6123be92b53bc234d555a8d162887ddecf Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 11:57:30 +0100 Subject: [PATCH 01/17] chore: add the required glance dependencies --- gradle/libs.versions.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ad0c68649..413c6a5f6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,6 +3,7 @@ aboutlibraries = "10.9.1" accompanist = "0.32.0" android-gradle-plugin = "8.1.2" androidx-espresso = "3.5.1" +androidx-glance = "1.0.0" androidx-hilt = "1.0.0" androidx-navigation = "2.7.4" androidx-room = "2.5.2" @@ -38,6 +39,8 @@ androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayo androidx-core = { module = "androidx.core:core-ktx", version = "1.12.0" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version = "1.0.0" } androidx-fragment = { module = "androidx.fragment:fragment-ktx", version = "1.6.1" } +androidx-glance-appwidget = { module = "androidx.glance:glance-appwidget", version.ref = "androidx-glance" } +androidx-glance-material3 = { module = "androidx.glance:glance-material3", version.ref = "androidx-glance" } androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "androidx-hilt" } androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version = "1.0.0" } androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "androidx-hilt" } From 5ead13e52f127c5a769266bb0822fb35498ed906 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 12:07:37 +0100 Subject: [PATCH 02/17] feat: add new module for the widget --- features/serieswidget/.gitignore | 1 + features/serieswidget/build.gradle.kts | 36 +++++++++++++++++++ features/serieswidget/consumer-rules.pro | 0 .../serieswidget/src/main/AndroidManifest.xml | 2 ++ settings.gradle.kts | 1 + 5 files changed, 40 insertions(+) create mode 100644 features/serieswidget/.gitignore create mode 100644 features/serieswidget/build.gradle.kts create mode 100644 features/serieswidget/consumer-rules.pro create mode 100644 features/serieswidget/src/main/AndroidManifest.xml diff --git a/features/serieswidget/.gitignore b/features/serieswidget/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/features/serieswidget/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/serieswidget/build.gradle.kts b/features/serieswidget/build.gradle.kts new file mode 100644 index 000000000..ce391cff3 --- /dev/null +++ b/features/serieswidget/build.gradle.kts @@ -0,0 +1,36 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.google.dagger.hilt.android) + alias(libs.plugins.google.devtools.ksp) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.chesire.nekome.feature.serieswidget" + compileSdk = libs.versions.sdk.get().toInt() + + defaultConfig { + minSdk = 21 + + consumerProguardFiles("consumer-rules.pro") + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() + } +} + +dependencies { + implementation(project(":core:compose")) + + implementation(libs.androidx.glance.appwidget) + implementation(libs.androidx.glance.material3) + implementation(libs.bundles.compose) + implementation(libs.google.hilt.android) + implementation(libs.kotlin.result) + implementation(libs.timber) + debugImplementation(libs.androidx.compose.ui.tooling) + ksp(libs.google.hilt.android.compiler) +} diff --git a/features/serieswidget/consumer-rules.pro b/features/serieswidget/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/features/serieswidget/src/main/AndroidManifest.xml b/features/serieswidget/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8072ee00d --- /dev/null +++ b/features/serieswidget/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 952962732..263cd4f53 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,6 +23,7 @@ include( ":features:login", ":features:search", ":features:series", + ":features:serieswidget", ":features:settings", ":libraries:core", ":libraries:database", From 3d36d75414258cc89b580a9042c79f3d689049ea Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 12:22:06 +0100 Subject: [PATCH 03/17] feat: allow adding widget to home screen --- app/build.gradle.kts | 1 + features/serieswidget/build.gradle.kts | 1 + .../serieswidget/src/main/AndroidManifest.xml | 16 ++++++++++- .../serieswidget/GlanceDataReceiver.kt | 9 +++++++ .../feature/serieswidget/SeriesWidget.kt | 27 +++++++++++++++++++ .../src/main/res/xml/series_widget_info.xml | 10 +++++++ 6 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidget.kt create mode 100644 features/serieswidget/src/main/res/xml/series_widget_info.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 209bffdc6..edabd1478 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -67,6 +67,7 @@ dependencies { implementation(project(":features:login")) implementation(project(":features:search")) implementation(project(":features:series")) + implementation(project(":features:serieswidget")) implementation(project(":features:settings")) implementation(project(":libraries:core")) implementation(project(":libraries:database")) diff --git a/features/serieswidget/build.gradle.kts b/features/serieswidget/build.gradle.kts index ce391cff3..6483f162f 100644 --- a/features/serieswidget/build.gradle.kts +++ b/features/serieswidget/build.gradle.kts @@ -24,6 +24,7 @@ android { dependencies { implementation(project(":core:compose")) + implementation(project(":core:resources")) implementation(libs.androidx.glance.appwidget) implementation(libs.androidx.glance.material3) diff --git a/features/serieswidget/src/main/AndroidManifest.xml b/features/serieswidget/src/main/AndroidManifest.xml index 8072ee00d..42cdec11f 100644 --- a/features/serieswidget/src/main/AndroidManifest.xml +++ b/features/serieswidget/src/main/AndroidManifest.xml @@ -1,2 +1,16 @@ - + + + + + + + + + + + diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt new file mode 100644 index 000000000..548dda9a5 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt @@ -0,0 +1,9 @@ +package com.chesire.nekome.feature.serieswidget + +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.GlanceAppWidgetReceiver + +class GlanceDataReceiver : GlanceAppWidgetReceiver() { + override val glanceAppWidget: GlanceAppWidget + get() = SeriesWidget() +} diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidget.kt new file mode 100644 index 000000000..ecfd35c75 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidget.kt @@ -0,0 +1,27 @@ +package com.chesire.nekome.feature.serieswidget + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.glance.GlanceId +import androidx.glance.GlanceModifier +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.provideContent +import androidx.glance.layout.Column +import androidx.glance.layout.fillMaxSize +import androidx.glance.text.Text + +class SeriesWidget : GlanceAppWidget() { + + override suspend fun provideGlance(context: Context, id: GlanceId) { + provideContent { + Render() + } + } + + @Composable + private fun Render() { + Column(modifier = GlanceModifier.fillMaxSize()) { + Text(text = "Test") + } + } +} diff --git a/features/serieswidget/src/main/res/xml/series_widget_info.xml b/features/serieswidget/src/main/res/xml/series_widget_info.xml new file mode 100644 index 000000000..5385d34fa --- /dev/null +++ b/features/serieswidget/src/main/res/xml/series_widget_info.xml @@ -0,0 +1,10 @@ + + From a1e8c3606eb4ad66a9661b24bd56facb0df56ce7 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 13:28:23 +0100 Subject: [PATCH 04/17] feat: display all active series on widget --- features/serieswidget/build.gradle.kts | 2 + .../serieswidget/GlanceDataReceiver.kt | 1 + .../feature/serieswidget/SeriesWidget.kt | 27 ------ .../serieswidget/SeriesWidgetEntryPoint.kt | 13 +++ .../core/RetrieveSeriesUseCase.kt | 25 ++++++ .../feature/serieswidget/ui/SeriesWidget.kt | 83 +++++++++++++++++++ .../serieswidget/ui/SeriesWidgetViewModel.kt | 49 +++++++++++ .../nekome/feature/serieswidget/ui/UIState.kt | 12 +++ .../feature/serieswidget/ui/ViewAction.kt | 6 ++ 9 files changed, 191 insertions(+), 27 deletions(-) delete mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidget.kt create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidgetEntryPoint.kt create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/RetrieveSeriesUseCase.kt create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt diff --git a/features/serieswidget/build.gradle.kts b/features/serieswidget/build.gradle.kts index 6483f162f..8c24273f6 100644 --- a/features/serieswidget/build.gradle.kts +++ b/features/serieswidget/build.gradle.kts @@ -25,6 +25,8 @@ android { dependencies { implementation(project(":core:compose")) implementation(project(":core:resources")) + implementation(project(":libraries:core")) + implementation(project(":libraries:datasource:series")) implementation(libs.androidx.glance.appwidget) implementation(libs.androidx.glance.material3) diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt index 548dda9a5..a93a2370f 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/GlanceDataReceiver.kt @@ -2,6 +2,7 @@ package com.chesire.nekome.feature.serieswidget import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.GlanceAppWidgetReceiver +import com.chesire.nekome.feature.serieswidget.ui.SeriesWidget class GlanceDataReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidget.kt deleted file mode 100644 index ecfd35c75..000000000 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidget.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.chesire.nekome.feature.serieswidget - -import android.content.Context -import androidx.compose.runtime.Composable -import androidx.glance.GlanceId -import androidx.glance.GlanceModifier -import androidx.glance.appwidget.GlanceAppWidget -import androidx.glance.appwidget.provideContent -import androidx.glance.layout.Column -import androidx.glance.layout.fillMaxSize -import androidx.glance.text.Text - -class SeriesWidget : GlanceAppWidget() { - - override suspend fun provideGlance(context: Context, id: GlanceId) { - provideContent { - Render() - } - } - - @Composable - private fun Render() { - Column(modifier = GlanceModifier.fillMaxSize()) { - Text(text = "Test") - } - } -} diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidgetEntryPoint.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidgetEntryPoint.kt new file mode 100644 index 000000000..17ae7f809 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/SeriesWidgetEntryPoint.kt @@ -0,0 +1,13 @@ +package com.chesire.nekome.feature.serieswidget + +import com.chesire.nekome.feature.serieswidget.ui.SeriesWidgetViewModel +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@EntryPoint +@InstallIn(SingletonComponent::class) +interface SeriesWidgetEntryPoint { + + fun seriesWidgetViewModel(): SeriesWidgetViewModel +} diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/RetrieveSeriesUseCase.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/RetrieveSeriesUseCase.kt new file mode 100644 index 000000000..8bb13e311 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/RetrieveSeriesUseCase.kt @@ -0,0 +1,25 @@ +package com.chesire.nekome.feature.serieswidget.core + +import com.chesire.nekome.core.flags.SeriesType +import com.chesire.nekome.core.flags.UserSeriesStatus +import com.chesire.nekome.datasource.series.SeriesDomain +import com.chesire.nekome.datasource.series.SeriesRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class RetrieveSeriesUseCase @Inject constructor( + private val seriesRepository: SeriesRepository +) { + + operator fun invoke(): Flow> { + return seriesRepository + .getSeries() + .map { seriesList -> + seriesList + .filter { series -> series.type == SeriesType.Anime } + .filter { series -> series.userSeriesStatus == UserSeriesStatus.Current } + .filter { series -> series.totalLength == 0 || series.progress < series.totalLength } + } + } +} diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt new file mode 100644 index 000000000..c07b3f966 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -0,0 +1,83 @@ +package com.chesire.nekome.feature.serieswidget.ui + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.graphics.Color +import androidx.glance.GlanceId +import androidx.glance.GlanceModifier +import androidx.glance.LocalContext +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.lazy.LazyColumn +import androidx.glance.appwidget.lazy.items +import androidx.glance.appwidget.provideContent +import androidx.glance.background +import androidx.glance.layout.Column +import androidx.glance.layout.fillMaxSize +import androidx.glance.text.Text +import com.chesire.nekome.feature.serieswidget.SeriesWidgetEntryPoint +import dagger.hilt.EntryPoints +import timber.log.Timber + +class SeriesWidget : GlanceAppWidget() { + + override suspend fun provideGlance(context: Context, id: GlanceId) { + + provideContent { + ProvideContent() + } + } + + @Composable + private fun ProvideContent() { + val ctx = LocalContext.current.applicationContext + + // TODO: Use remember for this? + val viewModel = EntryPoints.get( + ctx, + SeriesWidgetEntryPoint::class.java + ).seriesWidgetViewModel() + + val state by viewModel.uiState.collectAsState() + Render( + state = state, + updateSeries = { viewModel.execute(ViewAction.UpdateSeries(it)) } + ) + } + + @Composable + private fun Render( + state: UIState, + updateSeries: (String) -> Unit + ) { + LazyColumn(modifier = GlanceModifier.fillMaxSize().background(Color.Green)) { + items( + items = state.series, + itemId = { it.userId.toLong() } + ) { item -> + Timber.i("Adding $item") + SeriesCard( + title = item.title, + progress = item.progress, + isUpdating = item.isUpdating, + updateSeries = updateSeries + ) + } + } + } + + @Composable + private fun SeriesCard( + title: String, + progress: String, + isUpdating: Boolean, + updateSeries: (String) -> Unit + ) { + Timber.i("Adding column") + Column { + Text(text = title) + Text(text = progress) + } + } +} diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt new file mode 100644 index 000000000..8b1267c41 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt @@ -0,0 +1,49 @@ +package com.chesire.nekome.feature.serieswidget.ui + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.chesire.nekome.feature.serieswidget.core.RetrieveSeriesUseCase +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber + +class SeriesWidgetViewModel @Inject constructor( + private val retrieveSeries: RetrieveSeriesUseCase +) : ViewModel() { + + private val _uiState = MutableStateFlow(UIState()) + val uiState = _uiState.asStateFlow() + + init { + viewModelScope.launch { + retrieveSeries().collect { series -> + _uiState.update { + Timber.i("Updating") + it.copy( + series = series.map { + Series( + userId = it.userId, + title = it.title, + progress = it.progress.toString(), + isUpdating = false + ) + } + ) + } + } + } + } + + fun execute(viewAction: ViewAction) { + when (viewAction) { + is ViewAction.UpdateSeries -> handleUpdateSeries(viewAction.id) + } + } + + private fun handleUpdateSeries(id: String) { + // TODO: Hit usecase + } +} diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt new file mode 100644 index 000000000..88ad7ca1e --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt @@ -0,0 +1,12 @@ +package com.chesire.nekome.feature.serieswidget.ui + +data class UIState( + val series: List = emptyList() +) + +data class Series( + val userId: Int, + val title: String, + val progress: String, + val isUpdating: Boolean +) diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt new file mode 100644 index 000000000..681b54639 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt @@ -0,0 +1,6 @@ +package com.chesire.nekome.feature.serieswidget.ui + +sealed interface ViewAction { + + data class UpdateSeries(val id: String) : ViewAction +} From bdfa137033ff920dadf6adf9883ac8b506dd8945 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 13:49:48 +0100 Subject: [PATCH 05/17] feat: update series from the widget --- .../serieswidget/core/UpdateSeriesUseCase.kt | 30 +++++++++++++++ .../feature/serieswidget/ui/SeriesWidget.kt | 37 +++++++++++++------ .../serieswidget/ui/SeriesWidgetViewModel.kt | 8 ++-- .../feature/serieswidget/ui/ViewAction.kt | 2 +- 4 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/UpdateSeriesUseCase.kt diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/UpdateSeriesUseCase.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/UpdateSeriesUseCase.kt new file mode 100644 index 000000000..8681fc4d7 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/UpdateSeriesUseCase.kt @@ -0,0 +1,30 @@ +package com.chesire.nekome.feature.serieswidget.core + +import com.chesire.nekome.datasource.series.SeriesRepository +import com.github.michaelbull.result.Result +import com.github.michaelbull.result.mapEither +import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class UpdateSeriesUseCase @Inject constructor( + private val seriesRepository: SeriesRepository +) { + + suspend operator fun invoke(seriesId: Int): Result { + val currentSeries = seriesRepository.getSeries(seriesId) + return withContext(Dispatchers.IO) { + seriesRepository + .updateSeries( + currentSeries.userId, + currentSeries.progress + 1, + currentSeries.userSeriesStatus, + currentSeries.rating + ) + .mapEither( + success = { Unit }, + failure = { Unit } + ) + } + } +} diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt index c07b3f966..5e9e2d0b6 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -4,7 +4,8 @@ import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.glance.Button import androidx.glance.GlanceId import androidx.glance.GlanceModifier import androidx.glance.LocalContext @@ -12,13 +13,16 @@ import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.lazy.LazyColumn import androidx.glance.appwidget.lazy.items import androidx.glance.appwidget.provideContent -import androidx.glance.background +import androidx.glance.layout.Alignment import androidx.glance.layout.Column +import androidx.glance.layout.Row +import androidx.glance.layout.Spacer import androidx.glance.layout.fillMaxSize +import androidx.glance.layout.fillMaxWidth +import androidx.glance.layout.padding import androidx.glance.text.Text import com.chesire.nekome.feature.serieswidget.SeriesWidgetEntryPoint import dagger.hilt.EntryPoints -import timber.log.Timber class SeriesWidget : GlanceAppWidget() { @@ -49,15 +53,19 @@ class SeriesWidget : GlanceAppWidget() { @Composable private fun Render( state: UIState, - updateSeries: (String) -> Unit + updateSeries: (Int) -> Unit ) { - LazyColumn(modifier = GlanceModifier.fillMaxSize().background(Color.Green)) { + LazyColumn( + modifier = GlanceModifier + .fillMaxSize() + .padding(8.dp) + ) { items( items = state.series, itemId = { it.userId.toLong() } ) { item -> - Timber.i("Adding $item") SeriesCard( + id = item.userId, title = item.title, progress = item.progress, isUpdating = item.isUpdating, @@ -69,15 +77,22 @@ class SeriesWidget : GlanceAppWidget() { @Composable private fun SeriesCard( + id: Int, title: String, progress: String, isUpdating: Boolean, - updateSeries: (String) -> Unit + updateSeries: (Int) -> Unit ) { - Timber.i("Adding column") - Column { - Text(text = title) - Text(text = progress) + Row( + modifier = GlanceModifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Column { + Text(text = title) + Text(text = progress) + } + Spacer(modifier = GlanceModifier.defaultWeight()) + Button(text = "+1", onClick = { updateSeries(id) }) } } } diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt index 8b1267c41..95b554a23 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt @@ -3,6 +3,7 @@ package com.chesire.nekome.feature.serieswidget.ui import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.chesire.nekome.feature.serieswidget.core.RetrieveSeriesUseCase +import com.chesire.nekome.feature.serieswidget.core.UpdateSeriesUseCase import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -11,7 +12,8 @@ import kotlinx.coroutines.launch import timber.log.Timber class SeriesWidgetViewModel @Inject constructor( - private val retrieveSeries: RetrieveSeriesUseCase + private val retrieveSeries: RetrieveSeriesUseCase, + private val updateSeries: UpdateSeriesUseCase ) : ViewModel() { private val _uiState = MutableStateFlow(UIState()) @@ -43,7 +45,7 @@ class SeriesWidgetViewModel @Inject constructor( } } - private fun handleUpdateSeries(id: String) { - // TODO: Hit usecase + private fun handleUpdateSeries(id: Int) = viewModelScope.launch { + updateSeries(id) } } diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt index 681b54639..7c845d7d5 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/ViewAction.kt @@ -2,5 +2,5 @@ package com.chesire.nekome.feature.serieswidget.ui sealed interface ViewAction { - data class UpdateSeries(val id: String) : ViewAction + data class UpdateSeries(val id: Int) : ViewAction } From a458d88d91839d86bb59eed9fd61964f3452e674 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 14:25:36 +0100 Subject: [PATCH 06/17] feat: update the look of the widget --- .../nekome/core/compose/theme/Color.kt | 4 +- .../feature/serieswidget/ui/SeriesWidget.kt | 77 +++++++++++++++---- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/core/compose/src/main/java/com/chesire/nekome/core/compose/theme/Color.kt b/core/compose/src/main/java/com/chesire/nekome/core/compose/theme/Color.kt index d55e759c3..708744e89 100644 --- a/core/compose/src/main/java/com/chesire/nekome/core/compose/theme/Color.kt +++ b/core/compose/src/main/java/com/chesire/nekome/core/compose/theme/Color.kt @@ -4,7 +4,7 @@ import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.ui.graphics.Color -internal val DarkColorPalette = darkColorScheme( +val DarkColorPalette = darkColorScheme( primary = Color(0xFF65d3ff), onPrimary = Color(0xFF003546), primaryContainer = Color(0xFF004d64), @@ -30,7 +30,7 @@ internal val DarkColorPalette = darkColorScheme( onSurfaceVariant = Color(0xFFc0c8cd) ) -internal val LightColorPalette = lightColorScheme( +val LightColorPalette = lightColorScheme( primary = Color(0xFF006783), onPrimary = Color(0xFFffffff), primaryContainer = Color(0xFFbde9ff), diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt index 5e9e2d0b6..01b2227e2 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -10,17 +10,25 @@ import androidx.glance.GlanceId import androidx.glance.GlanceModifier import androidx.glance.LocalContext import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.appWidgetBackground +import androidx.glance.appwidget.background +import androidx.glance.appwidget.cornerRadius import androidx.glance.appwidget.lazy.LazyColumn import androidx.glance.appwidget.lazy.items import androidx.glance.appwidget.provideContent +import androidx.glance.color.ColorProvider import androidx.glance.layout.Alignment import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.layout.Spacer import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxWidth +import androidx.glance.layout.height import androidx.glance.layout.padding import androidx.glance.text.Text +import androidx.glance.text.TextStyle +import com.chesire.nekome.core.compose.theme.DarkColorPalette +import com.chesire.nekome.core.compose.theme.LightColorPalette import com.chesire.nekome.feature.serieswidget.SeriesWidgetEntryPoint import dagger.hilt.EntryPoints @@ -35,11 +43,11 @@ class SeriesWidget : GlanceAppWidget() { @Composable private fun ProvideContent() { - val ctx = LocalContext.current.applicationContext + val context = LocalContext.current.applicationContext // TODO: Use remember for this? val viewModel = EntryPoints.get( - ctx, + context, SeriesWidgetEntryPoint::class.java ).seriesWidgetViewModel() @@ -57,6 +65,11 @@ class SeriesWidget : GlanceAppWidget() { ) { LazyColumn( modifier = GlanceModifier + .appWidgetBackground() + .background( + day = LightColorPalette.surface, + night = DarkColorPalette.surface + ) .fillMaxSize() .padding(8.dp) ) { @@ -64,13 +77,16 @@ class SeriesWidget : GlanceAppWidget() { items = state.series, itemId = { it.userId.toLong() } ) { item -> - SeriesCard( - id = item.userId, - title = item.title, - progress = item.progress, - isUpdating = item.isUpdating, - updateSeries = updateSeries - ) + Column { + SeriesCard( + id = item.userId, + title = item.title, + progress = item.progress, + isUpdating = item.isUpdating, + updateSeries = updateSeries + ) + Spacer(modifier = GlanceModifier.height(8.dp).fillMaxWidth()) + } } } } @@ -81,18 +97,47 @@ class SeriesWidget : GlanceAppWidget() { title: String, progress: String, isUpdating: Boolean, - updateSeries: (Int) -> Unit + updateSeries: (Int) -> Unit, + modifier: GlanceModifier = GlanceModifier ) { Row( - modifier = GlanceModifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth() + .background( + day = LightColorPalette.primaryContainer, + night = DarkColorPalette.primaryContainer + ) + .cornerRadius(8.dp) + .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { - Column { - Text(text = title) - Text(text = progress) + Column( + modifier = GlanceModifier + .defaultWeight() + .padding(end = 8.dp) + ) { + Text( + text = title, + style = TextStyle( + color = ColorProvider( + day = LightColorPalette.onPrimaryContainer, + night = DarkColorPalette.onPrimaryContainer + ) + ) + ) + Text( + text = progress, + style = TextStyle( + color = ColorProvider( + day = LightColorPalette.onPrimaryContainer, + night = DarkColorPalette.onPrimaryContainer + ) + ) + ) } - Spacer(modifier = GlanceModifier.defaultWeight()) - Button(text = "+1", onClick = { updateSeries(id) }) + Button( + text = "+1", + onClick = { updateSeries(id) } + ) } } } From 7e8794c1082fc8bbe8e78cbc58507ce47a8ec17f Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 14:27:41 +0100 Subject: [PATCH 07/17] feat: update the widget defaults Update so that we display a slightly better UI when first starting the widget. --- .../serieswidget/src/main/res/xml/series_widget_info.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/features/serieswidget/src/main/res/xml/series_widget_info.xml b/features/serieswidget/src/main/res/xml/series_widget_info.xml index 5385d34fa..d39c1d91d 100644 --- a/features/serieswidget/src/main/res/xml/series_widget_info.xml +++ b/features/serieswidget/src/main/res/xml/series_widget_info.xml @@ -1,10 +1,9 @@ + android:widgetCategory="home_screen" /> From 9ba5cac6b9508c8c9b8e48023595082cb6043d7c Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 14:31:14 +0100 Subject: [PATCH 08/17] feat: display better progress string --- .../feature/serieswidget/ui/DomainMapper.kt | 21 +++++++++++++++++++ .../serieswidget/ui/SeriesWidgetViewModel.kt | 16 +++----------- 2 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt new file mode 100644 index 000000000..d53853cf0 --- /dev/null +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt @@ -0,0 +1,21 @@ +package com.chesire.nekome.feature.serieswidget.ui + +import com.chesire.nekome.datasource.series.SeriesDomain +import javax.inject.Inject + +class DomainMapper @Inject constructor() { + + fun toSeries(domain: SeriesDomain): Series { + return Series( + userId = domain.userId, + title = domain.title, + progress = buildProgress(domain.progress, domain.totalLength), + isUpdating = false + ) + } + + private fun buildProgress(progress: Int, totalLength: Int): String { + val maxLengthString = if (totalLength == 0) "-" else totalLength + return "$progress / $maxLengthString" + } +} diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt index 95b554a23..b120082c1 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt @@ -9,11 +9,11 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import timber.log.Timber class SeriesWidgetViewModel @Inject constructor( private val retrieveSeries: RetrieveSeriesUseCase, - private val updateSeries: UpdateSeriesUseCase + private val updateSeries: UpdateSeriesUseCase, + private val mapper: DomainMapper ) : ViewModel() { private val _uiState = MutableStateFlow(UIState()) @@ -23,17 +23,7 @@ class SeriesWidgetViewModel @Inject constructor( viewModelScope.launch { retrieveSeries().collect { series -> _uiState.update { - Timber.i("Updating") - it.copy( - series = series.map { - Series( - userId = it.userId, - title = it.title, - progress = it.progress.toString(), - isUpdating = false - ) - } - ) + it.copy(series = series.map(mapper::toSeries)) } } } From 987edee310541f87db6fe1f093165afa073d3105 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 14:43:56 +0100 Subject: [PATCH 09/17] feat: set max title length to 3 lines --- .../chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt | 6 +++--- .../nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt | 6 ++++-- .../com/chesire/nekome/feature/serieswidget/ui/UIState.kt | 3 +-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt index 01b2227e2..4fb5714ff 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -82,7 +82,6 @@ class SeriesWidget : GlanceAppWidget() { id = item.userId, title = item.title, progress = item.progress, - isUpdating = item.isUpdating, updateSeries = updateSeries ) Spacer(modifier = GlanceModifier.height(8.dp).fillMaxWidth()) @@ -96,7 +95,6 @@ class SeriesWidget : GlanceAppWidget() { id: Int, title: String, progress: String, - isUpdating: Boolean, updateSeries: (Int) -> Unit, modifier: GlanceModifier = GlanceModifier ) { @@ -122,7 +120,8 @@ class SeriesWidget : GlanceAppWidget() { day = LightColorPalette.onPrimaryContainer, night = DarkColorPalette.onPrimaryContainer ) - ) + ), + maxLines = 3 ) Text( text = progress, @@ -134,6 +133,7 @@ class SeriesWidget : GlanceAppWidget() { ) ) } + Button( text = "+1", onClick = { updateSeries(id) } diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt index b120082c1..0bbb32dec 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt @@ -35,7 +35,9 @@ class SeriesWidgetViewModel @Inject constructor( } } - private fun handleUpdateSeries(id: Int) = viewModelScope.launch { - updateSeries(id) + private fun handleUpdateSeries(id: Int) { + viewModelScope.launch { + updateSeries(id) + } } } diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt index 88ad7ca1e..9c9b8c544 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt @@ -7,6 +7,5 @@ data class UIState( data class Series( val userId: Int, val title: String, - val progress: String, - val isUpdating: Boolean + val progress: String ) From a99ea01fe60eaeda3ea2dd761cae87d757d17c4c Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 14:46:13 +0100 Subject: [PATCH 10/17] refactor: move the provideGlance method up --- .../feature/serieswidget/ui/DomainMapper.kt | 3 +-- .../feature/serieswidget/ui/SeriesWidget.kt | 24 ++++++------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt index d53853cf0..cb369c53d 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt @@ -9,8 +9,7 @@ class DomainMapper @Inject constructor() { return Series( userId = domain.userId, title = domain.title, - progress = buildProgress(domain.progress, domain.totalLength), - isUpdating = false + progress = buildProgress(domain.progress, domain.totalLength) ) } diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt index 4fb5714ff..8c9ed24b8 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -8,7 +8,6 @@ import androidx.compose.ui.unit.dp import androidx.glance.Button import androidx.glance.GlanceId import androidx.glance.GlanceModifier -import androidx.glance.LocalContext import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.appWidgetBackground import androidx.glance.appwidget.background @@ -35,27 +34,18 @@ import dagger.hilt.EntryPoints class SeriesWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { - - provideContent { - ProvideContent() - } - } - - @Composable - private fun ProvideContent() { - val context = LocalContext.current.applicationContext - - // TODO: Use remember for this? val viewModel = EntryPoints.get( context, SeriesWidgetEntryPoint::class.java ).seriesWidgetViewModel() - val state by viewModel.uiState.collectAsState() - Render( - state = state, - updateSeries = { viewModel.execute(ViewAction.UpdateSeries(it)) } - ) + provideContent { + val state by viewModel.uiState.collectAsState() + Render( + state = state, + updateSeries = { viewModel.execute(ViewAction.UpdateSeries(it)) } + ) + } } @Composable From 792d15982b3d79510403acd693a66aae844f6423 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 14:50:17 +0100 Subject: [PATCH 11/17] feat: show progress indicator while performing update --- .../feature/serieswidget/ui/DomainMapper.kt | 3 ++- .../feature/serieswidget/ui/SeriesWidget.kt | 26 ++++++++++++++----- .../serieswidget/ui/SeriesWidgetViewModel.kt | 25 ++++++++++++++++++ .../nekome/feature/serieswidget/ui/UIState.kt | 3 ++- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt index cb369c53d..d53853cf0 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/DomainMapper.kt @@ -9,7 +9,8 @@ class DomainMapper @Inject constructor() { return Series( userId = domain.userId, title = domain.title, - progress = buildProgress(domain.progress, domain.totalLength) + progress = buildProgress(domain.progress, domain.totalLength), + isUpdating = false ) } diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt index 8c9ed24b8..08d5b37a3 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.unit.dp import androidx.glance.Button import androidx.glance.GlanceId import androidx.glance.GlanceModifier +import androidx.glance.appwidget.CircularProgressIndicator import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.appWidgetBackground import androidx.glance.appwidget.background @@ -72,6 +73,7 @@ class SeriesWidget : GlanceAppWidget() { id = item.userId, title = item.title, progress = item.progress, + isUpdating = item.isUpdating, updateSeries = updateSeries ) Spacer(modifier = GlanceModifier.height(8.dp).fillMaxWidth()) @@ -85,11 +87,11 @@ class SeriesWidget : GlanceAppWidget() { id: Int, title: String, progress: String, - updateSeries: (Int) -> Unit, - modifier: GlanceModifier = GlanceModifier + isUpdating: Boolean, + updateSeries: (Int) -> Unit ) { Row( - modifier = modifier.fillMaxWidth() + modifier = GlanceModifier.fillMaxWidth() .background( day = LightColorPalette.primaryContainer, night = DarkColorPalette.primaryContainer @@ -124,10 +126,20 @@ class SeriesWidget : GlanceAppWidget() { ) } - Button( - text = "+1", - onClick = { updateSeries(id) } - ) + if (isUpdating) { + CircularProgressIndicator( + color = ColorProvider( + day = LightColorPalette.onPrimaryContainer, + night = DarkColorPalette.onPrimaryContainer + ), + modifier = GlanceModifier.height(36.dp) + ) + } else { + Button( + text = "+1", + onClick = { updateSeries(id) } + ) + } } } } diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt index 0bbb32dec..9521ad5ac 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidgetViewModel.kt @@ -4,6 +4,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.chesire.nekome.feature.serieswidget.core.RetrieveSeriesUseCase import com.chesire.nekome.feature.serieswidget.core.UpdateSeriesUseCase +import com.github.michaelbull.result.onFailure +import com.github.michaelbull.result.onSuccess import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -36,8 +38,31 @@ class SeriesWidgetViewModel @Inject constructor( } private fun handleUpdateSeries(id: Int) { + _uiState.update { + it.copy(series = updateIsUpdating(id, true)) + } viewModelScope.launch { updateSeries(id) + .onSuccess { + _uiState.update { + it.copy(series = updateIsUpdating(id, false)) + } + } + .onFailure { + _uiState.update { + it.copy(series = updateIsUpdating(id, false)) + } + } + } + } + + private fun updateIsUpdating(id: Int, isUpdating: Boolean): List { + return _uiState.value.series.map { + if (it.userId == id) { + it.copy(isUpdating = isUpdating) + } else { + it + } } } } diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt index 9c9b8c544..88ad7ca1e 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/UIState.kt @@ -7,5 +7,6 @@ data class UIState( data class Series( val userId: Int, val title: String, - val progress: String + val progress: String, + val isUpdating: Boolean ) From 1dc6d2f27d4aad568c6381e28f879a2a599715bc Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 14:53:29 +0100 Subject: [PATCH 12/17] feat: update the text style for title slightly --- .../chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt index 08d5b37a3..e4b81374b 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -25,6 +25,7 @@ import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.height import androidx.glance.layout.padding +import androidx.glance.text.FontWeight import androidx.glance.text.Text import androidx.glance.text.TextStyle import com.chesire.nekome.core.compose.theme.DarkColorPalette @@ -111,7 +112,8 @@ class SeriesWidget : GlanceAppWidget() { color = ColorProvider( day = LightColorPalette.onPrimaryContainer, night = DarkColorPalette.onPrimaryContainer - ) + ), + fontWeight = FontWeight.Medium ), maxLines = 3 ) From 0783e54fbc6b3cd58c14c9663ffcc0a6da562dc1 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 14:59:18 +0100 Subject: [PATCH 13/17] refactor: update to use string resources --- core/resources/src/main/res/values-ja/strings.xml | 3 +++ core/resources/src/main/res/values/strings.xml | 3 +++ .../chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt | 7 ++++++- .../serieswidget/src/main/res/xml/series_widget_info.xml | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/core/resources/src/main/res/values-ja/strings.xml b/core/resources/src/main/res/values-ja/strings.xml index ea7aa4c99..5c71b05b9 100644 --- a/core/resources/src/main/res/values-ja/strings.xml +++ b/core/resources/src/main/res/values-ja/strings.xml @@ -81,6 +81,9 @@ シリーズ%sの更新に失敗しました。再試行してください デリート + アニメ一覧 + +1 + 評価なし Kitsu eメール diff --git a/core/resources/src/main/res/values/strings.xml b/core/resources/src/main/res/values/strings.xml index b5274ca70..c2bd00f80 100644 --- a/core/resources/src/main/res/values/strings.xml +++ b/core/resources/src/main/res/values/strings.xml @@ -81,6 +81,9 @@ Failed to update series %s, please try again Delete + Anime list + +1 + No rating Kitsu email diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt index e4b81374b..8ee44c0ca 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -31,6 +31,7 @@ import androidx.glance.text.TextStyle import com.chesire.nekome.core.compose.theme.DarkColorPalette import com.chesire.nekome.core.compose.theme.LightColorPalette import com.chesire.nekome.feature.serieswidget.SeriesWidgetEntryPoint +import com.chesire.nekome.resources.StringResource import dagger.hilt.EntryPoints class SeriesWidget : GlanceAppWidget() { @@ -45,6 +46,7 @@ class SeriesWidget : GlanceAppWidget() { val state by viewModel.uiState.collectAsState() Render( state = state, + incrementText = context.getString(StringResource.series_widget_increment), updateSeries = { viewModel.execute(ViewAction.UpdateSeries(it)) } ) } @@ -53,6 +55,7 @@ class SeriesWidget : GlanceAppWidget() { @Composable private fun Render( state: UIState, + incrementText: String, updateSeries: (Int) -> Unit ) { LazyColumn( @@ -75,6 +78,7 @@ class SeriesWidget : GlanceAppWidget() { title = item.title, progress = item.progress, isUpdating = item.isUpdating, + incrementText = incrementText, updateSeries = updateSeries ) Spacer(modifier = GlanceModifier.height(8.dp).fillMaxWidth()) @@ -89,6 +93,7 @@ class SeriesWidget : GlanceAppWidget() { title: String, progress: String, isUpdating: Boolean, + incrementText: String, updateSeries: (Int) -> Unit ) { Row( @@ -138,7 +143,7 @@ class SeriesWidget : GlanceAppWidget() { ) } else { Button( - text = "+1", + text = incrementText, onClick = { updateSeries(id) } ) } diff --git a/features/serieswidget/src/main/res/xml/series_widget_info.xml b/features/serieswidget/src/main/res/xml/series_widget_info.xml index d39c1d91d..513db493e 100644 --- a/features/serieswidget/src/main/res/xml/series_widget_info.xml +++ b/features/serieswidget/src/main/res/xml/series_widget_info.xml @@ -3,7 +3,7 @@ android:minWidth="80dp" android:minHeight="80dp" android:updatePeriodMillis="86400000" - android:description="@string/nav_anime" + android:description="@string/series_widget_description" android:initialLayout="@layout/glance_default_loading_layout" android:resizeMode="horizontal|vertical" android:widgetCategory="home_screen" /> From b1cea1c48d8c546bdb0bf5d98ad10ddff790e666 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 15:21:13 +0100 Subject: [PATCH 14/17] feat: sort the series based on user preference --- features/serieswidget/build.gradle.kts | 1 + .../serieswidget/core/RetrieveSeriesUseCase.kt | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/features/serieswidget/build.gradle.kts b/features/serieswidget/build.gradle.kts index 8c24273f6..bd56bcdfc 100644 --- a/features/serieswidget/build.gradle.kts +++ b/features/serieswidget/build.gradle.kts @@ -24,6 +24,7 @@ android { dependencies { implementation(project(":core:compose")) + implementation(project(":core:preferences")) implementation(project(":core:resources")) implementation(project(":libraries:core")) implementation(project(":libraries:datasource:series")) diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/RetrieveSeriesUseCase.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/RetrieveSeriesUseCase.kt index 8bb13e311..5994493d0 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/RetrieveSeriesUseCase.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/core/RetrieveSeriesUseCase.kt @@ -2,17 +2,22 @@ package com.chesire.nekome.feature.serieswidget.core import com.chesire.nekome.core.flags.SeriesType import com.chesire.nekome.core.flags.UserSeriesStatus +import com.chesire.nekome.core.preferences.SeriesPreferences +import com.chesire.nekome.core.preferences.flags.SortOption import com.chesire.nekome.datasource.series.SeriesDomain import com.chesire.nekome.datasource.series.SeriesRepository import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map class RetrieveSeriesUseCase @Inject constructor( - private val seriesRepository: SeriesRepository + private val seriesRepository: SeriesRepository, + private val pref: SeriesPreferences ) { - operator fun invoke(): Flow> { + suspend operator fun invoke(): Flow> { + val sortOption = pref.sort.first() return seriesRepository .getSeries() .map { seriesList -> @@ -20,6 +25,15 @@ class RetrieveSeriesUseCase @Inject constructor( .filter { series -> series.type == SeriesType.Anime } .filter { series -> series.userSeriesStatus == UserSeriesStatus.Current } .filter { series -> series.totalLength == 0 || series.progress < series.totalLength } + .sortedWith( + when (sortOption) { + SortOption.Default -> compareBy { it.userId } + SortOption.Title -> compareBy { it.title } + SortOption.StartDate -> compareBy { it.startDate } + SortOption.EndDate -> compareBy { it.endDate } + SortOption.Rating -> compareBy { it.rating } + } + ) } } } From 4ad87a2804b242a848c92ebe5f4ca4a8941e87f9 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 16:03:24 +0100 Subject: [PATCH 15/17] feat: notify glance widget of data updates When the data updates such as updating a series progress, or adding a new series, we need to notify the glance widget. This is done via a singleton class that listens to the repository, and on updates to the data fires the required worker to update the widget. --- app/build.gradle.kts | 1 + app/src/main/java/com/chesire/nekome/App.kt | 11 ++++++ .../nekome/services/DataRefreshNotifier.kt | 35 +++++++++++++++++++ .../nekome/services/WidgetDataWorker.kt | 18 ++++++++++ 4 files changed, 65 insertions(+) create mode 100644 app/src/main/java/com/chesire/nekome/services/DataRefreshNotifier.kt create mode 100644 app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index edabd1478..be5ea1dbc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -90,6 +90,7 @@ dependencies { implementation(libs.androidx.constraintlayout) implementation(libs.androidx.core) implementation(libs.androidx.fragment) + implementation(libs.androidx.glance.appwidget) implementation(libs.androidx.hilt.work) implementation(libs.androidx.lifecycle.extensions) implementation(libs.androidx.lifecycle.livedata) diff --git a/app/src/main/java/com/chesire/nekome/App.kt b/app/src/main/java/com/chesire/nekome/App.kt index 9fa9c27b3..5022b74db 100644 --- a/app/src/main/java/com/chesire/nekome/App.kt +++ b/app/src/main/java/com/chesire/nekome/App.kt @@ -7,9 +7,13 @@ import androidx.work.Configuration import com.chesire.lifecyklelog.LifecykleLog import com.chesire.lifecyklelog.LogHandler import com.chesire.nekome.core.preferences.ApplicationPreferences +import com.chesire.nekome.services.DataRefreshNotifier import com.chesire.nekome.services.WorkerQueue import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import timber.log.Timber /** @@ -27,6 +31,10 @@ class App : Application(), Configuration.Provider { @Inject lateinit var workerQueue: WorkerQueue + @Inject + lateinit var dataRefreshNotifier: DataRefreshNotifier + + @OptIn(DelicateCoroutinesApi::class) override fun onCreate() { super.onCreate() @@ -46,6 +54,9 @@ class App : Application(), Configuration.Provider { workerQueue.enqueueAuthRefresh() workerQueue.enqueueSeriesRefresh() workerQueue.enqueueUserRefresh() + GlobalScope.launch { + dataRefreshNotifier.initialize() + } } override fun getWorkManagerConfiguration() = diff --git a/app/src/main/java/com/chesire/nekome/services/DataRefreshNotifier.kt b/app/src/main/java/com/chesire/nekome/services/DataRefreshNotifier.kt new file mode 100644 index 000000000..856f73de3 --- /dev/null +++ b/app/src/main/java/com/chesire/nekome/services/DataRefreshNotifier.kt @@ -0,0 +1,35 @@ +package com.chesire.nekome.services + +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import com.chesire.nekome.datasource.series.SeriesRepository +import javax.inject.Inject +import javax.inject.Singleton + +private const val WIDGET_DATA_NOTIFY_TAG = "WidgetData" +private const val WIDGET_DATA_UNIQUE_NAME = "WidgetSync" + +@Singleton +class DataRefreshNotifier @Inject constructor( + private val workManager: WorkManager, + private val seriesRepository: SeriesRepository +) { + + /** + * Initialize the notifier and listen to any data updates. + */ + suspend fun initialize() { + seriesRepository.getSeries().collect { + val request = OneTimeWorkRequestBuilder() + .addTag(WIDGET_DATA_NOTIFY_TAG) + .build() + + workManager.enqueueUniqueWork( + WIDGET_DATA_UNIQUE_NAME, + ExistingWorkPolicy.REPLACE, + request + ) + } + } +} diff --git a/app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt b/app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt new file mode 100644 index 000000000..c211647f7 --- /dev/null +++ b/app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt @@ -0,0 +1,18 @@ +package com.chesire.nekome.services + +import android.content.Context +import androidx.glance.appwidget.updateAll +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters +import com.chesire.nekome.feature.serieswidget.ui.SeriesWidget + +class WidgetDataWorker( + private val context: Context, + params: WorkerParameters, +) : CoroutineWorker(context, params) { + + override suspend fun doWork(): Result { + SeriesWidget().updateAll(context) + return Result.success() + } +} From 2e7825b0c9e79c7dc4fdf41637c17f4bc90f78fc Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 16:10:50 +0100 Subject: [PATCH 16/17] style: ktlint fixes --- .../main/java/com/chesire/nekome/services/WidgetDataWorker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt b/app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt index c211647f7..bac4987bd 100644 --- a/app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt +++ b/app/src/main/java/com/chesire/nekome/services/WidgetDataWorker.kt @@ -8,7 +8,7 @@ import com.chesire.nekome.feature.serieswidget.ui.SeriesWidget class WidgetDataWorker( private val context: Context, - params: WorkerParameters, + params: WorkerParameters ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { From 1de39773977e3d231160187ec59ad9d164ba70c7 Mon Sep 17 00:00:00 2001 From: Troy Rijkaard Date: Sun, 8 Oct 2023 16:12:01 +0100 Subject: [PATCH 17/17] style: detekt fixes --- .../com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt index 8ee44c0ca..a6c62a2f3 100644 --- a/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt +++ b/features/serieswidget/src/main/java/com/chesire/nekome/feature/serieswidget/ui/SeriesWidget.kt @@ -87,6 +87,7 @@ class SeriesWidget : GlanceAppWidget() { } } + @Suppress("LongParameterList") @Composable private fun SeriesCard( id: Int,