mirror of
https://github.com/home-assistant/android
synced 2024-10-15 12:32:54 +00:00
Update instead of cancel notification on replies (#3167)
* Update instead of cancel notification on reply - When a notification is replied to, update it instead of cancelling it to allow the other to continue manipulating it * Support multiple replies - Keep a history of replies in the bundle to match system behavior * Only keep replies that are actually shown
This commit is contained in:
parent
37e00233bd
commit
4ff2fcca4b
|
@ -43,6 +43,8 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.core.content.getSystemService
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.utils.toAndroidIconCompat
|
||||
import com.vdurmont.emoji.EmojiParser
|
||||
|
@ -286,19 +288,35 @@ class MessagingManager @Inject constructor(
|
|||
const val VIDEO_START_MICROSECONDS = 100000L
|
||||
const val VIDEO_INCREMENT_MICROSECONDS = 750000L
|
||||
const val VIDEO_GUESS_MILLISECONDS = 7000L
|
||||
|
||||
// Values for a notification that has been replied to
|
||||
const val SOURCE_REPLY = "REPLY_"
|
||||
const val SOURCE_REPLY_HISTORY = "reply_history_"
|
||||
}
|
||||
|
||||
private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
|
||||
private var textToSpeech: TextToSpeech? = null
|
||||
|
||||
fun handleMessage(jsonData: Map<String, String>, source: String) {
|
||||
fun handleMessage(notificationData: Map<String, String>, source: String) {
|
||||
|
||||
var now = System.currentTimeMillis()
|
||||
var jsonData = notificationData
|
||||
val notificationId: Long
|
||||
|
||||
if (source.startsWith(SOURCE_REPLY)) {
|
||||
notificationId = source.substringAfter(SOURCE_REPLY).toLong()
|
||||
notificationDao.get(notificationId.toInt())?.let {
|
||||
val dbData: Map<String, String> = jacksonObjectMapper().readValue(it.data)
|
||||
|
||||
now = it.received // Allow for updating the existing notification without a tag
|
||||
jsonData = jsonData + dbData // Add the notificationData, this contains the reply text
|
||||
} ?: return
|
||||
} else {
|
||||
val jsonObject = JSONObject(jsonData)
|
||||
val now = System.currentTimeMillis()
|
||||
val notificationRow =
|
||||
NotificationItem(0, now, jsonData[MESSAGE].toString(), jsonObject.toString(), source)
|
||||
notificationDao.add(notificationRow)
|
||||
notificationId = notificationDao.add(notificationRow)
|
||||
|
||||
val confirmation = jsonData[CONFIRMATION]?.toBoolean() ?: false
|
||||
if (confirmation) {
|
||||
|
@ -310,6 +328,7 @@ class MessagingManager @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
jsonData[MESSAGE] == REQUEST_LOCATION_UPDATE -> {
|
||||
|
@ -598,7 +617,7 @@ class MessagingManager @Inject constructor(
|
|||
}
|
||||
else -> mainScope.launch {
|
||||
Log.d(TAG, "Creating notification with following data: $jsonData")
|
||||
sendNotification(jsonData)
|
||||
sendNotification(jsonData, notificationId, now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1033,11 +1052,11 @@ class MessagingManager @Inject constructor(
|
|||
* Create and show a simple notification containing the received FCM message.
|
||||
*
|
||||
*/
|
||||
private suspend fun sendNotification(data: Map<String, String>) {
|
||||
private suspend fun sendNotification(data: Map<String, String>, id: Long? = null, received: Long? = null) {
|
||||
val notificationManagerCompat = NotificationManagerCompat.from(context)
|
||||
|
||||
val tag = data["tag"]
|
||||
val messageId = tag?.hashCode() ?: System.currentTimeMillis().toInt()
|
||||
val messageId = tag?.hashCode() ?: received?.toInt() ?: System.currentTimeMillis().toInt()
|
||||
|
||||
var group = data["group"]
|
||||
var groupId = 0
|
||||
|
@ -1087,7 +1106,9 @@ class MessagingManager @Inject constructor(
|
|||
|
||||
handleVisibility(notificationBuilder, data)
|
||||
|
||||
handleActions(notificationBuilder, tag, messageId, data)
|
||||
handleActions(notificationBuilder, tag, messageId, id, data)
|
||||
|
||||
handleReplyHistory(notificationBuilder, data)
|
||||
|
||||
handleDeleteIntent(notificationBuilder, data, messageId, group, groupId)
|
||||
|
||||
|
@ -1599,6 +1620,7 @@ class MessagingManager @Inject constructor(
|
|||
builder: NotificationCompat.Builder,
|
||||
tag: String?,
|
||||
messageId: Int,
|
||||
databaseId: Long?,
|
||||
data: Map<String, String>
|
||||
) {
|
||||
for (i in 1..3) {
|
||||
|
@ -1619,6 +1641,10 @@ class MessagingManager @Inject constructor(
|
|||
NotificationActionReceiver.EXTRA_NOTIFICATION_ACTION,
|
||||
notificationAction
|
||||
)
|
||||
putExtra(
|
||||
NotificationActionReceiver.EXTRA_NOTIFICATION_DB,
|
||||
databaseId
|
||||
)
|
||||
}
|
||||
|
||||
when (notificationAction.key) {
|
||||
|
@ -1638,7 +1664,7 @@ class MessagingManager @Inject constructor(
|
|||
}
|
||||
val replyPendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
0,
|
||||
messageId,
|
||||
eventIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
|
||||
)
|
||||
|
@ -1735,6 +1761,22 @@ class MessagingManager @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun handleReplyHistory(
|
||||
builder: NotificationCompat.Builder,
|
||||
data: Map<String, String>
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val replies = data.entries
|
||||
.filter { it.key.startsWith(SOURCE_REPLY_HISTORY) }
|
||||
.sortedBy { it.key.substringAfter(SOURCE_REPLY_HISTORY).toInt() }
|
||||
if (replies.any()) {
|
||||
val history = replies.map { it.value }.reversed().toTypedArray() // Reverse to have latest replies first
|
||||
builder.setRemoteInputHistory(history)
|
||||
builder.setOnlyAlertOnce(true) // Overwrites user settings to match system defaults
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleChannel(
|
||||
notificationManagerCompat: NotificationManagerCompat,
|
||||
data: Map<String, String>
|
||||
|
|
|
@ -27,6 +27,7 @@ class NotificationActionReceiver : BroadcastReceiver() {
|
|||
const val FIRE_EVENT = "FIRE_EVENT"
|
||||
const val EXTRA_NOTIFICATION_TAG = "EXTRA_NOTIFICATION_TAG"
|
||||
const val EXTRA_NOTIFICATION_ID = "EXTRA_NOTIFICATION_ID"
|
||||
const val EXTRA_NOTIFICATION_DB = "EXTRA_NOTIFICATION_DB"
|
||||
const val EXTRA_NOTIFICATION_ACTION = "EXTRA_ACTION_KEY"
|
||||
}
|
||||
|
||||
|
@ -35,6 +36,9 @@ class NotificationActionReceiver : BroadcastReceiver() {
|
|||
@Inject
|
||||
lateinit var integrationUseCase: IntegrationRepository
|
||||
|
||||
@Inject
|
||||
lateinit var messagingManager: MessagingManager
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
|
||||
val notificationAction =
|
||||
|
@ -47,7 +51,27 @@ class NotificationActionReceiver : BroadcastReceiver() {
|
|||
|
||||
val tag = intent.getStringExtra(EXTRA_NOTIFICATION_TAG)
|
||||
val messageId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
||||
val databaseId = intent.getLongExtra(EXTRA_NOTIFICATION_DB, 0)
|
||||
|
||||
val isReply = notificationAction.key == "REPLY"
|
||||
var replyText: String? = null
|
||||
|
||||
val onComplete: () -> Unit = {
|
||||
if (isReply && !replyText.isNullOrBlank()) {
|
||||
val replies = notificationAction.data.entries
|
||||
.filter { it.key.startsWith(MessagingManager.SOURCE_REPLY_HISTORY) }
|
||||
.sortedBy { it.key.substringAfter(MessagingManager.SOURCE_REPLY_HISTORY).toInt() }
|
||||
.map { it.value } + replyText
|
||||
messagingManager.handleMessage(
|
||||
replies
|
||||
.takeLast(3)
|
||||
.mapIndexed { index, text ->
|
||||
"${MessagingManager.SOURCE_REPLY_HISTORY}$index" to text!!
|
||||
}
|
||||
.toMap(),
|
||||
"${MessagingManager.SOURCE_REPLY}$databaseId"
|
||||
)
|
||||
} else {
|
||||
val notificationManagerCompat = NotificationManagerCompat.from(context)
|
||||
notificationManagerCompat.cancel(
|
||||
tag,
|
||||
|
@ -55,16 +79,16 @@ class NotificationActionReceiver : BroadcastReceiver() {
|
|||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
val onFailure: () -> Unit = {
|
||||
Handler(context.mainLooper).post {
|
||||
Toast.makeText(context, commonR.string.event_error, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
if (notificationAction.key == "REPLY") {
|
||||
notificationAction.data += Pair(
|
||||
"reply_text",
|
||||
RemoteInput.getResultsFromIntent(intent)?.getCharSequence(KEY_TEXT_REPLY).toString()
|
||||
)
|
||||
|
||||
if (isReply) {
|
||||
replyText = RemoteInput.getResultsFromIntent(intent)?.getCharSequence(KEY_TEXT_REPLY).toString()
|
||||
notificationAction.data += Pair("reply_text", replyText)
|
||||
}
|
||||
|
||||
when (intent.action) {
|
||||
|
@ -81,7 +105,9 @@ class NotificationActionReceiver : BroadcastReceiver() {
|
|||
try {
|
||||
integrationUseCase.fireEvent(
|
||||
"mobile_app_notification_action",
|
||||
action.data.plus(Pair("action", action.key))
|
||||
action.data
|
||||
.filter { !it.key.startsWith(MessagingManager.SOURCE_REPLY_HISTORY) }
|
||||
.plus(Pair("action", action.key))
|
||||
)
|
||||
onComplete()
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -9,7 +9,10 @@ import androidx.room.Query
|
|||
interface NotificationDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun add(notification: NotificationItem)
|
||||
fun add(notification: NotificationItem): Long
|
||||
|
||||
@Query("SELECT * FROM notification_history WHERE id = :id")
|
||||
fun get(id: Int): NotificationItem?
|
||||
|
||||
@Query("SELECT * FROM notification_history ORDER BY received DESC")
|
||||
fun getAll(): Array<NotificationItem>?
|
||||
|
|
Loading…
Reference in a new issue