registerForActivityResult has to be called at Fragment creation

This commit is contained in:
Benoit Marty 2020-10-07 11:52:29 +02:00 committed by Benoit Marty
parent a7f034f500
commit 05950ec1c8
5 changed files with 101 additions and 91 deletions

View File

@ -22,6 +22,7 @@ import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
@ -95,6 +96,12 @@ fun logPermissionStatuses(context: Context) {
} }
} }
fun Fragment.registerForPermissionsResult(allGranted: (Boolean) -> Unit): ActivityResultLauncher<Array<String>> {
return registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result ->
allGranted.invoke(result.keys.all { result[it] == true })
}
}
/** /**
* See [.checkPermissions] * See [.checkPermissions]
* *
@ -106,21 +113,21 @@ fun checkPermissions(permissionsToBeGrantedBitMap: Int,
activity: Activity, activity: Activity,
requestCode: Int, requestCode: Int,
@StringRes rationaleMessage: Int = 0): Boolean { @StringRes rationaleMessage: Int = 0): Boolean {
return checkPermissions(permissionsToBeGrantedBitMap, activity, null, null, requestCode, rationaleMessage) return checkPermissions(permissionsToBeGrantedBitMap, activity, null, requestCode, rationaleMessage)
} }
/** /**
* See [.checkPermissions] * See [.checkPermissions]
* *
* @param permissionsToBeGrantedBitMap * @param permissionsToBeGrantedBitMap
* @param fragment * @param activityResultLauncher from the calling fragment that is requesting the permissions
* @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow) * @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow)
*/ */
fun checkPermissions(permissionsToBeGrantedBitMap: Int, fun checkPermissions(permissionsToBeGrantedBitMap: Int,
fragment: Fragment, activity: Activity,
@StringRes rationaleMessage: Int = 0, activityResultLauncher: ActivityResultLauncher<Array<String>>,
allGranted: (Boolean) -> Unit): Boolean { @StringRes rationaleMessage: Int = 0): Boolean {
return checkPermissions(permissionsToBeGrantedBitMap, fragment.activity, fragment, allGranted, 0, rationaleMessage) return checkPermissions(permissionsToBeGrantedBitMap, activity, activityResultLauncher, 0, rationaleMessage)
} }
/** /**
@ -138,23 +145,19 @@ fun checkPermissions(permissionsToBeGrantedBitMap: Int,
* *
* @param permissionsToBeGrantedBitMap the permissions bit map to be granted * @param permissionsToBeGrantedBitMap the permissions bit map to be granted
* @param activity the calling Activity that is requesting the permissions (or fragment parent) * @param activity the calling Activity that is requesting the permissions (or fragment parent)
* @param fragment the calling fragment that is requesting the permissions * @param activityResultLauncher from the calling fragment that is requesting the permissions
* @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow) * @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow)
*/ */
private fun checkPermissions(permissionsToBeGrantedBitMap: Int, private fun checkPermissions(permissionsToBeGrantedBitMap: Int,
activity: Activity?, activity: Activity,
fragment: Fragment?, activityResultLauncher: ActivityResultLauncher<Array<String>>?,
allGranted: ((Boolean) -> Unit)?,
requestCode: Int, requestCode: Int,
@StringRes rationaleMessage: Int @StringRes rationaleMessage: Int
): Boolean { ): Boolean {
var isPermissionGranted = false var isPermissionGranted = false
// sanity check // sanity check
if (null == activity) { if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) {
Timber.w("## checkPermissions(): invalid input data")
isPermissionGranted = false
} else if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) {
isPermissionGranted = true isPermissionGranted = true
} else if (PERMISSIONS_FOR_AUDIO_IP_CALL != permissionsToBeGrantedBitMap } else if (PERMISSIONS_FOR_AUDIO_IP_CALL != permissionsToBeGrantedBitMap
&& PERMISSIONS_FOR_VIDEO_IP_CALL != permissionsToBeGrantedBitMap && PERMISSIONS_FOR_VIDEO_IP_CALL != permissionsToBeGrantedBitMap
@ -224,9 +227,7 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int,
.setOnCancelListener { Toast.makeText(activity, R.string.missing_permissions_warning, Toast.LENGTH_SHORT).show() } .setOnCancelListener { Toast.makeText(activity, R.string.missing_permissions_warning, Toast.LENGTH_SHORT).show() }
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
if (permissionsListToBeGranted.isNotEmpty()) { if (permissionsListToBeGranted.isNotEmpty()) {
fragment?.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> activityResultLauncher
allGranted?.invoke(result.keys.all { result[it] == true })
}
?.launch(permissionsListToBeGranted.toTypedArray()) ?.launch(permissionsListToBeGranted.toTypedArray())
?: run { ?: run {
ActivityCompat.requestPermissions(activity, permissionsListToBeGranted.toTypedArray(), requestCode) ActivityCompat.requestPermissions(activity, permissionsListToBeGranted.toTypedArray(), requestCode)
@ -267,9 +268,7 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int,
.show() .show()
*/ */
} else { } else {
fragment?.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> activityResultLauncher
allGranted?.invoke(result.keys.all { result[it] == true })
}
?.launch(permissionsArrayToBeGranted) ?.launch(permissionsArrayToBeGranted)
?: run { ?: run {
ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode) ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode)

View File

@ -28,6 +28,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import im.vector.app.features.qrcode.QrCodeScannerActivity import im.vector.app.features.qrcode.QrCodeScannerActivity
@ -73,12 +74,14 @@ class VerificationChooseMethodFragment @Inject constructor(
state.pendingRequest.invoke()?.transactionId ?: "")) state.pendingRequest.invoke()?.transactionId ?: ""))
} }
override fun openCamera() { private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this) { allGranted ->
if (allGranted) { if (allGranted) {
doOpenQRCodeScanner() doOpenQRCodeScanner()
} }
}) { }
override fun openCamera() {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) {
doOpenQRCodeScanner() doOpenQRCodeScanner()
} }
} }

