tauri-plugin-widgets 0.4.3

Tauri plugin for App Widgets on Android, iOS, and macOS (WidgetKit); Windows Widgets Board (Adaptive Cards) + desktop webview; Linux desktop webview with X11 DESKTOP pin.
Documentation
package git.s00d.widgets

import android.content.Context
import org.json.JSONArray
import org.json.JSONObject

/**
 * Render receipts live outside the config SharedPreferences map so writes never
 * bump `__meta_nonce__`. Prefs name: [WidgetStoreKeys.RECEIPTS_PREFS].
 */
object WidgetReceipts {
    private const val KEY = "receipts"

    /** Max age for "live" instances used by sync targeting (15 min). */
    const val LIVE_MAX_AGE_MS = 15 * 60 * 1000L

    fun write(context: Context, receipt: JSONObject) {
        if (!receipt.has("ts")) {
            receipt.put("ts", System.currentTimeMillis())
        }
        val prefs = context.getSharedPreferences(WidgetStoreKeys.RECEIPTS_PREFS, Context.MODE_PRIVATE)
        val arr = runCatching { JSONArray(prefs.getString(KEY, "[]") ?: "[]") }
            .getOrElse { JSONArray() }
        val instance = receipt.optString("instance", "")
        val group = receipt.optString("group", "")
        // Replace same (group, instance)
        val next = JSONArray()
        for (i in 0 until arr.length()) {
            val item = arr.optJSONObject(i) ?: continue
            if (item.optString("instance") == instance && item.optString("group") == group) {
                continue
            }
            next.put(item)
        }
        next.put(receipt)
        // Cap bag size
        while (next.length() > 64) {
            next.remove(0)
        }
        prefs.edit().putString(KEY, next.toString()).apply()
    }

    fun list(context: Context, group: String? = null): JSONArray {
        val prefs = context.getSharedPreferences(WidgetStoreKeys.RECEIPTS_PREFS, Context.MODE_PRIVATE)
        val arr = runCatching { JSONArray(prefs.getString(KEY, "[]") ?: "[]") }
            .getOrElse { JSONArray() }
        if (group.isNullOrBlank()) return arr
        val out = JSONArray()
        for (i in 0 until arr.length()) {
            val item = arr.optJSONObject(i) ?: continue
            if (item.optString("group") == group) out.put(item)
        }
        return out
    }

    /** Live appWidgetId strings that recently rendered [widgetId] for [group]. */
    fun liveInstances(context: Context, group: String, widgetId: String): List<Int> {
        val now = System.currentTimeMillis()
        val out = mutableListOf<Int>()
        val arr = list(context, group)
        for (i in 0 until arr.length()) {
            val item = arr.optJSONObject(i) ?: continue
            if (item.optString("widgetId") != widgetId) continue
            val ts = item.optLong("ts", 0L)
            if (now - ts > LIVE_MAX_AGE_MS) continue
            item.optString("instance").toIntOrNull()?.let { out.add(it) }
        }
        return out
    }
}