feat(notification): Turn off action and tap-to-open on background notification (#1408)
This commit is contained in:
9 files changed
+139
-9
No files matched your search
@@ -71,6 +71,10 @@
|
||||
android:name=".notification.NotificationReplyReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".notification.StopKeepConnectedReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
|
||||
+11
@@ -98,6 +98,17 @@ class BackgroundConnectionController(
|
||||
}
|
||||
}
|
||||
|
||||
/** The persistent opt-in was turned off while backgrounded (e.g. from the notification action). */
|
||||
fun onKeepConnectedDisabled() {
|
||||
val snapshot = snapshotProvider(false)
|
||||
if (!snapshot.appInForeground) {
|
||||
releaseLease()
|
||||
}
|
||||
// Force a re-post if the service stays up (pending reply) so the action button disappears.
|
||||
currentNotificationState = BackgroundNotificationState.None
|
||||
reconcileState()
|
||||
}
|
||||
|
||||
fun reconcileState() {
|
||||
val snapshot = snapshotProvider(false)
|
||||
val decision = BackgroundConnectionPolicy.evaluate(snapshot)
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.RemoteInput
|
||||
import com.m57.hermescontrol.MainActivity
|
||||
import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import com.m57.hermescontrol.data.remote.NetworkMonitor
|
||||
import com.m57.hermescontrol.data.session.ActiveSessionHolder
|
||||
import com.m57.hermescontrol.data.ws.HermesWsClient
|
||||
@@ -383,15 +384,48 @@ class ChatNotificationService : Service() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildForegroundNotification(text: String): Notification =
|
||||
NotificationCompat
|
||||
.Builder(this, SERVICE_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(getString(R.string.notif_title))
|
||||
.setContentText(text)
|
||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||
.setOngoing(true)
|
||||
.build()
|
||||
private fun buildForegroundNotification(text: String): Notification {
|
||||
val openIntent =
|
||||
Intent(this, MainActivity::class.java).apply {
|
||||
setPackage(packageName)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
}
|
||||
val builder =
|
||||
NotificationCompat
|
||||
.Builder(this, SERVICE_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(getString(R.string.notif_title))
|
||||
.setContentText(text)
|
||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||
.setOngoing(true)
|
||||
.setContentIntent(
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
openIntent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
),
|
||||
)
|
||||
|
||||
if (AuthManager.isKeepConnectedInBackground()) {
|
||||
val stopIntent =
|
||||
Intent(this, StopKeepConnectedReceiver::class.java).apply {
|
||||
action = StopKeepConnectedReceiver.ACTION_STOP_KEEP_CONNECTED
|
||||
setPackage(packageName)
|
||||
}
|
||||
builder.addAction(
|
||||
R.drawable.ic_notification,
|
||||
getString(R.string.notif_action_turn_off),
|
||||
PendingIntent.getBroadcast(
|
||||
this,
|
||||
0,
|
||||
stopIntent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
),
|
||||
)
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private fun createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.m57.hermescontrol.notification
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
|
||||
/**
|
||||
* Handles the "Turn off" action on the ongoing background notification:
|
||||
* disables "Stay connected in background" and lets the controller retire the
|
||||
* lease/service.
|
||||
*/
|
||||
class StopKeepConnectedReceiver : BroadcastReceiver() {
|
||||
companion object {
|
||||
const val ACTION_STOP_KEEP_CONNECTED = "com.m57.hermescontrol.ACTION_STOP_KEEP_CONNECTED"
|
||||
}
|
||||
|
||||
override fun onReceive(
|
||||
context: Context,
|
||||
intent: Intent,
|
||||
) {
|
||||
if (intent.action != ACTION_STOP_KEEP_CONNECTED) return
|
||||
AuthManager.setKeepConnectedInBackground(false)
|
||||
BackgroundConnectionController.default.onKeepConnectedDisabled()
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,7 @@
|
||||
<string name="notif_title">Hermes</string>
|
||||
<string name="notif_waiting_replies">بانتظار ردود Hermes</string>
|
||||
<string name="notif_connected_in_background">متصل في الخلفية</string>
|
||||
<string name="notif_action_turn_off">إيقاف</string>
|
||||
<string name="notif_connecting">جارٍ الاتصال…</string>
|
||||
<string name="notif_waiting_network">في انتظار الشبكة</string>
|
||||
<string name="notif_reconnecting">جارٍ إعادة الاتصال</string>
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
<string name="notif_title">헤르메스</string>
|
||||
<string name="notif_waiting_replies">헤르메스 응답 대기 중</string>
|
||||
<string name="notif_connected_in_background">백그라운드에서 연결됨</string>
|
||||
<string name="notif_action_turn_off">끄기</string>
|
||||
<string name="notif_connecting">연결 중…</string>
|
||||
<string name="notif_waiting_network">네트워크 대기 중</string>
|
||||
<string name="notif_reconnecting">재연결 중</string>
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
<string name="notif_title">Hermes</string>
|
||||
<string name="notif_waiting_replies">等待 Hermes 回复</string>
|
||||
<string name="notif_connected_in_background">在后台保持连接</string>
|
||||
<string name="notif_action_turn_off">关闭</string>
|
||||
<string name="notif_connecting">正在连接…</string>
|
||||
<string name="notif_waiting_network">等待网络连接</string>
|
||||
<string name="notif_reconnecting">正在重新连接</string>
|
||||
|
||||
@@ -152,6 +152,7 @@
|
||||
<string name="notif_title">Hermes</string>
|
||||
<string name="notif_waiting_replies">Waiting for Hermes replies</string>
|
||||
<string name="notif_connected_in_background">Connected in background</string>
|
||||
<string name="notif_action_turn_off">Turn off</string>
|
||||
<string name="notif_connecting">Connecting…</string>
|
||||
<string name="notif_waiting_network">Waiting for network</string>
|
||||
<string name="notif_reconnecting">Reconnecting</string>
|
||||
|
||||
+51
@@ -407,4 +407,55 @@ class BackgroundConnectionControllerTest {
|
||||
unmockkObject(NetworkMonitor)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnKeepConnectedDisabled_whenBackgrounded_releasesLeaseAndStopsService() {
|
||||
var leaseReleased = false
|
||||
var serviceStopped = false
|
||||
val snapshot =
|
||||
BackgroundConnectionSnapshot(
|
||||
appInForeground = false,
|
||||
keepConnectedOptIn = false,
|
||||
pendingReply = false,
|
||||
isEligibleForConnection = true,
|
||||
status = ConnectionStatus.CONNECTED,
|
||||
)
|
||||
val controller =
|
||||
BackgroundConnectionController(
|
||||
snapshotProvider = { snapshot },
|
||||
releaseLease = { leaseReleased = true },
|
||||
requestServiceStop = { serviceStopped = true },
|
||||
)
|
||||
|
||||
controller.onKeepConnectedDisabled()
|
||||
|
||||
assertTrue(leaseReleased)
|
||||
assertTrue(serviceStopped)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnKeepConnectedDisabled_whenReplyPending_keepsServiceAndRefreshesNotification() {
|
||||
var serviceStopped = false
|
||||
var refreshed: BackgroundNotificationState? = null
|
||||
val snapshot =
|
||||
BackgroundConnectionSnapshot(
|
||||
appInForeground = false,
|
||||
keepConnectedOptIn = false,
|
||||
pendingReply = true,
|
||||
isEligibleForConnection = true,
|
||||
status = ConnectionStatus.CONNECTED,
|
||||
)
|
||||
val controller =
|
||||
BackgroundConnectionController(
|
||||
snapshotProvider = { snapshot },
|
||||
releaseLease = {},
|
||||
requestServiceStop = { serviceStopped = true },
|
||||
onNotificationStateChanged = { refreshed = it },
|
||||
)
|
||||
|
||||
controller.onKeepConnectedDisabled()
|
||||
|
||||
assertFalse(serviceStopped)
|
||||
assertEquals(BackgroundNotificationState.WaitingForReplies, refreshed)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user