Merge pull request #3053 from element-hq/feature/bma/callSettings
Alert for incoming call even if notifications are disabled - WAITING FOR FINAL PRODUCT DECISION
This commit is contained in:
@@ -28,6 +28,7 @@ anvil {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.annotationjvm)
|
||||
implementation(libs.dagger)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package io.element.android.appconfig
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.ColorInt
|
||||
|
||||
object NotificationConfig {
|
||||
// TODO EAx Implement and set to true at some point
|
||||
const val SUPPORT_MARK_AS_READ_ACTION = false
|
||||
@@ -25,4 +28,7 @@ object NotificationConfig {
|
||||
|
||||
// TODO EAx Implement and set to true at some point
|
||||
const val SUPPORT_QUICK_REPLY_ACTION = false
|
||||
|
||||
@ColorInt
|
||||
val NOTIFICATION_ACCENT_COLOR: Int = Color.parseColor("#FF0DBD8B")
|
||||
}
|
||||
|
||||
1
changelog.d/3053.misc
Normal file
1
changelog.d/3053.misc
Normal file
@@ -0,0 +1 @@
|
||||
Alert for incoming call even if notifications are disabled
|
||||
@@ -42,6 +42,7 @@ open class NotificationSettingsStateProvider : PreviewParameterProvider<Notifica
|
||||
aInvalidNotificationSettingsState(),
|
||||
aInvalidNotificationSettingsState(fixFailed = true),
|
||||
aValidNotificationSettingsState(fullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(permissionGranted = false)),
|
||||
aValidNotificationSettingsState(appNotificationEnabled = false),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package io.element.android.libraries.push.impl.notifications.channels
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioManager
|
||||
@@ -26,8 +24,8 @@ import android.os.Build
|
||||
import androidx.annotation.ChecksSdkIntAtLeast
|
||||
import androidx.core.app.NotificationChannelCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.appconfig.NotificationConfig
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
@@ -38,14 +36,9 @@ import javax.inject.Inject
|
||||
/* ==========================================================================================
|
||||
* IDs for channels
|
||||
* ========================================================================================== */
|
||||
private const val LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID = "LISTEN_FOR_EVENTS_NOTIFICATION_CHANNEL_ID"
|
||||
internal const val SILENT_NOTIFICATION_CHANNEL_ID = "DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID_V2"
|
||||
internal const val NOISY_NOTIFICATION_CHANNEL_ID = "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID"
|
||||
|
||||
// Legacy channel
|
||||
private const val CALL_NOTIFICATION_CHANNEL_ID_V2 = "CALL_NOTIFICATION_CHANNEL_ID_V2"
|
||||
|
||||
internal const val CALL_NOTIFICATION_CHANNEL_ID_V3 = "CALL_NOTIFICATION_CHANNEL_ID_V3"
|
||||
internal const val CALL_NOTIFICATION_CHANNEL_ID = "CALL_NOTIFICATION_CHANNEL_ID_V3"
|
||||
internal const val RINGING_CALL_NOTIFICATION_CHANNEL_ID = "RINGING_CALL_NOTIFICATION_CHANNEL_ID"
|
||||
|
||||
/**
|
||||
@@ -96,7 +89,7 @@ class DefaultNotificationChannels @Inject constructor(
|
||||
return
|
||||
}
|
||||
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
val accentColor = NotificationConfig.NOTIFICATION_ACCENT_COLOR
|
||||
|
||||
// Migration - the noisy channel was deleted and recreated when sound preference was changed (id was DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID_BASE
|
||||
// + currentTimeMillis).
|
||||
@@ -110,76 +103,62 @@ class DefaultNotificationChannels @Inject constructor(
|
||||
}
|
||||
}
|
||||
// Migration - Remove deprecated channels
|
||||
for (channelId in listOf("DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID", "CALL_NOTIFICATION_CHANNEL_ID")) {
|
||||
for (channelId in listOf(
|
||||
"DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID",
|
||||
"CALL_NOTIFICATION_CHANNEL_ID",
|
||||
"CALL_NOTIFICATION_CHANNEL_ID_V2",
|
||||
"LISTEN_FOR_EVENTS_NOTIFICATION_CHANNEL_ID",
|
||||
)) {
|
||||
notificationManager.getNotificationChannel(channelId)?.let {
|
||||
notificationManager.deleteNotificationChannel(channelId)
|
||||
}
|
||||
}
|
||||
|
||||
// Migration - Create new call channel
|
||||
notificationManager.deleteNotificationChannel(CALL_NOTIFICATION_CHANNEL_ID_V2)
|
||||
|
||||
/**
|
||||
* Default notification importance: shows everywhere, makes noise, but does not visually
|
||||
* intrude.
|
||||
*/
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
NotificationChannelCompat.Builder(
|
||||
NOISY_NOTIFICATION_CHANNEL_ID,
|
||||
stringProvider.getString(R.string.notification_channel_noisy).ifEmpty { "Noisy notifications" },
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
NotificationManagerCompat.IMPORTANCE_DEFAULT
|
||||
)
|
||||
.apply {
|
||||
description = stringProvider.getString(R.string.notification_channel_noisy)
|
||||
enableVibration(true)
|
||||
enableLights(true)
|
||||
lightColor = accentColor
|
||||
}
|
||||
.setName(stringProvider.getString(R.string.notification_channel_noisy).ifEmpty { "Noisy notifications" })
|
||||
.setDescription(stringProvider.getString(R.string.notification_channel_noisy))
|
||||
.setVibrationEnabled(true)
|
||||
.setLightsEnabled(true)
|
||||
.setLightColor(accentColor)
|
||||
.build()
|
||||
)
|
||||
|
||||
/**
|
||||
* Low notification importance: shows everywhere, but is not intrusive.
|
||||
*/
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
NotificationChannelCompat.Builder(
|
||||
SILENT_NOTIFICATION_CHANNEL_ID,
|
||||
stringProvider.getString(R.string.notification_channel_silent).ifEmpty { "Silent notifications" },
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
NotificationManagerCompat.IMPORTANCE_LOW
|
||||
)
|
||||
.apply {
|
||||
description = stringProvider.getString(R.string.notification_channel_silent)
|
||||
setSound(null, null)
|
||||
enableLights(true)
|
||||
lightColor = accentColor
|
||||
}
|
||||
)
|
||||
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
LISTENING_FOR_EVENTS_NOTIFICATION_CHANNEL_ID,
|
||||
stringProvider.getString(R.string.notification_channel_listening_for_events).ifEmpty { "Listening for events" },
|
||||
NotificationManager.IMPORTANCE_MIN
|
||||
)
|
||||
.apply {
|
||||
description = stringProvider.getString(R.string.notification_channel_listening_for_events)
|
||||
setSound(null, null)
|
||||
setShowBadge(false)
|
||||
}
|
||||
.setName(stringProvider.getString(R.string.notification_channel_silent).ifEmpty { "Silent notifications" })
|
||||
.setDescription(stringProvider.getString(R.string.notification_channel_silent))
|
||||
.setSound(null, null)
|
||||
.setLightsEnabled(true)
|
||||
.setLightColor(accentColor)
|
||||
.build()
|
||||
)
|
||||
|
||||
// Register a channel for incoming and in progress call notifications with no ringing
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
CALL_NOTIFICATION_CHANNEL_ID_V3,
|
||||
stringProvider.getString(R.string.notification_channel_call).ifEmpty { "Call" },
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
NotificationChannelCompat.Builder(
|
||||
CALL_NOTIFICATION_CHANNEL_ID,
|
||||
NotificationManagerCompat.IMPORTANCE_HIGH
|
||||
)
|
||||
.apply {
|
||||
description = stringProvider.getString(R.string.notification_channel_call)
|
||||
enableVibration(true)
|
||||
enableLights(true)
|
||||
lightColor = accentColor
|
||||
}
|
||||
.setName(stringProvider.getString(R.string.notification_channel_call).ifEmpty { "Call" })
|
||||
.setDescription(stringProvider.getString(R.string.notification_channel_call))
|
||||
.setVibrationEnabled(true)
|
||||
.setLightsEnabled(true)
|
||||
.setLightColor(accentColor)
|
||||
.build()
|
||||
)
|
||||
|
||||
// Register a channel for incoming call notifications which will ring the device when received
|
||||
@@ -207,7 +186,7 @@ class DefaultNotificationChannels @Inject constructor(
|
||||
}
|
||||
|
||||
override fun getChannelForIncomingCall(ring: Boolean): String {
|
||||
return if (ring) RINGING_CALL_NOTIFICATION_CHANNEL_ID else CALL_NOTIFICATION_CHANNEL_ID_V3
|
||||
return if (ring) RINGING_CALL_NOTIFICATION_CHANNEL_ID else CALL_NOTIFICATION_CHANNEL_ID
|
||||
}
|
||||
|
||||
override fun getChannelIdForMessage(noisy: Boolean): String {
|
||||
|
||||
@@ -24,7 +24,6 @@ import androidx.annotation.DrawableRes
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.MessagingStyle
|
||||
import androidx.core.app.Person
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import coil.ImageLoader
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
@@ -107,6 +106,8 @@ class DefaultNotificationCreator @Inject constructor(
|
||||
private val acceptInvitationActionFactory: AcceptInvitationActionFactory,
|
||||
private val rejectInvitationActionFactory: RejectInvitationActionFactory
|
||||
) : NotificationCreator {
|
||||
private val accentColor = NotificationConfig.NOTIFICATION_ACCENT_COLOR
|
||||
|
||||
/**
|
||||
* Create a notification for a Room.
|
||||
*/
|
||||
@@ -121,7 +122,6 @@ class DefaultNotificationCreator @Inject constructor(
|
||||
imageLoader: ImageLoader,
|
||||
events: List<NotifiableMessageEvent>,
|
||||
): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
// Build the pending intent for when the notification is clicked
|
||||
val openIntent = when {
|
||||
threadId != null -> pendingIntentFactory.createOpenThreadPendingIntent(roomInfo, threadId)
|
||||
@@ -228,7 +228,6 @@ class DefaultNotificationCreator @Inject constructor(
|
||||
override fun createRoomInvitationNotification(
|
||||
inviteNotifiableEvent: InviteNotifiableEvent
|
||||
): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
val smallIcon = CommonDrawables.ic_notification_small
|
||||
val channelId = notificationChannels.getChannelIdForMessage(inviteNotifiableEvent.noisy)
|
||||
return NotificationCompat.Builder(context, channelId)
|
||||
@@ -273,7 +272,6 @@ class DefaultNotificationCreator @Inject constructor(
|
||||
override fun createSimpleEventNotification(
|
||||
simpleNotifiableEvent: SimpleNotifiableEvent,
|
||||
): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
val smallIcon = CommonDrawables.ic_notification_small
|
||||
|
||||
val channelId = notificationChannels.getChannelIdForMessage(simpleNotifiableEvent.noisy)
|
||||
@@ -307,7 +305,6 @@ class DefaultNotificationCreator @Inject constructor(
|
||||
override fun createFallbackNotification(
|
||||
fallbackNotifiableEvent: FallbackNotifiableEvent,
|
||||
): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
val smallIcon = CommonDrawables.ic_notification_small
|
||||
|
||||
val channelId = notificationChannels.getChannelIdForMessage(false)
|
||||
@@ -344,7 +341,6 @@ class DefaultNotificationCreator @Inject constructor(
|
||||
noisy: Boolean,
|
||||
lastMessageTimestamp: Long
|
||||
): Notification {
|
||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||
val smallIcon = CommonDrawables.ic_notification_small
|
||||
val channelId = notificationChannels.getChannelIdForMessage(noisy)
|
||||
return NotificationCompat.Builder(context, channelId)
|
||||
@@ -384,7 +380,7 @@ class DefaultNotificationCreator @Inject constructor(
|
||||
.setContentText(stringProvider.getString(R.string.notification_test_push_notification_content))
|
||||
.setSmallIcon(CommonDrawables.ic_notification_small)
|
||||
.setLargeIcon(getBitmap(R.drawable.element_logo_green))
|
||||
.setColor(ContextCompat.getColor(context, R.color.notification_accent_color))
|
||||
.setColor(accentColor)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||
.setAutoCancel(true)
|
||||
|
||||
@@ -96,17 +96,19 @@ class DefaultPushHandler @Inject constructor(
|
||||
Timber.w("Unable to get a session")
|
||||
return
|
||||
}
|
||||
val userPushStore = userPushStoreFactory.getOrCreate(userId)
|
||||
val areNotificationsEnabled = userPushStore.getNotificationEnabledForDevice().first()
|
||||
if (areNotificationsEnabled) {
|
||||
val notifiableEvent = notifiableEventResolver.resolveEvent(userId, pushData.roomId, pushData.eventId)
|
||||
when (notifiableEvent) {
|
||||
null -> Timber.tag(loggerTag.value).w("Unable to get a notification data")
|
||||
is NotifiableRingingCallEvent -> handleRingingCallEvent(notifiableEvent)
|
||||
else -> onNotifiableEventReceived.onNotifiableEventReceived(notifiableEvent)
|
||||
val notifiableEvent = notifiableEventResolver.resolveEvent(userId, pushData.roomId, pushData.eventId)
|
||||
when (notifiableEvent) {
|
||||
null -> Timber.tag(loggerTag.value).w("Unable to get a notification data")
|
||||
is NotifiableRingingCallEvent -> handleRingingCallEvent(notifiableEvent)
|
||||
else -> {
|
||||
val userPushStore = userPushStoreFactory.getOrCreate(userId)
|
||||
val areNotificationsEnabled = userPushStore.getNotificationEnabledForDevice().first()
|
||||
if (areNotificationsEnabled) {
|
||||
onNotifiableEventReceived.onNotifiableEventReceived(notifiableEvent)
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).i("Notification are disabled for this device, ignore push.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).i("Notification are disabled for this device, ignore push.")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(loggerTag.value).e(e, "## handleInternal() failed")
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2023 New Vector Ltd
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<color name="notification_accent_color">#368BD6</color>
|
||||
|
||||
</resources>
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package io.element.android.libraries.push.impl.notifications.channels
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationChannelCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
@@ -43,7 +42,6 @@ class NotificationChannelsTest {
|
||||
createNotificationChannels(notificationManager = notificationManager)
|
||||
|
||||
verify { notificationManager.createNotificationChannel(any<NotificationChannelCompat>()) }
|
||||
verify { notificationManager.createNotificationChannel(any<NotificationChannel>()) }
|
||||
verify { notificationManager.deleteNotificationChannel(any<String>()) }
|
||||
}
|
||||
|
||||
@@ -55,7 +53,7 @@ class NotificationChannelsTest {
|
||||
assertThat(ringingChannel).isEqualTo(RINGING_CALL_NOTIFICATION_CHANNEL_ID)
|
||||
|
||||
val normalChannel = notificationChannels.getChannelForIncomingCall(ring = false)
|
||||
assertThat(normalChannel).isEqualTo(CALL_NOTIFICATION_CHANNEL_ID_V3)
|
||||
assertThat(normalChannel).isEqualTo(CALL_NOTIFICATION_CHANNEL_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -118,7 +118,7 @@ class DefaultPushHandlerTest {
|
||||
incrementPushCounterResult.assertions()
|
||||
.isCalledOnce()
|
||||
notifiableEventResult.assertions()
|
||||
.isNeverCalled()
|
||||
.isCalledOnce()
|
||||
onNotifiableEventReceived.assertions()
|
||||
.isNeverCalled()
|
||||
}
|
||||
@@ -277,6 +277,34 @@ class DefaultPushHandlerTest {
|
||||
onNotifiableEventReceived.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when notify call PushData is received, the incoming call will be treated as a normal notification even if notification are disabled`() = runTest {
|
||||
val aPushData = PushData(
|
||||
eventId = AN_EVENT_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
unread = 0,
|
||||
clientSecret = A_SECRET,
|
||||
)
|
||||
val onNotifiableEventReceived = lambdaRecorder<NotifiableEvent, Unit> {}
|
||||
val handleIncomingCallLambda = lambdaRecorder<CallType.RoomCall, EventId, UserId, String?, String?, String?, String, Unit> { _, _, _, _, _, _, _ -> }
|
||||
val elementCallEntryPoint = FakeElementCallEntryPoint(handleIncomingCallResult = handleIncomingCallLambda)
|
||||
val defaultPushHandler = createDefaultPushHandler(
|
||||
elementCallEntryPoint = elementCallEntryPoint,
|
||||
onNotifiableEventReceived = onNotifiableEventReceived,
|
||||
notifiableEventResult = { _, _, _ -> aNotifiableCallEvent() },
|
||||
incrementPushCounterResult = {},
|
||||
userPushStore = FakeUserPushStore().apply {
|
||||
setNotificationEnabledForDevice(false)
|
||||
},
|
||||
pushClientSecret = FakePushClientSecret(
|
||||
getUserIdFromSecretResult = { A_USER_ID }
|
||||
),
|
||||
)
|
||||
defaultPushHandler.handle(aPushData)
|
||||
handleIncomingCallLambda.assertions().isCalledOnce()
|
||||
onNotifiableEventReceived.assertions().isNeverCalled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when diagnostic PushData is received, the diagnostic push handler is informed `() =
|
||||
runTest {
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user