feat(setup): bootstrap Android plugin and web UI
This initial commit adds a multi-part scaffold for Wechselbild, including a web UI and an Android plugin. It introduces frontend assets and a package.json-based dev setup, plus plugin scaffolding for Android with Kotlin sources and Cargo.toml for the Tauri plugin. - Adds Android wallpaper plugin with Kotlin sources and manifest - Includes web UI scaffold with index.html and package.json - Adds Tauri plugin scaffold with Cargo.toml and build config
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
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.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 }
|
||||
|
||||
@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 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")
|
||||
}
|
||||
|
||||
@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)
|
||||
if (args.name == "enabled") {
|
||||
if (args.value) WallpaperRotationService.start(activity) else WallpaperRotationService.stop(activity)
|
||||
}
|
||||
invoke.resolve(WallpaperStore.state(activity))
|
||||
} catch (error: Exception) { invoke.reject(error.message ?: "Einstellung konnte nicht gespeichert werden") }
|
||||
}
|
||||
|
||||
@Command fun nextWallpaper(invoke: Invoke) = io.execute {
|
||||
if (WallpaperStore.applyNext(activity)) invoke.resolve(WallpaperStore.state(activity)) else invoke.reject("Bitte wähle zuerst Bilder aus")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user