View File

@ -99,6 +99,7 @@ import im.vector.app.core.utils.createJSonViewerStyleProvider
import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.createUIHandler
import im.vector.app.core.utils.isValidUrl import im.vector.app.core.utils.isValidUrl
import im.vector.app.core.utils.openUrlInExternalBrowser import im.vector.app.core.utils.openUrlInExternalBrowser
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.saveMedia import im.vector.app.core.utils.saveMedia
import im.vector.app.core.utils.shareMedia import im.vector.app.core.utils.shareMedia
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
@ -782,40 +783,35 @@ class RoomDetailFragment @Inject constructor(
} }
} }
private val startCallActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (allGranted) {
(roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let {
roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(it)
}
} else {
context?.toast(R.string.permissions_action_not_performed_missing_permissions)
cleanUpAfterPermissionNotGranted()
}
}
private fun safeStartCall2(isVideoCall: Boolean) { private fun safeStartCall2(isVideoCall: Boolean) {
val startCallAction = RoomDetailAction.StartCall(isVideoCall) val startCallAction = RoomDetailAction.StartCall(isVideoCall)
roomDetailViewModel.pendingAction = startCallAction roomDetailViewModel.pendingAction = startCallAction
if (isVideoCall) { if (isVideoCall) {
if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL, if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL,
this, requireActivity(),
R.string.permissions_rationale_msg_camera_and_audio) { allGranted -> startCallActivityResultLauncher,
if (allGranted) { R.string.permissions_rationale_msg_camera_and_audio)) {
(roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let {
roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(it)
}
} else {
context?.toast(R.string.permissions_action_not_performed_missing_permissions)
cleanUpAfterPermissionNotGranted()
}
}) {
roomDetailViewModel.pendingAction = null roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(startCallAction) roomDetailViewModel.handle(startCallAction)
} }
} else { } else {
if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL, if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL,
this, requireActivity(),
R.string.permissions_rationale_msg_record_audio) { allGranted -> startCallActivityResultLauncher,
if (allGranted) { R.string.permissions_rationale_msg_record_audio)) {
(roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let {
roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(it)
}
} else {
context?.toast(R.string.permissions_action_not_performed_missing_permissions)
cleanUpAfterPermissionNotGranted()
}
}) {
roomDetailViewModel.pendingAction = null roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(startCallAction) roomDetailViewModel.handle(startCallAction)
} }
@ -1005,6 +1001,18 @@ class RoomDetailFragment @Inject constructor(
} }
} }
private val writingFileActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (allGranted) {
val pendingUri = roomDetailViewModel.pendingUri
if (pendingUri != null) {
roomDetailViewModel.pendingUri = null
sendUri(pendingUri)
}
} else {
cleanUpAfterPermissionNotGranted()
}
}
private fun setupComposer() { private fun setupComposer() {
autoCompleter.setup(composerLayout.composerEditText) autoCompleter.setup(composerLayout.composerEditText)
@ -1037,17 +1045,7 @@ class RoomDetailFragment @Inject constructor(
override fun onRichContentSelected(contentUri: Uri): Boolean { override fun onRichContentSelected(contentUri: Uri): Boolean {
// We need WRITE_EXTERNAL permission // We need WRITE_EXTERNAL permission
return if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this@RoomDetailFragment) { allGranted -> return if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), writingFileActivityResultLauncher)) {
if (allGranted) {
val pendingUri = roomDetailViewModel.pendingUri
if (pendingUri != null) {
roomDetailViewModel.pendingUri = null
sendUri(pendingUri)
}
} else {
cleanUpAfterPermissionNotGranted()
}
}) {
sendUri(contentUri) sendUri(contentUri)
} else { } else {
roomDetailViewModel.pendingUri = contentUri roomDetailViewModel.pendingUri = contentUri
@ -1556,9 +1554,7 @@ class RoomDetailFragment @Inject constructor(
) )
} }
private fun onSaveActionClicked(action: EventSharedAction.Save) { private val saveActionActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
&& !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this) { allGranted ->
if (allGranted) { if (allGranted) {
sharedActionViewModel.pendingAction?.let { sharedActionViewModel.pendingAction?.let {
handleActions(it) handleActions(it)
@ -1567,7 +1563,11 @@ class RoomDetailFragment @Inject constructor(
} else { } else {
cleanUpAfterPermissionNotGranted() cleanUpAfterPermissionNotGranted()
} }
}) { }
private fun onSaveActionClicked(action: EventSharedAction.Save) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
&& !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) {
sharedActionViewModel.pendingAction = action sharedActionViewModel.pendingAction = action
return return
} }
@ -1805,8 +1805,7 @@ class RoomDetailFragment @Inject constructor(
// AttachmentTypeSelectorView.Callback // AttachmentTypeSelectorView.Callback
override fun onTypeSelected(type: AttachmentTypeSelectorView.Type) { private val typeSelectedActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (checkPermissions(type.permissionsBit, this) { allGranted ->
if (allGranted) { if (allGranted) {
val pendingType = attachmentsHelper.pendingType val pendingType = attachmentsHelper.pendingType
if (pendingType != null) { if (pendingType != null) {
@ -1816,7 +1815,10 @@ class RoomDetailFragment @Inject constructor(
} else { } else {
cleanUpAfterPermissionNotGranted() cleanUpAfterPermissionNotGranted()
} }
}) { }
override fun onTypeSelected(type: AttachmentTypeSelectorView.Type) {
if (checkPermissions(type.permissionsBit, requireActivity(), typeSelectedActivityResultLauncher)) {
launchAttachmentProcess(type) launchAttachmentProcess(type)
} else { } else {
attachmentsHelper.pendingType = type attachmentsHelper.pendingType = type

View File

@ -48,6 +48,7 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.features.crypto.util.toImageRes import im.vector.app.features.crypto.util.toImageRes
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
@ -283,14 +284,16 @@ class RoomProfileFragment @Inject constructor(
.show() .show()
} }
private var avatarCameraUri: Uri? = null private val takePhotoActivityResultLauncher = registerForPermissionsResult { allGranted ->
private fun onAvatarTypeSelected(isCamera: Boolean) {
if (isCamera) {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this) { allGranted ->
if (allGranted) { if (allGranted) {
onAvatarTypeSelected(true) onAvatarTypeSelected(true)
} }
}) { }
private var avatarCameraUri: Uri? = null
private fun onAvatarTypeSelected(isCamera: Boolean) {
if (isCamera) {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), takePhotoActivityResultLauncher)) {
avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this)
} }
} else { } else {

View File

@ -51,6 +51,7 @@ import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.TextUtils import im.vector.app.core.utils.TextUtils
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.getSizeOfFiles import im.vector.app.core.utils.getSizeOfFiles
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.features.MainActivity import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs import im.vector.app.features.MainActivityArgs
@ -390,13 +391,15 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
}.show() }.show()
} }
private fun onAvatarTypeSelected(isCamera: Boolean) { private val takePhotoActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (isCamera) {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this) { allGranted ->
if (allGranted) { if (allGranted) {
onAvatarTypeSelected(true) onAvatarTypeSelected(true)
} }
}) { }
private fun onAvatarTypeSelected(isCamera: Boolean) {
if (isCamera) {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), takePhotoActivityResultLauncher)) {
avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this)
} }
} else { } else {