From 0f061936643c57a9f85c3ed2772b009aace63d57 Mon Sep 17 00:00:00 2001 From: bwees Date: Tue, 1 Jul 2025 16:13:51 -0500 Subject: [PATCH] cleanup --- .../immich/widget/ImageDownloadWorker.kt | 89 +++++++++---------- .../app/alextran/immich/widget/ImmichAPI.kt | 20 +++++ .../app/alextran/immich/widget/Model.kt | 16 ++-- .../widget/{PhotoWidget.kt => PhotoView.kt} | 6 +- .../alextran/immich/widget/RandomReceiver.kt | 15 +--- .../alextran/immich/widget/RandomWidget.kt | 12 +-- 6 files changed, 78 insertions(+), 80 deletions(-) rename mobile/android/app/src/main/kotlin/app/alextran/immich/widget/{PhotoWidget.kt => PhotoView.kt} (88%) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt index 151d57b594..6c159d734e 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt @@ -1,31 +1,14 @@ package app.alextran.immich.widget -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - import android.content.Context import android.graphics.Bitmap import android.util.Log +import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.stringPreferencesKey import androidx.glance.* import androidx.glance.appwidget.GlanceAppWidgetManager import androidx.glance.appwidget.state.updateAppWidgetState import androidx.work.* -import com.google.gson.Gson -import es.antonborri.home_widget.HomeWidgetPlugin import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File @@ -45,7 +28,7 @@ class ImageDownloadWorker( private val uniqueWorkName = ImageDownloadWorker::class.java.simpleName - fun enqueue(context: Context, appWidgetId: Int, config: WidgetConfig) { + fun enqueue(context: Context, appWidgetId: Int, widgetType: WidgetType) { val manager = WorkManager.getInstance(context) val workRequest = PeriodicWorkRequestBuilder( @@ -58,7 +41,7 @@ class ImageDownloadWorker( ) .setInputData( Data.Builder() - .putString("config", Gson().toJson(config)) + .putString("widgetType", widgetType.toString()) .putInt("widgetId", appWidgetId) .build() ) @@ -78,45 +61,43 @@ class ImageDownloadWorker( } } - private fun getServerConfig(): ServerConfig? { - val prefs = HomeWidgetPlugin.getData(context) - val serverURL = prefs.getString("widget_server_url", "") ?: "" - val sessionKey = prefs.getString("widget_auth_token", "") ?: "" - - if (serverURL == "" || sessionKey == "") { - return null - } - - return ServerConfig( - serverURL, - sessionKey - ) - } override suspend fun doWork(): Result { return try { - val configString = inputData.getString("config") - val config = Gson().fromJson(configString, WidgetConfig::class.java) + val widgetType = WidgetType.valueOf(inputData.getString("config") ?: "") val widgetId = inputData.getInt("widgetId", -1) val glanceId = GlanceAppWidgetManager(context).getGlanceIdBy(widgetId) - val serverConfig = getServerConfig() ?: return Result.success() + val currentState = getAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) + val currentImgUUID = currentState[stringPreferencesKey("uuid")] - // clear current image if it exists - val state = getAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) - val currentImgUUID = state[stringPreferencesKey("uuid")] - if (currentImgUUID != null) { - deleteImage(currentImgUUID) + val serverConfig = ImmichAPI.getServerConfig(context) + + // clear any image caches and go to "login" state if no credentials + if (serverConfig == null) { + if (!currentImgUUID.isNullOrEmpty()) { + deleteImage(currentImgUUID) + updateWidget(widgetType, glanceId, "", WidgetState.LOG_IN) + } + + return Result.success() } // fetch new image - val newBitmap = when (config.widgetType) { - WidgetType.RANDOM -> fetchRandom(serverConfig) + val newBitmap = when (widgetType) { + WidgetType.RANDOM -> fetchRandom(serverConfig, currentState) } + + // clear current image if it exists + if (!currentImgUUID.isNullOrEmpty()) { + deleteImage(currentImgUUID) + } + + // save a new image val imgUUID = saveImage(newBitmap) // trigger the update routine with new image uuid - updateWidget(config, glanceId, imgUUID) + updateWidget(widgetType, glanceId, imgUUID) Result.success() } catch (e: Exception) { @@ -129,19 +110,29 @@ class ImageDownloadWorker( } } - private suspend fun updateWidget(config: WidgetConfig, glanceId: GlanceId, imageUUID: String) { + private suspend fun updateWidget(type: WidgetType, glanceId: GlanceId, imageUUID: String, widgetState: WidgetState = WidgetState.SUCCESS) { updateAppWidgetState(context, glanceId) { prefs -> prefs[longPreferencesKey("now")] = System.currentTimeMillis() prefs[stringPreferencesKey("uuid")] = imageUUID + prefs[stringPreferencesKey("state")] = widgetState.toString() } - RandomWidget().update(context,glanceId) + when (type) { + WidgetType.RANDOM -> RandomWidget().update(context,glanceId) + } } - private suspend fun fetchRandom(serverConfig: ServerConfig): Bitmap { + private suspend fun fetchRandom(serverConfig: ServerConfig, widgetData: Preferences): Bitmap { val api = ImmichAPI(serverConfig) - val random = api.fetchSearchResults(SearchFilters(AssetType.IMAGE, size=1)) + val filters = SearchFilters(AssetType.IMAGE, size=1) + val albumId = widgetData[stringPreferencesKey("albumID")] + + if (albumId != null) { + filters.albumIds = listOf(albumId) + } + + val random = api.fetchSearchResults(filters) val image = api.fetchImage(random[0]) return image diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt index ec35a710ce..4ae35421ef 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt @@ -6,6 +6,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import com.google.gson.Gson import com.google.gson.reflect.TypeToken +import es.antonborri.home_widget.HomeWidgetPlugin import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.OutputStreamWriter @@ -18,6 +19,25 @@ import java.util.* class ImmichAPI(cfg: ServerConfig) { + companion object { + fun getServerConfig(context: Context): ServerConfig? { + val prefs = HomeWidgetPlugin.getData(context) + + val serverURL = prefs.getString("widget_server_url", "") ?: "" + val sessionKey = prefs.getString("widget_auth_token", "") ?: "" + + if (serverURL.isBlank() || sessionKey.isBlank()) { + return null + } + + return ServerConfig( + serverURL, + sessionKey + ) + } + } + + private val gson = Gson() private val serverConfig = cfg diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/Model.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/Model.kt index 0a24f59001..0db93a03c6 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/Model.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/Model.kt @@ -3,6 +3,8 @@ package app.alextran.immich.widget import androidx.datastore.preferences.core.stringPreferencesKey import androidx.glance.appwidget.GlanceAppWidget +// MARK: Immich Entities + enum class AssetType { IMAGE, VIDEO, AUDIO, OTHER } @@ -32,6 +34,8 @@ data class Album( val albumName: String ) +// MARK: Widget Specific + enum class WidgetType { RANDOM; @@ -41,15 +45,9 @@ enum class WidgetType { } } -data class WidgetConfig( - val widgetType: WidgetType, - val params: Map, -) +enum class WidgetState { + LOADING, SUCCESS, LOG_IN; +} data class ServerConfig(val serverEndpoint: String, val sessionKey: String) -// this value is in HomeWidgetPlugin.PREFERENCES but is marked internal -const val WIDGET_PREFERENCES = "HomeWidgetPreferences" - -val loggedInKey = stringPreferencesKey("isLoggedIn") -val lastUpdatedKey = stringPreferencesKey("lastUpdated") diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoView.kt similarity index 88% rename from mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt rename to mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoView.kt index 41bc791c2c..9501091df4 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoView.kt @@ -14,7 +14,7 @@ import androidx.glance.text.Text import app.alextran.immich.R @Composable -fun PhotoWidget(image: Bitmap?, error: String?, subtitle: String?) { +fun PhotoView(image: Bitmap?, subtitle: String?, loggedIn: Boolean) { Box( modifier = GlanceModifier @@ -28,12 +28,14 @@ fun PhotoWidget(image: Bitmap?, error: String?, subtitle: String?) { contentScale = ContentScale.Crop, modifier = GlanceModifier.fillMaxSize() ) + if (subtitle != null) + Text(subtitle) } else { Image( provider = ImageProvider(R.drawable.splash), contentDescription = null, ) - Text(error ?: "NOPERS") + } } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt index d2307bd3c6..8041a803e1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt @@ -3,17 +3,7 @@ package app.alextran.immich.widget import HomeWidgetGlanceWidgetReceiver import android.appwidget.AppWidgetManager import android.content.Context -import android.os.Handler -import android.os.Looper import android.util.Log -import androidx.glance.GlanceId -import androidx.glance.appwidget.GlanceAppWidgetManager -import androidx.work.Constraints -import androidx.work.ExistingPeriodicWorkPolicy -import androidx.work.NetworkType -import androidx.work.PeriodicWorkRequestBuilder -import androidx.work.WorkManager -import java.util.concurrent.TimeUnit class RandomReceiver : HomeWidgetGlanceWidgetReceiver() { override val glanceAppWidget = RandomWidget() @@ -25,11 +15,8 @@ class RandomReceiver : HomeWidgetGlanceWidgetReceiver() { ) { super.onUpdate(context, appWidgetManager, appWidgetIds) - val cfg = WidgetConfig(WidgetType.RANDOM, HashMap()) - appWidgetIds.forEach { widgetID -> - ImageDownloadWorker.enqueue(context, widgetID, cfg) - Log.w("WIDGET_UPDATE", "WORKER ENQUEUE CALLED: $widgetID") + ImageDownloadWorker.enqueue(context, widgetID, WidgetType.RANDOM) } } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomWidget.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomWidget.kt index 421552fcbd..68c277c3d3 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomWidget.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomWidget.kt @@ -1,19 +1,14 @@ package app.alextran.immich.widget -import HomeWidgetGlanceState -import HomeWidgetGlanceStateDefinition import android.content.Context import android.graphics.Bitmap -import android.util.Log import androidx.datastore.preferences.core.MutablePreferences -import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.glance.appwidget.* import androidx.glance.* import androidx.glance.state.GlanceStateDefinition import androidx.glance.state.PreferencesGlanceStateDefinition import java.io.File -import java.util.prefs.Preferences class RandomWidget : GlanceAppWidget() { override var stateDefinition: GlanceStateDefinition<*> = PreferencesGlanceStateDefinition @@ -23,7 +18,10 @@ class RandomWidget : GlanceAppWidget() { provideContent { val prefs = currentState() val imageUUID = prefs[stringPreferencesKey("uuid")] + + val subtitle: String? = prefs[stringPreferencesKey("subtitle")] var bitmap: Bitmap? = null + var loggedIn = true if (imageUUID != null) { // fetch a random photo from server @@ -32,9 +30,11 @@ class RandomWidget : GlanceAppWidget() { if (file.exists()) { bitmap = loadScaledBitmap(file, 500, 500) } + } else if (ImmichAPI.getServerConfig(context) == null) { + loggedIn = false } - PhotoWidget(image = bitmap, error = "NOPE", subtitle = "hello") + PhotoView(image = bitmap, subtitle = subtitle, loggedIn = loggedIn) } }