Extract from_notification extra to a constant in IntentNavigationExtras

Allow `IntentProvider` to receive extras and `PendingIntentFactory` to send them.
This commit is contained in:
Jorge Martín
2025-11-27 12:17:51 +01:00
committed by Jorge Martin Espinosa
parent 71bfffe58f
commit 884c547123
7 changed files with 54 additions and 15 deletions

View File

@@ -10,6 +10,7 @@ package io.element.android.x.intent
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.net.toUri
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
@@ -32,11 +33,12 @@ class DefaultIntentProvider(
roomId: RoomId?,
threadId: ThreadId?,
eventId: EventId?,
extras: Bundle?,
): Intent {
return Intent(context, MainActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = deepLinkCreator.create(sessionId, roomId, threadId, eventId).toUri()
putExtra("from_notification", true)
extras?.let(::putExtras)
}
}
}

View File

@@ -29,7 +29,6 @@ import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.JoinedRoom
import io.element.android.annotations.ContributesNode
import io.element.android.appnav.analytics.AnalyticsColdStartWatcher
import io.element.android.appnav.di.MatrixSessionCache
import io.element.android.appnav.intent.IntentResolver
import io.element.android.appnav.intent.ResolvedIntent
@@ -66,6 +65,7 @@ import io.element.android.libraries.ui.common.nodes.emptyNode
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher
import io.element.android.services.appnavstate.api.ROOM_OPENED_FROM_NOTIFICATION
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -318,7 +318,8 @@ class RootFlowNode(
val resolvedIntent = intentResolver.resolve(intent) ?: return
when (resolvedIntent) {
is ResolvedIntent.Navigation -> {
if (intent.getBooleanExtra("from_notification", false) && resolvedIntent.deeplinkData is DeeplinkData.Room) {
val openingRoomFromNotification = intent.getBooleanExtra(ROOM_OPENED_FROM_NOTIFICATION, false)
if (openingRoomFromNotification && resolvedIntent.deeplinkData is DeeplinkData.Room) {
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.NotificationTapOpensTimeline)
}
navigateTo(resolvedIntent.deeplinkData)

View File

@@ -9,6 +9,7 @@
package io.element.android.libraries.push.impl.intent
import android.content.Intent
import android.os.Bundle
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
@@ -23,5 +24,6 @@ interface IntentProvider {
roomId: RoomId?,
threadId: ThreadId?,
eventId: EventId?,
extras: Bundle? = null,
): Intent
}

View File

@@ -15,6 +15,7 @@ import androidx.annotation.ColorInt
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.MessagingStyle
import androidx.core.app.Person
import androidx.core.os.bundleOf
import coil3.ImageLoader
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
@@ -44,6 +45,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableMess
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
import io.element.android.libraries.push.impl.notifications.shortcut.createShortcutId
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.appnavstate.api.ROOM_OPENED_FROM_NOTIFICATION
import io.element.android.services.toolbox.api.strings.StringProvider
interface NotificationCreator {
@@ -138,7 +140,12 @@ class DefaultNotificationCreator(
val eventId = events.firstOrNull()?.eventId
val openIntent = when {
threadId != null -> pendingIntentFactory.createOpenThreadPendingIntent(roomInfo.sessionId, roomInfo.roomId, eventId, threadId)
else -> pendingIntentFactory.createOpenRoomPendingIntent(roomInfo.sessionId, roomInfo.roomId, eventId)
else -> pendingIntentFactory.createOpenRoomPendingIntent(
sessionId = roomInfo.sessionId,
roomId = roomInfo.roomId,
eventId = eventId,
extras = bundleOf(ROOM_OPENED_FROM_NOTIFICATION to true),
)
}
val containsMissedCall = events.any { it.type == EventType.RTC_NOTIFICATION }
val channelId = if (containsMissedCall) {
@@ -233,7 +240,11 @@ class DefaultNotificationCreator(
.addAction(rejectInvitationActionFactory.create(inviteNotifiableEvent))
.addAction(acceptInvitationActionFactory.create(inviteNotifiableEvent))
// Build the pending intent for when the notification is clicked
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(inviteNotifiableEvent.sessionId, inviteNotifiableEvent.roomId, null))
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(
sessionId = inviteNotifiableEvent.sessionId,
roomId = inviteNotifiableEvent.roomId,
eventId = null,
))
.apply {
if (inviteNotifiableEvent.noisy) {
// Compat
@@ -265,7 +276,12 @@ class DefaultNotificationCreator(
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
.configureWith(notificationAccountParams)
.setAutoCancel(true)
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(simpleNotifiableEvent.sessionId, simpleNotifiableEvent.roomId, null))
.setContentIntent(pendingIntentFactory.createOpenRoomPendingIntent(
sessionId = simpleNotifiableEvent.sessionId,
roomId = simpleNotifiableEvent.roomId,
eventId = null,
extras = bundleOf(ROOM_OPENED_FROM_NOTIFICATION to true),
))
.apply {
if (simpleNotifiableEvent.noisy) {
// Compat

View File

@@ -11,6 +11,7 @@ package io.element.android.libraries.push.impl.notifications.factories
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Bundle
import dev.zacsweers.metro.Inject
import io.element.android.libraries.androidutils.uri.createIgnoredUri
import io.element.android.libraries.di.annotations.ApplicationContext
@@ -31,20 +32,20 @@ class PendingIntentFactory(
private val clock: SystemClock,
private val actionIds: NotificationActionIds,
) {
fun createOpenSessionPendingIntent(sessionId: SessionId): PendingIntent? {
return createRoomPendingIntent(sessionId = sessionId, roomId = null, eventId = null, threadId = null)
fun createOpenSessionPendingIntent(sessionId: SessionId, extras: Bundle? = null): PendingIntent? {
return createRoomPendingIntent(sessionId = sessionId, roomId = null, eventId = null, threadId = null, extras = extras)
}
fun createOpenRoomPendingIntent(sessionId: SessionId, roomId: RoomId, eventId: EventId?): PendingIntent? {
return createRoomPendingIntent(sessionId = sessionId, roomId = roomId, eventId = eventId, threadId = null)
fun createOpenRoomPendingIntent(sessionId: SessionId, roomId: RoomId, eventId: EventId?, extras: Bundle? = null): PendingIntent? {
return createRoomPendingIntent(sessionId = sessionId, roomId = roomId, eventId = eventId, threadId = null, extras = extras)
}
fun createOpenThreadPendingIntent(sessionId: SessionId, roomId: RoomId, eventId: EventId?, threadId: ThreadId): PendingIntent? {
return createRoomPendingIntent(sessionId = sessionId, roomId = roomId, eventId = eventId, threadId = threadId)
fun createOpenThreadPendingIntent(sessionId: SessionId, roomId: RoomId, eventId: EventId?, threadId: ThreadId, extras: Bundle? = null): PendingIntent? {
return createRoomPendingIntent(sessionId = sessionId, roomId = roomId, eventId = eventId, threadId = threadId, extras = extras)
}
private fun createRoomPendingIntent(sessionId: SessionId, roomId: RoomId?, eventId: EventId?, threadId: ThreadId?): PendingIntent? {
val intent = intentProvider.getViewRoomIntent(sessionId = sessionId, roomId = roomId, eventId = eventId, threadId = threadId)
private fun createRoomPendingIntent(sessionId: SessionId, roomId: RoomId?, eventId: EventId?, threadId: ThreadId?, extras: Bundle? = null): PendingIntent? {
val intent = intentProvider.getViewRoomIntent(sessionId = sessionId, roomId = roomId, eventId = eventId, threadId = threadId, extras = extras)
return PendingIntent.getActivity(
context,
clock.epochMillis().toInt(),

View File

@@ -9,6 +9,7 @@
package io.element.android.libraries.push.impl.notifications.factories
import android.content.Intent
import android.os.Bundle
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
@@ -16,5 +17,11 @@ import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.push.impl.intent.IntentProvider
class FakeIntentProvider : IntentProvider {
override fun getViewRoomIntent(sessionId: SessionId, roomId: RoomId?, threadId: ThreadId?, eventId: EventId?) = Intent(Intent.ACTION_VIEW)
override fun getViewRoomIntent(
sessionId: SessionId,
roomId: RoomId?,
threadId: ThreadId?,
eventId: EventId?,
extras: Bundle?,
) = Intent(Intent.ACTION_VIEW)
}

View File

@@ -0,0 +1,10 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.services.appnavstate.api
const val ROOM_OPENED_FROM_NOTIFICATION = "opened_from_notification"