Request Camera permission before launching the external Camera app (#1395)
This commit is contained in:
parent
7b9d2f903b
commit
31eca73e8d
21 changed files with 124 additions and 20 deletions
|
|
@ -48,6 +48,7 @@ dependencies {
|
|||
implementation(projects.libraries.deeplink)
|
||||
implementation(projects.libraries.mediapickers.api)
|
||||
implementation(projects.libraries.mediaupload.api)
|
||||
implementation(projects.libraries.permissions.api)
|
||||
implementation(projects.libraries.usersearch.impl)
|
||||
implementation(projects.services.analytics.api)
|
||||
implementation(libs.coil.compose)
|
||||
|
|
@ -64,6 +65,7 @@ dependencies {
|
|||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.mediapickers.test)
|
||||
testImplementation(projects.libraries.mediaupload.test)
|
||||
testImplementation(projects.libraries.permissions.test)
|
||||
testImplementation(projects.libraries.usersearch.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ import io.element.android.libraries.matrix.api.createroom.RoomVisibility
|
|||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -52,10 +54,14 @@ class ConfigureRoomPresenter @Inject constructor(
|
|||
private val mediaPickerProvider: PickerProvider,
|
||||
private val mediaPreProcessor: MediaPreProcessor,
|
||||
private val analyticsService: AnalyticsService,
|
||||
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||
) : Presenter<ConfigureRoomState> {
|
||||
|
||||
private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
|
||||
|
||||
@Composable
|
||||
override fun present(): ConfigureRoomState {
|
||||
val cameraPermissionState = cameraPermissionPresenter.present()
|
||||
val createRoomConfig = dataStore.getCreateRoomConfig().collectAsState(CreateRoomConfig())
|
||||
|
||||
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(
|
||||
|
|
@ -93,7 +99,11 @@ class ConfigureRoomPresenter @Inject constructor(
|
|||
is ConfigureRoomEvents.HandleAvatarAction -> {
|
||||
when (event.action) {
|
||||
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
|
||||
AvatarAction.TakePhoto -> cameraPhotoPicker.launch()
|
||||
AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
|
||||
cameraPhotoPicker.launch()
|
||||
} else {
|
||||
cameraPermissionState.eventSink(PermissionsEvents.OpenSystemDialog)
|
||||
}
|
||||
AvatarAction.Remove -> dataStore.setAvatarUri(uri = null)
|
||||
}
|
||||
}
|
||||
|
|
@ -106,6 +116,7 @@ class ConfigureRoomPresenter @Inject constructor(
|
|||
config = createRoomConfig.value,
|
||||
avatarActions = avatarActions,
|
||||
createRoomAction = createRoomAction.value,
|
||||
cameraPermissionState = cameraPermissionState,
|
||||
eventSink = ::handleEvents,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
|
|||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.permissions.api.PermissionsState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class ConfigureRoomState(
|
||||
val config: CreateRoomConfig,
|
||||
val avatarActions: ImmutableList<AvatarAction>,
|
||||
val createRoomAction: Async<RoomId>,
|
||||
val cameraPermissionState: PermissionsState,
|
||||
val eventSink: (ConfigureRoomEvents) -> Unit
|
||||
) {
|
||||
val isCreateButtonEnabled: Boolean = config.roomName.isNullOrEmpty().not()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
import io.element.android.features.createroom.impl.CreateRoomConfig
|
||||
import io.element.android.features.createroom.impl.userlist.aListOfSelectedUsers
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.permissions.api.aPermissionsState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomState> {
|
||||
|
|
@ -41,5 +42,6 @@ fun aConfigureRoomState() = ConfigureRoomState(
|
|||
config = CreateRoomConfig(),
|
||||
avatarActions = persistentListOf(),
|
||||
createRoomAction = Async.Uninitialized,
|
||||
cameraPermissionState = aPermissionsState(showDialog = false),
|
||||
eventSink = { },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
|
||||
import io.element.android.libraries.matrix.ui.components.SelectedUsersList
|
||||
import io.element.android.libraries.matrix.ui.components.UnsavedAvatar
|
||||
import io.element.android.libraries.permissions.api.PermissionsView
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -172,6 +173,8 @@ fun ConfigureRoomView(
|
|||
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
PermissionsView(state = state.cameraPermissionState)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
|
|||
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.mockk.every
|
||||
|
|
@ -70,6 +72,7 @@ class ConfigureRoomPresenterTests {
|
|||
private lateinit var fakePickerProvider: FakePickerProvider
|
||||
private lateinit var fakeMediaPreProcessor: FakeMediaPreProcessor
|
||||
private lateinit var fakeAnalyticsService: FakeAnalyticsService
|
||||
private lateinit var fakePermissionsPresenter: FakePermissionsPresenter
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
|
|
@ -79,12 +82,18 @@ class ConfigureRoomPresenterTests {
|
|||
fakePickerProvider = FakePickerProvider()
|
||||
fakeMediaPreProcessor = FakeMediaPreProcessor()
|
||||
fakeAnalyticsService = FakeAnalyticsService()
|
||||
fakePermissionsPresenter = FakePermissionsPresenter()
|
||||
presenter = ConfigureRoomPresenter(
|
||||
dataStore = createRoomDataStore,
|
||||
matrixClient = fakeMatrixClient,
|
||||
mediaPickerProvider = fakePickerProvider,
|
||||
mediaPreProcessor = fakeMediaPreProcessor,
|
||||
analyticsService = fakeAnalyticsService,
|
||||
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
||||
override fun create(permission: String): PermissionsPresenter {
|
||||
return fakePermissionsPresenter
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
mockkStatic(File::readBytes)
|
||||
|
|
@ -170,8 +179,6 @@ class ConfigureRoomPresenterTests {
|
|||
// Room avatar
|
||||
// Pick avatar
|
||||
fakePickerProvider.givenResult(null)
|
||||
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
// From gallery
|
||||
val uriFromGallery = Uri.parse(AN_URI_FROM_GALLERY)
|
||||
fakePickerProvider.givenResult(uriFromGallery)
|
||||
|
|
@ -182,6 +189,10 @@ class ConfigureRoomPresenterTests {
|
|||
// From camera
|
||||
val uriFromCamera = Uri.parse(AN_URI_FROM_CAMERA)
|
||||
fakePickerProvider.givenResult(uriFromCamera)
|
||||
assertThat(newState.cameraPermissionState.permissionGranted).isFalse()
|
||||
fakePermissionsPresenter.setPermissionGranted()
|
||||
newState = awaitItem()
|
||||
assertThat(newState.cameraPermissionState.permissionGranted).isTrue()
|
||||
newState.eventSink(ConfigureRoomEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
newState = awaitItem()
|
||||
expectedConfig = expectedConfig.copy(avatarUri = uriFromCamera)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,6 @@ open class NotificationsOptInStateProvider : PreviewParameterProvider<Notificati
|
|||
}
|
||||
|
||||
fun aNotificationsOptInState() = NotificationsOptInState(
|
||||
notificationsPermissionState = aPermissionsState(),
|
||||
notificationsPermissionState = aPermissionsState(showDialog = false),
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ dependencies {
|
|||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.mediapickers.api)
|
||||
implementation(projects.libraries.mediaupload.api)
|
||||
implementation(projects.libraries.permissions.api)
|
||||
implementation(projects.features.rageshake.api)
|
||||
implementation(projects.features.analytics.api)
|
||||
implementation(projects.features.ftue.api)
|
||||
|
|
@ -71,6 +72,7 @@ dependencies {
|
|||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.libraries.mediapickers.test)
|
||||
testImplementation(projects.libraries.mediaupload.test)
|
||||
testImplementation(projects.libraries.permissions.test)
|
||||
testImplementation(projects.libraries.preferences.test)
|
||||
testImplementation(projects.libraries.pushstore.test)
|
||||
testImplementation(projects.features.rageshake.test)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
|
|||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -49,8 +51,11 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
|||
private val matrixClient: MatrixClient,
|
||||
private val mediaPickerProvider: PickerProvider,
|
||||
private val mediaPreProcessor: MediaPreProcessor,
|
||||
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||
) : Presenter<EditUserProfileState> {
|
||||
|
||||
private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(matrixUser: MatrixUser): EditUserProfilePresenter
|
||||
|
|
@ -58,6 +63,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun present(): EditUserProfileState {
|
||||
val cameraPermissionState = cameraPermissionPresenter.present()
|
||||
var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.let { Uri.parse(it) }) }
|
||||
var userDisplayName by rememberSaveable { mutableStateOf(matrixUser.displayName) }
|
||||
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(
|
||||
|
|
@ -85,7 +91,11 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
|||
is EditUserProfileEvents.HandleAvatarAction -> {
|
||||
when (event.action) {
|
||||
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
|
||||
AvatarAction.TakePhoto -> cameraPhotoPicker.launch()
|
||||
AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
|
||||
cameraPhotoPicker.launch()
|
||||
} else {
|
||||
cameraPermissionState.eventSink.invoke(PermissionsEvents.OpenSystemDialog)
|
||||
}
|
||||
AvatarAction.Remove -> userAvatarUri = null
|
||||
}
|
||||
}
|
||||
|
|
@ -108,6 +118,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
|||
avatarActions = avatarActions,
|
||||
saveButtonEnabled = canSave && saveAction.value !is Async.Loading,
|
||||
saveAction = saveAction.value,
|
||||
cameraPermissionState = cameraPermissionState,
|
||||
eventSink = { handleEvents(it) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import android.net.Uri
|
|||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.permissions.api.PermissionsState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class EditUserProfileState(
|
||||
|
|
@ -29,5 +30,6 @@ data class EditUserProfileState(
|
|||
val avatarActions: ImmutableList<AvatarAction>,
|
||||
val saveButtonEnabled: Boolean,
|
||||
val saveAction: Async<Unit>,
|
||||
val cameraPermissionState: PermissionsState,
|
||||
val eventSink: (EditUserProfileEvents) -> Unit
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.preferences.impl.user.editprofile
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.permissions.api.aPermissionsState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
open class EditUserProfileStateProvider : PreviewParameterProvider<EditUserProfileState> {
|
||||
|
|
@ -36,5 +37,6 @@ fun aEditUserProfileState() = EditUserProfileState(
|
|||
avatarActions = persistentListOf(),
|
||||
saveAction = Async.Uninitialized,
|
||||
saveButtonEnabled = true,
|
||||
cameraPermissionState = aPermissionsState(showDialog = false),
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ import io.element.android.libraries.designsystem.theme.components.TextButton
|
|||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
|
||||
import io.element.android.libraries.matrix.ui.components.EditableAvatarView
|
||||
import io.element.android.libraries.permissions.api.PermissionsView
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -168,6 +169,7 @@ fun EditUserProfileView(
|
|||
else -> Unit
|
||||
}
|
||||
}
|
||||
PermissionsView(state = state.cameraPermissionState)
|
||||
}
|
||||
|
||||
private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier =
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
|
|||
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||
import io.mockk.every
|
||||
|
|
@ -78,12 +80,18 @@ class EditUserProfilePresenterTest {
|
|||
private fun createEditUserProfilePresenter(
|
||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||
matrixUser: MatrixUser = aMatrixUser(),
|
||||
permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(),
|
||||
): EditUserProfilePresenter {
|
||||
return EditUserProfilePresenter(
|
||||
matrixClient = matrixClient,
|
||||
matrixUser = matrixUser,
|
||||
mediaPickerProvider = fakePickerProvider,
|
||||
mediaPreProcessor = fakeMediaPreProcessor,
|
||||
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
||||
override fun create(permission: String): PermissionsPresenter {
|
||||
return permissionsPresenter
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -157,13 +165,21 @@ class EditUserProfilePresenterTest {
|
|||
fun `present - obtains avatar uris from camera`() = runTest {
|
||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||
val presenter = createEditUserProfilePresenter(matrixUser = user)
|
||||
val fakePermissionsPresenter = FakePermissionsPresenter()
|
||||
val presenter = createEditUserProfilePresenter(
|
||||
matrixUser = user,
|
||||
permissionsPresenter = fakePermissionsPresenter,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.userAvatarUrl).isEqualTo(userAvatarUri)
|
||||
initialState.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
assertThat(initialState.cameraPermissionState.permissionGranted).isFalse()
|
||||
fakePermissionsPresenter.setPermissionGranted()
|
||||
val stateWithPermission = awaitItem()
|
||||
assertThat(stateWithPermission.cameraPermissionState.permissionGranted).isTrue()
|
||||
stateWithPermission.eventSink(EditUserProfileEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
awaitItem().apply {
|
||||
assertThat(userAvatarUrl).isEqualTo(anotherAvatarUri)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ dependencies {
|
|||
implementation(projects.libraries.mediapickers.api)
|
||||
implementation(projects.libraries.mediaupload.api)
|
||||
implementation(projects.libraries.featureflag.api)
|
||||
implementation(projects.libraries.permissions.api)
|
||||
api(projects.features.roomdetails.api)
|
||||
api(projects.libraries.usersearch.api)
|
||||
api(projects.services.apperror.api)
|
||||
|
|
@ -59,6 +60,7 @@ dependencies {
|
|||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.mediaupload.test)
|
||||
testImplementation(projects.libraries.mediapickers.test)
|
||||
testImplementation(projects.libraries.permissions.test)
|
||||
testImplementation(projects.libraries.usersearch.test)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
|
|||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -49,10 +51,14 @@ class RoomDetailsEditPresenter @Inject constructor(
|
|||
private val room: MatrixRoom,
|
||||
private val mediaPickerProvider: PickerProvider,
|
||||
private val mediaPreProcessor: MediaPreProcessor,
|
||||
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||
) : Presenter<RoomDetailsEditState> {
|
||||
|
||||
private val cameraPermissionPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
|
||||
|
||||
@Composable
|
||||
override fun present(): RoomDetailsEditState {
|
||||
val cameraPermissionState = cameraPermissionPresenter.present()
|
||||
val roomSyncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
|
||||
// Since there is no way to obtain the new avatar uri after uploading a new avatar,
|
||||
|
|
@ -110,7 +116,11 @@ class RoomDetailsEditPresenter @Inject constructor(
|
|||
is RoomDetailsEditEvents.HandleAvatarAction -> {
|
||||
when (event.action) {
|
||||
AvatarAction.ChoosePhoto -> galleryImagePicker.launch()
|
||||
AvatarAction.TakePhoto -> cameraPhotoPicker.launch()
|
||||
AvatarAction.TakePhoto -> if (cameraPermissionState.permissionGranted) {
|
||||
cameraPhotoPicker.launch()
|
||||
} else {
|
||||
cameraPermissionState.eventSink(PermissionsEvents.OpenSystemDialog)
|
||||
}
|
||||
AvatarAction.Remove -> roomAvatarUri = null
|
||||
}
|
||||
}
|
||||
|
|
@ -132,6 +142,7 @@ class RoomDetailsEditPresenter @Inject constructor(
|
|||
avatarActions = avatarActions,
|
||||
saveButtonEnabled = saveButtonEnabled,
|
||||
saveAction = saveAction.value,
|
||||
cameraPermissionState = cameraPermissionState,
|
||||
eventSink = ::handleEvents,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@
|
|||
package io.element.android.features.roomdetails.impl.edit
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.permissions.api.PermissionsState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class RoomDetailsEditState(
|
||||
|
|
@ -32,5 +33,6 @@ data class RoomDetailsEditState(
|
|||
val avatarActions: ImmutableList<AvatarAction>,
|
||||
val saveButtonEnabled: Boolean,
|
||||
val saveAction: Async<Unit>,
|
||||
val cameraPermissionState: PermissionsState,
|
||||
val eventSink: (RoomDetailsEditEvents) -> Unit
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.roomdetails.impl.edit
|
|||
import android.net.Uri
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.permissions.api.aPermissionsState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
open class RoomDetailsEditStateProvider : PreviewParameterProvider<RoomDetailsEditState> {
|
||||
|
|
@ -45,5 +46,6 @@ fun aRoomDetailsEditState() = RoomDetailsEditState(
|
|||
avatarActions = persistentListOf(),
|
||||
saveButtonEnabled = true,
|
||||
saveAction = Async.Uninitialized,
|
||||
cameraPermissionState = aPermissionsState(showDialog = false),
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ import io.element.android.libraries.designsystem.theme.components.TextButton
|
|||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet
|
||||
import io.element.android.libraries.matrix.ui.components.EditableAvatarView
|
||||
import io.element.android.libraries.permissions.api.PermissionsView
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -193,6 +194,8 @@ fun RoomDetailsEditView(
|
|||
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
PermissionsView(state = state.cameraPermissionState)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
|
|||
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
|
@ -74,11 +76,19 @@ class RoomDetailsEditPresenterTest {
|
|||
unmockkAll()
|
||||
}
|
||||
|
||||
private fun aRoomDetailsEditPresenter(room: MatrixRoom): RoomDetailsEditPresenter {
|
||||
private fun aRoomDetailsEditPresenter(
|
||||
room: MatrixRoom,
|
||||
permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(),
|
||||
): RoomDetailsEditPresenter {
|
||||
return RoomDetailsEditPresenter(
|
||||
room = room,
|
||||
mediaPickerProvider = fakePickerProvider,
|
||||
mediaPreProcessor = fakeMediaPreProcessor,
|
||||
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
||||
override fun create(permission: String): PermissionsPresenter {
|
||||
return permissionsPresenter
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -252,16 +262,22 @@ class RoomDetailsEditPresenterTest {
|
|||
val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL)
|
||||
|
||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||
|
||||
val presenter = aRoomDetailsEditPresenter(room)
|
||||
val fakePermissionsPresenter = FakePermissionsPresenter()
|
||||
val presenter = aRoomDetailsEditPresenter(
|
||||
room = room,
|
||||
permissionsPresenter = fakePermissionsPresenter,
|
||||
)
|
||||
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri)
|
||||
|
||||
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
assertThat(initialState.cameraPermissionState.permissionGranted).isFalse()
|
||||
fakePermissionsPresenter.setPermissionGranted()
|
||||
val stateWithPermission = awaitItem()
|
||||
assertThat(stateWithPermission.cameraPermissionState.permissionGranted).isTrue()
|
||||
stateWithPermission.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto))
|
||||
awaitItem().apply {
|
||||
assertThat(roomAvatarUrl).isEqualTo(anotherAvatarUri)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,17 +22,19 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
open class PermissionsViewStateProvider : PreviewParameterProvider<PermissionsState> {
|
||||
override val values: Sequence<PermissionsState>
|
||||
get() = sequenceOf(
|
||||
aPermissionsState(),
|
||||
aPermissionsState().copy(shouldShowRationale = true),
|
||||
aPermissionsState().copy(permissionAlreadyDenied = true),
|
||||
aPermissionsState(showDialog = true),
|
||||
aPermissionsState(showDialog = true).copy(shouldShowRationale = true),
|
||||
aPermissionsState(showDialog = true).copy(permissionAlreadyDenied = true),
|
||||
)
|
||||
}
|
||||
|
||||
fun aPermissionsState() = PermissionsState(
|
||||
fun aPermissionsState(
|
||||
showDialog: Boolean,
|
||||
) = PermissionsState(
|
||||
permission = Manifest.permission.INTERNET,
|
||||
permissionGranted = false,
|
||||
shouldShowRationale = false,
|
||||
showDialog = true,
|
||||
showDialog = showDialog,
|
||||
permissionAlreadyAsked = false,
|
||||
permissionAlreadyDenied = false,
|
||||
eventSink = {}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import io.element.android.libraries.permissions.api.PermissionsState
|
|||
import io.element.android.libraries.permissions.api.aPermissionsState
|
||||
|
||||
class FakePermissionsPresenter(
|
||||
private val initialState: PermissionsState = aPermissionsState().copy(showDialog = false),
|
||||
private val initialState: PermissionsState = aPermissionsState(showDialog = false),
|
||||
) : PermissionsPresenter {
|
||||
|
||||
private fun eventSink(events: PermissionsEvents) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue