Browse Source

Move open system setting to the PermissionsPresenter

pull/1400/head
Benoit Marty 1 year ago committed by Benoit Marty
parent
commit
dd5d67d186
  1. 4
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt
  2. 3
      features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt
  3. 4
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileNode.kt
  4. 3
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt
  5. 4
      features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt
  6. 3
      features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt
  7. 1
      libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsEvents.kt
  8. 5
      libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt
  9. 6
      libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt
  10. 34
      libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/AndroidLocationActions.kt
  11. 21
      libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/action/PermissionActions.kt
  12. 55
      libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
  13. 26
      libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/action/FakePermissionActions.kt
  14. 1
      libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt

4
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.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
@ -29,7 +28,6 @@ import dagger.assisted.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.createroom.impl.di.CreateRoomScope 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.libraries.matrix.api.core.RoomId
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
@ -60,13 +58,11 @@ class ConfigureRoomNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
val context = LocalContext.current
ConfigureRoomView( ConfigureRoomView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackPressed = this::navigateUp, onBackPressed = this::navigateUp,
onRoomCreated = this::onRoomCreated, onRoomCreated = this::onRoomCreated,
onOpenSystemSettings = { context.openAppSettingsPage() }
) )
} }
} }

3
features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt

@ -76,7 +76,6 @@ fun ConfigureRoomView(
state: ConfigureRoomState, state: ConfigureRoomState,
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
onRoomCreated: (RoomId) -> Unit, onRoomCreated: (RoomId) -> Unit,
onOpenSystemSettings: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@ -177,7 +176,6 @@ fun ConfigureRoomView(
PermissionsView( PermissionsView(
state = state.cameraPermissionState, state = state.cameraPermissionState,
onOpenSystemSettings = onOpenSystemSettings,
) )
} }
@ -287,6 +285,5 @@ internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvid
state = state, state = state,
onBackPressed = {}, onBackPressed = {},
onRoomCreated = {}, onRoomCreated = {},
onOpenSystemSettings = {},
) )
} }

4
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.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode 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.NodeInputs
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@ -48,12 +46,10 @@ class EditUserProfileNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
val context = LocalContext.current
EditUserProfileView( EditUserProfileView(
state = state, state = state,
onBackPressed = ::navigateUp, onBackPressed = ::navigateUp,
onProfileEdited = ::navigateUp, onProfileEdited = ::navigateUp,
onOpenSystemSettings = { context.openAppSettingsPage() },
modifier = modifier modifier = modifier
) )
} }

3
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileView.kt

@ -69,7 +69,6 @@ fun EditUserProfileView(
state: EditUserProfileState, state: EditUserProfileState,
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
onProfileEdited: () -> Unit, onProfileEdited: () -> Unit,
onOpenSystemSettings: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@ -172,7 +171,6 @@ fun EditUserProfileView(
} }
PermissionsView( PermissionsView(
state = state.cameraPermissionState, state = state.cameraPermissionState,
onOpenSystemSettings = onOpenSystemSettings,
) )
} }
@ -190,7 +188,6 @@ internal fun EditUserProfileViewPreview(@PreviewParameter(EditUserProfileStatePr
EditUserProfileView( EditUserProfileView(
onBackPressed = {}, onBackPressed = {},
onProfileEdited = {}, onProfileEdited = {},
onOpenSystemSettings = {},
state = state, state = state,
) )
} }

4
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.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
@ -27,7 +26,6 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.anvilannotations.ContributesNode 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.libraries.di.RoomScope
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
@ -50,12 +48,10 @@ class RoomDetailsEditNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
val state = presenter.present() val state = presenter.present()
val context = LocalContext.current
RoomDetailsEditView( RoomDetailsEditView(
state = state, state = state,
onBackPressed = ::navigateUp, onBackPressed = ::navigateUp,
onRoomEdited = ::navigateUp, onRoomEdited = ::navigateUp,
onOpenSystemSettings = { context.openAppSettingsPage() },
modifier = modifier, modifier = modifier,
) )
} }

3
features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt

@ -73,7 +73,6 @@ fun RoomDetailsEditView(
state: RoomDetailsEditState, state: RoomDetailsEditState,
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
onRoomEdited: () -> Unit, onRoomEdited: () -> Unit,
onOpenSystemSettings: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@ -198,7 +197,6 @@ fun RoomDetailsEditView(
PermissionsView( PermissionsView(
state = state.cameraPermissionState, state = state.cameraPermissionState,
onOpenSystemSettings = onOpenSystemSettings,
) )
} }
@ -242,6 +240,5 @@ internal fun RoomDetailsEditViewPreview(@PreviewParameter(RoomDetailsEditStatePr
state = state, state = state,
onBackPressed = {}, onBackPressed = {},
onRoomEdited = {}, onRoomEdited = {},
onOpenSystemSettings = {},
) )
} }

1
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 { sealed interface PermissionsEvents {
data object AskPermissionToUser : PermissionsEvents data object AskPermissionToUser : PermissionsEvents
data object CloseDialog : PermissionsEvents data object CloseDialog : PermissionsEvents
data object OpenSystemSettingAndCloseDialog : PermissionsEvents
} }

