Do not rely only on distributor name but consider value (appId) as well.
This will fix issue when multiple UnifiedPush distributor with the same friendly name are available on the phone. Fixes #4306
This commit is contained in:
parent
aca34c0dec
commit
68a76f83a0
7 changed files with 95 additions and 40 deletions
|
|
@ -83,19 +83,19 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// List of Distributor names
|
// List of Distributors
|
||||||
val distributorNames = remember {
|
val availableDistributors = remember {
|
||||||
distributors.map { it.second.name }.toImmutableList()
|
distributors.map { it.second }.toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentDistributorName by remember { mutableStateOf<AsyncData<String>>(AsyncData.Uninitialized) }
|
var currentDistributor by remember { mutableStateOf<AsyncData<Distributor>>(AsyncData.Uninitialized) }
|
||||||
var refreshPushProvider by remember { mutableIntStateOf(0) }
|
var refreshPushProvider by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
LaunchedEffect(refreshPushProvider) {
|
LaunchedEffect(refreshPushProvider) {
|
||||||
val p = pushService.getCurrentPushProvider()
|
val p = pushService.getCurrentPushProvider()
|
||||||
val name = p?.getCurrentDistributor(matrixClient.sessionId)?.name
|
val distributor = p?.getCurrentDistributor(matrixClient.sessionId)
|
||||||
currentDistributorName = if (name != null) {
|
currentDistributor = if (distributor != null) {
|
||||||
AsyncData.Success(name)
|
AsyncData.Success(distributor)
|
||||||
} else {
|
} else {
|
||||||
AsyncData.Failure(Exception("Failed to get current push provider"))
|
AsyncData.Failure(Exception("Failed to get current push provider"))
|
||||||
}
|
}
|
||||||
|
|
@ -108,24 +108,23 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
) = launch {
|
) = launch {
|
||||||
showChangePushProviderDialog = false
|
showChangePushProviderDialog = false
|
||||||
data ?: return@launch
|
data ?: return@launch
|
||||||
// No op if the value is the same.
|
val (pushProvider, distributor) = data
|
||||||
if (data.second.name == currentDistributorName.dataOrNull()) return@launch
|
// No op if the distributor is the same.
|
||||||
currentDistributorName = AsyncData.Loading(currentDistributorName.dataOrNull())
|
if (distributor == currentDistributor.dataOrNull()) return@launch
|
||||||
data.let { (pushProvider, distributor) ->
|
currentDistributor = AsyncData.Loading(currentDistributor.dataOrNull())
|
||||||
pushService.registerWith(
|
pushService.registerWith(
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
pushProvider = pushProvider,
|
pushProvider = pushProvider,
|
||||||
distributor = distributor
|
distributor = distributor
|
||||||
|
)
|
||||||
|
.fold(
|
||||||
|
{
|
||||||
|
refreshPushProvider++
|
||||||
|
},
|
||||||
|
{
|
||||||
|
currentDistributor = AsyncData.Failure(it)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.fold(
|
|
||||||
{
|
|
||||||
refreshPushProvider++
|
|
||||||
},
|
|
||||||
{
|
|
||||||
currentDistributorName = AsyncData.Failure(it)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvents(event: NotificationSettingsEvents) {
|
fun handleEvents(event: NotificationSettingsEvents) {
|
||||||
|
|
@ -162,8 +161,8 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
appNotificationsEnabled = appNotificationsEnabled.value
|
appNotificationsEnabled = appNotificationsEnabled.value
|
||||||
),
|
),
|
||||||
changeNotificationSettingAction = changeNotificationSettingAction.value,
|
changeNotificationSettingAction = changeNotificationSettingAction.value,
|
||||||
currentPushDistributor = currentDistributorName,
|
currentPushDistributor = currentDistributor,
|
||||||
availablePushDistributors = distributorNames,
|
availablePushDistributors = availableDistributors,
|
||||||
showChangePushProviderDialog = showChangePushProviderDialog,
|
showChangePushProviderDialog = showChangePushProviderDialog,
|
||||||
fullScreenIntentPermissionsState = key(refreshFullScreenIntentSettings) { fullScreenIntentPermissionsPresenter.present() },
|
fullScreenIntentPermissionsState = key(refreshFullScreenIntentSettings) { fullScreenIntentPermissionsPresenter.present() },
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.pushproviders.api.Distributor
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
@ -19,8 +20,8 @@ data class NotificationSettingsState(
|
||||||
val matrixSettings: MatrixSettings,
|
val matrixSettings: MatrixSettings,
|
||||||
val appSettings: AppSettings,
|
val appSettings: AppSettings,
|
||||||
val changeNotificationSettingAction: AsyncAction<Unit>,
|
val changeNotificationSettingAction: AsyncAction<Unit>,
|
||||||
val currentPushDistributor: AsyncData<String>,
|
val currentPushDistributor: AsyncData<Distributor>,
|
||||||
val availablePushDistributors: ImmutableList<String>,
|
val availablePushDistributors: ImmutableList<Distributor>,
|
||||||
val showChangePushProviderDialog: Boolean,
|
val showChangePushProviderDialog: Boolean,
|
||||||
val fullScreenIntentPermissionsState: FullScreenIntentPermissionsState,
|
val fullScreenIntentPermissionsState: FullScreenIntentPermissionsState,
|
||||||
val eventSink: (NotificationSettingsEvents) -> Unit,
|
val eventSink: (NotificationSettingsEvents) -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
||||||
import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState
|
import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.pushproviders.api.Distributor
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
|
|
@ -24,11 +25,19 @@ open class NotificationSettingsStateProvider : PreviewParameterProvider<Notifica
|
||||||
aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Loading),
|
aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Loading),
|
||||||
aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Failure(Throwable("error"))),
|
aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Failure(Throwable("error"))),
|
||||||
aValidNotificationSettingsState(
|
aValidNotificationSettingsState(
|
||||||
availablePushDistributors = listOf("Firebase"),
|
availablePushDistributors = listOf(aDistributor("Firebase")),
|
||||||
changeNotificationSettingAction = AsyncAction.Failure(Throwable("error")),
|
changeNotificationSettingAction = AsyncAction.Failure(Throwable("error")),
|
||||||
),
|
),
|
||||||
aValidNotificationSettingsState(availablePushDistributors = listOf("Firebase")),
|
aValidNotificationSettingsState(availablePushDistributors = listOf(aDistributor("Firebase"))),
|
||||||
aValidNotificationSettingsState(showChangePushProviderDialog = true),
|
aValidNotificationSettingsState(showChangePushProviderDialog = true),
|
||||||
|
aValidNotificationSettingsState(
|
||||||
|
availablePushDistributors = listOf(
|
||||||
|
aDistributor("Firebase"),
|
||||||
|
aDistributor("ntfy", "app.id1"),
|
||||||
|
aDistributor("ntfy", "app.id2"),
|
||||||
|
),
|
||||||
|
showChangePushProviderDialog = true,
|
||||||
|
),
|
||||||
aValidNotificationSettingsState(currentPushDistributor = AsyncData.Loading()),
|
aValidNotificationSettingsState(currentPushDistributor = AsyncData.Loading()),
|
||||||
aValidNotificationSettingsState(currentPushDistributor = AsyncData.Failure(Exception("Failed to change distributor"))),
|
aValidNotificationSettingsState(currentPushDistributor = AsyncData.Failure(Exception("Failed to change distributor"))),
|
||||||
aInvalidNotificationSettingsState(),
|
aInvalidNotificationSettingsState(),
|
||||||
|
|
@ -45,8 +54,11 @@ fun aValidNotificationSettingsState(
|
||||||
inviteForMeNotificationsEnabled: Boolean = true,
|
inviteForMeNotificationsEnabled: Boolean = true,
|
||||||
systemNotificationsEnabled: Boolean = true,
|
systemNotificationsEnabled: Boolean = true,
|
||||||
appNotificationEnabled: Boolean = true,
|
appNotificationEnabled: Boolean = true,
|
||||||
currentPushDistributor: AsyncData<String> = AsyncData.Success("Firebase"),
|
currentPushDistributor: AsyncData<Distributor> = AsyncData.Success(aDistributor("Firebase")),
|
||||||
availablePushDistributors: List<String> = listOf("Firebase", "ntfy"),
|
availablePushDistributors: List<Distributor> = listOf(
|
||||||
|
aDistributor("Firebase"),
|
||||||
|
aDistributor("ntfy"),
|
||||||
|
),
|
||||||
showChangePushProviderDialog: Boolean = false,
|
showChangePushProviderDialog: Boolean = false,
|
||||||
fullScreenIntentPermissionsState: FullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(),
|
fullScreenIntentPermissionsState: FullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(),
|
||||||
eventSink: (NotificationSettingsEvents) -> Unit = {},
|
eventSink: (NotificationSettingsEvents) -> Unit = {},
|
||||||
|
|
@ -88,3 +100,11 @@ fun aInvalidNotificationSettingsState(
|
||||||
fullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(),
|
fullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(),
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun aDistributor(
|
||||||
|
name: String = "Name",
|
||||||
|
value: String = "$name Value",
|
||||||
|
) = Distributor(
|
||||||
|
value = value,
|
||||||
|
name = name,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ private fun NotificationSettingsContentView(
|
||||||
stringResource(id = CommonStrings.common_error)
|
stringResource(id = CommonStrings.common_error)
|
||||||
)
|
)
|
||||||
is AsyncData.Success -> ListItemContent.Text(
|
is AsyncData.Success -> ListItemContent.Text(
|
||||||
state.currentPushDistributor.dataOrNull() ?: ""
|
state.currentPushDistributor.dataOrNull()?.name ?: ""
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -219,8 +219,14 @@ private fun NotificationSettingsContentView(
|
||||||
if (state.showChangePushProviderDialog) {
|
if (state.showChangePushProviderDialog) {
|
||||||
SingleSelectionDialog(
|
SingleSelectionDialog(
|
||||||
title = stringResource(id = R.string.screen_advanced_settings_choose_distributor_dialog_title_android),
|
title = stringResource(id = R.string.screen_advanced_settings_choose_distributor_dialog_title_android),
|
||||||
options = state.availablePushDistributors.map {
|
options = state.availablePushDistributors.map { distributor ->
|
||||||
ListOption(title = it)
|
// If there are several distributors with the same name, use the full name
|
||||||
|
val title = if (state.availablePushDistributors.count { it.name == distributor.name } > 1) {
|
||||||
|
distributor.fullName
|
||||||
|
} else {
|
||||||
|
distributor.name
|
||||||
|
}
|
||||||
|
ListOption(title = title)
|
||||||
}.toImmutableList(),
|
}.toImmutableList(),
|
||||||
initialSelection = state.availablePushDistributors.indexOf(state.currentPushDistributor.dataOrNull()),
|
initialSelection = state.availablePushDistributors.indexOf(state.currentPushDistributor.dataOrNull()),
|
||||||
onSelectOption = { index ->
|
onSelectOption = { index ->
|
||||||
|
|
|
||||||
|
|
@ -240,8 +240,11 @@ class NotificationSettingsPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitLastSequentialItem()
|
val initialState = awaitLastSequentialItem()
|
||||||
assertThat(initialState.currentPushDistributor).isEqualTo(AsyncData.Success("aDistributorName0"))
|
assertThat(initialState.currentPushDistributor).isEqualTo(AsyncData.Success(Distributor(value = "aDistributorValue0", name = "aDistributorName0")))
|
||||||
assertThat(initialState.availablePushDistributors).containsExactly("aDistributorName0", "aDistributorName1")
|
assertThat(initialState.availablePushDistributors).containsExactly(
|
||||||
|
Distributor(value = "aDistributorValue0", name = "aDistributorName0"),
|
||||||
|
Distributor(value = "aDistributorValue1", name = "aDistributorName1"),
|
||||||
|
)
|
||||||
initialState.eventSink.invoke(NotificationSettingsEvents.ChangePushProvider)
|
initialState.eventSink.invoke(NotificationSettingsEvents.ChangePushProvider)
|
||||||
val withDialog = awaitItem()
|
val withDialog = awaitItem()
|
||||||
assertThat(withDialog.showChangePushProviderDialog).isTrue()
|
assertThat(withDialog.showChangePushProviderDialog).isTrue()
|
||||||
|
|
@ -257,11 +260,35 @@ class NotificationSettingsPresenterTest {
|
||||||
assertThat(withNewProvider.currentPushDistributor).isInstanceOf(AsyncData.Loading::class.java)
|
assertThat(withNewProvider.currentPushDistributor).isInstanceOf(AsyncData.Loading::class.java)
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
val lastItem = awaitItem()
|
val lastItem = awaitItem()
|
||||||
assertThat(lastItem.currentPushDistributor).isEqualTo(AsyncData.Success("aDistributorName1"))
|
assertThat(lastItem.currentPushDistributor).isEqualTo(AsyncData.Success(Distributor(value = "aDistributorValue1", name = "aDistributorName1")))
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - change push provider to the same value is no op`() = runTest {
|
||||||
|
val presenter = createNotificationSettingsPresenter(
|
||||||
|
pushService = createFakePushService(),
|
||||||
|
)
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
val initialState = awaitLastSequentialItem()
|
||||||
|
assertThat(initialState.currentPushDistributor).isEqualTo(AsyncData.Success(Distributor(value = "aDistributorValue0", name = "aDistributorName0")))
|
||||||
|
assertThat(initialState.availablePushDistributors).containsExactly(
|
||||||
|
Distributor(value = "aDistributorValue0", name = "aDistributorName0"),
|
||||||
|
Distributor(value = "aDistributorValue1", name = "aDistributorName1"),
|
||||||
|
)
|
||||||
|
initialState.eventSink.invoke(NotificationSettingsEvents.ChangePushProvider)
|
||||||
|
assertThat(awaitItem().showChangePushProviderDialog).isTrue()
|
||||||
|
// Choose the same value (index 0)
|
||||||
|
initialState.eventSink(NotificationSettingsEvents.SetPushProvider(0))
|
||||||
|
val withNewProvider = awaitItem()
|
||||||
|
assertThat(withNewProvider.showChangePushProviderDialog).isFalse()
|
||||||
|
expectNoEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - RefreshSystemNotificationsEnabled also refreshes fullScreenIntentState`() = runTest {
|
fun `present - RefreshSystemNotificationsEnabled also refreshes fullScreenIntentState`() = runTest {
|
||||||
var lambdaResult = aFullScreenIntentPermissionsState(permissionGranted = false)
|
var lambdaResult = aFullScreenIntentPermissionsState(permissionGranted = false)
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ class NotificationSettingsViewTest {
|
||||||
state = aValidNotificationSettingsState(
|
state = aValidNotificationSettingsState(
|
||||||
eventSink = eventsRecorder,
|
eventSink = eventsRecorder,
|
||||||
showChangePushProviderDialog = true,
|
showChangePushProviderDialog = true,
|
||||||
availablePushDistributors = listOf("P1", "P2")
|
availablePushDistributors = listOf(aDistributor("P1"), aDistributor("P2"))
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
rule.onNodeWithText("P2").performClick()
|
rule.onNodeWithText("P2").performClick()
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,6 @@ package io.element.android.libraries.pushproviders.api
|
||||||
data class Distributor(
|
data class Distributor(
|
||||||
val value: String,
|
val value: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
)
|
) {
|
||||||
|
val fullName = "$name ($value)"
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue