mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
add deeplinks
This commit is contained in:
parent
42e53232c3
commit
403b5dd930
@ -94,14 +94,20 @@ class ImageDownloadWorker(
|
|||||||
if (serverConfig == null) {
|
if (serverConfig == null) {
|
||||||
if (!currentImgUUID.isNullOrEmpty()) {
|
if (!currentImgUUID.isNullOrEmpty()) {
|
||||||
deleteImage(currentImgUUID)
|
deleteImage(currentImgUUID)
|
||||||
updateWidget(glanceId, "", "", WidgetState.LOG_IN)
|
updateWidget(
|
||||||
|
glanceId,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"immich://",
|
||||||
|
WidgetState.LOG_IN
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch new image
|
// fetch new image
|
||||||
val (newBitmap, subtitle) = when (widgetType) {
|
val entry = when (widgetType) {
|
||||||
WidgetType.RANDOM -> fetchRandom(serverConfig, widgetConfig)
|
WidgetType.RANDOM -> fetchRandom(serverConfig, widgetConfig)
|
||||||
WidgetType.MEMORIES -> fetchMemory(serverConfig)
|
WidgetType.MEMORIES -> fetchMemory(serverConfig)
|
||||||
}
|
}
|
||||||
@ -113,10 +119,10 @@ class ImageDownloadWorker(
|
|||||||
|
|
||||||
// save a new image
|
// save a new image
|
||||||
val imgUUID = UUID.randomUUID().toString()
|
val imgUUID = UUID.randomUUID().toString()
|
||||||
saveImage(newBitmap, imgUUID)
|
saveImage(entry.image, imgUUID)
|
||||||
|
|
||||||
// trigger the update routine with new image uuid
|
// trigger the update routine with new image uuid
|
||||||
updateWidget(glanceId, imgUUID, subtitle)
|
updateWidget(glanceId, imgUUID, entry.subtitle, entry.deeplink)
|
||||||
|
|
||||||
Result.success()
|
Result.success()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -133,6 +139,7 @@ class ImageDownloadWorker(
|
|||||||
glanceId: GlanceId,
|
glanceId: GlanceId,
|
||||||
imageUUID: String,
|
imageUUID: String,
|
||||||
subtitle: String?,
|
subtitle: String?,
|
||||||
|
deeplink: String?,
|
||||||
widgetState: WidgetState = WidgetState.SUCCESS
|
widgetState: WidgetState = WidgetState.SUCCESS
|
||||||
) {
|
) {
|
||||||
updateAppWidgetState(context, glanceId) { prefs ->
|
updateAppWidgetState(context, glanceId) { prefs ->
|
||||||
@ -140,6 +147,7 @@ class ImageDownloadWorker(
|
|||||||
prefs[kImageUUID] = imageUUID
|
prefs[kImageUUID] = imageUUID
|
||||||
prefs[kWidgetState] = widgetState.toString()
|
prefs[kWidgetState] = widgetState.toString()
|
||||||
prefs[kSubtitleText] = subtitle ?: ""
|
prefs[kSubtitleText] = subtitle ?: ""
|
||||||
|
prefs[kDeeplinkURL] = deeplink ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
PhotoWidget().update(context,glanceId)
|
PhotoWidget().update(context,glanceId)
|
||||||
@ -148,7 +156,7 @@ class ImageDownloadWorker(
|
|||||||
private suspend fun fetchRandom(
|
private suspend fun fetchRandom(
|
||||||
serverConfig: ServerConfig,
|
serverConfig: ServerConfig,
|
||||||
widgetConfig: Preferences
|
widgetConfig: Preferences
|
||||||
): Pair<Bitmap, String?> {
|
): WidgetEntry {
|
||||||
val api = ImmichAPI(serverConfig)
|
val api = ImmichAPI(serverConfig)
|
||||||
|
|
||||||
val filters = SearchFilters(AssetType.IMAGE, size=1)
|
val filters = SearchFilters(AssetType.IMAGE, size=1)
|
||||||
@ -164,17 +172,21 @@ class ImageDownloadWorker(
|
|||||||
val random = api.fetchSearchResults(filters).first()
|
val random = api.fetchSearchResults(filters).first()
|
||||||
val image = api.fetchImage(random)
|
val image = api.fetchImage(random)
|
||||||
|
|
||||||
return Pair(image, subtitle)
|
return WidgetEntry(
|
||||||
|
image,
|
||||||
|
subtitle,
|
||||||
|
assetDeeplink(random)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchMemory(
|
private suspend fun fetchMemory(
|
||||||
serverConfig: ServerConfig
|
serverConfig: ServerConfig
|
||||||
): Pair<Bitmap, String?> {
|
): WidgetEntry {
|
||||||
val api = ImmichAPI(serverConfig)
|
val api = ImmichAPI(serverConfig)
|
||||||
|
|
||||||
val today = LocalDate.now()
|
val today = LocalDate.now()
|
||||||
val memories = api.fetchMemory(today)
|
val memories = api.fetchMemory(today)
|
||||||
val asset: SearchResult
|
val asset: Asset
|
||||||
var subtitle: String? = null
|
var subtitle: String? = null
|
||||||
|
|
||||||
if (memories.isNotEmpty()) {
|
if (memories.isNotEmpty()) {
|
||||||
@ -189,7 +201,11 @@ class ImageDownloadWorker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val image = api.fetchImage(asset)
|
val image = api.fetchImage(asset)
|
||||||
return Pair(image, subtitle)
|
return WidgetEntry(
|
||||||
|
image,
|
||||||
|
subtitle,
|
||||||
|
assetDeeplink(asset)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun deleteImage(uuid: String) = withContext(Dispatchers.IO) {
|
private suspend fun deleteImage(uuid: String) = withContext(Dispatchers.IO) {
|
||||||
|
@ -53,7 +53,7 @@ class ImmichAPI(cfg: ServerConfig) {
|
|||||||
return URL(urlString.toString())
|
return URL(urlString.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchSearchResults(filters: SearchFilters): List<SearchResult> = withContext(Dispatchers.IO) {
|
suspend fun fetchSearchResults(filters: SearchFilters): List<Asset> = withContext(Dispatchers.IO) {
|
||||||
val url = buildRequestURL("/search/random")
|
val url = buildRequestURL("/search/random")
|
||||||
val connection = (url.openConnection() as HttpURLConnection).apply {
|
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||||
requestMethod = "POST"
|
requestMethod = "POST"
|
||||||
@ -69,7 +69,7 @@ class ImmichAPI(cfg: ServerConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val response = connection.inputStream.bufferedReader().readText()
|
val response = connection.inputStream.bufferedReader().readText()
|
||||||
val type = object : TypeToken<List<SearchResult>>() {}.type
|
val type = object : TypeToken<List<Asset>>() {}.type
|
||||||
gson.fromJson(response, type)
|
gson.fromJson(response, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ class ImmichAPI(cfg: ServerConfig) {
|
|||||||
gson.fromJson(response, type)
|
gson.fromJson(response, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchImage(asset: SearchResult): Bitmap = withContext(Dispatchers.IO) {
|
suspend fun fetchImage(asset: Asset): Bitmap = withContext(Dispatchers.IO) {
|
||||||
val url = buildRequestURL("/assets/${asset.id}/thumbnail", listOf("size" to "preview"))
|
val url = buildRequestURL("/assets/${asset.id}/thumbnail", listOf("size" to "preview"))
|
||||||
val connection = url.openConnection()
|
val connection = url.openConnection()
|
||||||
val data = connection.getInputStream().readBytes()
|
val data = connection.getInputStream().readBytes()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package app.alextran.immich.widget
|
package app.alextran.immich.widget
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
import androidx.datastore.preferences.core.doublePreferencesKey
|
import androidx.datastore.preferences.core.doublePreferencesKey
|
||||||
import androidx.datastore.preferences.core.longPreferencesKey
|
import androidx.datastore.preferences.core.longPreferencesKey
|
||||||
@ -12,9 +13,9 @@ enum class AssetType {
|
|||||||
IMAGE, VIDEO, AUDIO, OTHER
|
IMAGE, VIDEO, AUDIO, OTHER
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SearchResult(
|
data class Asset(
|
||||||
val id: String,
|
val id: String,
|
||||||
val type: AssetType
|
val type: AssetType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SearchFilters(
|
data class SearchFilters(
|
||||||
@ -25,7 +26,7 @@ data class SearchFilters(
|
|||||||
|
|
||||||
data class MemoryResult(
|
data class MemoryResult(
|
||||||
val id: String,
|
val id: String,
|
||||||
var assets: List<SearchResult>,
|
var assets: List<Asset>,
|
||||||
val type: String,
|
val type: String,
|
||||||
val data: MemoryData
|
val data: MemoryData
|
||||||
) {
|
) {
|
||||||
@ -47,6 +48,12 @@ enum class WidgetState {
|
|||||||
LOADING, SUCCESS, LOG_IN;
|
LOADING, SUCCESS, LOG_IN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class WidgetEntry (
|
||||||
|
val image: Bitmap,
|
||||||
|
val subtitle: String?,
|
||||||
|
val deeplink: String?
|
||||||
|
)
|
||||||
|
|
||||||
data class ServerConfig(val serverEndpoint: String, val sessionKey: String)
|
data class ServerConfig(val serverEndpoint: String, val sessionKey: String)
|
||||||
|
|
||||||
// MARK: Widget State Keys
|
// MARK: Widget State Keys
|
||||||
@ -57,6 +64,7 @@ val kWidgetState = stringPreferencesKey("state")
|
|||||||
val kSelectedAlbum = stringPreferencesKey("albumID")
|
val kSelectedAlbum = stringPreferencesKey("albumID")
|
||||||
val kSelectedAlbumName = stringPreferencesKey("albumName")
|
val kSelectedAlbumName = stringPreferencesKey("albumName")
|
||||||
val kShowAlbumName = booleanPreferencesKey("showAlbumName")
|
val kShowAlbumName = booleanPreferencesKey("showAlbumName")
|
||||||
|
val kDeeplinkURL = stringPreferencesKey("deeplink")
|
||||||
|
|
||||||
const val kWorkerWidgetType = "widgetType"
|
const val kWorkerWidgetType = "widgetType"
|
||||||
const val kWorkerWidgetID = "widgetId"
|
const val kWorkerWidgetID = "widgetId"
|
||||||
@ -64,3 +72,7 @@ const val kWorkerWidgetID = "widgetId"
|
|||||||
fun imageFilename(id: String): String {
|
fun imageFilename(id: String): String {
|
||||||
return "widget_image_$id.jpg"
|
return "widget_image_$id.jpg"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun assetDeeplink(asset: Asset): String {
|
||||||
|
return "immich://asset?id=${asset.id}"
|
||||||
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package app.alextran.immich.widget
|
package app.alextran.immich.widget
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.*
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.datastore.preferences.core.MutablePreferences
|
import androidx.datastore.preferences.core.MutablePreferences
|
||||||
import androidx.glance.appwidget.*
|
import androidx.glance.appwidget.*
|
||||||
import androidx.glance.*
|
import androidx.glance.*
|
||||||
@ -25,7 +27,8 @@ class PhotoWidget : GlanceAppWidget() {
|
|||||||
val prefs = currentState<MutablePreferences>()
|
val prefs = currentState<MutablePreferences>()
|
||||||
|
|
||||||
val imageUUID = prefs[kImageUUID]
|
val imageUUID = prefs[kImageUUID]
|
||||||
val subtitle: String? = prefs[kSubtitleText]
|
val subtitle = prefs[kSubtitleText]
|
||||||
|
val deeplinkURL = prefs[kDeeplinkURL]?.toUri()
|
||||||
var bitmap: Bitmap? = null
|
var bitmap: Bitmap? = null
|
||||||
|
|
||||||
if (imageUUID != null) {
|
if (imageUUID != null) {
|
||||||
@ -42,6 +45,11 @@ class PhotoWidget : GlanceAppWidget() {
|
|||||||
modifier = GlanceModifier
|
modifier = GlanceModifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(Color.White)
|
.background(Color.White)
|
||||||
|
.clickable {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, deeplinkURL ?: "immich://".toUri())
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
Image(
|
Image(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user