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:
2026-08-20 17:46:28 +02:00
commit 2a85d5f622
148 changed files with 18356 additions and 0 deletions
@@ -0,0 +1,56 @@
package de.wechselbild.wallpaper
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import java.util.concurrent.Executors
class WallpaperRotationService : Service() {
private val worker = Executors.newSingleThreadExecutor()
private val screenReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_SCREEN_ON && WallpaperStore.enabled(context)) worker.execute { WallpaperStore.applyNext(context) }
}
}
override fun onCreate() {
super.onCreate()
createChannel()
val launch = packageManager.getLaunchIntentForPackage(packageName)
val pending = PendingIntent.getActivity(this, 0, launch, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
val notification = NotificationCompat.Builder(this, CHANNEL)
.setSmallIcon(android.R.drawable.ic_menu_gallery)
.setContentTitle("Wechselbild ist aktiv")
.setContentText("Das Motiv wechselt beim Aktivieren des Displays.")
.setOngoing(true).setSilent(true).setContentIntent(pending).build()
startForeground(NOTIFICATION_ID, notification)
ContextCompat.registerReceiver(this, screenReceiver, IntentFilter(Intent.ACTION_SCREEN_ON), ContextCompat.RECEIVER_NOT_EXPORTED)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY
override fun onBind(intent: Intent?): IBinder? = null
override fun onDestroy() { runCatching { unregisterReceiver(screenReceiver) }; worker.shutdown(); super.onDestroy() }
private fun createChannel() {
if (Build.VERSION.SDK_INT >= 26) {
val channel = NotificationChannel(CHANNEL, "Automatischer Bildwechsel", NotificationManager.IMPORTANCE_LOW)
getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
}
}
companion object {
private const val CHANNEL = "wechselbild_rotation"
private const val NOTIFICATION_ID = 4217
fun start(context: Context) = ContextCompat.startForegroundService(context, Intent(context, WallpaperRotationService::class.java))
fun stop(context: Context) { context.stopService(Intent(context, WallpaperRotationService::class.java)) }
}
}