feat(wallpaper): add apply_wallpaper workflow and rotation worker
Adds a new apply_wallpaper command and its plumbing across desktop, mobile, and Android. A WorkManager-based rotation worker handles applying wallpapers in the background. Wallpaper state now tracks currentId and imageIds for precise control. - Introduced apply_wallpaper command across desktop, mobile and Android - Added WorkManager-based rotation worker to apply wallpapers in the background - Extended wallpaper state with currentId and imageIds and updated schemas/permissions
This commit is contained in:
@@ -37,6 +37,7 @@ dependencies {
|
|||||||
implementation("androidx.core:core-ktx:1.9.0")
|
implementation("androidx.core:core-ktx:1.9.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.0")
|
implementation("androidx.appcompat:appcompat:1.6.0")
|
||||||
implementation("com.google.android.material:material:1.7.0")
|
implementation("com.google.android.material:material:1.7.0")
|
||||||
|
implementation("androidx.work:work-runtime-ktx:2.10.1")
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<application>
|
<application>
|
||||||
<service android:name="de.wechselbild.wallpaper.WallpaperRotationService" android:exported="false" android:foregroundServiceType="specialUse">
|
<service android:name="de.wechselbild.wallpaper.WallpaperRotationService" android:exported="false" android:foregroundServiceType="specialUse">
|
||||||
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Changes the user-selected lock-screen wallpaper at the configured interval" />
|
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Changes the user-selected lock-screen wallpaper at the configured interval" />
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package de.wechselbild.wallpaper
|
|||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
class WallpaperAlarmReceiver : BroadcastReceiver() {
|
class WallpaperAlarmReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
@@ -11,24 +10,10 @@ class WallpaperAlarmReceiver : BroadcastReceiver() {
|
|||||||
WallpaperScheduler.cancel(context)
|
WallpaperScheduler.cancel(context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val applicationContext = context.applicationContext
|
if (WallpaperScheduler.claimIfDue(context)) {
|
||||||
val result = goAsync()
|
WallpaperRotationWorker.enqueue(context)
|
||||||
executor.execute {
|
|
||||||
try {
|
|
||||||
if (WallpaperScheduler.claimIfDue(applicationContext)) try {
|
|
||||||
WallpaperStore.applyNext(applicationContext)
|
|
||||||
} finally {
|
|
||||||
WallpaperScheduler.scheduleNext(applicationContext)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
WallpaperScheduler.ensureScheduled(applicationContext)
|
|
||||||
WallpaperRotationService.rescheduleRunningService()
|
|
||||||
result.finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
WallpaperScheduler.ensureScheduled(context)
|
||||||
|
WallpaperRotationService.rescheduleRunningService()
|
||||||
companion object {
|
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ class GalleryArgs { var offset: Int = 0; var limit: Int = 48 }
|
|||||||
@InvokeArg
|
@InvokeArg
|
||||||
class DeleteImageArgs { lateinit var id: String }
|
class DeleteImageArgs { lateinit var id: String }
|
||||||
|
|
||||||
|
@InvokeArg
|
||||||
|
class ApplyWallpaperArgs { lateinit var id: String }
|
||||||
|
|
||||||
@InvokeArg
|
@InvokeArg
|
||||||
class DeleteImagesArgs { var ids: Array<String> = emptyArray() }
|
class DeleteImagesArgs { var ids: Array<String> = emptyArray() }
|
||||||
|
|
||||||
@@ -130,7 +133,7 @@ class WallpaperPlugin(private val activity: Activity) : Plugin(activity) {
|
|||||||
try {
|
try {
|
||||||
val args = invoke.parseArgs(SettingArgs::class.java)
|
val args = invoke.parseArgs(SettingArgs::class.java)
|
||||||
WallpaperStore.set(activity, args.name, args.value)
|
WallpaperStore.set(activity, args.name, args.value)
|
||||||
invoke.resolve(WallpaperStore.state(activity, includePreviews = false))
|
invoke.resolve(WallpaperStore.state(activity))
|
||||||
} catch (error: Exception) { invoke.reject(error.message ?: "Einstellung konnte nicht gespeichert werden") }
|
} catch (error: Exception) { invoke.reject(error.message ?: "Einstellung konnte nicht gespeichert werden") }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +142,7 @@ class WallpaperPlugin(private val activity: Activity) : Plugin(activity) {
|
|||||||
val args = invoke.parseArgs(IntervalArgs::class.java)
|
val args = invoke.parseArgs(IntervalArgs::class.java)
|
||||||
WallpaperStore.setInterval(activity, args.minutes)
|
WallpaperStore.setInterval(activity, args.minutes)
|
||||||
if (args.minutes > 0) WallpaperRotationService.restart(activity) else WallpaperRotationService.stop(activity)
|
if (args.minutes > 0) WallpaperRotationService.restart(activity) else WallpaperRotationService.stop(activity)
|
||||||
invoke.resolve(WallpaperStore.state(activity, includePreviews = false))
|
invoke.resolve(WallpaperStore.state(activity))
|
||||||
} catch (error: Exception) { invoke.reject(error.message ?: "Wechselintervall konnte nicht gespeichert werden") }
|
} catch (error: Exception) { invoke.reject(error.message ?: "Wechselintervall konnte nicht gespeichert werden") }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +153,14 @@ class WallpaperPlugin(private val activity: Activity) : Plugin(activity) {
|
|||||||
} catch (error: Exception) { invoke.reject(error.message ?: "Das Hintergrundbild konnte nicht geändert werden") }
|
} catch (error: Exception) { invoke.reject(error.message ?: "Das Hintergrundbild konnte nicht geändert werden") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Command fun applyWallpaper(invoke: Invoke) = io.execute {
|
||||||
|
try {
|
||||||
|
val args = invoke.parseArgs(ApplyWallpaperArgs::class.java)
|
||||||
|
if (WallpaperStore.apply(activity, args.id)) invoke.resolve(WallpaperStore.state(activity))
|
||||||
|
else invoke.reject("Bild konnte nicht angewendet werden")
|
||||||
|
} catch (error: Exception) { invoke.reject(error.message ?: "Das Hintergrundbild konnte nicht geändert werden") }
|
||||||
|
}
|
||||||
|
|
||||||
@Command fun getImmichConnection(invoke: Invoke) = io.execute {
|
@Command fun getImmichConnection(invoke: Invoke) = io.execute {
|
||||||
invoke.resolve(ImmichClient.connection(activity))
|
invoke.resolve(ImmichClient.connection(activity))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,8 @@ class WallpaperRotationService : Service() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
worker.execute {
|
worker.execute {
|
||||||
if (WallpaperScheduler.claimIfDue(this@WallpaperRotationService)) try {
|
if (WallpaperScheduler.claimIfDue(this@WallpaperRotationService)) {
|
||||||
WallpaperStore.applyNext(this@WallpaperRotationService)
|
WallpaperRotationWorker.enqueue(this@WallpaperRotationService)
|
||||||
} finally {
|
|
||||||
WallpaperScheduler.scheduleNext(this@WallpaperRotationService)
|
|
||||||
}
|
}
|
||||||
handler.post { scheduleLocalTimer() }
|
handler.post { scheduleLocalTimer() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package de.wechselbild.wallpaper
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.Worker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
|
||||||
|
class WallpaperRotationWorker(context: Context, parameters: WorkerParameters) : Worker(context, parameters) {
|
||||||
|
override fun doWork(): Result {
|
||||||
|
if (!WallpaperStore.enabled(applicationContext)) return Result.success()
|
||||||
|
if (WallpaperStore.applyNext(applicationContext)) return Result.success()
|
||||||
|
return if (runAttemptCount < 2) Result.retry() else Result.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val WORK_NAME = "wallpaper-rotation"
|
||||||
|
|
||||||
|
fun enqueue(context: Context) {
|
||||||
|
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||||
|
WORK_NAME,
|
||||||
|
ExistingWorkPolicy.KEEP,
|
||||||
|
OneTimeWorkRequestBuilder<WallpaperRotationWorker>().build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,8 @@ object WallpaperScheduler {
|
|||||||
}
|
}
|
||||||
val now = SystemClock.elapsedRealtime()
|
val now = SystemClock.elapsedRealtime()
|
||||||
val storedTrigger = preferences(context).getLong(KEY_NEXT_TRIGGER, 0L)
|
val storedTrigger = preferences(context).getLong(KEY_NEXT_TRIGGER, 0L)
|
||||||
if (storedTrigger <= now) scheduleNext(context)
|
val interval = WallpaperStore.intervalMinutes(context) * 60_000L
|
||||||
|
if (storedTrigger <= now || storedTrigger > now + interval) scheduleNext(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remainingDelay(context: Context): Long {
|
fun remainingDelay(context: Context): Long {
|
||||||
@@ -32,10 +33,11 @@ object WallpaperScheduler {
|
|||||||
val preferences = preferences(context)
|
val preferences = preferences(context)
|
||||||
val trigger = preferences.getLong(KEY_NEXT_TRIGGER, 0L)
|
val trigger = preferences.getLong(KEY_NEXT_TRIGGER, 0L)
|
||||||
if (trigger <= 0L || trigger > SystemClock.elapsedRealtime() + 1_000L) return false
|
if (trigger <= 0L || trigger > SystemClock.elapsedRealtime() + 1_000L) return false
|
||||||
preferences.edit().putLong(KEY_NEXT_TRIGGER, Long.MAX_VALUE).commit()
|
scheduleNext(context)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun scheduleNext(context: Context) {
|
fun scheduleNext(context: Context) {
|
||||||
val minutes = WallpaperStore.intervalMinutes(context)
|
val minutes = WallpaperStore.intervalMinutes(context)
|
||||||
if (minutes <= 0) {
|
if (minutes <= 0) {
|
||||||
@@ -50,7 +52,7 @@ object WallpaperScheduler {
|
|||||||
} else {
|
} else {
|
||||||
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger, operation)
|
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger, operation)
|
||||||
}
|
}
|
||||||
preferences(context).edit().putLong(KEY_NEXT_TRIGGER, trigger).apply()
|
preferences(context).edit().putLong(KEY_NEXT_TRIGGER, trigger).commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancel(context: Context) {
|
fun cancel(context: Context) {
|
||||||
|
|||||||
@@ -119,6 +119,10 @@ object WallpaperStore {
|
|||||||
put("shuffle", shuffle(context))
|
put("shuffle", shuffle(context))
|
||||||
put("lockScreenOnly", lockOnly(context))
|
put("lockScreenOnly", lockOnly(context))
|
||||||
put("currentIndex", previewIndex)
|
put("currentIndex", previewIndex)
|
||||||
|
put("currentId", items.getOrNull(index)?.id ?: JSONObject.NULL)
|
||||||
|
val ids = JSArray()
|
||||||
|
previewItems.forEach { ids.put(it.id) }
|
||||||
|
put("imageIds", ids)
|
||||||
val previews = JSArray()
|
val previews = JSArray()
|
||||||
if (includePreviews) previewItems.forEach { previews.put(thumbnailDataUrl(it.preview)) }
|
if (includePreviews) previewItems.forEach { previews.put(thumbnailDataUrl(it.preview)) }
|
||||||
put("imageUrls", previews)
|
put("imageUrls", previews)
|
||||||
@@ -318,6 +322,18 @@ object WallpaperStore {
|
|||||||
val candidates = if (shuffle(context) && items.size > 1) {
|
val candidates = if (shuffle(context) && items.size > 1) {
|
||||||
items.indices.filter { it != previous }.shuffled() + previous
|
items.indices.filter { it != previous }.shuffled() + previous
|
||||||
} else (1..items.size).map { (previous + it).mod(items.size) }
|
} else (1..items.size).map { (previous + it).mod(items.size) }
|
||||||
|
return applyCandidates(context, items, candidates)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun apply(context: Context, id: String): Boolean {
|
||||||
|
val items = entries(context)
|
||||||
|
val index = items.indexOfFirst { it.id == id }
|
||||||
|
if (index < 0) return false
|
||||||
|
return applyCandidates(context, items, listOf(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyCandidates(context: Context, items: List<Entry>, candidates: List<Int>): Boolean {
|
||||||
val unavailableServers = mutableSetOf<String>()
|
val unavailableServers = mutableSetOf<String>()
|
||||||
for (index in candidates) {
|
for (index in candidates) {
|
||||||
val entry = items[index]
|
val entry = items[index]
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const COMMANDS: &[&str] = &[
|
|||||||
"set_setting",
|
"set_setting",
|
||||||
"set_interval",
|
"set_interval",
|
||||||
"next_wallpaper",
|
"next_wallpaper",
|
||||||
|
"apply_wallpaper",
|
||||||
"get_immich_connection",
|
"get_immich_connection",
|
||||||
"connect_immich",
|
"connect_immich",
|
||||||
"disconnect_immich",
|
"disconnect_immich",
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
"$schema" = "../../schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-apply-wallpaper"
|
||||||
|
description = "Enables the apply_wallpaper command without any pre-configured scope."
|
||||||
|
commands.allow = ["apply_wallpaper"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-apply-wallpaper"
|
||||||
|
description = "Denies the apply_wallpaper command without any pre-configured scope."
|
||||||
|
commands.deny = ["apply_wallpaper"]
|
||||||
@@ -14,6 +14,7 @@ Allow the LockScreenWallpaper app to manage its wallpaper collection
|
|||||||
- `allow-set-setting`
|
- `allow-set-setting`
|
||||||
- `allow-set-interval`
|
- `allow-set-interval`
|
||||||
- `allow-next-wallpaper`
|
- `allow-next-wallpaper`
|
||||||
|
- `allow-apply-wallpaper`
|
||||||
- `allow-get-immich-connection`
|
- `allow-get-immich-connection`
|
||||||
- `allow-connect-immich`
|
- `allow-connect-immich`
|
||||||
- `allow-disconnect-immich`
|
- `allow-disconnect-immich`
|
||||||
@@ -31,6 +32,32 @@ Allow the LockScreenWallpaper app to manage its wallpaper collection
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
`wallpaper:allow-apply-wallpaper`
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Enables the apply_wallpaper command without any pre-configured scope.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
`wallpaper:deny-apply-wallpaper`
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Denies the apply_wallpaper command without any pre-configured scope.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[default]
|
[default]
|
||||||
description = "Allow the LockScreenWallpaper app to manage its wallpaper collection"
|
description = "Allow the LockScreenWallpaper app to manage its wallpaper collection"
|
||||||
permissions = ["allow-get-state", "allow-get-gallery", "allow-select-images", "allow-delete-image", "allow-get-image-ids", "allow-delete-images", "allow-set-image-crop", "allow-set-setting", "allow-set-interval", "allow-next-wallpaper", "allow-get-immich-connection", "allow-connect-immich", "allow-disconnect-immich", "allow-get-immich-albums", "allow-get-immich-assets", "allow-import-immich-assets", "allow-get-immich-import-progress"]
|
permissions = ["allow-get-state", "allow-get-gallery", "allow-select-images", "allow-delete-image", "allow-get-image-ids", "allow-delete-images", "allow-set-image-crop", "allow-set-setting", "allow-set-interval", "allow-next-wallpaper", "allow-apply-wallpaper", "allow-get-immich-connection", "allow-connect-immich", "allow-disconnect-immich", "allow-get-immich-albums", "allow-get-immich-assets", "allow-import-immich-assets", "allow-get-immich-import-progress"]
|
||||||
|
|||||||
@@ -294,6 +294,18 @@
|
|||||||
"PermissionKind": {
|
"PermissionKind": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"description": "Enables the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "allow-apply-wallpaper",
|
||||||
|
"markdownDescription": "Enables the apply_wallpaper command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "deny-apply-wallpaper",
|
||||||
|
"markdownDescription": "Denies the apply_wallpaper command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the connect_immich command without any pre-configured scope.",
|
"description": "Enables the connect_immich command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -499,10 +511,10 @@
|
|||||||
"markdownDescription": "Denies the set_setting command without any pre-configured scope."
|
"markdownDescription": "Denies the set_setting command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "default",
|
"const": "default",
|
||||||
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ pub(crate) async fn set_interval<R: Runtime>(app: AppHandle<R>, minutes: usize)
|
|||||||
pub(crate) async fn next_wallpaper<R: Runtime>(app: AppHandle<R>) -> Result<WallpaperState> {
|
pub(crate) async fn next_wallpaper<R: Runtime>(app: AppHandle<R>) -> Result<WallpaperState> {
|
||||||
app.wallpaper().next_wallpaper()
|
app.wallpaper().next_wallpaper()
|
||||||
}
|
}
|
||||||
|
#[command]
|
||||||
|
pub(crate) async fn apply_wallpaper<R: Runtime>(app: AppHandle<R>, id: String) -> Result<WallpaperState> {
|
||||||
|
app.wallpaper().apply_wallpaper(ApplyWallpaperRequest { id })
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub(crate) async fn get_immich_connection<R: Runtime>(app: AppHandle<R>) -> Result<ImmichConnection> {
|
pub(crate) async fn get_immich_connection<R: Runtime>(app: AppHandle<R>) -> Result<ImmichConnection> {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ impl<R: Runtime> Wallpaper<R> {
|
|||||||
shuffle: true,
|
shuffle: true,
|
||||||
lock_screen_only: true,
|
lock_screen_only: true,
|
||||||
current_index: 0,
|
current_index: 0,
|
||||||
|
current_id: Some("demo-0".into()),
|
||||||
|
image_ids: vec!["demo-0".into(), "demo-1".into(), "demo-2".into()],
|
||||||
image_urls: vec![
|
image_urls: vec![
|
||||||
"/wallpapers/alpine.png".into(),
|
"/wallpapers/alpine.png".into(),
|
||||||
"/wallpapers/waterfall.png".into(),
|
"/wallpapers/waterfall.png".into(),
|
||||||
@@ -100,6 +102,15 @@ impl<R: Runtime> Wallpaper<R> {
|
|||||||
pub fn next_wallpaper(&self) -> crate::Result<WallpaperState> {
|
pub fn next_wallpaper(&self) -> crate::Result<WallpaperState> {
|
||||||
let mut state = Self::demo();
|
let mut state = Self::demo();
|
||||||
state.current_index = 1;
|
state.current_index = 1;
|
||||||
|
state.current_id = Some("demo-1".into());
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
pub fn apply_wallpaper(&self, payload: ApplyWallpaperRequest) -> crate::Result<WallpaperState> {
|
||||||
|
let mut state = Self::demo();
|
||||||
|
if let Some(index) = state.image_ids.iter().position(|id| id == &payload.id) {
|
||||||
|
state.current_index = index;
|
||||||
|
state.current_id = Some(payload.id);
|
||||||
|
}
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
pub fn get_immich_connection(&self) -> crate::Result<ImmichConnection> {
|
pub fn get_immich_connection(&self) -> crate::Result<ImmichConnection> {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
commands::set_setting,
|
commands::set_setting,
|
||||||
commands::set_interval,
|
commands::set_interval,
|
||||||
commands::next_wallpaper,
|
commands::next_wallpaper,
|
||||||
|
commands::apply_wallpaper,
|
||||||
commands::get_immich_connection,
|
commands::get_immich_connection,
|
||||||
commands::connect_immich,
|
commands::connect_immich,
|
||||||
commands::disconnect_immich,
|
commands::disconnect_immich,
|
||||||
|
|||||||
@@ -70,6 +70,11 @@ impl<R: Runtime> Wallpaper<R> {
|
|||||||
.run_mobile_plugin("nextWallpaper", ())
|
.run_mobile_plugin("nextWallpaper", ())
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
pub fn apply_wallpaper(&self, payload: ApplyWallpaperRequest) -> crate::Result<WallpaperState> {
|
||||||
|
self.0
|
||||||
|
.run_mobile_plugin("applyWallpaper", payload)
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
pub fn get_immich_connection(&self) -> crate::Result<ImmichConnection> {
|
pub fn get_immich_connection(&self) -> crate::Result<ImmichConnection> {
|
||||||
self.0.run_mobile_plugin("getImmichConnection", ()).map_err(Into::into)
|
self.0.run_mobile_plugin("getImmichConnection", ()).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ pub struct WallpaperState {
|
|||||||
pub shuffle: bool,
|
pub shuffle: bool,
|
||||||
pub lock_screen_only: bool,
|
pub lock_screen_only: bool,
|
||||||
pub current_index: usize,
|
pub current_index: usize,
|
||||||
|
pub current_id: Option<String>,
|
||||||
|
pub image_ids: Vec<String>,
|
||||||
pub image_urls: Vec<String>,
|
pub image_urls: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +40,12 @@ pub struct DeleteImageRequest {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ApplyWallpaperRequest {
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DeleteImagesRequest {
|
pub struct DeleteImagesRequest {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -2193,10 +2193,16 @@
|
|||||||
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "wallpaper:default",
|
"const": "wallpaper:default",
|
||||||
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "wallpaper:allow-apply-wallpaper",
|
||||||
|
"markdownDescription": "Enables the apply_wallpaper command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the connect_immich command without any pre-configured scope.",
|
"description": "Enables the connect_immich command without any pre-configured scope.",
|
||||||
@@ -2300,6 +2306,12 @@
|
|||||||
"const": "wallpaper:allow-set-setting",
|
"const": "wallpaper:allow-set-setting",
|
||||||
"markdownDescription": "Enables the set_setting command without any pre-configured scope."
|
"markdownDescription": "Enables the set_setting command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "wallpaper:deny-apply-wallpaper",
|
||||||
|
"markdownDescription": "Denies the apply_wallpaper command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the connect_immich command without any pre-configured scope.",
|
"description": "Denies the connect_immich command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -2193,10 +2193,16 @@
|
|||||||
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "wallpaper:default",
|
"const": "wallpaper:default",
|
||||||
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "wallpaper:allow-apply-wallpaper",
|
||||||
|
"markdownDescription": "Enables the apply_wallpaper command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the connect_immich command without any pre-configured scope.",
|
"description": "Enables the connect_immich command without any pre-configured scope.",
|
||||||
@@ -2300,6 +2306,12 @@
|
|||||||
"const": "wallpaper:allow-set-setting",
|
"const": "wallpaper:allow-set-setting",
|
||||||
"markdownDescription": "Enables the set_setting command without any pre-configured scope."
|
"markdownDescription": "Enables the set_setting command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "wallpaper:deny-apply-wallpaper",
|
||||||
|
"markdownDescription": "Denies the apply_wallpaper command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the connect_immich command without any pre-configured scope.",
|
"description": "Denies the connect_immich command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -2193,10 +2193,16 @@
|
|||||||
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "wallpaper:default",
|
"const": "wallpaper:default",
|
||||||
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "wallpaper:allow-apply-wallpaper",
|
||||||
|
"markdownDescription": "Enables the apply_wallpaper command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the connect_immich command without any pre-configured scope.",
|
"description": "Enables the connect_immich command without any pre-configured scope.",
|
||||||
@@ -2300,6 +2306,12 @@
|
|||||||
"const": "wallpaper:allow-set-setting",
|
"const": "wallpaper:allow-set-setting",
|
||||||
"markdownDescription": "Enables the set_setting command without any pre-configured scope."
|
"markdownDescription": "Enables the set_setting command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "wallpaper:deny-apply-wallpaper",
|
||||||
|
"markdownDescription": "Denies the apply_wallpaper command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the connect_immich command without any pre-configured scope.",
|
"description": "Denies the connect_immich command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -2193,10 +2193,16 @@
|
|||||||
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
"description": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "wallpaper:default",
|
"const": "wallpaper:default",
|
||||||
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
"markdownDescription": "Allow the LockScreenWallpaper app to manage its wallpaper collection\n#### This default permission set includes:\n\n- `allow-get-state`\n- `allow-get-gallery`\n- `allow-select-images`\n- `allow-delete-image`\n- `allow-get-image-ids`\n- `allow-delete-images`\n- `allow-set-image-crop`\n- `allow-set-setting`\n- `allow-set-interval`\n- `allow-next-wallpaper`\n- `allow-apply-wallpaper`\n- `allow-get-immich-connection`\n- `allow-connect-immich`\n- `allow-disconnect-immich`\n- `allow-get-immich-albums`\n- `allow-get-immich-assets`\n- `allow-import-immich-assets`\n- `allow-get-immich-import-progress`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "wallpaper:allow-apply-wallpaper",
|
||||||
|
"markdownDescription": "Enables the apply_wallpaper command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the connect_immich command without any pre-configured scope.",
|
"description": "Enables the connect_immich command without any pre-configured scope.",
|
||||||
@@ -2300,6 +2306,12 @@
|
|||||||
"const": "wallpaper:allow-set-setting",
|
"const": "wallpaper:allow-set-setting",
|
||||||
"markdownDescription": "Enables the set_setting command without any pre-configured scope."
|
"markdownDescription": "Enables the set_setting command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the apply_wallpaper command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "wallpaper:deny-apply-wallpaper",
|
||||||
|
"markdownDescription": "Denies the apply_wallpaper command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the connect_immich command without any pre-configured scope.",
|
"description": "Denies the connect_immich command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
+33
-8
@@ -1,10 +1,10 @@
|
|||||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||||
import { ArrowLeft, Check, ChevronRight, Cloud, CloudDownload, Crop, Home, Images, Languages, Link2Off, LockKeyhole, Plus, RotateCcw, RotateCw, Save, Server, Settings, Shuffle, Smartphone, Sparkles, Trash2 } from "lucide-react";
|
import { ArrowLeft, Check, ChevronRight, Cloud, CloudDownload, Crop, Home, Images, Languages, Link2Off, LockKeyhole, Plus, RotateCcw, RotateCw, Save, Server, Settings, Shuffle, Smartphone, Sparkles, Trash2 } from "lucide-react";
|
||||||
import { connectImmich, deleteImages, disconnectImmich, getGallery, getImageIds, getImmichAlbums, getImmichAssets, getImmichConnection, getImmichImportProgress, getState, importImmichAssets, nextWallpaper, selectImages, setImageCrop, setIntervalMinutes, setSetting, type GalleryImage, type ImmichAlbum, type ImmichAsset, type ImmichConnection, type ImmichImportProgress, type WallpaperState } from "./native";
|
import { applyWallpaper, connectImmich, deleteImages, disconnectImmich, getGallery, getImageIds, getImmichAlbums, getImmichAssets, getImmichConnection, getImmichImportProgress, getState, importImmichAssets, nextWallpaper, selectImages, setImageCrop, setIntervalMinutes, setSetting, type GalleryImage, type ImmichAlbum, type ImmichAsset, type ImmichConnection, type ImmichImportProgress, type WallpaperState } from "./native";
|
||||||
import { initialLanguage, languageNames, languages, translations, type Language } from "./i18n-local";
|
import { initialLanguage, languageNames, languages, translations, type Language } from "./i18n-local";
|
||||||
import { immichTranslations } from "./immich-i18n";
|
import { immichTranslations } from "./immich-i18n";
|
||||||
|
|
||||||
const initial: WallpaperState = { imageCount: 0, enabled: false, intervalMinutes: 0, shuffle: true, lockScreenOnly: true, currentIndex: 0, imageUrls: [] };
|
const initial: WallpaperState = { imageCount: 0, enabled: false, intervalMinutes: 0, shuffle: true, lockScreenOnly: true, currentIndex: 0, currentId: null, imageIds: [], imageUrls: [] };
|
||||||
|
|
||||||
function Switch({ checked, onChange, label }: { checked: boolean; onChange: (value: boolean) => void; label: string }) {
|
function Switch({ checked, onChange, label }: { checked: boolean; onChange: (value: boolean) => void; label: string }) {
|
||||||
return <button className={`switch ${checked ? "on" : ""}`} role="switch" aria-checked={checked} aria-label={label} onClick={() => onChange(!checked)}><span /></button>;
|
return <button className={`switch ${checked ? "on" : ""}`} role="switch" aria-checked={checked} aria-label={label} onClick={() => onChange(!checked)}><span /></button>;
|
||||||
@@ -50,7 +50,19 @@ export default function App() {
|
|||||||
const galleryScrollPosition = useRef(0);
|
const galleryScrollPosition = useRef(0);
|
||||||
const restoreGalleryScroll = useRef(false);
|
const restoreGalleryScroll = useRef(false);
|
||||||
|
|
||||||
useEffect(() => { getState().then(setState).catch(() => setState(initial)); }, []);
|
useEffect(() => {
|
||||||
|
const refresh = () => { void getState().then(setState).catch(() => undefined); };
|
||||||
|
refresh();
|
||||||
|
const onVisibilityChange = () => { if (!document.hidden) refresh(); };
|
||||||
|
const refreshInterval = window.setInterval(() => { if (!document.hidden) refresh(); }, 15000);
|
||||||
|
document.addEventListener("visibilitychange", onVisibilityChange);
|
||||||
|
window.addEventListener("focus", refresh);
|
||||||
|
return () => {
|
||||||
|
window.clearInterval(refreshInterval);
|
||||||
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
||||||
|
window.removeEventListener("focus", refresh);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getImmichConnection().then(connection => {
|
getImmichConnection().then(connection => {
|
||||||
setImmichConnection(connection);
|
setImmichConnection(connection);
|
||||||
@@ -62,6 +74,9 @@ export default function App() {
|
|||||||
const timeout = window.setTimeout(() => setNotice(""), 3000);
|
const timeout = window.setTimeout(() => setNotice(""), 3000);
|
||||||
return () => window.clearTimeout(timeout);
|
return () => window.clearTimeout(timeout);
|
||||||
}, [notice]);
|
}, [notice]);
|
||||||
|
useEffect(() => {
|
||||||
|
setGallery(previous => previous.map(image => ({ ...image, selected: image.id === state.currentId })));
|
||||||
|
}, [state.currentId]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.localStorage.setItem("wallpaperflow-language", language);
|
window.localStorage.setItem("wallpaperflow-language", language);
|
||||||
document.documentElement.lang = language;
|
document.documentElement.lang = language;
|
||||||
@@ -79,7 +94,8 @@ export default function App() {
|
|||||||
const t = translations[language];
|
const t = translations[language];
|
||||||
const it = immichTranslations[language];
|
const it = immichTranslations[language];
|
||||||
const previewDate = useMemo(() => new Intl.DateTimeFormat(language, { weekday: "long", day: "numeric", month: "long" }).format(new Date()), [language]);
|
const previewDate = useMemo(() => new Intl.DateTimeFormat(language, { weekday: "long", day: "numeric", month: "long" }).format(new Date()), [language]);
|
||||||
const current = state.imageUrls[state.currentIndex] ?? "/wallpapers/alpine.png";
|
const currentPreviewIndex = state.imageIds.indexOf(state.currentId ?? "");
|
||||||
|
const current = state.imageUrls[currentPreviewIndex] ?? "/wallpapers/alpine.png";
|
||||||
const photos = useMemo(() => state.imageUrls, [state.imageUrls]);
|
const photos = useMemo(() => state.imageUrls, [state.imageUrls]);
|
||||||
|
|
||||||
async function loadGallery(offset = 0, append = false) {
|
async function loadGallery(offset = 0, append = false) {
|
||||||
@@ -96,7 +112,7 @@ export default function App() {
|
|||||||
setState(prev => ({ ...prev, [name]: value }));
|
setState(prev => ({ ...prev, [name]: value }));
|
||||||
try {
|
try {
|
||||||
const saved = await setSetting(name, value);
|
const saved = await setSetting(name, value);
|
||||||
setState(prev => ({ ...saved, imageUrls: saved.imageUrls.length ? saved.imageUrls : prev.imageUrls }));
|
setState(saved);
|
||||||
setNotice(t.settingSaved);
|
setNotice(t.settingSaved);
|
||||||
} catch { setNotice(t.androidOnly); }
|
} catch { setNotice(t.androidOnly); }
|
||||||
}
|
}
|
||||||
@@ -105,7 +121,7 @@ export default function App() {
|
|||||||
setState(prev => ({ ...prev, intervalMinutes: minutes, enabled: minutes > 0 }));
|
setState(prev => ({ ...prev, intervalMinutes: minutes, enabled: minutes > 0 }));
|
||||||
try {
|
try {
|
||||||
const saved = await setIntervalMinutes(minutes);
|
const saved = await setIntervalMinutes(minutes);
|
||||||
setState(prev => ({ ...saved, imageUrls: saved.imageUrls.length ? saved.imageUrls : prev.imageUrls }));
|
setState(saved);
|
||||||
setNotice(minutes > 0 ? t.intervalSaved : t.automaticDisabled);
|
setNotice(minutes > 0 ? t.intervalSaved : t.automaticDisabled);
|
||||||
}
|
}
|
||||||
catch { setNotice(t.androidOnly); }
|
catch { setNotice(t.androidOnly); }
|
||||||
@@ -364,7 +380,16 @@ export default function App() {
|
|||||||
|
|
||||||
async function next() {
|
async function next() {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
try { setState(await nextWallpaper()); setNotice(t.wallpaperUpdated); } catch { setState(prev => ({ ...prev, currentIndex: (prev.currentIndex + 1) % Math.max(1, photos.length) })); }
|
try { setState(await nextWallpaper()); setNotice(t.wallpaperUpdated); }
|
||||||
|
catch (error) { setNotice(String(error).replace(/^Error:\s*/, "")); }
|
||||||
|
finally { setBusy(false); }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectWallpaper(id: string) {
|
||||||
|
if (busy || id === state.currentId) return;
|
||||||
|
setBusy(true);
|
||||||
|
try { setState(await applyWallpaper(id)); setNotice(t.wallpaperUpdated); }
|
||||||
|
catch (error) { setNotice(String(error).replace(/^Error:\s*/, "")); }
|
||||||
finally { setBusy(false); }
|
finally { setBusy(false); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +413,7 @@ export default function App() {
|
|||||||
<button className="primary" onClick={choose} disabled={busy}><Images />{busy ? t.pleaseWait : t.selectImages}</button>
|
<button className="primary" onClick={choose} disabled={busy}><Images />{busy ? t.pleaseWait : t.selectImages}</button>
|
||||||
|
|
||||||
<section className="collection"><div className="section-heading"><h2>{t.collection}</h2><button onClick={openGallery}>{state.imageCount} {state.imageCount === 1 ? t.image : t.images} <ChevronRight /></button></div>
|
<section className="collection"><div className="section-heading"><h2>{t.collection}</h2><button onClick={openGallery}>{state.imageCount} {state.imageCount === 1 ? t.image : t.images} <ChevronRight /></button></div>
|
||||||
{photos.length ? <div className="photo-rail">{photos.map((photo, index) => <button key={`${photo}-${index}`} className={index === state.currentIndex ? "selected" : ""} onClick={() => setState(prev => ({ ...prev, currentIndex: index }))}><img src={photo} alt={`${t.motif} ${index + 1}`} />{index === state.currentIndex && <span><Check /></span>}</button>)}</div> : <button className="empty-collection" onClick={choose}><Images /><span>{t.noImages}</span></button>}
|
{photos.length ? <div className="photo-rail">{photos.map((photo, index) => <button key={state.imageIds[index] ?? `${photo}-${index}`} className={state.imageIds[index] === state.currentId ? "selected" : ""} disabled={busy} onClick={() => void selectWallpaper(state.imageIds[index])}><img src={photo} alt={`${t.motif} ${index + 1}`} />{state.imageIds[index] === state.currentId && <span><Check /></span>}</button>)}</div> : <button className="empty-collection" onClick={choose}><Images /><span>{t.noImages}</span></button>}
|
||||||
<p className="hint">{t.collectionHint}</p>
|
<p className="hint">{t.collectionHint}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
+21
-1
@@ -7,6 +7,8 @@ export type WallpaperState = {
|
|||||||
shuffle: boolean;
|
shuffle: boolean;
|
||||||
lockScreenOnly: boolean;
|
lockScreenOnly: boolean;
|
||||||
currentIndex: number;
|
currentIndex: number;
|
||||||
|
currentId: string | null;
|
||||||
|
imageIds: string[];
|
||||||
imageUrls: string[];
|
imageUrls: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,6 +69,8 @@ const demoState: WallpaperState = {
|
|||||||
shuffle: true,
|
shuffle: true,
|
||||||
lockScreenOnly: true,
|
lockScreenOnly: true,
|
||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
|
currentId: "demo-0",
|
||||||
|
imageIds: ["demo-0", "demo-1", "demo-2"],
|
||||||
imageUrls: ["/wallpapers/alpine.png", "/wallpapers/waterfall.png", "/wallpapers/coast.png"],
|
imageUrls: ["/wallpapers/alpine.png", "/wallpapers/waterfall.png", "/wallpapers/coast.png"],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -102,6 +106,8 @@ export async function deleteImage(id: string): Promise<WallpaperState> {
|
|||||||
demoState.imageUrls.splice(index, 1);
|
demoState.imageUrls.splice(index, 1);
|
||||||
demoState.imageCount = demoState.imageUrls.length;
|
demoState.imageCount = demoState.imageUrls.length;
|
||||||
demoState.currentIndex = Math.min(demoState.currentIndex, Math.max(0, demoState.imageCount - 1));
|
demoState.currentIndex = Math.min(demoState.currentIndex, Math.max(0, demoState.imageCount - 1));
|
||||||
|
demoState.imageIds = demoState.imageUrls.map((_, itemIndex) => `demo-${itemIndex}`);
|
||||||
|
demoState.currentId = demoState.imageIds[demoState.currentIndex] ?? null;
|
||||||
}
|
}
|
||||||
return { ...demoState, imageUrls: [...demoState.imageUrls] };
|
return { ...demoState, imageUrls: [...demoState.imageUrls] };
|
||||||
}
|
}
|
||||||
@@ -117,6 +123,8 @@ export async function deleteImages(ids: string[]): Promise<WallpaperState> {
|
|||||||
demoState.imageUrls = demoState.imageUrls.filter((_, index) => !indexes.has(index));
|
demoState.imageUrls = demoState.imageUrls.filter((_, index) => !indexes.has(index));
|
||||||
demoState.imageCount = demoState.imageUrls.length;
|
demoState.imageCount = demoState.imageUrls.length;
|
||||||
demoState.currentIndex = Math.min(demoState.currentIndex, Math.max(0, demoState.imageCount - 1));
|
demoState.currentIndex = Math.min(demoState.currentIndex, Math.max(0, demoState.imageCount - 1));
|
||||||
|
demoState.imageIds = demoState.imageUrls.map((_, index) => `demo-${index}`);
|
||||||
|
demoState.currentId = demoState.imageIds[demoState.currentIndex] ?? null;
|
||||||
return { ...demoState, imageUrls: [...demoState.imageUrls] };
|
return { ...demoState, imageUrls: [...demoState.imageUrls] };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,12 +164,24 @@ export async function setIntervalMinutes(value: number) {
|
|||||||
|
|
||||||
export async function nextWallpaper(): Promise<WallpaperState> {
|
export async function nextWallpaper(): Promise<WallpaperState> {
|
||||||
if (!inTauri()) {
|
if (!inTauri()) {
|
||||||
demoState.currentIndex = (demoState.currentIndex + 1) % demoState.imageUrls.length;
|
if (demoState.imageUrls.length) demoState.currentIndex = (demoState.currentIndex + 1) % demoState.imageUrls.length;
|
||||||
|
demoState.currentId = demoState.imageIds[demoState.currentIndex] ?? null;
|
||||||
return { ...demoState };
|
return { ...demoState };
|
||||||
}
|
}
|
||||||
return invoke<WallpaperState>("plugin:wallpaper|next_wallpaper");
|
return invoke<WallpaperState>("plugin:wallpaper|next_wallpaper");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function applyWallpaper(id: string): Promise<WallpaperState> {
|
||||||
|
if (!inTauri()) {
|
||||||
|
const index = demoState.imageIds.indexOf(id);
|
||||||
|
if (index < 0) throw new Error("Image not found");
|
||||||
|
demoState.currentIndex = index;
|
||||||
|
demoState.currentId = id;
|
||||||
|
return { ...demoState };
|
||||||
|
}
|
||||||
|
return invoke<WallpaperState>("plugin:wallpaper|apply_wallpaper", { id });
|
||||||
|
}
|
||||||
|
|
||||||
export async function getImmichConnection(): Promise<ImmichConnection> {
|
export async function getImmichConnection(): Promise<ImmichConnection> {
|
||||||
return inTauri() ? invoke<ImmichConnection>("plugin:wallpaper|get_immich_connection") : demoImmichConnection;
|
return inTauri() ? invoke<ImmichConnection>("plugin:wallpaper|get_immich_connection") : demoImmichConnection;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user