diff options
author | Philipp Heckel <pheckel@datto.com> | 2022-11-29 22:46:38 -0500 |
---|---|---|
committer | Philipp Heckel <pheckel@datto.com> | 2022-11-29 22:46:38 -0500 |
commit | 55ad2e65b5a7ea521bf8bcc211f563ae9f9d4e8c (patch) | |
tree | 673b12db5885832ec1169e65a497e9f808f028e8 /app | |
parent | e18be4a2a79b3d16aefd5113d2df2274d683e60b (diff) | |
download | ponypush-55ad2e65b5a7ea521bf8bcc211f563ae9f9d4e8c.tar.gz ponypush-55ad2e65b5a7ea521bf8bcc211f563ae9f9d4e8c.tar.bz2 ponypush-55ad2e65b5a7ea521bf8bcc211f563ae9f9d4e8c.zip |
Works!
Diffstat (limited to 'app')
-rw-r--r-- | app/src/main/AndroidManifest.xml | 7 | ||||
-rw-r--r-- | app/src/main/java/io/heckel/ntfy/app/Application.kt | 6 | ||||
-rw-r--r-- | app/src/main/java/io/heckel/ntfy/db/Repository.kt | 15 | ||||
-rw-r--r-- | app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt | 103 | ||||
-rw-r--r-- | app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt | 1 | ||||
-rw-r--r-- | app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt | 20 | ||||
-rw-r--r-- | app/src/main/res/values/strings.xml | 3 | ||||
-rw-r--r-- | app/src/main/res/values/values.xml | 1 | ||||
-rw-r--r-- | app/src/main/res/xml/main_preferences.xml | 4 |
9 files changed, 98 insertions, 62 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 35689fd..8120b56 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -134,7 +134,12 @@ android:exported="false"> </receiver> - <receiver android:name=".msg.NotificationService$AlarmReceiver"/> + <!-- Broadcast receiver for when the notification is swiped away (currently only to cancel the insistent sound) --> + <receiver + android:name=".msg.NotificationService$DeleteBroadcastReceiver" + android:enabled="true" + android:exported="false"> + </receiver> <!-- Firebase messaging (note that this is empty in the F-Droid flavor) --> <service diff --git a/app/src/main/java/io/heckel/ntfy/app/Application.kt b/app/src/main/java/io/heckel/ntfy/app/Application.kt index b4b104c..f6cb30c 100644 --- a/app/src/main/java/io/heckel/ntfy/app/Application.kt +++ b/app/src/main/java/io/heckel/ntfy/app/Application.kt @@ -1,16 +1,10 @@ package io.heckel.ntfy.app import android.app.Application -import android.content.Context -import io.heckel.ntfy.db.Database import io.heckel.ntfy.db.Repository import io.heckel.ntfy.util.Log class Application : Application() { - private val database by lazy { - Log.init(this) // What a hack, but this is super early and used everywhere - Database.getInstance(this) - } val repository by lazy { val repository = Repository.getInstance(applicationContext) if (repository.getRecordLogs()) { diff --git a/app/src/main/java/io/heckel/ntfy/db/Repository.kt b/app/src/main/java/io/heckel/ntfy/db/Repository.kt index cee2985..d92e4a8 100644 --- a/app/src/main/java/io/heckel/ntfy/db/Repository.kt +++ b/app/src/main/java/io/heckel/ntfy/db/Repository.kt @@ -2,6 +2,7 @@ package io.heckel.ntfy.db import android.content.Context import android.content.SharedPreferences +import android.media.MediaPlayer import android.os.Build import androidx.annotation.WorkerThread import androidx.appcompat.app.AppCompatDelegate @@ -18,7 +19,10 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas private val connectionStates = ConcurrentHashMap<Long, ConnectionState>() private val connectionStatesLiveData = MutableLiveData(connectionStates) + + // TODO Move these into an ApplicationState singleton val detailViewSubscriptionId = AtomicLong(0L) // Omg, what a hack ... + val mediaPlayer = MediaPlayer() init { Log.d(TAG, "Created $this") @@ -288,6 +292,16 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas .apply() } + fun getInsistentMaxPriorityEnabled(): Boolean { + return sharedPrefs.getBoolean(SHARED_PREFS_INSISTENT_MAX_PRIORITY_ENABLED, false) // Disabled by default + } + + fun setInsistentMaxPriorityEnabled(enabled: Boolean) { + sharedPrefs.edit() + .putBoolean(SHARED_PREFS_INSISTENT_MAX_PRIORITY_ENABLED, enabled) + .apply() + } + fun getRecordLogs(): Boolean { return sharedPrefs.getBoolean(SHARED_PREFS_RECORD_LOGS_ENABLED, false) // Disabled by default } @@ -459,6 +473,7 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas const val SHARED_PREFS_CONNECTION_PROTOCOL = "ConnectionProtocol" const val SHARED_PREFS_DARK_MODE = "DarkMode" const val SHARED_PREFS_BROADCAST_ENABLED = "BroadcastEnabled" + const val SHARED_PREFS_INSISTENT_MAX_PRIORITY_ENABLED = "InsistentMaxPriority" const val SHARED_PREFS_RECORD_LOGS_ENABLED = "RecordLogs" const val SHARED_PREFS_BATTERY_OPTIMIZATIONS_REMIND_TIME = "BatteryOptimizationsRemindTime" const val SHARED_PREFS_WEBSOCKET_REMIND_TIME = "JsonStreamRemindTime" // "Use WebSocket" banner (used to be JSON stream deprecation banner) diff --git a/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt b/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt index a2adb41..6f46ca6 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt @@ -7,7 +7,6 @@ import android.content.Context import android.content.Intent import android.media.AudioAttributes import android.media.AudioManager -import android.media.MediaPlayer import android.media.RingtoneManager import android.net.Uri import android.os.Build @@ -24,9 +23,9 @@ import io.heckel.ntfy.ui.MainActivity import io.heckel.ntfy.util.* import java.util.* - class NotificationService(val context: Context) { private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + private val repository = Repository.getInstance(context) fun display(subscription: Subscription, notification: Notification) { Log.d(TAG, "Displaying notification $notification") @@ -66,6 +65,7 @@ class NotificationService(val context: Context) { private fun displayInternal(subscription: Subscription, notification: Notification, update: Boolean = false) { val title = formatTitle(subscription, notification) val channelId = toChannelId(notification.priority) + val insistent = notification.priority == 5 && repository.getInsistentMaxPriorityEnabled() val builder = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_notification) .setColor(ContextCompat.getColor(context, Colors.notificationIcon(context))) @@ -74,7 +74,8 @@ class NotificationService(val context: Context) { .setAutoCancel(true) // Cancel when notification is clicked setStyleAndText(builder, subscription, notification) // Preview picture or big text style setClickAction(builder, subscription, notification) - maybeSetSound(builder, update) + maybeSetDeleteIntent(builder, insistent) + maybeSetSound(builder, insistent, update) maybeSetProgress(builder, notification) maybeAddOpenAction(builder, notification) maybeAddBrowseAction(builder, notification) @@ -82,65 +83,24 @@ class NotificationService(val context: Context) { maybeAddCancelAction(builder, notification) maybeAddUserActions(builder, notification) - - maybeCreateNotificationChannel(notification.priority) - val systemNotification = builder.build() - if (channelId == CHANNEL_ID_MAX) { - //systemNotification.flags = systemNotification.flags or android.app.Notification.FLAG_INSISTENT - } - notificationManager.notify(notification.notificationId, systemNotification) - - if (channelId == CHANNEL_ID_MAX) { - Log.d(TAG, "Setting alarm") - /*val calendar = Calendar.getInstance() - val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager - val intent = Intent(context, AlarmReceiver::class.java) - val pendingIntent = PendingIntent.getBroadcast(context, 1111, intent, PendingIntent.FLAG_IMMUTABLE) - // when using setAlarmClock() it displays a notification until alarm rings and when pressed it takes us to mainActivity - - alarmManager?.set( - AlarmManager.RTC_WAKEUP, - calendar.timeInMillis, pendingIntent - )*/ - - val alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) - val mMediaPlayer = MediaPlayer() - - mMediaPlayer.setDataSource(context, alert) - val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager - if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { - mMediaPlayer.setAudioAttributes(AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build()) - mMediaPlayer.isLooping = true; - mMediaPlayer.prepare(); - mMediaPlayer.start(); - mMediaPlayer.stop() - } + maybePlayInsistentSound(insistent) - } + notificationManager.notify(notification.notificationId, builder.build()) } - class AlarmReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - Log.d(TAG, "AlarmReceiver.onReceive ${intent}") - val context = context ?: return - - val alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) - val mMediaPlayer = MediaPlayer() - - mMediaPlayer.setDataSource(context, alert) - val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager - if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { - mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); - mMediaPlayer.setLooping(true); - mMediaPlayer.prepare(); - mMediaPlayer.start(); - } + private fun maybeSetDeleteIntent(builder: NotificationCompat.Builder, insistent: Boolean) { + if (!insistent) { + return } + val intent = Intent(context, DeleteBroadcastReceiver::class.java) + val pendingIntent = PendingIntent.getBroadcast(context, Random().nextInt(), intent, PendingIntent.FLAG_IMMUTABLE) + builder.setDeleteIntent(pendingIntent) } - private fun maybeSetSound(builder: NotificationCompat.Builder, update: Boolean) { - if (!update) { + private fun maybeSetSound(builder: NotificationCompat.Builder, insistent: Boolean, update: Boolean) { + val hasSound = !update && !insistent + if (hasSound) { val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) builder.setSound(defaultSoundUri) } else { @@ -353,6 +313,17 @@ class NotificationService(val context: Context) { } } + /** + * Receives a broadcast when a notification is swiped away. This is currently + * only called for notifications with an insistent sound. + */ + class DeleteBroadcastReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val mediaPlayer = Repository.getInstance(context).mediaPlayer + mediaPlayer.stop() + } + } + private fun detailActivityIntent(subscription: Subscription): PendingIntent? { val intent = Intent(context, DetailActivity::class.java).apply { putExtra(MainActivity.EXTRA_SUBSCRIPTION_ID, subscription.id) @@ -416,6 +387,28 @@ class NotificationService(val context: Context) { } } + private fun maybePlayInsistentSound(insistent: Boolean) { + if (!insistent) { + return + } + try { + Log.d(TAG, "Playing insistent alarm") + val mediaPlayer = repository.mediaPlayer + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) + if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) { + mediaPlayer.reset() + mediaPlayer.setDataSource(context, alert) + mediaPlayer.setAudioAttributes(AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build()) + mediaPlayer.isLooping = true; + mediaPlayer.prepare() + mediaPlayer.start() + } + } catch (e: Exception) { + Log.w(TAG, "Failed playing insistent alarm", e) + } + } + /** * Activity used to launch a URL. * . 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 4fc9ec7..0eec67b 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt @@ -297,6 +297,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra } } } + repository.mediaPlayer.stop() } override fun onCreateOptionsMenu(menu: Menu): Boolean { 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 cc6f882..859232a 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/SettingsActivity.kt @@ -200,6 +200,26 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere } } + // Keep alerting for max priority + val insistentMaxPriorityPrefId = context?.getString(R.string.settings_notifications_insistent_max_priority_key) ?: return + val insistentMaxPriority: SwitchPreference? = findPreference(insistentMaxPriorityPrefId) + insistentMaxPriority?.isChecked = repository.getInsistentMaxPriorityEnabled() + insistentMaxPriority?.preferenceDataStore = object : PreferenceDataStore() { + override fun putBoolean(key: String?, value: Boolean) { + repository.setInsistentMaxPriorityEnabled(value) + } + override fun getBoolean(key: String?, defValue: Boolean): Boolean { + return repository.getInsistentMaxPriorityEnabled() + } + } + insistentMaxPriority?.summaryProvider = Preference.SummaryProvider<SwitchPreference> { pref -> + if (pref.isChecked) { + getString(R.string.settings_notifications_insistent_max_priority_summary_enabled) + } else { + getString(R.string.settings_notifications_insistent_max_priority_summary_disabled) + } + } + // Channel settings val channelPrefsPrefId = context?.getString(R.string.settings_notifications_channel_prefs_key) ?: return val channelPrefs: Preference? = findPreference(channelPrefsPrefId) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 59c9817..b8ea38b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -279,6 +279,9 @@ <string name="settings_notifications_auto_delete_one_week">After one week</string> <string name="settings_notifications_auto_delete_one_month">After one month</string> <string name="settings_notifications_auto_delete_three_months">After 3 months</string> + <string name="settings_notifications_insistent_max_priority_title">Keep alerting for highest priority</string> + <string name="settings_notifications_insistent_max_priority_summary_enabled">Max priority notifications continuously play the notification sound until dismissed. This overrides Do Not Disturb mode.</string> + <string name="settings_notifications_insistent_max_priority_summary_disabled">Max priority notifications alert only once. If enabled, the notification sound will repeat and override Do Not Disturb mode.</string> <string name="settings_general_header">General</string> <string name="settings_general_default_base_url_title">Default server</string> <string name="settings_general_default_base_url_message">Enter your server\'s root URL to use your own server as a default when subscribing to new topics and/or sharing to topics.</string> diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index 2ccbda8..0b33496 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -18,6 +18,7 @@ <string name="settings_notifications_channel_prefs_key" translatable="false">ChannelPrefs</string> <string name="settings_notifications_auto_download_key" translatable="false">AutoDownload</string> <string name="settings_notifications_auto_delete_key" translatable="false">AutoDelete</string> + <string name="settings_notifications_insistent_max_priority_key" translatable="false">InsistentMaxPriority</string> <string name="settings_general_default_base_url_key" translatable="false">DefaultBaseURL</string> <string name="settings_general_users_key" translatable="false">ManageUsers</string> <string name="settings_general_dark_mode_key" translatable="false">DarkMode</string> diff --git a/app/src/main/res/xml/main_preferences.xml b/app/src/main/res/xml/main_preferences.xml index 42603a2..d7178a6 100644 --- a/app/src/main/res/xml/main_preferences.xml +++ b/app/src/main/res/xml/main_preferences.xml @@ -25,6 +25,10 @@ app:entries="@array/settings_notifications_auto_delete_entries" app:entryValues="@array/settings_notifications_auto_delete_values" app:defaultValue="2592000"/> + <SwitchPreference + app:key="@string/settings_notifications_insistent_max_priority_key" + app:title="@string/settings_notifications_insistent_max_priority_title" + app:defaultValue="false"/> <Preference app:key="@string/settings_notifications_channel_prefs_key" app:title="@string/settings_notifications_channel_prefs_title" |