5
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 @Composable
fun PermissionsView( fun PermissionsView(
state: PermissionsState, state: PermissionsState,
onOpenSystemSettings: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
if (state.showDialog.not()) return if (state.showDialog.not()) return
@ -40,8 +39,7 @@ fun PermissionsView(
content = state.permission.toDialogContent(), content = state.permission.toDialogContent(),
submitText = stringResource(id = CommonStrings.action_open_settings), submitText = stringResource(id = CommonStrings.action_open_settings),
onSubmitClicked = { onSubmitClicked = {
state.eventSink.invoke(PermissionsEvents.CloseDialog) state.eventSink.invoke(PermissionsEvents.OpenSystemSettingAndCloseDialog)
onOpenSystemSettings()
}, },
onDismiss = { state.eventSink.invoke(PermissionsEvents.CloseDialog) }, onDismiss = { state.eventSink.invoke(PermissionsEvents.CloseDialog) },
) )
@ -62,6 +60,5 @@ private fun String.toDialogContent(): String {
internal fun PermissionsViewPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = ElementPreview { internal fun PermissionsViewPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = ElementPreview {
PermissionsView( PermissionsView(
state = state, state = state,
onOpenSystemSettings = {},
) )
} }

6
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.PermissionsPresenter
import io.element.android.libraries.permissions.api.PermissionsState import io.element.android.libraries.permissions.api.PermissionsState
import io.element.android.libraries.permissions.api.PermissionsStore import io.element.android.libraries.permissions.api.PermissionsStore
import io.element.android.libraries.permissions.impl.action.PermissionActions
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -48,6 +49,7 @@ class DefaultPermissionsPresenter @AssistedInject constructor(
@Assisted val permission: String, @Assisted val permission: String,
private val permissionsStore: PermissionsStore, private val permissionsStore: PermissionsStore,
private val composablePermissionStateProvider: ComposablePermissionStateProvider, private val composablePermissionStateProvider: ComposablePermissionStateProvider,
private val permissionActions: PermissionActions,
) : PermissionsPresenter { ) : PermissionsPresenter {
@AssistedFactory @AssistedFactory
@ -117,6 +119,10 @@ class DefaultPermissionsPresenter @AssistedInject constructor(
permissionState.launchPermissionRequest() permissionState.launchPermissionRequest()
} }
} }
PermissionsEvents.OpenSystemSettingAndCloseDialog -> {
permissionActions.openSettings()
showDialog.value = false
}
} }
} }

34
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()
}
}

21
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()
}

55
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.accompanist.permissions.PermissionStatus
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.permissions.api.PermissionsEvents 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.libraries.permissions.test.InMemoryPermissionsStore
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -52,7 +53,8 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter( val presenter = DefaultPermissionsPresenter(
A_PERMISSION, A_PERMISSION,
permissionsStore, permissionsStore,
permissionStateProvider permissionStateProvider,
FakePermissionActions(),
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -84,7 +86,8 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter( val presenter = DefaultPermissionsPresenter(
A_PERMISSION, A_PERMISSION,
permissionsStore, permissionsStore,
permissionStateProvider permissionStateProvider,
FakePermissionActions(),
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() 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 @Test
fun `present - user does not grant permission`() = runTest { fun `present - user does not grant permission`() = runTest {
val permissionsStore = InMemoryPermissionsStore() val permissionsStore = InMemoryPermissionsStore()
@ -113,7 +152,8 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter( val presenter = DefaultPermissionsPresenter(
A_PERMISSION, A_PERMISSION,
permissionsStore, permissionsStore,
permissionStateProvider permissionStateProvider,
FakePermissionActions(),
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -147,7 +187,8 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter( val presenter = DefaultPermissionsPresenter(
A_PERMISSION, A_PERMISSION,
permissionsStore, permissionsStore,
permissionStateProvider permissionStateProvider,
FakePermissionActions(),
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -185,7 +226,8 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter( val presenter = DefaultPermissionsPresenter(
A_PERMISSION, A_PERMISSION,
permissionsStore, permissionsStore,
permissionStateProvider permissionStateProvider,
FakePermissionActions(),
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -215,7 +257,8 @@ class DefaultPermissionsPresenterTest {
val presenter = DefaultPermissionsPresenter( val presenter = DefaultPermissionsPresenter(
A_PERMISSION, A_PERMISSION,
permissionsStore, permissionsStore,
permissionStateProvider permissionStateProvider,
FakePermissionActions(),
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()

26
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
}
}

1
libraries/permissions/test/src/main/kotlin/io/element/android/libraries/permissions/test/FakePermissionsPresenter.kt

@ -31,6 +31,7 @@ class FakePermissionsPresenter(
when (events) { when (events) {
PermissionsEvents.AskPermissionToUser -> state.value = state.value.copy(showDialog = true, permissionAlreadyAsked = true) PermissionsEvents.AskPermissionToUser -> state.value = state.value.copy(showDialog = true, permissionAlreadyAsked = true)
PermissionsEvents.CloseDialog -> state.value = state.value.copy(showDialog = false) PermissionsEvents.CloseDialog -> state.value = state.value.copy(showDialog = false)
PermissionsEvents.OpenSystemSettingAndCloseDialog -> state.value = state.value.copy(showDialog = false)
} }
} }

Loading…
Cancel
Save