Adds Immich image prefetch and a mobile data option to the UI. Android now ships ImmichPrefetchWorker to fetch originals in the background. Frontend and desktop code are updated to expose and persist new settings. Translations cover the new labels and hints in multiple languages. - Introduce ImmichPrefetchWorker for background prefetch - Add allowMobileData and prefetchImmich UI controls - Wire changes to desktop state and translations
207 lines
8.3 KiB
Kotlin
207 lines
8.3 KiB
Kotlin
package de.wechselbild.wallpaper
|
|
|
|
import android.app.Activity
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import androidx.activity.result.ActivityResult
|
|
import app.tauri.annotation.ActivityCallback
|
|
import app.tauri.annotation.Command
|
|
import app.tauri.annotation.InvokeArg
|
|
import app.tauri.annotation.TauriPlugin
|
|
import app.tauri.plugin.Invoke
|
|
import app.tauri.plugin.JSArray
|
|
import app.tauri.plugin.JSObject
|
|
import app.tauri.plugin.Plugin
|
|
import java.io.File
|
|
import java.util.UUID
|
|
import java.util.concurrent.Executors
|
|
|
|
@InvokeArg
|
|
class SettingArgs { lateinit var name: String; var value: Boolean = false }
|
|
|
|
@InvokeArg
|
|
class IntervalArgs { var minutes: Int = 0 }
|
|
|
|
@InvokeArg
|
|
class GalleryArgs { var offset: Int = 0; var limit: Int = 48 }
|
|
|
|
@InvokeArg
|
|
class DeleteImageArgs { lateinit var id: String }
|
|
|
|
@InvokeArg
|
|
class ApplyWallpaperArgs { lateinit var id: String }
|
|
|
|
@InvokeArg
|
|
class DeleteImagesArgs { var ids: Array<String> = emptyArray() }
|
|
|
|
@InvokeArg
|
|
class ImageCropArgs {
|
|
lateinit var id: String
|
|
lateinit var mode: String
|
|
var zoom: Double = 1.0
|
|
var positionX: Double = 0.5
|
|
var positionY: Double = 0.5
|
|
var rotation: Int = 0
|
|
}
|
|
|
|
@InvokeArg
|
|
class ImmichConnectArgs { lateinit var serverUrl: String; lateinit var apiKey: String }
|
|
|
|
@InvokeArg
|
|
class ImmichAssetsArgs { var albumId: String? = null; var page: Int = 1; var size: Int = 30 }
|
|
|
|
@InvokeArg
|
|
class ImmichImportArgs { var ids: Array<String> = emptyArray() }
|
|
|
|
@TauriPlugin
|
|
class WallpaperPlugin(private val activity: Activity) : Plugin(activity) {
|
|
private val io = Executors.newSingleThreadExecutor()
|
|
|
|
@Command fun getState(invoke: Invoke) {
|
|
io.execute { invoke.resolve(WallpaperStore.state(activity)) }
|
|
}
|
|
|
|
@Command fun getGallery(invoke: Invoke) = io.execute {
|
|
try {
|
|
val args = invoke.parseArgs(GalleryArgs::class.java)
|
|
invoke.resolve(WallpaperStore.gallery(activity, args.offset, args.limit))
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Galerie konnte nicht geladen werden") }
|
|
}
|
|
|
|
@Command fun selectImages(invoke: Invoke) {
|
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
addCategory(Intent.CATEGORY_OPENABLE)
|
|
type = "image/*"
|
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
}
|
|
startActivityForResult(invoke, intent, "selectedImages")
|
|
}
|
|
|
|
@Command fun deleteImage(invoke: Invoke) = io.execute {
|
|
try {
|
|
val args = invoke.parseArgs(DeleteImageArgs::class.java)
|
|
if (!WallpaperStore.delete(activity, args.id)) throw IllegalArgumentException("Bild wurde nicht gefunden")
|
|
invoke.resolve(WallpaperStore.state(activity))
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Bild konnte nicht gelöscht werden") }
|
|
}
|
|
|
|
@Command fun getImageIds(invoke: Invoke) = io.execute {
|
|
val ids = JSArray().apply { WallpaperStore.imageIds(activity).forEach { put(it) } }
|
|
invoke.resolve(JSObject().apply { put("ids", ids) })
|
|
}
|
|
|
|
@Command fun deleteImages(invoke: Invoke) = io.execute {
|
|
try {
|
|
val args = invoke.parseArgs(DeleteImagesArgs::class.java)
|
|
if (WallpaperStore.deleteMany(activity, args.ids.toList()) == 0) throw IllegalArgumentException("Keine Bilder gefunden")
|
|
invoke.resolve(WallpaperStore.state(activity))
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Bilder konnten nicht gelöscht werden") }
|
|
}
|
|
|
|
@Command fun setImageCrop(invoke: Invoke) = io.execute {
|
|
try {
|
|
val args = invoke.parseArgs(ImageCropArgs::class.java)
|
|
val image = WallpaperStore.setCrop(activity, args.id, args.mode, args.zoom, args.positionX, args.positionY, args.rotation)
|
|
?: throw IllegalArgumentException("Bild wurde nicht gefunden")
|
|
invoke.resolve(image)
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Bildausschnitt konnte nicht gespeichert werden") }
|
|
}
|
|
|
|
@ActivityCallback
|
|
fun selectedImages(invoke: Invoke, result: ActivityResult) {
|
|
if (result.resultCode != Activity.RESULT_OK) { invoke.reject("Bildauswahl abgebrochen"); return }
|
|
val uris = mutableListOf<Uri>()
|
|
result.data?.data?.let(uris::add)
|
|
result.data?.clipData?.let { clip -> for (i in 0 until clip.itemCount) uris.add(clip.getItemAt(i).uri) }
|
|
if (uris.isEmpty()) { invoke.reject("Keine Bilder ausgewählt"); return }
|
|
io.execute {
|
|
try {
|
|
uris.forEach { uri ->
|
|
val type = activity.contentResolver.getType(uri)
|
|
val extension = when (type) { "image/png" -> "png"; "image/webp" -> "webp"; else -> "jpg" }
|
|
val target = File(WallpaperStore.directory(activity), "${System.currentTimeMillis()}-${UUID.randomUUID()}.$extension")
|
|
activity.contentResolver.openInputStream(uri).use { input -> requireNotNull(input) { "Bild konnte nicht geöffnet werden" }; target.outputStream().use(input::copyTo) }
|
|
}
|
|
invoke.resolve(WallpaperStore.state(activity))
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Bilder konnten nicht importiert werden") }
|
|
}
|
|
}
|
|
|
|
@Command fun setSetting(invoke: Invoke) {
|
|
try {
|
|
val args = invoke.parseArgs(SettingArgs::class.java)
|
|
WallpaperStore.set(activity, args.name, args.value)
|
|
invoke.resolve(WallpaperStore.state(activity))
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Einstellung konnte nicht gespeichert werden") }
|
|
}
|
|
|
|
@Command fun setInterval(invoke: Invoke) {
|
|
try {
|
|
val args = invoke.parseArgs(IntervalArgs::class.java)
|
|
WallpaperStore.setInterval(activity, args.minutes)
|
|
WallpaperScheduler.cancel(activity)
|
|
ScreenOnRotationService.stop(activity)
|
|
if (args.minutes == -1) ScreenOnRotationService.start(activity)
|
|
else if (args.minutes > 0) WallpaperScheduler.scheduleNext(activity)
|
|
invoke.resolve(WallpaperStore.state(activity))
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Wechselintervall konnte nicht gespeichert werden") }
|
|
}
|
|
|
|
@Command fun nextWallpaper(invoke: Invoke) = io.execute {
|
|
try {
|
|
if (WallpaperStore.applyNext(activity)) invoke.resolve(WallpaperStore.state(activity))
|
|
else invoke.reject("Kein verfügbares Bild konnte angewendet 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 {
|
|
invoke.resolve(ImmichClient.connection(activity))
|
|
}
|
|
|
|
@Command fun connectImmich(invoke: Invoke) = io.execute {
|
|
try {
|
|
val args = invoke.parseArgs(ImmichConnectArgs::class.java)
|
|
val result = ImmichClient.connect(activity, args.serverUrl, args.apiKey)
|
|
if (WallpaperStore.prefetchImmich(activity)) ImmichPrefetchWorker.enqueue(activity)
|
|
invoke.resolve(result)
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Immich-Verbindung fehlgeschlagen") }
|
|
}
|
|
|
|
@Command fun disconnectImmich(invoke: Invoke) = io.execute {
|
|
ImmichPrefetchWorker.cancel(activity)
|
|
invoke.resolve(ImmichClient.disconnect(activity))
|
|
}
|
|
|
|
@Command fun getImmichAlbums(invoke: Invoke) = io.execute {
|
|
try { invoke.resolve(ImmichClient.albums(activity)) }
|
|
catch (error: Exception) { invoke.reject(error.message ?: "Immich-Alben konnten nicht geladen werden") }
|
|
}
|
|
|
|
@Command fun getImmichAssets(invoke: Invoke) = io.execute {
|
|
try {
|
|
val args = invoke.parseArgs(ImmichAssetsArgs::class.java)
|
|
invoke.resolve(ImmichClient.assets(activity, args.albumId, args.page, args.size))
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Immich-Bilder konnten nicht geladen werden") }
|
|
}
|
|
|
|
@Command fun importImmichAssets(invoke: Invoke) = io.execute {
|
|
try {
|
|
val args = invoke.parseArgs(ImmichImportArgs::class.java)
|
|
invoke.resolve(ImmichClient.importAssets(activity, args.ids.toList()))
|
|
} catch (error: Exception) { invoke.reject(error.message ?: "Immich-Bilder konnten nicht importiert werden") }
|
|
}
|
|
|
|
@Command fun getImmichImportProgress(invoke: Invoke) {
|
|
invoke.resolve(ImmichClient.importProgress())
|
|
}
|
|
}
|