Merge branch 'develop' into feature/bma/fixFdroidNotification
This commit is contained in:
commit
69dbb08034
172 changed files with 1110 additions and 68 deletions
|
|
@ -25,9 +25,9 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.compound.theme.Theme
|
||||
import io.element.android.compound.theme.mapToTheme
|
||||
import io.element.android.features.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.features.preferences.api.store.SessionPreferencesStore
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import io.element.android.appconfig.ElementCallConfig
|
||||
import io.element.android.features.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
||||
|
|
@ -42,6 +41,7 @@ import io.element.android.libraries.featureflag.api.Feature
|
|||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -30,6 +31,7 @@ import io.element.android.libraries.architecture.AsyncAction
|
|||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runUpdatingStateNoSuccess
|
||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsPresenter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
|
|
@ -54,6 +56,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
|||
private val matrixClient: MatrixClient,
|
||||
private val pushService: PushService,
|
||||
private val systemNotificationsEnabledProvider: SystemNotificationsEnabledProvider,
|
||||
private val fullScreenIntentPermissionsPresenter: FullScreenIntentPermissionsPresenter,
|
||||
) : Presenter<NotificationSettingsState> {
|
||||
@Composable
|
||||
override fun present(): NotificationSettingsState {
|
||||
|
|
@ -72,6 +75,9 @@ class NotificationSettingsPresenter @Inject constructor(
|
|||
mutableStateOf(NotificationSettingsState.MatrixSettings.Uninitialized)
|
||||
}
|
||||
|
||||
// Used to force a recomposition
|
||||
var refreshFullScreenIntentSettings by remember { mutableIntStateOf(0) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
fetchSettings(matrixSettings)
|
||||
observeNotificationSettings(matrixSettings, changeNotificationSettingAction)
|
||||
|
|
@ -149,6 +155,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
|||
NotificationSettingsEvents.FixConfigurationMismatch -> localCoroutineScope.fixConfigurationMismatch(matrixSettings)
|
||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> {
|
||||
systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled()
|
||||
refreshFullScreenIntentSettings++
|
||||
}
|
||||
NotificationSettingsEvents.ClearNotificationChangeError -> changeNotificationSettingAction.value = AsyncAction.Uninitialized
|
||||
NotificationSettingsEvents.ChangePushProvider -> showChangePushProviderDialog = true
|
||||
|
|
@ -167,6 +174,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
|||
currentPushDistributor = currentDistributorName,
|
||||
availablePushDistributors = distributorNames,
|
||||
showChangePushProviderDialog = showChangePushProviderDialog,
|
||||
fullScreenIntentPermissionsState = key(refreshFullScreenIntentSettings) { fullScreenIntentPermissionsPresenter.present() },
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.preferences.impl.notifications
|
|||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ data class NotificationSettingsState(
|
|||
val currentPushDistributor: AsyncData<String>,
|
||||
val availablePushDistributors: ImmutableList<String>,
|
||||
val showChangePushProviderDialog: Boolean,
|
||||
val fullScreenIntentPermissionsState: FullScreenIntentPermissionsState,
|
||||
val eventSink: (NotificationSettingsEvents) -> Unit,
|
||||
) {
|
||||
sealed interface MatrixSettings {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.preferences.impl.notifications
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -40,6 +41,7 @@ open class NotificationSettingsStateProvider : PreviewParameterProvider<Notifica
|
|||
aValidNotificationSettingsState(currentPushDistributor = AsyncData.Failure(Exception("Failed to change distributor"))),
|
||||
aInvalidNotificationSettingsState(),
|
||||
aInvalidNotificationSettingsState(fixFailed = true),
|
||||
aValidNotificationSettingsState(fullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(permissionGranted = false)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +55,7 @@ fun aValidNotificationSettingsState(
|
|||
currentPushDistributor: AsyncData<String> = AsyncData.Success("Firebase"),
|
||||
availablePushDistributors: List<String> = listOf("Firebase", "ntfy"),
|
||||
showChangePushProviderDialog: Boolean = false,
|
||||
fullScreenIntentPermissionsState: FullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(),
|
||||
eventSink: (NotificationSettingsEvents) -> Unit = {},
|
||||
) = NotificationSettingsState(
|
||||
matrixSettings = NotificationSettingsState.MatrixSettings.Valid(
|
||||
|
|
@ -70,6 +73,7 @@ fun aValidNotificationSettingsState(
|
|||
currentPushDistributor = currentPushDistributor,
|
||||
availablePushDistributors = availablePushDistributors.toImmutableList(),
|
||||
showChangePushProviderDialog = showChangePushProviderDialog,
|
||||
fullScreenIntentPermissionsState = fullScreenIntentPermissionsState,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
||||
|
|
@ -88,5 +92,18 @@ fun aInvalidNotificationSettingsState(
|
|||
currentPushDistributor = AsyncData.Uninitialized,
|
||||
availablePushDistributors = persistentListOf(),
|
||||
showChangePushProviderDialog = false,
|
||||
fullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(),
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
||||
internal fun aFullScreenIntentPermissionsState(
|
||||
permissionGranted: Boolean = true,
|
||||
shouldDisplay: Boolean = false,
|
||||
openFullScreenIntentSettings: () -> Unit = {},
|
||||
dismissFullScreenIntentBanner: () -> Unit = {},
|
||||
) = FullScreenIntentPermissionsState(
|
||||
permissionGranted = permissionGranted,
|
||||
shouldDisplayBanner = shouldDisplay,
|
||||
openFullScreenIntentSettings = openFullScreenIntentSettings,
|
||||
dismissFullScreenIntentBanner = dismissFullScreenIntentBanner,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -136,6 +136,18 @@ private fun NotificationSettingsContentView(
|
|||
)
|
||||
|
||||
if (systemSettings.appNotificationsEnabled) {
|
||||
if (!state.fullScreenIntentPermissionsState.permissionGranted) {
|
||||
PreferenceCategory {
|
||||
PreferenceText(
|
||||
icon = CompoundIcons.VoiceCall(),
|
||||
title = stringResource(id = R.string.full_screen_intent_banner_title),
|
||||
subtitle = stringResource(R.string.full_screen_intent_banner_message,),
|
||||
onClick = {
|
||||
state.fullScreenIntentPermissionsState.openFullScreenIntentSettings()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_notification_section_title)) {
|
||||
PreferenceText(
|
||||
title = stringResource(id = R.string.screen_notification_settings_group_chats),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="full_screen_intent_banner_message">"To ensure you never miss an important call, please change your settings to allow full-screen notifications when your phone is locked."</string>
|
||||
<string name="full_screen_intent_banner_title">"Enhance your call experience"</string>
|
||||
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Choose how to receive notifications"</string>
|
||||
<string name="screen_advanced_settings_developer_mode">"Developer mode"</string>
|
||||
<string name="screen_advanced_settings_developer_mode_description">"Enable to have access to features and functionality for developers."</string>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import app.cash.molecule.moleculeFlow
|
|||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.fullscreenintent.test.FakeFullScreenIntentPermissionsPresenter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.test.A_THROWABLE
|
||||
|
|
@ -269,6 +270,32 @@ class NotificationSettingsPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - RefreshSystemNotificationsEnabled also refreshes fullScreenIntentState`() = runTest {
|
||||
val fullScreenIntentPermissionsPresenter = FakeFullScreenIntentPermissionsPresenter().apply {
|
||||
state = state.copy(permissionGranted = false)
|
||||
}
|
||||
val presenter = createNotificationSettingsPresenter(
|
||||
pushService = createFakePushService(),
|
||||
fullScreenIntentPermissionsPresenter = fullScreenIntentPermissionsPresenter,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitLastSequentialItem()
|
||||
assertThat(initialState.fullScreenIntentPermissionsState.permissionGranted).isFalse()
|
||||
|
||||
// Change the notification settings
|
||||
fullScreenIntentPermissionsPresenter.state = fullScreenIntentPermissionsPresenter.state.copy(permissionGranted = true)
|
||||
// Check it's not changed unless we refresh
|
||||
expectNoEvents()
|
||||
|
||||
// Refresh
|
||||
initialState.eventSink.invoke(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
|
||||
assertThat(awaitItem().fullScreenIntentPermissionsState.permissionGranted).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - change push provider error`() = runTest {
|
||||
val presenter = createNotificationSettingsPresenter(
|
||||
|
|
@ -318,6 +345,7 @@ class NotificationSettingsPresenterTest {
|
|||
private fun createNotificationSettingsPresenter(
|
||||
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||
pushService: PushService = FakePushService(),
|
||||
fullScreenIntentPermissionsPresenter: FakeFullScreenIntentPermissionsPresenter = FakeFullScreenIntentPermissionsPresenter()
|
||||
): NotificationSettingsPresenter {
|
||||
val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
|
||||
return NotificationSettingsPresenter(
|
||||
|
|
@ -326,6 +354,7 @@ class NotificationSettingsPresenterTest {
|
|||
matrixClient = matrixClient,
|
||||
pushService = pushService,
|
||||
systemNotificationsEnabledProvider = FakeSystemNotificationsEnabledProvider(),
|
||||
fullScreenIntentPermissionsPresenter = fullScreenIntentPermissionsPresenter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.
|
||||
*/
|
||||
|
||||
package io.element.android.features.preferences.impl.tasks
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.ftue.test.FakeFtueService
|
||||
import io.element.android.features.preferences.impl.DefaultCacheService
|
||||
import io.element.android.features.roomlist.impl.migration.InMemoryMigrationScreenStore
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okhttp3.OkHttpClient
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class DefaultClearCacheUseCaseTest {
|
||||
@Test
|
||||
fun `execute clear cache should do all the expected tasks`() = runTest {
|
||||
val clearCacheLambda = lambdaRecorder<Unit> { }
|
||||
val matrixClient = FakeMatrixClient(
|
||||
clearCacheLambda = clearCacheLambda,
|
||||
)
|
||||
val defaultCacheService = DefaultCacheService()
|
||||
val resetFtueLambda = lambdaRecorder<Unit> { }
|
||||
val ftueService = FakeFtueService(
|
||||
resetLambda = resetFtueLambda,
|
||||
)
|
||||
val resetMigrationLambda = lambdaRecorder<Unit> { }
|
||||
val migrationScreenStore = InMemoryMigrationScreenStore(
|
||||
resetLambda = resetMigrationLambda,
|
||||
)
|
||||
val sut = DefaultClearCacheUseCase(
|
||||
context = InstrumentationRegistry.getInstrumentation().context,
|
||||
matrixClient = matrixClient,
|
||||
coroutineDispatchers = testCoroutineDispatchers(),
|
||||
defaultCacheService = defaultCacheService,
|
||||
okHttpClient = { OkHttpClient.Builder().build() },
|
||||
ftueService = ftueService,
|
||||
migrationScreenStore = migrationScreenStore
|
||||
)
|
||||
defaultCacheService.clearedCacheEventFlow.test {
|
||||
sut.invoke()
|
||||
clearCacheLambda.assertions().isCalledOnce()
|
||||
resetFtueLambda.assertions().isCalledOnce()
|
||||
resetMigrationLambda.assertions().isCalledOnce()
|
||||
assertThat(awaitItem()).isEqualTo(matrixClient.sessionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue