mirror of
https://github.com/Chesire/Nekome.git
synced 2026-03-13 09:01:28 +08:00
feat: add basic series widget (#1094)
* chore: add the required glance dependencies * feat: add new module for the widget * feat: allow adding widget to home screen * feat: display all active series on widget * feat: update series from the widget * feat: update the look of the widget * feat: update the widget defaults Update so that we display a slightly better UI when first starting the widget. * feat: display better progress string * feat: set max title length to 3 lines * refactor: move the provideGlance method up * feat: show progress indicator while performing update * feat: update the text style for title slightly * refactor: update to use string resources * feat: sort the series based on user preference * 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. * style: ktlint fixes * style: detekt fixes
This commit is contained in:
@@ -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"))
|
||||
@@ -89,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)
|
||||
|
||||
@@ -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() =
|
||||
|
||||
@@ -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<WidgetDataWorker>()
|
||||
.addTag(WIDGET_DATA_NOTIFY_TAG)
|
||||
.build()
|
||||
|
||||
workManager.enqueueUniqueWork(
|
||||
WIDGET_DATA_UNIQUE_NAME,
|
||||
ExistingWorkPolicy.REPLACE,
|
||||
request
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -81,6 +81,9 @@
|
||||
<string name="series_detail_failure">シリーズ%sの更新に失敗しました。再試行してください</string>
|
||||
<string name="series_detail_delete">デリート</string>
|
||||
|
||||
<string name="series_widget_description">アニメ一覧</string>
|
||||
<string name="series_widget_increment">+1</string>
|
||||
|
||||
<string name="rating_none">評価なし</string>
|
||||
|
||||
<string name="login_username">Kitsu eメール</string>
|
||||
|
||||
@@ -81,6 +81,9 @@
|
||||
<string name="series_detail_failure">Failed to update series %s, please try again</string>
|
||||
<string name="series_detail_delete">Delete</string>
|
||||
|
||||
<string name="series_widget_description">Anime list</string>
|
||||
<string name="series_widget_increment">+1</string>
|
||||
|
||||
<string name="rating_none">No rating</string>
|
||||
|
||||
<string name="login_username">Kitsu email</string>
|
||||
|
||||
1
features/serieswidget/.gitignore
vendored
Normal file
1
features/serieswidget/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
40
features/serieswidget/build.gradle.kts
Normal file
40
features/serieswidget/build.gradle.kts
Normal file
@@ -0,0 +1,40 @@
|
||||
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(project(":core:preferences"))
|
||||
implementation(project(":core:resources"))
|
||||
implementation(project(":libraries:core"))
|
||||
implementation(project(":libraries:datasource:series"))
|
||||
|
||||
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)
|
||||
}
|
||||
0
features/serieswidget/consumer-rules.pro
Normal file
0
features/serieswidget/consumer-rules.pro
Normal file
16
features/serieswidget/src/main/AndroidManifest.xml
Normal file
16
features/serieswidget/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<receiver
|
||||
android:name="GlanceDataReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/series_widget_info" />
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,10 @@
|
||||
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
|
||||
get() = SeriesWidget()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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 pref: SeriesPreferences
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(): Flow<List<SeriesDomain>> {
|
||||
val sortOption = pref.sort.first()
|
||||
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 }
|
||||
.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 }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Unit, Unit> {
|
||||
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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
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.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
|
||||
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.FontWeight
|
||||
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 com.chesire.nekome.resources.StringResource
|
||||
import dagger.hilt.EntryPoints
|
||||
|
||||
class SeriesWidget : GlanceAppWidget() {
|
||||
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
val viewModel = EntryPoints.get(
|
||||
context,
|
||||
SeriesWidgetEntryPoint::class.java
|
||||
).seriesWidgetViewModel()
|
||||
|
||||
provideContent {
|
||||
val state by viewModel.uiState.collectAsState()
|
||||
Render(
|
||||
state = state,
|
||||
incrementText = context.getString(StringResource.series_widget_increment),
|
||||
updateSeries = { viewModel.execute(ViewAction.UpdateSeries(it)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Render(
|
||||
state: UIState,
|
||||
incrementText: String,
|
||||
updateSeries: (Int) -> Unit
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = GlanceModifier
|
||||
.appWidgetBackground()
|
||||
.background(
|
||||
day = LightColorPalette.surface,
|
||||
night = DarkColorPalette.surface
|
||||
)
|
||||
.fillMaxSize()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
items(
|
||||
items = state.series,
|
||||
itemId = { it.userId.toLong() }
|
||||
) { item ->
|
||||
Column {
|
||||
SeriesCard(
|
||||
id = item.userId,
|
||||
title = item.title,
|
||||
progress = item.progress,
|
||||
isUpdating = item.isUpdating,
|
||||
incrementText = incrementText,
|
||||
updateSeries = updateSeries
|
||||
)
|
||||
Spacer(modifier = GlanceModifier.height(8.dp).fillMaxWidth())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun SeriesCard(
|
||||
id: Int,
|
||||
title: String,
|
||||
progress: String,
|
||||
isUpdating: Boolean,
|
||||
incrementText: String,
|
||||
updateSeries: (Int) -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = GlanceModifier.fillMaxWidth()
|
||||
.background(
|
||||
day = LightColorPalette.primaryContainer,
|
||||
night = DarkColorPalette.primaryContainer
|
||||
)
|
||||
.cornerRadius(8.dp)
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = GlanceModifier
|
||||
.defaultWeight()
|
||||
.padding(end = 8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = TextStyle(
|
||||
color = ColorProvider(
|
||||
day = LightColorPalette.onPrimaryContainer,
|
||||
night = DarkColorPalette.onPrimaryContainer
|
||||
),
|
||||
fontWeight = FontWeight.Medium
|
||||
),
|
||||
maxLines = 3
|
||||
)
|
||||
Text(
|
||||
text = progress,
|
||||
style = TextStyle(
|
||||
color = ColorProvider(
|
||||
day = LightColorPalette.onPrimaryContainer,
|
||||
night = DarkColorPalette.onPrimaryContainer
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (isUpdating) {
|
||||
CircularProgressIndicator(
|
||||
color = ColorProvider(
|
||||
day = LightColorPalette.onPrimaryContainer,
|
||||
night = DarkColorPalette.onPrimaryContainer
|
||||
),
|
||||
modifier = GlanceModifier.height(36.dp)
|
||||
)
|
||||
} else {
|
||||
Button(
|
||||
text = incrementText,
|
||||
onClick = { updateSeries(id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
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 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
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SeriesWidgetViewModel @Inject constructor(
|
||||
private val retrieveSeries: RetrieveSeriesUseCase,
|
||||
private val updateSeries: UpdateSeriesUseCase,
|
||||
private val mapper: DomainMapper
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableStateFlow(UIState())
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
retrieveSeries().collect { series ->
|
||||
_uiState.update {
|
||||
it.copy(series = series.map(mapper::toSeries))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun execute(viewAction: ViewAction) {
|
||||
when (viewAction) {
|
||||
is ViewAction.UpdateSeries -> handleUpdateSeries(viewAction.id)
|
||||
}
|
||||
}
|
||||
|
||||
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<Series> {
|
||||
return _uiState.value.series.map {
|
||||
if (it.userId == id) {
|
||||
it.copy(isUpdating = isUpdating)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.chesire.nekome.feature.serieswidget.ui
|
||||
|
||||
data class UIState(
|
||||
val series: List<Series> = emptyList()
|
||||
)
|
||||
|
||||
data class Series(
|
||||
val userId: Int,
|
||||
val title: String,
|
||||
val progress: String,
|
||||
val isUpdating: Boolean
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.chesire.nekome.feature.serieswidget.ui
|
||||
|
||||
sealed interface ViewAction {
|
||||
|
||||
data class UpdateSeries(val id: Int) : ViewAction
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="80dp"
|
||||
android:minHeight="80dp"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:description="@string/series_widget_description"
|
||||
android:initialLayout="@layout/glance_default_loading_layout"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
android:widgetCategory="home_screen" />
|
||||
@@ -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" }
|
||||
|
||||
@@ -23,6 +23,7 @@ include(
|
||||
":features:login",
|
||||
":features:search",
|
||||
":features:series",
|
||||
":features:serieswidget",
|
||||
":features:settings",
|
||||
":libraries:core",
|
||||
":libraries:database",
|
||||
|
||||
Reference in New Issue
Block a user