From 73d4ba24f951b8d4075e3f198846db99aa624736 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 8 Dec 2024 17:50:34 +0100 Subject: [PATCH] feat: rework wallpaper changer - allow multiple configs at the same time --- .../bnyro/wallpaper/enums/WallpaperSource.kt | 1 - .../bnyro/wallpaper/obj/WallpaperConfig.kt | 26 +- .../bnyro/wallpaper/receivers/BootReceiver.kt | 3 +- .../ui/components/WallpaperChangerPref.kt | 259 ++++++++++++------ .../ui/components/prefs/BlockPreference.kt | 8 +- .../bnyro/wallpaper/ui/pages/SettingsPage.kt | 155 ++++++----- .../bnyro/wallpaper/util/BackgroundWorker.kt | 29 +- .../com/bnyro/wallpaper/util/Preferences.kt | 7 +- .../com/bnyro/wallpaper/util/WorkerHelper.kt | 61 +++-- app/src/main/res/values/strings.xml | 1 + 10 files changed, 363 insertions(+), 187 deletions(-) diff --git a/app/src/main/java/com/bnyro/wallpaper/enums/WallpaperSource.kt b/app/src/main/java/com/bnyro/wallpaper/enums/WallpaperSource.kt index 8867609e..92495528 100644 --- a/app/src/main/java/com/bnyro/wallpaper/enums/WallpaperSource.kt +++ b/app/src/main/java/com/bnyro/wallpaper/enums/WallpaperSource.kt @@ -4,5 +4,4 @@ enum class WallpaperSource { ONLINE, FAVORITES, LOCAL, - NONE; } diff --git a/app/src/main/java/com/bnyro/wallpaper/obj/WallpaperConfig.kt b/app/src/main/java/com/bnyro/wallpaper/obj/WallpaperConfig.kt index 76b709ab..a563ac0d 100644 --- a/app/src/main/java/com/bnyro/wallpaper/obj/WallpaperConfig.kt +++ b/app/src/main/java/com/bnyro/wallpaper/obj/WallpaperConfig.kt @@ -1,15 +1,39 @@ package com.bnyro.wallpaper.obj +import android.content.Context +import androidx.work.NetworkType +import com.bnyro.wallpaper.R import com.bnyro.wallpaper.enums.WallpaperSource import com.bnyro.wallpaper.enums.WallpaperTarget +import com.bnyro.wallpaper.ext.formatMinutes import com.bnyro.wallpaper.ui.nav.DrawerScreens import kotlinx.serialization.Serializable @Serializable data class WallpaperConfig( + val id: Int, + var changeIntervalMinutes: Int = 12 * 60, + var networkType: NetworkType = NetworkType.CONNECTED, var target: WallpaperTarget = WallpaperTarget.BOTH, var source: WallpaperSource = WallpaperSource.ONLINE, var applyImageFilters: Boolean = true, var selectedApiRoutes: List = listOf(DrawerScreens.apiScreens[0].route), var localFolderUris: List = listOf() -) +) { + fun getSummary(context: Context): String { + val targetString = when (target) { + WallpaperTarget.HOME -> R.string.home + WallpaperTarget.LOCK -> R.string.lockscreen + WallpaperTarget.BOTH -> R.string.both + } + val sourceString = when (source) { + WallpaperSource.LOCAL -> R.string.local + WallpaperSource.ONLINE -> R.string.online + WallpaperSource.FAVORITES -> R.string.favorites + } + + return "${context.getString(sourceString)} - ${context.getString(targetString)} (${ + changeIntervalMinutes.toLong().formatMinutes() + })" + } +} diff --git a/app/src/main/java/com/bnyro/wallpaper/receivers/BootReceiver.kt b/app/src/main/java/com/bnyro/wallpaper/receivers/BootReceiver.kt index c1b57bf2..bc72dca4 100644 --- a/app/src/main/java/com/bnyro/wallpaper/receivers/BootReceiver.kt +++ b/app/src/main/java/com/bnyro/wallpaper/receivers/BootReceiver.kt @@ -3,10 +3,11 @@ package com.bnyro.wallpaper.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import com.bnyro.wallpaper.util.Preferences import com.bnyro.wallpaper.util.WorkerHelper class BootReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - WorkerHelper.enqueue(context, true) + WorkerHelper.enqueueOrCancelAll(context, Preferences.getWallpaperConfigs(), true) } } diff --git a/app/src/main/java/com/bnyro/wallpaper/ui/components/WallpaperChangerPref.kt b/app/src/main/java/com/bnyro/wallpaper/ui/components/WallpaperChangerPref.kt index d905a50b..5b2e6c08 100644 --- a/app/src/main/java/com/bnyro/wallpaper/ui/components/WallpaperChangerPref.kt +++ b/app/src/main/java/com/bnyro/wallpaper/ui/components/WallpaperChangerPref.kt @@ -1,6 +1,7 @@ package com.bnyro.wallpaper.ui.components import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -10,11 +11,13 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -26,10 +29,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.net.toUri import androidx.documentfile.provider.DocumentFile +import androidx.work.NetworkType import com.bnyro.wallpaper.R import com.bnyro.wallpaper.obj.WallpaperConfig import com.bnyro.wallpaper.enums.WallpaperSource import com.bnyro.wallpaper.enums.WallpaperTarget +import com.bnyro.wallpaper.ext.formatMinutes import com.bnyro.wallpaper.ui.components.prefs.CheckboxPref import com.bnyro.wallpaper.ui.components.prefs.MultiSelectionBlockPreference import com.bnyro.wallpaper.ui.components.prefs.ListPreference @@ -38,119 +43,197 @@ import com.bnyro.wallpaper.ui.nav.DrawerScreens import com.bnyro.wallpaper.util.PickFolderContract import com.bnyro.wallpaper.util.str +private val changeIntervals = listOf( + 15L, + 30L, + 60L, + 180L, + 360L, + 720L, + 1440L +) + +private val networkTypes = listOf( + R.string.all_networks to NetworkType.CONNECTED, + R.string.unmetered to NetworkType.UNMETERED, + R.string.metered to NetworkType.METERED, + R.string.not_roaming to NetworkType.NOT_ROAMING, +) + @Composable -fun WallpaperChangerPref(config: WallpaperConfig, onChange: (WallpaperConfig) -> Unit) { +fun WallpaperChangerPrefDialog( + config: WallpaperConfig, + onConfigChange: (WallpaperConfig) -> Unit, + onDismissRequest: () -> Unit +) { val context = LocalContext.current + var wallpaperSource by remember { mutableStateOf(config.source) } + var wallpaperTarget by remember { mutableStateOf(config.target) } + var changeInterval by remember { mutableIntStateOf(config.changeIntervalMinutes) } + var networkType by remember { mutableStateOf(config.networkType) } + val wallpaperSources = + listOf(R.string.online, R.string.favorites, R.string.local, R.string.none) + var wallpaperEnginesIndices = remember { + config.selectedApiRoutes.map { route -> + DrawerScreens.apiScreens.indexOfFirst { it.route == route } + } + } val localFolderUris = remember { config.localFolderUris.toMutableStateList() } - - val localWallpaperDirChooser = rememberLauncherForActivityResult(PickFolderContract()) { - val uri = it ?: return@rememberLauncherForActivityResult - - config.localFolderUris += uri.toString() - localFolderUris.add(uri.toString()) - onChange(config) + var applyImageFilters by remember { + mutableStateOf(config.applyImageFilters) } - SettingsCategory( - title = stringResource( - when (config.target) { - WallpaperTarget.BOTH -> R.string.both - WallpaperTarget.HOME -> R.string.home - WallpaperTarget.LOCK -> R.string.lockscreen + AlertDialog( + onDismissRequest = onDismissRequest, + dismissButton = { + DialogButton(stringResource(android.R.string.cancel)) { + onDismissRequest() } - ) - ) - val wallpaperSources = listOf(R.string.online, R.string.favorites, R.string.local, R.string.none) - var wallpaperSource by remember { mutableStateOf(config.source) } - ListPreference( - prefKey = null, - title = stringResource(R.string.wallpaper_changer_source), - entries = wallpaperSources.map { stringResource(it) }, - values = List(wallpaperSources.size) { index -> index.toString() }, - defaultValue = wallpaperSource.ordinal.toString() - ) { newValue -> - config.source = WallpaperSource.values()[newValue.toInt()] - wallpaperSource = config.source - onChange(config) - } + }, + confirmButton = { + DialogButton(stringResource(android.R.string.ok)) { + val newConfig = WallpaperConfig( + id = config.id, + changeIntervalMinutes = changeInterval, + networkType = networkType, + target = wallpaperTarget, + source = wallpaperSource, + selectedApiRoutes = wallpaperEnginesIndices.map { DrawerScreens.apiScreens[it].route }, + localFolderUris = localFolderUris, + applyImageFilters = applyImageFilters + ) + onConfigChange(newConfig) + onDismissRequest() + } + }, + title = { + Text(stringResource(R.string.wallpaper_changer)) + }, + text = { + val localWallpaperDirChooser = rememberLauncherForActivityResult(PickFolderContract()) { + val uri = it ?: return@rememberLauncherForActivityResult - Crossfade(targetState = wallpaperSource, label = "wallpaper_source") { state -> - when (state) { - WallpaperSource.ONLINE -> { - var currentSelections = remember { - config.selectedApiRoutes.map { route -> - DrawerScreens.apiScreens.indexOfFirst { it.route == route } - } - } + config.localFolderUris += uri.toString() + localFolderUris.add(uri.toString()) + } + + Column { MultiSelectionBlockPreference( preferenceKey = null, - entries = DrawerScreens.apiScreens.map { it.title.str() }, - values = DrawerScreens.apiScreens.map { it.route }, - defaultSelections = currentSelections + entries = listOf(R.string.home, R.string.lockscreen).map { stringResource(it) }, + values = listOf(WallpaperTarget.HOME, WallpaperTarget.LOCK).map { it.name }, + defaultSelections = when (wallpaperTarget) { + WallpaperTarget.BOTH -> listOf(0, 1) + WallpaperTarget.HOME -> listOf(0) + else -> listOf(1) + }, + requireAtLeastOne = true ) { selections -> - config.selectedApiRoutes = selections.map { DrawerScreens.apiScreens[it].route } - currentSelections = selections - onChange(config) + wallpaperTarget = when { + selections.size == 2 -> WallpaperTarget.BOTH + selections[0] == 0 -> WallpaperTarget.HOME + else -> WallpaperTarget.LOCK + } } - } - WallpaperSource.LOCAL -> Column( - modifier = Modifier.fillMaxWidth() - ) { - SettingsCategory(title = stringResource(R.string.directories)) + ListPreference( + prefKey = null, + title = stringResource(R.string.change_interval), + entries = changeIntervals.map { it.formatMinutes() }, + values = changeIntervals.map { it.toString() }, + defaultValue = changeInterval.toString() + ) { + changeInterval = it.toInt() + } - localFolderUris.forEach { - var selectedDirectoryName by remember { - mutableStateOf("") + AnimatedVisibility(visible = wallpaperSource != WallpaperSource.LOCAL) { + ListPreference( + prefKey = null, + title = stringResource(R.string.network_type), + entries = networkTypes.map { stringResource(id = it.first) }, + values = networkTypes.map { it.second.name }, + defaultValue = networkType.name + ) { + networkType = NetworkType.valueOf(it) } + } + + ListPreference( + prefKey = null, + title = stringResource(R.string.wallpaper_changer_source), + entries = wallpaperSources.map { stringResource(it) }, + values = List(wallpaperSources.size) { index -> index.toString() }, + defaultValue = wallpaperSource.ordinal.toString() + ) { newValue -> + wallpaperSource = WallpaperSource.values()[newValue.toInt()] + } - LaunchedEffect(it) { - DocumentFile.fromTreeUri(context, it.toUri())?.let { file -> - selectedDirectoryName = file.name ?: it + Crossfade(targetState = wallpaperSource, label = "wallpaper_source") { state -> + when (state) { + WallpaperSource.ONLINE -> { + MultiSelectionBlockPreference( + preferenceKey = null, + entries = DrawerScreens.apiScreens.map { it.title.str() }, + values = DrawerScreens.apiScreens.map { it.route }, + defaultSelections = wallpaperEnginesIndices + ) { selections -> + wallpaperEnginesIndices = selections + } } - } - Spacer(modifier = Modifier.width(12.dp)) - Row( - modifier = Modifier.padding(start = 10.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Text(selectedDirectoryName) - Spacer(modifier = Modifier.weight(1f)) - ButtonWithIcon(icon = Icons.Default.Delete) { - config.localFolderUris -= it - localFolderUris.remove(it) - onChange(config) + WallpaperSource.LOCAL -> Column( + modifier = Modifier.fillMaxWidth() + ) { + SettingsCategory(title = stringResource(R.string.directories)) + + localFolderUris.forEach { + var selectedDirectoryName by remember { + mutableStateOf("") + } + + LaunchedEffect(it) { + DocumentFile.fromTreeUri(context, it.toUri())?.let { file -> + selectedDirectoryName = file.name ?: it + } + } + + Spacer(modifier = Modifier.width(12.dp)) + Row( + modifier = Modifier.padding(start = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text(selectedDirectoryName) + Spacer(modifier = Modifier.weight(1f)) + ButtonWithIcon(icon = Icons.Default.Delete) { + localFolderUris.remove(it) + } + } + } + + Button( + onClick = { + localWallpaperDirChooser.launch(null) + } + ) { + Text(stringResource(R.string.choose_dir)) + } } + + else -> Unit } } - Button( - onClick = { - localWallpaperDirChooser.launch(null) - } - ) { - Text(stringResource(R.string.choose_dir)) + CheckboxPref( + prefKey = null, + title = stringResource(id = R.string.apply_image_filters), + defaultValue = applyImageFilters + ) { newValue -> + applyImageFilters = newValue } } - - else -> Unit } - } - - var applyImageFilters by remember { - mutableStateOf(config.applyImageFilters) - } - CheckboxPref( - prefKey = null, - title = stringResource(id = R.string.apply_image_filters), - defaultValue = applyImageFilters - ) { newValue -> - config.applyImageFilters = newValue - applyImageFilters = newValue - onChange(config) - } + ) } diff --git a/app/src/main/java/com/bnyro/wallpaper/ui/components/prefs/BlockPreference.kt b/app/src/main/java/com/bnyro/wallpaper/ui/components/prefs/BlockPreference.kt index ed5b7ec8..74dba964 100644 --- a/app/src/main/java/com/bnyro/wallpaper/ui/components/prefs/BlockPreference.kt +++ b/app/src/main/java/com/bnyro/wallpaper/ui/components/prefs/BlockPreference.kt @@ -17,6 +17,7 @@ fun MultiSelectionBlockPreference( entries: List, values: List, defaultSelections: List = listOf(0), + requireAtLeastOne: Boolean = false, onSelectionChange: (List) -> Unit = {} ) { val selected = remember { @@ -34,7 +35,12 @@ fun MultiSelectionBlockPreference( text = it, selected = selected.contains(index) ) { - if (selected.contains(index)) selected.remove(index) else selected.add(index) + if (!selected.contains(index)) { + selected.add(index) + } else if (!requireAtLeastOne || selected.size > 1) { + selected.remove(index) + } + if (preferenceKey != null) Preferences.edit { putString(preferenceKey, values.joinToString(",")) } diff --git a/app/src/main/java/com/bnyro/wallpaper/ui/pages/SettingsPage.kt b/app/src/main/java/com/bnyro/wallpaper/ui/pages/SettingsPage.kt index f8e28b19..a8c93342 100644 --- a/app/src/main/java/com/bnyro/wallpaper/ui/pages/SettingsPage.kt +++ b/app/src/main/java/com/bnyro/wallpaper/ui/pages/SettingsPage.kt @@ -1,33 +1,46 @@ package com.bnyro.wallpaper.ui.pages +import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Edit import androidx.compose.material3.Button +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.runtime.toMutableStateList +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.work.NetworkType import com.bnyro.wallpaper.R import com.bnyro.wallpaper.enums.ThemeMode -import com.bnyro.wallpaper.obj.WallpaperConfig -import com.bnyro.wallpaper.enums.WallpaperTarget import com.bnyro.wallpaper.ext.formatBinarySize -import com.bnyro.wallpaper.ext.formatMinutes -import com.bnyro.wallpaper.ui.components.WallpaperChangerPref +import com.bnyro.wallpaper.obj.WallpaperConfig +import com.bnyro.wallpaper.ui.components.ButtonWithIcon +import com.bnyro.wallpaper.ui.components.WallpaperChangerPrefDialog import com.bnyro.wallpaper.ui.components.about.AboutContainer import com.bnyro.wallpaper.ui.components.prefs.CheckboxPref import com.bnyro.wallpaper.ui.components.prefs.ListPreference @@ -117,23 +130,6 @@ fun SettingsPage( } } - val changeIntervals = listOf( - 15L, - 30L, - 60L, - 180L, - 360L, - 720L, - 1440L - ) - - val networkTypes = listOf( - R.string.all_networks to NetworkType.CONNECTED, - R.string.unmetered to NetworkType.UNMETERED, - R.string.metered to NetworkType.METERED, - R.string.not_roaming to NetworkType.NOT_ROAMING, - ) - AboutContainer { SettingsCategory( title = stringResource(R.string.wallpaper_changer) @@ -142,50 +138,85 @@ fun SettingsPage( modifier = Modifier .height(5.dp) ) + + var wallpaperChangerEnabled by remember { + mutableStateOf(Preferences.getBoolean(Preferences.wallpaperChangerKey, false)) + } CheckboxPref( prefKey = Preferences.wallpaperChangerKey, title = stringResource(R.string.wallpaper_changer) - ) { - WorkerHelper.enqueue(context, true) - } - ListPreference( - prefKey = Preferences.wallpaperChangerNetworkTypeKey, - title = stringResource(R.string.network_type), - entries = networkTypes.map { stringResource(id = it.first) }, - values = networkTypes.map { it.second.name }, - defaultValue = NetworkType.CONNECTED.name - ) { - WorkerHelper.enqueue(context, true) - } - ListPreference( - prefKey = Preferences.wallpaperChangerIntervalKey, - title = stringResource(R.string.change_interval), - entries = changeIntervals.map { it.formatMinutes() }, - values = changeIntervals.map { it.toString() }, - defaultValue = Preferences.defaultWallpaperChangeInterval.toString() - ) { - WorkerHelper.enqueue(context, true) - } - CheckboxPref( - prefKey = Preferences.combineWallpaperChangers, - title = stringResource(R.string.combine_wallpaper_changers), - summary = stringResource(R.string.combine_wallpaper_changers_summary), - defaultValue = true - ) { newState -> - wallpaperConfigs.clear() - val availableTargets = if (newState) { - listOf(WallpaperTarget.BOTH) - } else { - listOf(WallpaperTarget.HOME, WallpaperTarget.LOCK) - } - wallpaperConfigs.addAll(availableTargets.map { WallpaperConfig(it) }) - Preferences.setWallpaperConfigs(wallpaperConfigs) + ) { newValue -> + wallpaperChangerEnabled = newValue + + WorkerHelper.enqueueOrCancelAll(context, wallpaperConfigs) } - wallpaperConfigs.forEachIndexed { index, wallpaperConfig -> - Spacer(modifier = Modifier.height(10.dp)) - WallpaperChangerPref(wallpaperConfig) { - wallpaperConfigs[index] = wallpaperConfig - Preferences.setWallpaperConfigs(wallpaperConfigs) + + AnimatedVisibility(visible = wallpaperChangerEnabled) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + wallpaperConfigs.forEachIndexed { index, wallpaperConfig -> + Spacer(modifier = Modifier.height(5.dp)) + + var showWallpaperConfigDialog by remember { + mutableStateOf(false) + } + Row( + modifier = Modifier.fillMaxWidth().padding(start = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text(text = wallpaperConfig.getSummary(context)) + + Spacer(modifier = Modifier.weight(1f)) + + ButtonWithIcon( + icon = Icons.Default.Edit + ) { + showWallpaperConfigDialog = true + } + + ButtonWithIcon( + icon = Icons.Default.Delete + ) { + WorkerHelper.cancelWork(context, wallpaperConfig) + wallpaperConfigs.removeAt(index) + Preferences.setWallpaperConfigs(wallpaperConfigs) + } + } + + if (showWallpaperConfigDialog) { + WallpaperChangerPrefDialog( + wallpaperConfig, + onConfigChange = { newConfig -> + wallpaperConfigs[index] = newConfig + Preferences.setWallpaperConfigs(wallpaperConfigs) + WorkerHelper.enqueue(context, wallpaperConfig, true) + }, + onDismissRequest = { showWallpaperConfigDialog = false } + ) + } + } + + Button( + onClick = { + val maxId = if (wallpaperConfigs.isNotEmpty()) { + wallpaperConfigs.maxBy { it.id }.id + } else { + -1 + } + + val newConfig = WallpaperConfig(id = maxId + 1) + wallpaperConfigs.add(newConfig) + Preferences.setWallpaperConfigs(wallpaperConfigs) + WorkerHelper.enqueue(context, newConfig, true) + } + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(imageVector = Icons.Default.Add, contentDescription = null) + Spacer(Modifier.width(5.dp)) + Text(stringResource(R.string.add_wallpaper_changer_rule)) + } + } } } } diff --git a/app/src/main/java/com/bnyro/wallpaper/util/BackgroundWorker.kt b/app/src/main/java/com/bnyro/wallpaper/util/BackgroundWorker.kt index c4889fa3..bc084818 100644 --- a/app/src/main/java/com/bnyro/wallpaper/util/BackgroundWorker.kt +++ b/app/src/main/java/com/bnyro/wallpaper/util/BackgroundWorker.kt @@ -2,6 +2,7 @@ package com.bnyro.wallpaper.util import android.content.Context import android.graphics.Bitmap +import android.util.Log import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.bnyro.wallpaper.db.DatabaseHolder @@ -13,23 +14,33 @@ import kotlinx.coroutines.withContext class BackgroundWorker( applicationContext: Context, - workerParameters: WorkerParameters + private val workerParameters: WorkerParameters ) : CoroutineWorker(applicationContext, workerParameters) { override suspend fun doWork(): Result { - Preferences.getWallpaperConfigs().forEach { - runWallpaperChanger(it) - } + val configId = workerParameters.inputData.getInt(WorkerHelper.WALLPAPER_CONFIG_ID, -1) + if (configId == -1) return Result.failure() + + val config = Preferences.getWallpaperConfigs().firstOrNull { + it.id == configId + } ?: return Result.success() - return Result.success() + Log.e("wallpaper changer", "found appropriate wallpaper config") + return if (runWallpaperChanger(config)) Result.success() + else Result.retry() } - private suspend fun runWallpaperChanger(config: WallpaperConfig) { + /** + * Fetch and apply a wallpaper using the given config + * + * @return Whether the wallpaper change applied successfully without errors + */ + private suspend fun runWallpaperChanger(config: WallpaperConfig): Boolean { val bitmap = when (config.source) { WallpaperSource.ONLINE -> getOnlineWallpaper(config) WallpaperSource.FAVORITES -> getFavoritesWallpaper() WallpaperSource.LOCAL -> getLocalWallpaper(config) - else -> return - } ?: return + else -> return true + } ?: return false if (config.applyImageFilters) { WallpaperHelper.setWallpaperWithFilters( @@ -44,6 +55,8 @@ class BackgroundWorker( config.target ) } + + return true } private suspend fun getOnlineWallpaper(config: WallpaperConfig): Bitmap? { diff --git a/app/src/main/java/com/bnyro/wallpaper/util/Preferences.kt b/app/src/main/java/com/bnyro/wallpaper/util/Preferences.kt index ebb42f59..72ce7b7a 100644 --- a/app/src/main/java/com/bnyro/wallpaper/util/Preferences.kt +++ b/app/src/main/java/com/bnyro/wallpaper/util/Preferences.kt @@ -22,10 +22,7 @@ object Preferences { const val contrastKey = "contrast" const val wallpaperChangerKey = "wallpaperChanger" - const val wallpaperChangerIntervalKey = "wallpaperChangerInterval" - const val wallpaperChangerNetworkTypeKey = "wallpaperChangerNetworkType" - private const val wallpaperChangerConfigKey = "wallpaperChangerConfiguration" - const val combineWallpaperChangers = "combineWallpaperChangers" + private const val wallpaperChangerConfigKey = "wallpaperChangerConfigurations" const val defaultDiskCacheSize = 128L * 1024 * 1024 const val defaultWallpaperChangeInterval = 12L * 60 @@ -61,7 +58,7 @@ object Preferences { RetrofitHelper.json.decodeFromString>(prefString) } catch (e: Exception) { Log.e(this.javaClass.name, e.stackTraceToString()) - listOf(WallpaperConfig(WallpaperTarget.BOTH)) + listOf() } } } diff --git a/app/src/main/java/com/bnyro/wallpaper/util/WorkerHelper.kt b/app/src/main/java/com/bnyro/wallpaper/util/WorkerHelper.kt index 515883ca..27692244 100644 --- a/app/src/main/java/com/bnyro/wallpaper/util/WorkerHelper.kt +++ b/app/src/main/java/com/bnyro/wallpaper/util/WorkerHelper.kt @@ -2,22 +2,23 @@ package com.bnyro.wallpaper.util import android.content.Context import androidx.work.Constraints +import androidx.work.Data import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import com.bnyro.wallpaper.enums.WallpaperSource +import com.bnyro.wallpaper.obj.WallpaperConfig import java.util.concurrent.TimeUnit object WorkerHelper { - private const val JOB_NAME = "WallpaperChanger" + private const val JOB_NAME_PREFIX = "WallpaperChanger" + const val WALLPAPER_CONFIG_ID = "WallpaperConfigId" - private fun getWorkerConstraints(): Constraints { - val wallpaperConfigs = Preferences.getWallpaperConfigs() + private fun getWorkerConstraints(config: WallpaperConfig): Constraints { // only require internet when the source is not local - val networkType = if (wallpaperConfigs.any { it.source != WallpaperSource.LOCAL }) { - val pref = Preferences.getString(Preferences.wallpaperChangerNetworkTypeKey, NetworkType.CONNECTED.name) - NetworkType.valueOf(pref) + val networkType = if (config.source != WallpaperSource.LOCAL) { + config.networkType } else { NetworkType.NOT_REQUIRED } @@ -27,26 +28,46 @@ object WorkerHelper { .build() } - fun enqueue(context: Context, forceUpdate: Boolean = false) { - if (!Preferences.getBoolean(Preferences.wallpaperChangerKey, false)) { - WorkManager.getInstance(context) - .cancelUniqueWork(JOB_NAME) - return - } + private fun getWorkName(config: WallpaperConfig): String { + return JOB_NAME_PREFIX + "_" + config.id.toString() + } + + fun cancelWork(context: Context, config: WallpaperConfig) { + val jobName = getWorkName(config) + + WorkManager.getInstance(context) + .cancelUniqueWork(jobName) + } + + fun enqueue(context: Context, config: WallpaperConfig, forceUpdate: Boolean = false) { + val jobName = getWorkName(config) + val inputData = Data.Builder() + .putInt(WALLPAPER_CONFIG_ID, config.id) + .build() - val repeatIntervalMinutes = Preferences.getString( - Preferences.wallpaperChangerIntervalKey, - Preferences.defaultWallpaperChangeInterval.toString() - ).toLong() val job = PeriodicWorkRequestBuilder( - repeatIntervalMinutes, + config.changeIntervalMinutes.toLong(), TimeUnit.MINUTES ) - .setConstraints(getWorkerConstraints()) + .setConstraints(getWorkerConstraints(config)) + .setInputData(inputData) .build() - val policy = if (forceUpdate) ExistingPeriodicWorkPolicy.UPDATE else ExistingPeriodicWorkPolicy.KEEP + val policy = + if (forceUpdate) ExistingPeriodicWorkPolicy.UPDATE else ExistingPeriodicWorkPolicy.KEEP WorkManager.getInstance(context) - .enqueueUniquePeriodicWork(JOB_NAME, policy, job) + .enqueueUniquePeriodicWork(jobName, policy, job) + } + + fun enqueueOrCancelAll(context: Context, configs: List, forceUpdate: Boolean = false) { + if (Preferences.getBoolean(Preferences.wallpaperChangerKey, false)) { + for (config in configs) { + enqueue(context, config, forceUpdate) + } + } else { + for (config in configs) { + cancelWork(context, config) + } + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7a687adf..b94ac433 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,6 +58,7 @@ Invert wallpaper by theme Invert the wallpaper to be dark on dark themes and light on light themes. Apply image filters + Add wallpaper changer rule Network type All networks