1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
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<String, String>,
) {
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
}
}
|