diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt index 36e10f9..7b3f606 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt @@ -9,7 +9,6 @@ import android.content.Intent.ACTION_VIEW import android.net.Uri import android.os.Bundle import android.text.Html -import android.util.Base64 import android.view.ActionMode import android.view.Menu import android.view.MenuItem @@ -33,7 +32,6 @@ import io.heckel.ntfy.db.Subscription import io.heckel.ntfy.firebase.FirebaseMessenger import io.heckel.ntfy.util.Log import io.heckel.ntfy.msg.ApiService -import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64 import io.heckel.ntfy.msg.NotificationService import io.heckel.ntfy.service.SubscriberServiceManager import io.heckel.ntfy.util.* @@ -249,15 +247,24 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra } // Mark this subscription as "open" so we don't receive notifications for it - Log.d(TAG, "onCreate hook: Marking subscription $subscriptionId as 'open'") repository.detailViewSubscriptionId.set(subscriptionId) } override fun onResume() { super.onResume() - Log.d(TAG, "onResume hook: Marking subscription $subscriptionId as 'open'") - repository.detailViewSubscriptionId.set(subscriptionId) // Mark as "open" so we don't send notifications while this is open + // Mark as "open" so we don't send notifications while this is open + repository.detailViewSubscriptionId.set(subscriptionId) + + // Update buttons (this is for when we return from the preferences screen) + lifecycleScope.launch(Dispatchers.IO) { + val subscription = repository.getSubscription(subscriptionId) ?: return@launch + subscriptionInstant = subscription.instant + subscriptionMutedUntil = subscription.mutedUntil + + showHideInstantMenuItems(subscriptionInstant) + showHideMutedUntilMenuItems(subscriptionMutedUntil) + } } override fun onPause() { @@ -290,7 +297,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra // Show and hide buttons showHideInstantMenuItems(subscriptionInstant) - showHideNotificationMenuItems(subscriptionMutedUntil) + showHideMutedUntilMenuItems(subscriptionMutedUntil) // Regularly check if "notification muted" time has passed // NOTE: This is done here, because then we know that we've initialized the menu items. @@ -300,6 +307,8 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra } private fun startNotificationMutedChecker() { + // FIXME This is awful and has to go. + lifecycleScope.launch(Dispatchers.IO) { delay(1000) // Just to be sure we've initialized all the things, we wait a bit ... while (isActive) { @@ -309,7 +318,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra if (mutedUntilExpired) { val newSubscription = subscription.copy(mutedUntil = 0L) repository.updateSubscription(newSubscription) - showHideNotificationMenuItems(0L) + showHideMutedUntilMenuItems(0L) } delay(60_000) } @@ -414,7 +423,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra val newSubscription = subscription?.copy(mutedUntil = mutedUntilTimestamp) newSubscription?.let { repository.updateSubscription(newSubscription) } subscriptionMutedUntil = mutedUntilTimestamp - showHideNotificationMenuItems(mutedUntilTimestamp) + showHideMutedUntilMenuItems(mutedUntilTimestamp) runOnUiThread { when (mutedUntilTimestamp) { 0L -> Toast.makeText(this@DetailActivity, getString(R.string.notification_dialog_enabled_toast_message), Toast.LENGTH_LONG).show() @@ -493,6 +502,9 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra } private fun showHideInstantMenuItems(enable: Boolean) { + if (!this::menu.isInitialized) { + return + } subscriptionInstant = enable runOnUiThread { val appBaseUrl = getString(R.string.app_base_url) @@ -509,7 +521,10 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra } } - private fun showHideNotificationMenuItems(mutedUntilTimestamp: Long) { + private fun showHideMutedUntilMenuItems(mutedUntilTimestamp: Long) { + if (!this::menu.isInitialized) { + return + } subscriptionMutedUntil = mutedUntilTimestamp runOnUiThread { val notificationsEnabledItem = menu.findItem(R.id.detail_menu_notifications_enabled) diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt index a3aefc5..7499f1c 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt @@ -3,20 +3,14 @@ package io.heckel.ntfy.ui import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope -import androidx.preference.ListPreference -import androidx.preference.Preference -import androidx.preference.PreferenceDataStore -import androidx.preference.PreferenceFragmentCompat +import androidx.preference.* +import io.heckel.ntfy.BuildConfig import io.heckel.ntfy.R import io.heckel.ntfy.db.Repository import io.heckel.ntfy.db.Subscription -import io.heckel.ntfy.util.Log import io.heckel.ntfy.service.SubscriberServiceManager -import io.heckel.ntfy.util.formatDateShort -import io.heckel.ntfy.util.toPriorityString -import io.heckel.ntfy.util.topicShortUrl -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import io.heckel.ntfy.util.* +import kotlinx.coroutines.* import java.util.* /** @@ -77,18 +71,43 @@ class DetailSettingsActivity : AppCompatActivity() { // Load subscription and users val subscriptionId = arguments?.getLong(DetailActivity.EXTRA_SUBSCRIPTION_ID) ?: return - lifecycleScope.launch(Dispatchers.IO) { - subscription = repository.getSubscription(subscriptionId) ?: return@launch - activity?.runOnUiThread { - loadView() + runBlocking { + withContext(Dispatchers.IO) { + subscription = repository.getSubscription(subscriptionId) ?: return@withContext + activity?.runOnUiThread { + loadView() + } } } } private fun loadView() { + // Instant delivery + val appBaseUrl = getString(R.string.app_base_url) + val instantEnabledPrefId = context?.getString(R.string.detail_settings_notifications_instant_key) ?: return + val instantEnabled: SwitchPreference? = findPreference(instantEnabledPrefId) + instantEnabled?.isVisible = BuildConfig.FIREBASE_AVAILABLE && subscription.baseUrl == appBaseUrl + instantEnabled?.isChecked = subscription.instant + instantEnabled?.preferenceDataStore = object : PreferenceDataStore() { + override fun putBoolean(key: String?, value: Boolean) { + save(subscription.copy(instant = value), refresh = true) + } + override fun getBoolean(key: String?, defValue: Boolean): Boolean { + return subscription.instant + } + } + instantEnabled?.summaryProvider = Preference.SummaryProvider { pref -> + if (pref.isChecked) { + getString(R.string.detail_settings_notifications_instant_summary_on) + } else { + getString(R.string.detail_settings_notifications_instant_summary_off) + } + } + // Notifications muted until val mutedUntilPrefId = context?.getString(R.string.detail_settings_notifications_muted_until_key) ?: return val mutedUntil: ListPreference? = findPreference(mutedUntilPrefId) + mutedUntil?.isVisible = true // Hack: Show all settings at once, because subscription is loaded asynchronously mutedUntil?.value = subscription.mutedUntil.toString() mutedUntil?.preferenceDataStore = object : PreferenceDataStore() { override fun putString(key: String?, value: String?) { @@ -130,6 +149,7 @@ class DetailSettingsActivity : AppCompatActivity() { // Minimum priority val minPriorityPrefId = context?.getString(R.string.detail_settings_notifications_min_priority_key) ?: return val minPriority: ListPreference? = findPreference(minPriorityPrefId) + minPriority?.isVisible = true // Hack: Show all settings at once, because subscription is loaded asynchronously minPriority?.value = subscription.minPriority.toString() minPriority?.preferenceDataStore = object : PreferenceDataStore() { override fun putString(key: String?, value: String?) { @@ -160,6 +180,7 @@ class DetailSettingsActivity : AppCompatActivity() { // Auto delete val autoDeletePrefId = context?.getString(R.string.detail_settings_notifications_auto_delete_key) ?: return val autoDelete: ListPreference? = findPreference(autoDeletePrefId) + autoDelete?.isVisible = true // Hack: Show all settings at once, because subscription is loaded asynchronously autoDelete?.value = subscription.autoDelete.toString() autoDelete?.preferenceDataStore = object : PreferenceDataStore() { override fun putString(key: String?, value: String?) { @@ -189,16 +210,19 @@ class DetailSettingsActivity : AppCompatActivity() { } } - private fun save(newSubscription: Subscription) { + private fun save(newSubscription: Subscription, refresh: Boolean = false) { subscription = newSubscription lifecycleScope.launch(Dispatchers.IO) { repository.updateSubscription(newSubscription) + if (refresh) { + SubscriberServiceManager.refresh(requireContext()) + } } } private fun maybeAppendGlobal(summary: String, global: Boolean): String { return if (global) { - summary + " (" + getString(R.string.settings_global_setting_suffix) + ")" + summary + " (" + getString(R.string.detail_settings_global_setting_suffix) + ")" } else { summary } 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 d220513..2271d48 100644 --- a/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt +++ b/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt @@ -9,6 +9,7 @@ import io.heckel.ntfy.db.Subscription import io.heckel.ntfy.service.SubscriberServiceManager import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.randomString +import io.heckel.ntfy.util.shortUrl import io.heckel.ntfy.util.topicUrlUp import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -95,6 +96,10 @@ class BroadcastReceiver : android.content.BroadcastReceiver() { Log.w(TAG, "Failed to add subscription", e) distributor.sendRegistrationFailed(appId, connectorToken, e.message) } + + // Add to log scrubber + Log.addScrubTerm(shortUrl(baseUrl), Log.TermType.Domain) + Log.addScrubTerm(topic) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9b34be3..d608aa1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -334,8 +334,13 @@ Version ntfy %1$s (%2$s) Copied to clipboard - Use global setting - global + + + Instant delivery + Notifications are delivered instantly. Requires a foreground service and consumes more battery. + Notifications are delivered using Firebase. Delivery may be delayed, but consumes less battery. + Use global setting + global Add user diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index 8005f8e..05cb39b 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -31,6 +31,7 @@ Version + SubscriptionInstant SubscriptionMutedUntil SubscriptionMinPriority SubscriptionAutoDelete @@ -69,7 +70,7 @@ 5 - @string/settings_global_setting_title + @string/detail_settings_global_setting_title @string/settings_notifications_min_priority_min @string/settings_notifications_min_priority_low @string/settings_notifications_min_priority_default @@ -121,7 +122,7 @@ 7776000 - @string/settings_global_setting_title + @string/detail_settings_global_setting_title @string/settings_notifications_auto_delete_never @string/settings_notifications_auto_delete_one_day @string/settings_notifications_auto_delete_three_days diff --git a/app/src/main/res/xml/detail_preferences.xml b/app/src/main/res/xml/detail_preferences.xml index e5a18c6..e63185f 100644 --- a/app/src/main/res/xml/detail_preferences.xml +++ b/app/src/main/res/xml/detail_preferences.xml @@ -1,23 +1,30 @@ + + app:defaultValue="0" + app:isPreferenceVisible="false"/> + app:defaultValue="0" + app:isPreferenceVisible="false"/> + app:defaultValue="-1" + app:isPreferenceVisible="false"/>