diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt index 974877466c..b09863b205 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt @@ -18,7 +18,6 @@ package io.element.android.features.createroom.impl.configureroom import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -29,7 +28,6 @@ import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.anvilannotations.ContributesNode import io.element.android.features.createroom.impl.di.CreateRoomScope -import io.element.android.libraries.androidutils.system.openAppSettingsPage import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.services.analytics.api.AnalyticsService @@ -60,13 +58,11 @@ class ConfigureRoomNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val state = presenter.present() - val context = LocalContext.current ConfigureRoomView( state = state, modifier = modifier, onBackPressed = this::navigateUp, onRoomCreated = this::onRoomCreated, - onOpenSystemSettings = { context.openAppSettingsPage() } ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 830401d237..4b56829657 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -76,7 +76,6 @@ fun ConfigureRoomView( state: ConfigureRoomState, onBackPressed: () -> Unit, onRoomCreated: (RoomId) -> Unit, - onOpenSystemSettings: () -> Unit, modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() @@ -177,7 +176,6 @@ fun ConfigureRoomView( PermissionsView( state = state.cameraPermissionState, - onOpenSystemSettings = onOpenSystemSettings, ) } @@ -287,6 +285,5 @@ internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvid state = state, onBackPressed = {}, onRoomCreated = {}, - onOpenSystemSettings = {}, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt index 80eab4bf39..738ae4ec6d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt @@ -18,14 +18,12 @@ package io.element.android.features.preferences.impl.user.editprofile import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.androidutils.system.openAppSettingsPage import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -48,12 +46,10 @@ class EditUserProfileNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val state = presenter.present() - val context = LocalContext.current EditUserProfileView( state = state, onBackPressed = ::navigateUp, onProfileEdited = ::navigateUp, - onOpenSystemSettings = { context.openAppSettingsPage() }, modifier = modifier ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt index 44930b2768..0e21729a83 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt @@ -69,7 +69,6 @@ fun EditUserProfileView( state: EditUserProfileState, onBackPressed: () -> Unit, onProfileEdited: () -> Unit, - onOpenSystemSettings: () -> Unit, modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() @@ -172,7 +171,6 @@ fun EditUserProfileView( } PermissionsView( state = state.cameraPermissionState, - onOpenSystemSettings = onOpenSystemSettings, ) } @@ -190,7 +188,6 @@ internal fun EditUserProfileViewPreview(@PreviewParameter(EditUserProfileStatePr EditUserProfileView( onBackPressed = {}, onProfileEdited = {}, - onOpenSystemSettings = {}, state = state, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt index 28b32f7c58..e81cd84c24 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt @@ -18,7 +18,6 @@ package io.element.android.features.roomdetails.impl.edit import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -27,7 +26,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.androidutils.system.openAppSettingsPage import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService @@ -50,12 +48,10 @@ class RoomDetailsEditNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val state = presenter.present() - val context = LocalContext.current RoomDetailsEditView( state = state, onBackPressed = ::navigateUp, onRoomEdited = ::navigateUp, - onOpenSystemSettings = { context.openAppSettingsPage() }, modifier = modifier, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index 27836bd62b..2a9e766f44 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -73,7 +73,6 @@ fun RoomDetailsEditView( state: RoomDetailsEditState, onBackPressed: () -> Unit, onRoomEdited: () -> Unit, - onOpenSystemSettings: () -> Unit, modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() @@ -198,7 +197,6 @@ fun RoomDetailsEditView( PermissionsView( state = state.cameraPermissionState, - onOpenSystemSettings = onOpenSystemSettings, ) } @@ -242,6 +240,5 @@ internal fun RoomDetailsEditViewPreview(@PreviewParameter(RoomDetailsEditStatePr state = state, onBackPressed = {}, onRoomEdited = {}, - onOpenSystemSettings = {}, ) } diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt index 44b7ef62c0..393f91cab5 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt @@ -19,4 +19,5 @@ package io.element.android.libraries.permissions.api sealed interface PermissionsEvents { data object AskPermissionToUser : PermissionsEvents data object CloseDialog : PermissionsEvents + data object OpenSystemSettingAndCloseDialog : PermissionsEvents } diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt index 46758fc636..48f7e0a9c3 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt @@ -29,7 +29,6 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun PermissionsView( state: PermissionsState, - onOpenSystemSettings: () -> Unit, modifier: Modifier = Modifier, ) { if (state.showDialog.not()) return @@ -40,8 +39,7 @@ fun PermissionsView( content = state.permission.toDialogContent(), submitText = stringResource(id = CommonStrings.action_open_settings), onSubmitClicked = { - state.eventSink.invoke(PermissionsEvents.CloseDialog) - onOpenSystemSettings() + state.eventSink.invoke(PermissionsEvents.OpenSystemSettingAndCloseDialog) }, onDismiss = { state.eventSink.invoke(PermissionsEvents.CloseDialog) }, ) @@ -62,6 +60,5 @@ private fun String.toDialogContent(): String { internal fun PermissionsViewPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = ElementPreview { PermissionsView( state = state, - onOpenSystemSettings = {}, ) } diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt index c80b3e2466..a5030178c3 100644 --- a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt +++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt @@ -39,6 +39,7 @@ import io.element.android.libraries.permissions.api.PermissionsEvents import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.PermissionsStore +import io.element.android.libraries.permissions.impl.action.PermissionActions import kotlinx.coroutines.launch import timber.log.Timber @@ -48,6 +49,7 @@ class DefaultPermissionsPresenter @AssistedInject constructor( @Assisted val permission: String, private val permissionsStore: PermissionsStore, private val composablePermissionStateProvider: ComposablePermissionStateProvider, + private val permissionActions: PermissionActions, ) : PermissionsPresenter { @AssistedFactory @@ -117,6 +119,10 @@ class DefaultPermissionsPresenter @AssistedInject constructor( permissionState.launchPermissionRequest() } } + PermissionsEvents.OpenSystemSettingAndCloseDialog -> { + permissionActions.openSettings() + showDialog.value = false + } } } diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/AndroidLocationActions.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/AndroidLocationActions.kt new file mode 100644 index 0000000000..a694c079e3 --- /dev/null +++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/AndroidLocationActions.kt @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package io.element.android.libraries.permissions.impl.action + +import android.content.Context +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.androidutils.system.openAppSettingsPage +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.ApplicationContext +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class AndroidPermissionActions @Inject constructor( + @ApplicationContext private val context: Context +) : PermissionActions { + + override fun openSettings() { + context.openAppSettingsPage() + } +} diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/PermissionActions.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/PermissionActions.kt new file mode 100644 index 0000000000..66c37bdb4d --- /dev/null +++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/PermissionActions.kt @@ -0,0 +1,21 @@ +/* + * 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. + */ + +package io.element.android.libraries.permissions.impl.action + +interface PermissionActions { + fun openSettings() +} diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt index 420d298eac..51f9ecc5e6 100644 --- a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt +++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt @@ -25,6 +25,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.PermissionStatus import com.google.common.truth.Truth.assertThat import io.element.android.libraries.permissions.api.PermissionsEvents +import io.element.android.libraries.permissions.impl.action.FakePermissionActions import io.element.android.libraries.permissions.test.InMemoryPermissionsStore import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest @@ -52,7 +53,8 @@ class DefaultPermissionsPresenterTest { val presenter = DefaultPermissionsPresenter( A_PERMISSION, permissionsStore, - permissionStateProvider + permissionStateProvider, + FakePermissionActions(), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -84,7 +86,8 @@ class DefaultPermissionsPresenterTest { val presenter = DefaultPermissionsPresenter( A_PERMISSION, permissionsStore, - permissionStateProvider + permissionStateProvider, + FakePermissionActions(), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -99,6 +102,42 @@ class DefaultPermissionsPresenterTest { } } + @Test + fun `present - user open settings`() = runTest { + val permissionsStore = InMemoryPermissionsStore( + permissionDenied = true, + permissionAsked = true + ) + val permissionState = FakePermissionState( + A_PERMISSION, + PermissionStatus.Denied(shouldShowRationale = false) + ) + val permissionStateProvider = + FakeComposablePermissionStateProvider( + permissionState + ) + val permissionActions = FakePermissionActions() + val presenter = DefaultPermissionsPresenter( + A_PERMISSION, + permissionsStore, + permissionStateProvider, + permissionActions, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + val initialState = awaitItem() + initialState.eventSink.invoke(PermissionsEvents.AskPermissionToUser) + val withDialogState = awaitItem() + assertThat(withDialogState.showDialog).isTrue() + assertThat(permissionActions.openSettingsCalled).isFalse() + withDialogState.eventSink.invoke(PermissionsEvents.OpenSystemSettingAndCloseDialog) + assertThat(awaitItem().showDialog).isFalse() + assertThat(permissionActions.openSettingsCalled).isTrue() + } + } + @Test fun `present - user does not grant permission`() = runTest { val permissionsStore = InMemoryPermissionsStore() @@ -113,7 +152,8 @@ class DefaultPermissionsPresenterTest { val presenter = DefaultPermissionsPresenter( A_PERMISSION, permissionsStore, - permissionStateProvider + permissionStateProvider, + FakePermissionActions(), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -147,7 +187,8 @@ class DefaultPermissionsPresenterTest { val presenter = DefaultPermissionsPresenter( A_PERMISSION, permissionsStore, - permissionStateProvider + permissionStateProvider, + FakePermissionActions(), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -185,7 +226,8 @@ class DefaultPermissionsPresenterTest { val presenter = DefaultPermissionsPresenter( A_PERMISSION, permissionsStore, - permissionStateProvider + permissionStateProvider, + FakePermissionActions(), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -215,7 +257,8 @@ class DefaultPermissionsPresenterTest { val presenter = DefaultPermissionsPresenter( A_PERMISSION, permissionsStore, - permissionStateProvider + permissionStateProvider, + FakePermissionActions(), ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/action/FakePermissionActions.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/action/FakePermissionActions.kt new file mode 100644 index 0000000000..fa17329900 --- /dev/null +++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/action/FakePermissionActions.kt @@ -0,0 +1,26 @@ +/* + * 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. + */ + +package io.element.android.libraries.permissions.impl.action + +class FakePermissionActions : PermissionActions { + var openSettingsCalled = false + private set + + override fun openSettings() { + openSettingsCalled = true + } +} diff --git a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt index 0e581f3cef..e7c639e8a1 100644 --- a/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt +++ b/libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt @@ -31,6 +31,7 @@ class FakePermissionsPresenter( when (events) { PermissionsEvents.AskPermissionToUser -> state.value = state.value.copy(showDialog = true, permissionAlreadyAsked = true) PermissionsEvents.CloseDialog -> state.value = state.value.copy(showDialog = false) + PermissionsEvents.OpenSystemSettingAndCloseDialog -> state.value = state.value.copy(showDialog = false) } }