package org.meshbay.client.notify import android.app.Activity import android.app.NotificationManager import android.os.Build import org.json.JSONArray import org.json.JSONObject import org.unifiedpush.android.connector.UnifiedPush /** * Notifications on this phone (§11.3), with nothing to install. * * Turned on, the phone fetches what is new every quarter of an hour (`PollJob`). * If a UnifiedPush distributor is already on the phone — ntfy, or an application * that carries one — it is used as well, and notifications arrive at once; if it * refuses or goes away, the phone simply goes back to fetching. The page gives * the hub whatever this side ends up with. Phone-only: the desktop has no * counterpart, and its preload has no such channels. * * Whether a notification is wanted at all — every one turned off, or one group * muted — is decided on the hub, which then creates nothing, so nothing is * pushed or fetched. Nothing here second-guesses it. */ class PushChannels( private val activity: Activity, private val state: PushState, private val channelNames: () -> Map, ) { fun handles(channel: String) = channel.startsWith("push:") fun call(channel: String, args: JSONArray): Any? = when (channel) { "push:status" -> status() "push:enable" -> enable() "push:disable" -> { disable(); status() } "push:remember" -> { state.remember(args.optString(0, ""), args.optString(1, ""), args.optString(2, ""), args.optString(3, "")) Poller.schedule(activity, pushed = state.endpoint != null) status() } else -> throw org.meshbay.client.bridge.Refused("Refused: no such channel") } private fun distributors() = UnifiedPush.getDistributors(activity).any { it != activity.packageName } private fun status(): JSONObject = state.status() .put("distributors", distributors()) .put("permitted", activity.getSystemService(NotificationManager::class.java).areNotificationsEnabled()) private fun enable(): JSONObject { state.requested() Notifier.ensureChannels(activity, channelNames()) if (!distributors()) state.fellBack("NO_DISTRIBUTOR") activity.runOnUiThread { if (Build.VERSION.SDK_INT >= 33) { activity.requestPermissions(arrayOf(android.Manifest.permission.POST_NOTIFICATIONS), PERMISSION_REQUEST) } // Only when one is installed: the system's chooser for a person who // has none would be a screen about something they never asked for. if (distributors()) UnifiedPush.tryUseDefaultDistributor(activity) { found -> if (found) UnifiedPush.register(activity, messageForDistributor = "MeshBay") else state.fellBack("NO_DISTRIBUTOR") } } return status() } private fun disable() { Poller.cancel(activity) try { UnifiedPush.removeDistributor(activity) } catch (e: Exception) { /* none was saved */ } state.cleared() } companion object { private const val PERMISSION_REQUEST = 4207 } }