mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
finish minor refactoring and add some polish :)
This commit is contained in:
parent
403b5dd930
commit
9d19c5e3f3
@ -146,7 +146,8 @@
|
|||||||
<!-- Widgets -->
|
<!-- Widgets -->
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widget.RandomReceiver"
|
android:name=".widget.RandomReceiver"
|
||||||
android:exported="true">
|
android:exported="true"
|
||||||
|
android:label="@string/random_widget_title">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@ -157,7 +158,8 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widget.MemoryReceiver"
|
android:name=".widget.MemoryReceiver"
|
||||||
android:exported="true">
|
android:exported="true"
|
||||||
|
android:label="@string/memory_widget_title">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
@ -194,7 +194,8 @@ class ImageDownloadWorker(
|
|||||||
val memory = memories.random()
|
val memory = memories.random()
|
||||||
asset = memory.assets.random()
|
asset = memory.assets.random()
|
||||||
|
|
||||||
subtitle = "${today.year - memory.data.year} years ago"
|
val yearDiff = today.year - memory.data.year
|
||||||
|
subtitle = "$yearDiff ${if (yearDiff == 1) "year" else "years"} ago"
|
||||||
} else {
|
} else {
|
||||||
val filters = SearchFilters(AssetType.IMAGE, size=1)
|
val filters = SearchFilters(AssetType.IMAGE, size=1)
|
||||||
asset = api.fetchSearchResults(filters).first()
|
asset = api.fetchSearchResults(filters).first()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package app.alextran.immich.widget
|
package app.alextran.immich.widget
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
@ -13,11 +12,8 @@ import java.io.OutputStreamWriter
|
|||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
class ImmichAPI(cfg: ServerConfig) {
|
class ImmichAPI(cfg: ServerConfig) {
|
||||||
|
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
package app.alextran.immich.widget
|
package app.alextran.immich.widget
|
||||||
|
|
||||||
import HomeWidgetGlanceWidgetReceiver
|
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||||
|
import es.antonborri.home_widget.HomeWidgetPlugin
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MemoryReceiver : HomeWidgetGlanceWidgetReceiver<PhotoWidget>() {
|
class MemoryReceiver : GlanceAppWidgetReceiver() {
|
||||||
override val glanceAppWidget = PhotoWidget()
|
override val glanceAppWidget = PhotoWidget()
|
||||||
|
|
||||||
override fun onUpdate(
|
override fun onUpdate(
|
||||||
@ -18,5 +24,23 @@ class MemoryReceiver : HomeWidgetGlanceWidgetReceiver<PhotoWidget>() {
|
|||||||
ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.MEMORIES)
|
ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.MEMORIES)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false)
|
||||||
|
|
||||||
|
// Launch coroutine to setup a single shot if the app requested the update
|
||||||
|
if (fromMainApp) {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
val provider = ComponentName(context, MemoryReceiver::class.java)
|
||||||
|
val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider)
|
||||||
|
|
||||||
|
glanceIds.forEach { widgetID ->
|
||||||
|
ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onReceive(context, intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ val kDeeplinkURL = stringPreferencesKey("deeplink")
|
|||||||
|
|
||||||
const val kWorkerWidgetType = "widgetType"
|
const val kWorkerWidgetType = "widgetType"
|
||||||
const val kWorkerWidgetID = "widgetId"
|
const val kWorkerWidgetID = "widgetId"
|
||||||
|
const val kTriggeredFromApp = "triggeredFromApp"
|
||||||
|
|
||||||
fun imageFilename(id: String): String {
|
fun imageFilename(id: String): String {
|
||||||
return "widget_image_$id.jpg"
|
return "widget_image_$id.jpg"
|
||||||
|
@ -3,6 +3,9 @@ package app.alextran.immich.widget
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
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.core.net.toUri
|
||||||
@ -14,6 +17,7 @@ import androidx.glance.layout.*
|
|||||||
import androidx.glance.state.GlanceStateDefinition
|
import androidx.glance.state.GlanceStateDefinition
|
||||||
import androidx.glance.state.PreferencesGlanceStateDefinition
|
import androidx.glance.state.PreferencesGlanceStateDefinition
|
||||||
import androidx.glance.text.Text
|
import androidx.glance.text.Text
|
||||||
|
import androidx.glance.text.TextAlign
|
||||||
import androidx.glance.text.TextStyle
|
import androidx.glance.text.TextStyle
|
||||||
import androidx.glance.unit.ColorProvider
|
import androidx.glance.unit.ColorProvider
|
||||||
import app.alextran.immich.R
|
import app.alextran.immich.R
|
||||||
@ -29,6 +33,7 @@ class PhotoWidget : GlanceAppWidget() {
|
|||||||
val imageUUID = prefs[kImageUUID]
|
val imageUUID = prefs[kImageUUID]
|
||||||
val subtitle = prefs[kSubtitleText]
|
val subtitle = prefs[kSubtitleText]
|
||||||
val deeplinkURL = prefs[kDeeplinkURL]?.toUri()
|
val deeplinkURL = prefs[kDeeplinkURL]?.toUri()
|
||||||
|
val widgetState = prefs[kWidgetState]
|
||||||
var bitmap: Bitmap? = null
|
var bitmap: Bitmap? = null
|
||||||
|
|
||||||
if (imageUUID != null) {
|
if (imageUUID != null) {
|
||||||
@ -44,7 +49,7 @@ class PhotoWidget : GlanceAppWidget() {
|
|||||||
Box(
|
Box(
|
||||||
modifier = GlanceModifier
|
modifier = GlanceModifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(Color.White)
|
.background(GlanceTheme.colors.background)
|
||||||
.clickable {
|
.clickable {
|
||||||
val intent = Intent(Intent.ACTION_VIEW, deeplinkURL ?: "immich://".toUri())
|
val intent = Intent(Intent.ACTION_VIEW, deeplinkURL ?: "immich://".toUri())
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
@ -81,10 +86,39 @@ class PhotoWidget : GlanceAppWidget() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Column(
|
||||||
|
modifier = GlanceModifier.fillMaxSize(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
Image(
|
Image(
|
||||||
provider = ImageProvider(R.drawable.splash),
|
provider = ImageProvider(R.drawable.splash),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (widgetState == WidgetState.LOG_IN.toString()) {
|
||||||
|
Box(
|
||||||
|
modifier = GlanceModifier.fillMaxWidth().padding(16.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text("Log in to your Immich server", style = TextStyle(textAlign = TextAlign.Center, color = GlanceTheme.colors.primary))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = GlanceModifier.fillMaxWidth().padding(16.dp)
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = GlanceModifier.size(12.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = GlanceModifier.width(8.dp))
|
||||||
|
|
||||||
|
Text("Loading widget...", style = TextStyle(textAlign = TextAlign.Center, color = GlanceTheme.colors.primary))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
package app.alextran.immich.widget
|
package app.alextran.immich.widget
|
||||||
|
|
||||||
import HomeWidgetGlanceWidgetReceiver
|
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.content.Intent
|
||||||
|
import es.antonborri.home_widget.HomeWidgetPlugin
|
||||||
|
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class RandomReceiver : HomeWidgetGlanceWidgetReceiver<PhotoWidget>() {
|
class RandomReceiver : GlanceAppWidgetReceiver() {
|
||||||
override val glanceAppWidget = PhotoWidget()
|
override val glanceAppWidget = PhotoWidget()
|
||||||
|
|
||||||
override fun onUpdate(
|
override fun onUpdate(
|
||||||
@ -19,5 +24,23 @@ class RandomReceiver : HomeWidgetGlanceWidgetReceiver<PhotoWidget>() {
|
|||||||
ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.RANDOM)
|
ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.RANDOM)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false)
|
||||||
|
|
||||||
|
// Launch coroutine to setup a single shot if the app requested the update
|
||||||
|
if (fromMainApp) {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
val provider = ComponentName(context, RandomReceiver::class.java)
|
||||||
|
val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider)
|
||||||
|
|
||||||
|
glanceIds.forEach { widgetID ->
|
||||||
|
ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onReceive(context, intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LightDarkTheme(
|
fun LightDarkTheme(
|
||||||
useDarkTheme: Boolean = isSystemInDarkTheme(), // ← This line is key
|
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 240 KiB |
Binary file not shown.
After Width: | Height: | Size: 244 KiB |
8
mobile/android/app/src/main/res/values/strings.xml
Normal file
8
mobile/android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="memory_widget_title">Memories</string>
|
||||||
|
<string name="random_widget_title">Random</string>
|
||||||
|
|
||||||
|
<string name="memory_widget_description">See memories from Immich.</string>
|
||||||
|
<string name="random_widget_description">View a random image from your library or a specific album.</string>
|
||||||
|
</resources>
|
@ -4,4 +4,6 @@
|
|||||||
android:minHeight="110dp"
|
android:minHeight="110dp"
|
||||||
android:resizeMode="horizontal|vertical"
|
android:resizeMode="horizontal|vertical"
|
||||||
android:updatePeriodMillis="1200000"
|
android:updatePeriodMillis="1200000"
|
||||||
|
android:description="@string/memory_widget_description"
|
||||||
|
android:previewImage="@drawable/memory_preview"
|
||||||
/>
|
/>
|
||||||
|
@ -8,4 +8,6 @@
|
|||||||
android:configure="app.alextran.immich.widget.configure.RandomConfigure"
|
android:configure="app.alextran.immich.widget.configure.RandomConfigure"
|
||||||
android:widgetFeatures="reconfigurable|configuration_optional"
|
android:widgetFeatures="reconfigurable|configuration_optional"
|
||||||
tools:targetApi="28"
|
tools:targetApi="28"
|
||||||
|
android:description="@string/random_widget_description"
|
||||||
|
android:previewImage="@drawable/random_preview"
|
||||||
/>
|
/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user