feat(notification): Turn off action and tap-to-open on background notification (#1408)
This commit is contained in:
@@ -71,6 +71,10 @@
|
|||||||
android:name=".notification.NotificationReplyReceiver"
|
android:name=".notification.NotificationReplyReceiver"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".notification.StopKeepConnectedReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.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() {
|
fun reconcileState() {
|
||||||
val snapshot = snapshotProvider(false)
|
val snapshot = snapshotProvider(false)
|
||||||
val decision = BackgroundConnectionPolicy.evaluate(snapshot)
|
val decision = BackgroundConnectionPolicy.evaluate(snapshot)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.core.app.NotificationCompat
|
|||||||
import androidx.core.app.RemoteInput
|
import androidx.core.app.RemoteInput
|
||||||
import com.m57.hermescontrol.MainActivity
|
import com.m57.hermescontrol.MainActivity
|
||||||
import com.m57.hermescontrol.R
|
import com.m57.hermescontrol.R
|
||||||
|
import com.m57.hermescontrol.data.local.AuthManager
|
||||||
import com.m57.hermescontrol.data.remote.NetworkMonitor
|
import com.m57.hermescontrol.data.remote.NetworkMonitor
|
||||||
import com.m57.hermescontrol.data.session.ActiveSessionHolder
|
import com.m57.hermescontrol.data.session.ActiveSessionHolder
|
||||||
import com.m57.hermescontrol.data.ws.HermesWsClient
|
import com.m57.hermescontrol.data.ws.HermesWsClient
|
||||||
@@ -383,15 +384,48 @@ class ChatNotificationService : Service() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildForegroundNotification(text: String): Notification =
|
private fun buildForegroundNotification(text: String): Notification {
|
||||||
NotificationCompat
|
val openIntent =
|
||||||
.Builder(this, SERVICE_CHANNEL_ID)
|
Intent(this, MainActivity::class.java).apply {
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
setPackage(packageName)
|
||||||
.setContentTitle(getString(R.string.notif_title))
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
.setContentText(text)
|
}
|
||||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
val builder =
|
||||||
.setOngoing(true)
|
NotificationCompat
|
||||||
.build()
|
.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() {
|
private fun createNotificationChannels() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
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_title">Hermes</string>
|
||||||
<string name="notif_waiting_replies">بانتظار ردود Hermes</string>
|
<string name="notif_waiting_replies">بانتظار ردود Hermes</string>
|
||||||
<string name="notif_connected_in_background">متصل في الخلفية</string>
|
<string name="notif_connected_in_background">متصل في الخلفية</string>
|
||||||
|
<string name="notif_action_turn_off">إيقاف</string>
|
||||||
<string name="notif_connecting">جارٍ الاتصال…</string>
|
<string name="notif_connecting">جارٍ الاتصال…</string>
|
||||||
<string name="notif_waiting_network">في انتظار الشبكة</string>
|
<string name="notif_waiting_network">في انتظار الشبكة</string>
|
||||||
<string name="notif_reconnecting">جارٍ إعادة الاتصال</string>
|
<string name="notif_reconnecting">جارٍ إعادة الاتصال</string>
|
||||||
|
|||||||
@@ -126,6 +126,7 @@
|
|||||||
<string name="notif_title">헤르메스</string>
|
<string name="notif_title">헤르메스</string>
|
||||||
<string name="notif_waiting_replies">헤르메스 응답 대기 중</string>
|
<string name="notif_waiting_replies">헤르메스 응답 대기 중</string>
|
||||||
<string name="notif_connected_in_background">백그라운드에서 연결됨</string>
|
<string name="notif_connected_in_background">백그라운드에서 연결됨</string>
|
||||||
|
<string name="notif_action_turn_off">끄기</string>
|
||||||
<string name="notif_connecting">연결 중…</string>
|
<string name="notif_connecting">연결 중…</string>
|
||||||
<string name="notif_waiting_network">네트워크 대기 중</string>
|
<string name="notif_waiting_network">네트워크 대기 중</string>
|
||||||
<string name="notif_reconnecting">재연결 중</string>
|
<string name="notif_reconnecting">재연결 중</string>
|
||||||
|
|||||||
@@ -129,6 +129,7 @@
|
|||||||
<string name="notif_title">Hermes</string>
|
<string name="notif_title">Hermes</string>
|
||||||
<string name="notif_waiting_replies">等待 Hermes 回复</string>
|
<string name="notif_waiting_replies">等待 Hermes 回复</string>
|
||||||
<string name="notif_connected_in_background">在后台保持连接</string>
|
<string name="notif_connected_in_background">在后台保持连接</string>
|
||||||
|
<string name="notif_action_turn_off">关闭</string>
|
||||||
<string name="notif_connecting">正在连接…</string>
|
<string name="notif_connecting">正在连接…</string>
|
||||||
<string name="notif_waiting_network">等待网络连接</string>
|
<string name="notif_waiting_network">等待网络连接</string>
|
||||||
<string name="notif_reconnecting">正在重新连接</string>
|
<string name="notif_reconnecting">正在重新连接</string>
|
||||||
|
|||||||
@@ -152,6 +152,7 @@
|
|||||||
<string name="notif_title">Hermes</string>
|
<string name="notif_title">Hermes</string>
|
||||||
<string name="notif_waiting_replies">Waiting for Hermes replies</string>
|
<string name="notif_waiting_replies">Waiting for Hermes replies</string>
|
||||||
<string name="notif_connected_in_background">Connected in background</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_connecting">Connecting…</string>
|
||||||
<string name="notif_waiting_network">Waiting for network</string>
|
<string name="notif_waiting_network">Waiting for network</string>
|
||||||
<string name="notif_reconnecting">Reconnecting</string>
|
<string name="notif_reconnecting">Reconnecting</string>
|
||||||
|
|||||||
+51
@@ -407,4 +407,55 @@ class BackgroundConnectionControllerTest {
|
|||||||
unmockkObject(NetworkMonitor)
|
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