diff --git a/app/src/main/java/io/heckel/ntfy/msg/NotificationDispatcher.kt b/app/src/main/java/io/heckel/ntfy/msg/NotificationDispatcher.kt
index 57cab2d..f2c13d0 100644
--- a/app/src/main/java/io/heckel/ntfy/msg/NotificationDispatcher.kt
+++ b/app/src/main/java/io/heckel/ntfy/msg/NotificationDispatcher.kt
@@ -23,8 +23,8 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
fun dispatch(subscription: Subscription, notification: Notification) {
val muted = checkMuted(subscription)
val notify = checkNotify(subscription, notification, muted)
- val broadcast = subscription.upAppId == null
- val distribute = subscription.upAppId != null
+ val broadcast = subscription.upAppId == null // Never broadcast for UnifiedPush
+ val distribute = subscription.upAppId != null // Only distribute for UnifiedPush subscriptions
if (notify) {
notifier.send(subscription, notification)
}
diff --git a/app/src/main/java/io/heckel/ntfy/service/SubscriberServiceManager.kt b/app/src/main/java/io/heckel/ntfy/service/SubscriberServiceManager.kt
index e3d6174..a4ecfbb 100644
--- a/app/src/main/java/io/heckel/ntfy/service/SubscriberServiceManager.kt
+++ b/app/src/main/java/io/heckel/ntfy/service/SubscriberServiceManager.kt
@@ -11,6 +11,11 @@ import io.heckel.ntfy.up.BroadcastReceiver
/**
* This class only manages the SubscriberService, i.e. it starts or stops it.
* It's used in multiple activities.
+ *
+ * We are starting the service via a worker and not directly because since Android 7
+ * (but officially since Lollipop!), any process called by a BroadcastReceiver
+ * (only manifest-declared receiver) is run at low priority and hence eventually
+ * killed by Android.
*/
class SubscriberServiceManager(private val context: Context) {
fun refresh() {
@@ -20,6 +25,10 @@ class SubscriberServiceManager(private val context: Context) {
workManager.enqueue(startServiceRequest)
}
+ /**
+ * Starts or stops the foreground service by figuring out how many instant delivery subscriptions
+ * exist. If there's > 0, then we need a foreground service.
+ */
class RefreshWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
if (context.applicationContext !is Application) {
diff --git a/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt
index ae87e6a..7a3992d 100644
--- a/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt
+++ b/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt
@@ -1,10 +1,15 @@
package io.heckel.ntfy.ui
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
+import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.*
+import androidx.preference.Preference.OnPreferenceClickListener
import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
@@ -37,6 +42,10 @@ class SettingsActivity : AppCompatActivity() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.main_preferences, rootKey)
+ // Important note: We do not use the default shared prefs to store settings. Every
+ // preferenceDataStore is overridden to use the repository. This is convenient, because
+ // everybody has access to the repository.
+
// UnifiedPush Enabled
val upEnabledPrefId = context?.getString(R.string.pref_unified_push_enabled) ?: return
val upEnabled: SwitchPreference? = findPreference(upEnabledPrefId)
@@ -82,7 +91,18 @@ class SettingsActivity : AppCompatActivity() {
// Version
val versionPrefId = context?.getString(R.string.pref_version) ?: return
val versionPref: Preference? = findPreference(versionPrefId)
- versionPref?.summary = getString(R.string.settings_about_version_format, BuildConfig.VERSION_NAME, BuildConfig.FLAVOR)
+ val version = getString(R.string.settings_about_version_format, BuildConfig.VERSION_NAME, BuildConfig.FLAVOR)
+ versionPref?.summary = version
+ versionPref?.onPreferenceClickListener = OnPreferenceClickListener {
+ val context = context ?: return@OnPreferenceClickListener false
+ val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ val clip = ClipData.newPlainText("app version", version)
+ clipboard.setPrimaryClip(clip)
+ Toast
+ .makeText(context, getString(R.string.settings_about_version_copied_to_clipboard_message), Toast.LENGTH_LONG)
+ .show()
+ true
+ }
}
}
}
diff --git a/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt b/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt
index 8e07ee0..2a7d64c 100644
--- a/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt
+++ b/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt
@@ -16,6 +16,10 @@ import kotlinx.coroutines.launch
import java.util.*
import kotlin.random.Random
+/**
+ * This is the UnifiedPush broadcast receiver to handle the distributor actions REGISTER and UNREGISTER.
+ * See https://unifiedpush.org/spec/android/ for details.
+ */
class BroadcastReceiver : android.content.BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (context == null || intent == null) {
@@ -69,7 +73,6 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
newCount = 0,
lastActive = Date().time/1000
)
-
Log.d(TAG, "Adding subscription with for app $appId (connectorToken $connectorToken): $subscription")
repository.addSubscription(subscription)
distributor.sendEndpoint(appId, connectorToken, endpoint)
diff --git a/app/src/main/java/io/heckel/ntfy/up/Distributor.kt b/app/src/main/java/io/heckel/ntfy/up/Distributor.kt
index a4c1ad9..38273a9 100644
--- a/app/src/main/java/io/heckel/ntfy/up/Distributor.kt
+++ b/app/src/main/java/io/heckel/ntfy/up/Distributor.kt
@@ -4,6 +4,10 @@ import android.content.Context
import android.content.Intent
import android.util.Log
+/**
+ * This is the UnifiedPush distributor, an amalgamation of messages to be sent as part of the spec.
+ * See https://unifiedpush.org/spec/android/ for details.
+ */
class Distributor(val context: Context) {
fun sendMessage(app: String, connectorToken: String, message: String) {
Log.d(TAG, "Sending MESSAGE to $app (token=$connectorToken): $message")
diff --git a/app/src/main/java/io/heckel/ntfy/util/Util.kt b/app/src/main/java/io/heckel/ntfy/util/Util.kt
index 1b88747..23c3747 100644
--- a/app/src/main/java/io/heckel/ntfy/util/Util.kt
+++ b/app/src/main/java/io/heckel/ntfy/util/Util.kt
@@ -104,6 +104,7 @@ fun fadeStatusBarColor(window: Window, fromColor: Int, toColor: Int) {
statusBarColorAnimation.start()
}
+// Generates a (cryptographically secure) random string of a certain length
fun randomString(len: Int): String {
val random = SecureRandom()
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray()
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9a8e887..d209226 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -157,6 +157,7 @@
About
Version
ntfy %1$s (%2$s)
+ Copied to clipboard
UnifiedPushEnabled