Respect Do Not Disturb for custom alerts
This commit is contained in:
+17
@@ -1,5 +1,6 @@
|
|||||||
package se.ajpanton.notificationsmaster.alerts
|
package se.ajpanton.notificationsmaster.alerts
|
||||||
|
|
||||||
|
import android.app.NotificationManager
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -20,4 +21,20 @@ class AndroidAlertEffectPlayerTest {
|
|||||||
assertTrue(completion.await(2, TimeUnit.SECONDS))
|
assertTrue(completion.await(2, TimeUnit.SECONDS))
|
||||||
player.stop()
|
player.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dndSkipsPlaybackAndCompletesImmediately() {
|
||||||
|
val completion = CountDownLatch(1)
|
||||||
|
val player = AndroidAlertEffectPlayer(
|
||||||
|
InstrumentationRegistry.getInstrumentation().targetContext,
|
||||||
|
) { NotificationManager.INTERRUPTION_FILTER_NONE }
|
||||||
|
|
||||||
|
player.play(
|
||||||
|
QueuedAlert("test", AlertSnapshot(null, listOf(0, 1_000), false, false)),
|
||||||
|
completion::countDown,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(completion.await(100, TimeUnit.MILLISECONDS))
|
||||||
|
player.stop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,15 @@ object AlertRuleDialog {
|
|||||||
val pattern = EditText(context).apply { hint = "Text to match (optional)"; setText(existing?.matcher?.value) }
|
val pattern = EditText(context).apply { hint = "Text to match (optional)"; setText(existing?.matcher?.value) }
|
||||||
val regex = SwitchMaterial(context).apply { text = "Use regular expression"; isChecked = existing?.matcher?.mode == AlertTextMode.REGEX }
|
val regex = SwitchMaterial(context).apply { text = "Use regular expression"; isChecked = existing?.matcher?.mode == AlertTextMode.REGEX }
|
||||||
val protected = SwitchMaterial(context).apply { text = "Play to completion"; isChecked = existing?.playToCompletion == true }
|
val protected = SwitchMaterial(context).apply { text = "Play to completion"; isChecked = existing?.playToCompletion == true }
|
||||||
val dnd = SwitchMaterial(context).apply { text = "Allow during DND when supported"; isChecked = existing?.allowDuringDnd == true }
|
val dnd = SwitchMaterial(context).apply {
|
||||||
|
text = "Allow during DND (requires system support)"
|
||||||
|
isChecked = existing?.allowDuringDnd == true
|
||||||
|
}
|
||||||
listOf(profile, post, update, pattern, regex, protected, dnd).forEach(form::addView)
|
listOf(profile, post, update, pattern, regex, protected, dnd).forEach(form::addView)
|
||||||
|
form.addView(android.widget.TextView(context).apply {
|
||||||
|
text = "Android blocks direct custom alerts during Do Not Disturb. This setting is retained for optional system support, but does not bypass Do Not Disturb in the normal app."
|
||||||
|
textSize = 14f
|
||||||
|
})
|
||||||
val dialog = MaterialAlertDialogBuilder(context).setTitle(if (existing == null) "Add rule" else "Edit rule")
|
val dialog = MaterialAlertDialogBuilder(context).setTitle(if (existing == null) "Add rule" else "Edit rule")
|
||||||
.setView(form).setNegativeButton("Cancel", null).setPositiveButton("Save", null)
|
.setView(form).setNegativeButton("Cancel", null).setPositiveButton("Save", null)
|
||||||
.apply { if (onDelete != null) setNeutralButton("Delete", null) }.show()
|
.apply { if (onDelete != null) setNeutralButton("Delete", null) }.show()
|
||||||
|
|||||||
+13
-1
@@ -1,6 +1,7 @@
|
|||||||
package se.ajpanton.notificationsmaster.alerts
|
package se.ajpanton.notificationsmaster.alerts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.app.NotificationManager
|
||||||
import android.media.AudioAttributes
|
import android.media.AudioAttributes
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -12,7 +13,12 @@ import android.os.VibratorManager
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
||||||
/** Plays resolved notification-class effects and releases all platform resources on stop. */
|
/** Plays resolved notification-class effects and releases all platform resources on stop. */
|
||||||
class AndroidAlertEffectPlayer(context: Context) : AlertEffectPlayer {
|
class AndroidAlertEffectPlayer(
|
||||||
|
context: Context,
|
||||||
|
private val interruptionFilter: () -> Int = {
|
||||||
|
context.getSystemService(NotificationManager::class.java).currentInterruptionFilter
|
||||||
|
},
|
||||||
|
) : AlertEffectPlayer {
|
||||||
private val context = context.applicationContext
|
private val context = context.applicationContext
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
private val vibrator = context.getSystemService(VibratorManager::class.java).defaultVibrator
|
private val vibrator = context.getSystemService(VibratorManager::class.java).defaultVibrator
|
||||||
@@ -27,6 +33,12 @@ class AndroidAlertEffectPlayer(context: Context) : AlertEffectPlayer {
|
|||||||
val token = generation
|
val token = generation
|
||||||
completion = onFinished
|
completion = onFinished
|
||||||
val snapshot = alert.snapshot
|
val snapshot = alert.snapshot
|
||||||
|
if (!DndPolicy.allowsCustomAlert(interruptionFilter())) {
|
||||||
|
Log.i(TAG, "Skipping custom notification alert during Do Not Disturb")
|
||||||
|
completion = null
|
||||||
|
onFinished()
|
||||||
|
return
|
||||||
|
}
|
||||||
val vibrationDuration = snapshot.vibrationPattern.sum()
|
val vibrationDuration = snapshot.vibrationPattern.sum()
|
||||||
remainingEffects = (if (snapshot.soundUri == null) 0 else 1) + (if (vibrationDuration > 0) 1 else 0)
|
remainingEffects = (if (snapshot.soundUri == null) 0 else 1) + (if (vibrationDuration > 0) 1 else 0)
|
||||||
snapshot.soundUri?.let { startSound(Uri.parse(it), token) }
|
snapshot.soundUri?.let { startSound(Uri.parse(it), token) }
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package se.ajpanton.notificationsmaster.alerts
|
||||||
|
|
||||||
|
import android.app.NotificationManager
|
||||||
|
|
||||||
|
/** Direct custom effects must not bypass the user's Do Not Disturb choice. */
|
||||||
|
internal object DndPolicy {
|
||||||
|
fun allowsCustomAlert(interruptionFilter: Int) =
|
||||||
|
interruptionFilter == NotificationManager.INTERRUPTION_FILTER_ALL
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package se.ajpanton.notificationsmaster.alerts
|
||||||
|
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class DndPolicyTest {
|
||||||
|
@Test
|
||||||
|
fun allowsCustomAlertsOnlyWhenInterruptionsAreNormal() {
|
||||||
|
assertTrue(DndPolicy.allowsCustomAlert(NotificationManager.INTERRUPTION_FILTER_ALL))
|
||||||
|
listOf(
|
||||||
|
NotificationManager.INTERRUPTION_FILTER_UNKNOWN,
|
||||||
|
NotificationManager.INTERRUPTION_FILTER_PRIORITY,
|
||||||
|
NotificationManager.INTERRUPTION_FILTER_NONE,
|
||||||
|
NotificationManager.INTERRUPTION_FILTER_ALARMS,
|
||||||
|
).forEach { assertFalse(DndPolicy.allowsCustomAlert(it)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user