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