From fa51b5aaaba3423c4f7625dca9cc4db54878dad4 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Sun, 12 Mar 2023 23:57:31 +0100 Subject: [PATCH 01/33] Update the target & compile SDK to version 33 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 64a8e37b..bbc922c4 100644 --- a/build.gradle +++ b/build.gradle @@ -26,8 +26,8 @@ subprojects { ext { minSdkVersion = 24 - compileSdkVersion = 31 - targetSdkVersion = 31 + compileSdkVersion = 33 + targetSdkVersion = 33 buildToolsVersion = "30.0.3" daggerVersion = gradle.ext.daggerVersion From 5b72e65341320e83195a40c1c106893110e4476f Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Sun, 12 Mar 2023 23:58:46 +0100 Subject: [PATCH 02/33] Update gradle file --- mediapicker/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediapicker/build.gradle b/mediapicker/build.gradle index ffec6e9d..15d40bad 100644 --- a/mediapicker/build.gradle +++ b/mediapicker/build.gradle @@ -29,7 +29,7 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = JavaVersion.VERSION_1_8 } lintOptions { From 42737135463f27b090e17f6dc93f5991fae0fcb4 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Sun, 12 Mar 2023 23:59:04 +0100 Subject: [PATCH 03/33] Add new media permissions --- mediapicker/src/main/AndroidManifest.xml | 3 +++ .../main/java/org/wordpress/android/mediapicker/Permissions.kt | 3 +++ sampleapp/src/main/AndroidManifest.xml | 1 + 3 files changed, 7 insertions(+) diff --git a/mediapicker/src/main/AndroidManifest.xml b/mediapicker/src/main/AndroidManifest.xml index 266b6bfb..0f9b7c86 100644 --- a/mediapicker/src/main/AndroidManifest.xml +++ b/mediapicker/src/main/AndroidManifest.xml @@ -3,6 +3,9 @@ package="org.wordpress.android.mediapicker"> + + + diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/Permissions.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/Permissions.kt index 6ad61fc5..5d93ecba 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/Permissions.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/Permissions.kt @@ -23,6 +23,9 @@ internal class Permissions @Inject constructor( companion object { val PERMISSION_STORAGE_READ = booleanPreferencesKey("PERMISSION_STORAGE_READ") val PERMISSION_CAMERA = booleanPreferencesKey("PERMISSION_CAMERA") + val PERMISSION_IMAGES_READ = booleanPreferencesKey("PERMISSION_IMAGES_READ") + val PERMISSION_VIDEO_READ = booleanPreferencesKey("PERMISSION_VIDEO_READ") + val PERMISSION_AUDIO_READ = booleanPreferencesKey("PERMISSION_AUDIO_READ") } private val Context.dataStore: DataStore by preferencesDataStore("permissions") diff --git a/sampleapp/src/main/AndroidManifest.xml b/sampleapp/src/main/AndroidManifest.xml index 64486e21..ce8a80a0 100644 --- a/sampleapp/src/main/AndroidManifest.xml +++ b/sampleapp/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + Date: Sun, 12 Mar 2023 23:59:53 +0100 Subject: [PATCH 04/33] Update the string resources --- .../android/mediapicker/util/ImageUtils.kt | 4 ++-- .../res/layout/media_picker_lib_fragment.xml | 2 +- mediapicker/src/main/res/values/strings.xml | 24 ++++++++----------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/ImageUtils.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/ImageUtils.kt index 3e2d8841..3a8a1b2f 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/ImageUtils.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/ImageUtils.kt @@ -24,11 +24,11 @@ fun ImageView.setImageResourceWithTint( fun ImageView.announceSelectedImageForAccessibility(itemSelected: Boolean) { if (itemSelected) { announceForAccessibility( - context.getString(R.string.photo_picker_image_thumbnail_selected) + context.getString(R.string.media_picker_item_thumbnail_selected) ) } else { announceForAccessibility( - context.getString(R.string.photo_picker_image_thumbnail_unselected) + context.getString(R.string.media_picker_item_thumbnail_unselected) ) } } diff --git a/mediapicker/src/main/res/layout/media_picker_lib_fragment.xml b/mediapicker/src/main/res/layout/media_picker_lib_fragment.xml index 7350cb36..5836697b 100644 --- a/mediapicker/src/main/res/layout/media_picker_lib_fragment.xml +++ b/mediapicker/src/main/res/layout/media_picker_lib_fragment.xml @@ -77,7 +77,7 @@ android:visibility="gone" app:aevButton="@string/photo_picker_soft_ask_allow" app:aevImage="@drawable/media_picker_lib_missing_permission_image" - app:aevTitle="@string/photo_picker_soft_ask_label" + app:aevTitle="@string/media_picker_soft_ask_label_media_files" tools:visibility="visible" /> diff --git a/mediapicker/src/main/res/values/strings.xml b/mediapicker/src/main/res/values/strings.xml index 3eee7218..beb89256 100644 --- a/mediapicker/src/main/res/values/strings.xml +++ b/mediapicker/src/main/res/values/strings.xml @@ -1,35 +1,31 @@ - - SD Card Required - A mounted SD card is required to upload media - ,Selected - Image selected - Image unselected + + Item selected + Item unselected You don\'t have any media Use this photo Use this video Use this audio Use this media - Denied access to your photos. To fix this, edit your permissions and turn on %1$s. - We need permission to access your images + Denied access to your media files. To fix this, edit your permissions and turn on %1$s. + We need permission to access your media files + We need permission to access your %1$s We need permission to access your camera - We need permission to access your camera & files + We need permission to access your camera & media files Edit permissions Allow Storage Files and media Camera Microphone + Images + Video + Audio Unknown Add 0 - A network error occurred. Please check your connection and try again. Retry Loading… - Search free photo library - Photo library - Preview Add %d - Cancel 4:3 Image Thumbnail Play video From d718cc33ffac8905014dc5e867b0030ce8e71e62 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 13 Mar 2023 00:01:12 +0100 Subject: [PATCH 05/33] Add methods to request the new media permissions --- .../mediapicker/api/MediaPickerSetup.kt | 13 +++++ .../android/mediapicker/MediaPickerTracker.kt | 11 ++++ .../mediapicker/model/MediaNavigationEvent.kt | 4 ++ .../mediapicker/model/UiStateModels.kt | 54 ++++++++++++++++++- .../util/MediaPickerPermissionUtils.kt | 34 +++++++++++- 5 files changed, 113 insertions(+), 3 deletions(-) diff --git a/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/api/MediaPickerSetup.kt b/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/api/MediaPickerSetup.kt index 9a58a161..c3d8564b 100644 --- a/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/api/MediaPickerSetup.kt +++ b/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/api/MediaPickerSetup.kt @@ -1,9 +1,13 @@ package org.wordpress.android.mediapicker.api import android.content.Intent +import android.os.Build import android.os.Bundle import androidx.annotation.StringRes import org.wordpress.android.mediapicker.model.MediaType +import org.wordpress.android.mediapicker.model.MediaType.AUDIO +import org.wordpress.android.mediapicker.model.MediaType.IMAGE +import org.wordpress.android.mediapicker.model.MediaType.VIDEO data class MediaPickerSetup( val primaryDataSource: DataSource, @@ -23,6 +27,15 @@ data class MediaPickerSetup( HIDDEN, VISIBLE_TOGGLED, VISIBLE_UNTOGGLED } + val isImagesPermissionRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + allowedTypes.contains(IMAGE) + + val isVideoPermissionRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + allowedTypes.contains(VIDEO) + + val isAudioPermissionRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + allowedTypes.contains(AUDIO) + fun toBundle(bundle: Bundle) { bundle.putInt(KEY_PRIMARY_DATA_SOURCE, primaryDataSource.ordinal) bundle.putIntegerArrayList(KEY_AVAILABLE_DATA_SOURCES, ArrayList(availableDataSources.map { it.ordinal })) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/MediaPickerTracker.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/MediaPickerTracker.kt index adf6225f..bd4e696b 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/MediaPickerTracker.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/MediaPickerTracker.kt @@ -124,6 +124,17 @@ internal class MediaPickerTracker @Inject constructor( tracker.track(MEDIA_PICKER_SHOW_PERMISSIONS_SCREEN, properties) } + fun trackShowMultiplePermissionsScreen( + mediaPickerSetup: MediaPickerSetup, + permissions: List, + isAlwaysDenied: Boolean + ) { + val properties = mediaPickerSetup.toProperties() + properties["always_denied"] = isAlwaysDenied + properties["permission_requested"] = permissions.joinToString { it.name } + tracker.track(MEDIA_PICKER_SHOW_PERMISSIONS_SCREEN, properties) + } + fun trackItemSelected(mediaPickerSetup: MediaPickerSetup) { tracker.track(MEDIA_PICKER_ITEM_SELECTED, mediaPickerSetup.toProperties()) } diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/MediaNavigationEvent.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/MediaNavigationEvent.kt index 956c955b..2213cf76 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/MediaNavigationEvent.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/MediaNavigationEvent.kt @@ -2,6 +2,7 @@ package org.wordpress.android.mediapicker.model import android.net.Uri import org.wordpress.android.mediapicker.model.MediaItem.Identifier +import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested internal sealed class MediaNavigationEvent { data class PreviewUrl(val url: String) : MediaNavigationEvent() @@ -15,4 +16,7 @@ internal sealed class MediaNavigationEvent { object ShowAppSettings : MediaNavigationEvent() object RequestStoragePermission : MediaNavigationEvent() object RequestCameraPermission : MediaNavigationEvent() + data class RequestMediaPermissions( + val permissions: List + ) : MediaNavigationEvent() } diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/UiStateModels.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/UiStateModels.kt index 92c30c1b..887f0251 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/UiStateModels.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/UiStateModels.kt @@ -1,8 +1,14 @@ package org.wordpress.android.mediapicker.model +import android.Manifest +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcelable import kotlinx.parcelize.Parcelize import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource +import org.wordpress.android.mediapicker.model.MediaType.AUDIO +import org.wordpress.android.mediapicker.model.MediaType.IMAGE +import org.wordpress.android.mediapicker.model.MediaType.VIDEO import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested.STORAGE import org.wordpress.android.mediapicker.model.UiString.UiStringRes @@ -66,11 +72,55 @@ internal class UiStateModels { @Parcelize data class SoftAskRequest( val show: Boolean, - val type: PermissionsRequested = STORAGE, + val types: List = emptyList(), val isAlwaysDenied: Boolean = false, ) : Parcelable enum class PermissionsRequested { - CAMERA, STORAGE + CAMERA, STORAGE, IMAGES, VIDEO, AUDIO; + + companion object { + fun fromString(permission: String): PermissionsRequested { + return when (permission) { + Manifest.permission.CAMERA -> CAMERA + Manifest.permission.READ_EXTERNAL_STORAGE -> STORAGE + Manifest.permission.READ_MEDIA_IMAGES -> IMAGES + Manifest.permission.READ_MEDIA_VIDEO -> VIDEO + Manifest.permission.READ_MEDIA_AUDIO -> AUDIO + else -> throw UnsupportedOperationException("Unsupported permission: $permission") + } + } + + fun fromMediaType(type: MediaType): PermissionsRequested { + return when (type) { + IMAGE -> IMAGES + MediaType.VIDEO -> VIDEO + MediaType.AUDIO -> AUDIO + else -> throw UnsupportedOperationException("Unsupported media type: $type") + } + } + } + + override fun toString(): String { + return when (this) { + CAMERA -> Manifest.permission.CAMERA + STORAGE -> Manifest.permission.READ_EXTERNAL_STORAGE + IMAGES -> if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + Manifest.permission.READ_MEDIA_IMAGES + } else { + throw UnsupportedOperationException("Unsupported permission: $this") + } + VIDEO -> if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + Manifest.permission.READ_MEDIA_VIDEO + } else { + throw UnsupportedOperationException("Unsupported permission: $this") + } + AUDIO -> if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + Manifest.permission.READ_MEDIA_AUDIO + } else { + throw UnsupportedOperationException("Unsupported permission: $this") + } + } + } } } diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt index 178ff3cb..aab49ae5 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt @@ -8,7 +8,9 @@ import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build +import android.os.Build.VERSION_CODES import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS +import androidx.annotation.RequiresApi import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import dagger.hilt.android.qualifiers.ApplicationContext @@ -69,6 +71,30 @@ internal class MediaPickerPermissionUtils @Inject constructor( ) == PackageManager.PERMISSION_GRANTED } + @RequiresApi(VERSION_CODES.TIRAMISU) + fun hasImagesPermission(): Boolean { + return ContextCompat.checkSelfPermission( + context, + permission.READ_MEDIA_IMAGES + ) == PackageManager.PERMISSION_GRANTED + } + + @RequiresApi(VERSION_CODES.TIRAMISU) + fun hasAudioPermission(): Boolean { + return ContextCompat.checkSelfPermission( + context, + permission.READ_MEDIA_AUDIO + ) == PackageManager.PERMISSION_GRANTED + } + + @RequiresApi(VERSION_CODES.TIRAMISU) + fun hasVideoPermission(): Boolean { + return ContextCompat.checkSelfPermission( + context, + permission.READ_MEDIA_VIDEO + ) == PackageManager.PERMISSION_GRANTED + } + /* * returns true if we know the app has asked for the passed permission */ @@ -133,6 +159,9 @@ internal class MediaPickerPermissionUtils @Inject constructor( return when (permission) { Manifest.permission.READ_EXTERNAL_STORAGE -> Permissions.PERMISSION_STORAGE_READ Manifest.permission.CAMERA -> Permissions.PERMISSION_CAMERA + Manifest.permission.READ_MEDIA_IMAGES -> Permissions.PERMISSION_IMAGES_READ + Manifest.permission.READ_MEDIA_VIDEO -> Permissions.PERMISSION_VIDEO_READ + Manifest.permission.READ_MEDIA_AUDIO -> Permissions.PERMISSION_AUDIO_READ else -> { log.w("No key for requested permission") null @@ -147,13 +176,16 @@ internal class MediaPickerPermissionUtils @Inject constructor( return when (permission) { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE -> context.getString( - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) + if (Build.VERSION.SDK_INT > VERSION_CODES.Q) string.permission_files_and_media else string.permission_storage ) Manifest.permission.CAMERA -> context.getString(string.permission_camera) Manifest.permission.RECORD_AUDIO -> context.getString(string.permission_microphone) + Manifest.permission.READ_MEDIA_IMAGES -> context.getString(string.permission_images) + Manifest.permission.READ_MEDIA_VIDEO -> context.getString(string.permission_video) + Manifest.permission.READ_MEDIA_AUDIO -> context.getString(string.permission_audio) else -> { log.w("No name for requested permission") context.getString(string.unknown) From a516a32dbfbd47a85ea6ec7440b005c67ac65f72 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 13 Mar 2023 00:01:26 +0100 Subject: [PATCH 06/33] Update the VM logic and UI --- .../mediapicker/ui/MediaPickerFragment.kt | 200 +++++++++++------- .../viewmodel/MediaPickerViewModel.kt | 155 ++++++++++++-- 2 files changed, 256 insertions(+), 99 deletions(-) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt index 2ece7da3..8b1c0a39 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt @@ -4,6 +4,8 @@ import android.Manifest.permission.CAMERA import android.Manifest.permission.READ_EXTERNAL_STORAGE import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.app.Activity +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater @@ -16,6 +18,7 @@ import android.view.ViewGroup import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult +import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.SearchView import androidx.core.text.HtmlCompat @@ -41,6 +44,7 @@ import org.wordpress.android.mediapicker.model.MediaNavigationEvent.ChooseMediaP import org.wordpress.android.mediapicker.model.MediaNavigationEvent.Exit import org.wordpress.android.mediapicker.model.MediaNavigationEvent.PreviewUrl import org.wordpress.android.mediapicker.model.MediaNavigationEvent.RequestCameraPermission +import org.wordpress.android.mediapicker.model.MediaNavigationEvent.RequestMediaPermissions import org.wordpress.android.mediapicker.model.MediaNavigationEvent.RequestStoragePermission import org.wordpress.android.mediapicker.model.MediaNavigationEvent.ReturnCapturedImage import org.wordpress.android.mediapicker.model.MediaNavigationEvent.ReturnSelectedMedia @@ -53,6 +57,7 @@ import org.wordpress.android.mediapicker.model.MediaPickerUiItem import org.wordpress.android.mediapicker.model.MediaUri import org.wordpress.android.mediapicker.model.UiStateModels.ActionModeUiModel import org.wordpress.android.mediapicker.model.UiStateModels.FabUiModel +import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested import org.wordpress.android.mediapicker.model.UiStateModels.PhotoListUiModel import org.wordpress.android.mediapicker.model.UiStateModels.PhotoListUiModel.Data import org.wordpress.android.mediapicker.model.UiStateModels.PhotoListUiModel.Empty @@ -120,6 +125,16 @@ internal class MediaPickerFragment : Fragment() { } } + @RequiresApi(VERSION_CODES.TIRAMISU) + private val mediaPermissionRequest = registerForActivityResult( + RequestMultiplePermissions() + ) { permissions -> + lifecycleScope.launch { + permissionUtils.persistPermissionRequestResults(permissions) + checkMediaPermissions(permissions.keys.map { PermissionsRequested.fromString(it) }) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -201,71 +216,68 @@ internal class MediaPickerFragment : Fragment() { } var isShowingActionMode = false - viewModel.uiState.observe( - viewLifecycleOwner, - { - it?.let { uiState -> - setupPhotoList(uiState.photoListUiModel) - setupSoftAskView(uiState.softAskViewUiModel) - if (uiState.actionModeUiModel is ActionModeUiModel.Visible && !isShowingActionMode) { - isShowingActionMode = true - (activity as AppCompatActivity).startSupportActionMode( - MediaPickerActionModeCallback( - viewModel - ) + viewModel.uiState.observe(viewLifecycleOwner) { + it?.let { uiState -> + setupPhotoList(uiState.photoListUiModel) + setupSoftAskView(uiState.softAskViewUiModel) + if (uiState.actionModeUiModel is ActionModeUiModel.Visible && !isShowingActionMode) { + isShowingActionMode = true + (activity as AppCompatActivity).startSupportActionMode( + MediaPickerActionModeCallback( + viewModel ) - } else if (uiState.actionModeUiModel is ActionModeUiModel.Hidden && isShowingActionMode) { - isShowingActionMode = false - } - setupFab(uiState.fabUiModel) - pullToRefresh.isRefreshing = uiState.isRefreshing + ) + } else if (uiState.actionModeUiModel is ActionModeUiModel.Hidden && isShowingActionMode) { + isShowingActionMode = false } + setupFab(uiState.fabUiModel) + pullToRefresh.isRefreshing = uiState.isRefreshing } - ) + } - viewModel.onNavigate.observeEvent( - viewLifecycleOwner, - { navigationEvent -> - when (navigationEvent) { - is PreviewUrl -> { - MediaViewerFragment.previewUrl( - requireActivity(), - navigationEvent.url - ) - } - is ReturnSelectedMedia -> { - val resultIntent = ResultIntentHelper.getSelectedMediaResultIntent( - navigationEvent.identifiers, - mediaPickerSetup.primaryDataSource - ) - requireActivity().apply { - setResult(Activity.RESULT_OK, resultIntent) - finish() - } + viewModel.onNavigate.observeEvent(viewLifecycleOwner) { navigationEvent -> + when (navigationEvent) { + is PreviewUrl -> { + MediaViewerFragment.previewUrl( + requireActivity(), + navigationEvent.url + ) + } + is ReturnSelectedMedia -> { + val resultIntent = ResultIntentHelper.getSelectedMediaResultIntent( + navigationEvent.identifiers, + mediaPickerSetup.primaryDataSource + ) + requireActivity().apply { + setResult(Activity.RESULT_OK, resultIntent) + finish() } - is ReturnCapturedImage -> { - val resultIntent = ResultIntentHelper.getCapturedImageResultIntent( - navigationEvent.areResultsQueued, - navigationEvent.capturedImageUri - ) - requireActivity().apply { - setResult(Activity.RESULT_OK, resultIntent) - finish() - } + } + is ReturnCapturedImage -> { + val resultIntent = ResultIntentHelper.getCapturedImageResultIntent( + navigationEvent.areResultsQueued, + navigationEvent.capturedImageUri + ) + requireActivity().apply { + setResult(Activity.RESULT_OK, resultIntent) + finish() } - is ChooseMediaPickerAction -> onActionSelected(navigationEvent.action) - Exit -> { - requireActivity().apply { - setResult(Activity.RESULT_CANCELED) - finish() - } + } + is ChooseMediaPickerAction -> onActionSelected(navigationEvent.action) + Exit -> { + requireActivity().apply { + setResult(Activity.RESULT_CANCELED) + finish() } - RequestCameraPermission -> requestCameraPermissions() - RequestStoragePermission -> requestStoragePermission() - ShowAppSettings -> permissionUtils.showAppSettings(requireActivity()) } + RequestCameraPermission -> requestCameraPermissions() + RequestStoragePermission -> requestStoragePermission() + is RequestMediaPermissions -> if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + requestMediaPermissions(navigationEvent.permissions) + } + ShowAppSettings -> permissionUtils.showAppSettings(requireActivity()) } - ) + } (requireActivity() as AppCompatActivity).supportActionBar ?.setTitle(mediaPickerSetup.title) @@ -316,28 +328,25 @@ internal class MediaPickerFragment : Fragment() { } initializeSearchView(searchMenuItem) - viewModel.uiState.observe( - viewLifecycleOwner, - { uiState -> - val searchView = searchMenuItem.actionView as SearchView - - if (uiState.searchUiModel is SearchUiModel.Expanded && !searchMenuItem.isActionViewExpanded) { - searchMenuItem.expandActionView() - searchView.maxWidth = Integer.MAX_VALUE - searchView.setQuery(uiState.searchUiModel.filter, true) - searchView.setOnCloseListener { !uiState.searchUiModel.closeable } - } else if (uiState.searchUiModel is SearchUiModel.Collapsed && searchMenuItem.isActionViewExpanded) { - searchMenuItem.collapseActionView() - } + viewModel.uiState.observe(viewLifecycleOwner) { uiState -> + val searchView = searchMenuItem.actionView as SearchView + + if (uiState.searchUiModel is SearchUiModel.Expanded && !searchMenuItem.isActionViewExpanded) { + searchMenuItem.expandActionView() + searchView.maxWidth = Integer.MAX_VALUE + searchView.setQuery(uiState.searchUiModel.filter, true) + searchView.setOnCloseListener { !uiState.searchUiModel.closeable } + } else if (uiState.searchUiModel is SearchUiModel.Collapsed && searchMenuItem.isActionViewExpanded) { + searchMenuItem.collapseActionView() + } - searchMenuItem.isVisible = uiState.searchUiModel !is SearchUiModel.Hidden + searchMenuItem.isVisible = uiState.searchUiModel !is SearchUiModel.Hidden - val shownActions = uiState.browseMenuUiModel.shownActions - browseMenuItem.isVisible = shownActions.contains(SYSTEM_PICKER) - deviceMenuItem.isVisible = shownActions.contains(DEVICE) - tenorLibraryMenuItem.isVisible = shownActions.contains(GIF_LIBRARY) - } - ) + val shownActions = uiState.browseMenuUiModel.shownActions + browseMenuItem.isVisible = shownActions.contains(SYSTEM_PICKER) + deviceMenuItem.isVisible = shownActions.contains(DEVICE) + tenorLibraryMenuItem.isVisible = shownActions.contains(GIF_LIBRARY) + } } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -358,13 +367,13 @@ internal class MediaPickerFragment : Fragment() { private fun initializeSearchView(actionMenuItem: MenuItem) { var isExpanding = false actionMenuItem.setOnActionExpandListener(object : OnActionExpandListener { - override fun onMenuItemActionExpand(item: MenuItem?): Boolean { + override fun onMenuItemActionExpand(item: MenuItem): Boolean { viewModel.onSearchExpanded() isExpanding = true return true } - override fun onMenuItemActionCollapse(item: MenuItem?): Boolean { + override fun onMenuItemActionCollapse(item: MenuItem): Boolean { viewModel.onSearchCollapsed() return true } @@ -526,7 +535,11 @@ internal class MediaPickerFragment : Fragment() { if (mediaPickerSetup.primaryDataSource == DataSource.CAMERA) { checkCameraPermissions() } else { - checkStoragePermission() + if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + (mediaPickerSetup.allowedTypes) + } else { + checkStoragePermission() + } } } } @@ -539,10 +552,17 @@ internal class MediaPickerFragment : Fragment() { return permissionUtils.isPermissionAlwaysDenied(requireActivity(), CAMERA) } + @RequiresApi(VERSION_CODES.TIRAMISU) + private suspend fun isMediaPermissionAlwaysDenied(permissions: List): Boolean { + return permissions.any { permission -> + permissionUtils.isPermissionAlwaysDenied(requireActivity(), permission.toString()) + } + } + /* - * load the photos if we have the necessary permission, otherwise show the "soft ask" view - * which asks the user to allow the permission - */ + * load the photos if we have the necessary permission, otherwise show the "soft ask" view + * which asks the user to allow the permission + */ private suspend fun checkStoragePermission() { if (!isAdded) { return @@ -560,6 +580,17 @@ internal class MediaPickerFragment : Fragment() { ) } + @RequiresApi(VERSION_CODES.TIRAMISU) + private suspend fun checkMediaPermissions(permissions: List) { + if (!isAdded) { + return + } + viewModel.checkMediaPermissions( + permissions = permissions, + isAlwaysDenied = isMediaPermissionAlwaysDenied(permissions) + ) + } + private fun requestStoragePermission() { storagePermissionRequest.launch(arrayOf(READ_EXTERNAL_STORAGE)) } @@ -572,4 +603,9 @@ internal class MediaPickerFragment : Fragment() { } cameraPermissionRequest.launch(permissions) } + + @RequiresApi(VERSION_CODES.TIRAMISU) + private fun requestMediaPermissions(permissions: List) { + mediaPermissionRequest.launch(permissions.map { it.toString() }.toTypedArray()) + } } diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt index 6106e580..710db9d3 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt @@ -1,6 +1,8 @@ package org.wordpress.android.mediapicker.viewmodel import android.Manifest.permission +import android.os.Build.VERSION_CODES +import androidx.annotation.RequiresApi import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle @@ -37,6 +39,7 @@ import org.wordpress.android.mediapicker.model.MediaNavigationEvent.ChooseMediaP import org.wordpress.android.mediapicker.model.MediaNavigationEvent.Exit import org.wordpress.android.mediapicker.model.MediaNavigationEvent.PreviewUrl import org.wordpress.android.mediapicker.model.MediaNavigationEvent.RequestCameraPermission +import org.wordpress.android.mediapicker.model.MediaNavigationEvent.RequestMediaPermissions import org.wordpress.android.mediapicker.model.MediaNavigationEvent.RequestStoragePermission import org.wordpress.android.mediapicker.model.MediaNavigationEvent.ReturnCapturedImage import org.wordpress.android.mediapicker.model.MediaNavigationEvent.ReturnSelectedMedia @@ -66,6 +69,7 @@ import org.wordpress.android.mediapicker.model.UiStateModels.FabUiModel import org.wordpress.android.mediapicker.model.UiStateModels.MediaPickerUiState import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested.CAMERA +import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested.IMAGES import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested.STORAGE import org.wordpress.android.mediapicker.model.UiStateModels.PhotoListUiModel import org.wordpress.android.mediapicker.model.UiStateModels.PhotoListUiModel.Data @@ -550,7 +554,10 @@ internal class MediaPickerViewModel @Inject constructor( if (permissionsHandler.hasReadStoragePermission()) { hideSoftRequest(STORAGE) } else { - showSoftRequest(permission = STORAGE, isAlwaysDenied = isAlwaysDenied) + showSoftRequest( + permissions = listOf(STORAGE), + isAlwaysDenied = isAlwaysDenied + ) } } @@ -559,7 +566,10 @@ internal class MediaPickerViewModel @Inject constructor( mediaPickerSetup.isStoragePermissionRequired ) ) { - showSoftRequest(permission = CAMERA, isAlwaysDenied = isCameraPermissionAlwaysDenied) + showSoftRequest( + permissions = listOf(CAMERA), + isAlwaysDenied = isCameraPermissionAlwaysDenied + ) } else { _domainModel.value = _domainModel.value?.copy( domainItems = emptyList(), @@ -574,18 +584,47 @@ internal class MediaPickerViewModel @Inject constructor( } } + @RequiresApi(VERSION_CODES.TIRAMISU) + fun checkMediaPermissions(permissions: List, isAlwaysDenied: Boolean) { + if (!mediaPickerSetup.isImagesPermissionRequired || + !mediaPickerSetup.isVideoPermissionRequired || + !mediaPickerSetup.isAudioPermissionRequired + ) { + return + } + + val haveAllPermissions = permissions.all { + when (it) { + IMAGES -> permissionsHandler.hasImagesPermission() + PermissionsRequested.VIDEO -> permissionsHandler.hasVideoPermission() + PermissionsRequested.AUDIO -> permissionsHandler.hasAudioPermission() + else -> throw UnsupportedOperationException("Unsupported permission: $it") + } + } + + if (haveAllPermissions) { + hideSoftRequest(IMAGES) + return + } else { + showSoftRequest( + permissions = permissions, + isAlwaysDenied = isAlwaysDenied + ) + } + } + private fun hideSoftRequest(permission: PermissionsRequested) { _softAskRequest.value = SoftAskRequest(show = false) - if (permission == STORAGE && _domainModel.value?.domainItems.isNullOrEmpty()) { + if (permission != CAMERA && _domainModel.value?.domainItems.isNullOrEmpty()) { refreshData(false) } } - private fun showSoftRequest(permission: PermissionsRequested, isAlwaysDenied: Boolean) { + private fun showSoftRequest(permissions: List, isAlwaysDenied: Boolean) { _softAskRequest.value = SoftAskRequest( show = true, - type = permission, + types = permissions, isAlwaysDenied = isAlwaysDenied ) _domainModel.value = _domainModel.value?.copy(isLoading = false, emptyState = null) @@ -597,36 +636,43 @@ internal class MediaPickerViewModel @Inject constructor( private fun buildSoftAskView(softAskRequest: SoftAskRequest?): SoftAskViewUiModel { if (softAskRequest != null && softAskRequest.show) { + val type = softAskRequest.types.first() mediaPickerTracker.trackShowPermissionsScreen( mediaPickerSetup, - softAskRequest.type, + type, softAskRequest.isAlwaysDenied ) val alsoStorageAccess = mediaPickerSetup.isStoragePermissionRequired val label = if (softAskRequest.isAlwaysDenied) { - val storage = permissionsHandler.getPermissionName(permission.READ_EXTERNAL_STORAGE) val camera = permissionsHandler.getPermissionName(permission.CAMERA) - val permission = ( + val storage = permissionsHandler.getPermissionName(permission.READ_EXTERNAL_STORAGE) + val permission = "${ - when (softAskRequest.type) { - STORAGE -> storage - CAMERA -> camera + if (alsoStorageAccess) " & $storage" else "" - } + when (type) { + STORAGE -> storage + CAMERA -> camera + if (alsoStorageAccess) " & $storage" else "" + else -> throw UnsupportedOperationException( + "Unknown permission type: $type" + ) + } }" - ) String.format( resourceProvider.getString(string.media_picker_soft_ask_permissions_denied), permission ) } else { - when (softAskRequest.type) { - STORAGE -> resourceProvider.getString(string.photo_picker_soft_ask_label) - CAMERA -> + when (type) { + STORAGE -> resourceProvider.getString(string.media_picker_soft_ask_label_media_files) + CAMERA -> { if (alsoStorageAccess) { resourceProvider.getString(string.camera_and_files_soft_ask_label) } else { resourceProvider.getString(string.camera_soft_ask_label) } + } + else -> throw UnsupportedOperationException( + "Unknown permission type: $type" + ) } } val allowId = if (softAskRequest.isAlwaysDenied) { @@ -639,7 +685,70 @@ internal class MediaPickerViewModel @Inject constructor( UiStringRes(allowId), softAskRequest.isAlwaysDenied, onClick = { - onPermissionRequested(softAskRequest.type, softAskRequest.isAlwaysDenied) + onPermissionRequested(type, softAskRequest.isAlwaysDenied) + } + ) + } else { + return SoftAskViewUiModel.Hidden + } + } + + @RequiresApi(VERSION_CODES.TIRAMISU) + private fun buildMediaSoftAskRequest(softAskRequest: SoftAskRequest?): SoftAskViewUiModel { + if (softAskRequest != null && softAskRequest.show) { + if (softAskRequest.types.size > 1) { + mediaPickerTracker.trackShowMultiplePermissionsScreen( + mediaPickerSetup, + softAskRequest.types, + softAskRequest.isAlwaysDenied + ) + } else { + mediaPickerTracker.trackShowPermissionsScreen( + mediaPickerSetup, + softAskRequest.types.first(), + softAskRequest.isAlwaysDenied + ) + } + val camera = permissionsHandler.getPermissionName(permission.CAMERA) + val images = permissionsHandler.getPermissionName(permission.READ_MEDIA_VIDEO) + val video = permissionsHandler.getPermissionName(permission.READ_MEDIA_VIDEO) + val audio = permissionsHandler.getPermissionName(permission.READ_MEDIA_AUDIO) + val permissions = + "${ + softAskRequest.types.joinToString { type -> + when (type) { + IMAGES -> images + PermissionsRequested.VIDEO -> video + PermissionsRequested.AUDIO -> audio + CAMERA -> camera + else -> throw UnsupportedOperationException( + "Unknown permission type: $type" + ) + } + } + }" + val label = if (softAskRequest.isAlwaysDenied) { + String.format( + resourceProvider.getString(string.media_picker_soft_ask_permissions_denied), + permissions + ) + } else { + resourceProvider.getString(string.media_picker_soft_ask_label_multiple, permissions) + } + val allowId = if (softAskRequest.isAlwaysDenied) { + string.button_edit_permissions + } else { + string.photo_picker_soft_ask_allow + } + return SoftAskViewUiModel.Visible( + label, + UiStringRes(allowId), + softAskRequest.isAlwaysDenied, + onClick = { + onMultiplePermissionsRequested( + permissions = softAskRequest.types, + isAlwaysDenied = softAskRequest.isAlwaysDenied + ) } ) } else { @@ -716,6 +825,7 @@ internal class MediaPickerViewModel @Inject constructor( val navigationEvent = when (permission) { CAMERA -> RequestCameraPermission STORAGE -> RequestStoragePermission + else -> throw UnsupportedOperationException("Invalid permission request: $permission") } if (isAlwaysDenied) { _onNavigate.postValue(Event(ShowAppSettings)) @@ -723,4 +833,15 @@ internal class MediaPickerViewModel @Inject constructor( _onNavigate.postValue(Event(navigationEvent)) } } + + private fun onMultiplePermissionsRequested( + permissions: List, + isAlwaysDenied: Boolean + ) { + if (isAlwaysDenied) { + _onNavigate.postValue(Event(ShowAppSettings)) + } else { + _onNavigate.postValue(Event(RequestMediaPermissions(permissions))) + } + } } From 14142133b7e295307abdd077bf5574bd6038bbae Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 13 Mar 2023 00:15:30 +0100 Subject: [PATCH 07/33] Fix the permission request logic --- .../mediapicker/ui/MediaPickerFragment.kt | 7 ++++++- .../viewmodel/MediaPickerViewModel.kt | 21 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt index 8b1c0a39..6d33966a 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt @@ -58,6 +58,7 @@ import org.wordpress.android.mediapicker.model.MediaUri import org.wordpress.android.mediapicker.model.UiStateModels.ActionModeUiModel import org.wordpress.android.mediapicker.model.UiStateModels.FabUiModel import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested +import org.wordpress.android.mediapicker.model.UiStateModels.PermissionsRequested.Companion import org.wordpress.android.mediapicker.model.UiStateModels.PhotoListUiModel import org.wordpress.android.mediapicker.model.UiStateModels.PhotoListUiModel.Data import org.wordpress.android.mediapicker.model.UiStateModels.PhotoListUiModel.Empty @@ -536,7 +537,11 @@ internal class MediaPickerFragment : Fragment() { checkCameraPermissions() } else { if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { - (mediaPickerSetup.allowedTypes) + checkMediaPermissions( + mediaPickerSetup.allowedTypes.map { + PermissionsRequested.fromMediaType(it) + } + ) } else { checkStoragePermission() } diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt index 710db9d3..c8a74856 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt @@ -1,6 +1,7 @@ package org.wordpress.android.mediapicker.viewmodel import android.Manifest.permission +import android.os.Build.VERSION import android.os.Build.VERSION_CODES import androidx.annotation.RequiresApi import androidx.lifecycle.LiveData @@ -586,8 +587,8 @@ internal class MediaPickerViewModel @Inject constructor( @RequiresApi(VERSION_CODES.TIRAMISU) fun checkMediaPermissions(permissions: List, isAlwaysDenied: Boolean) { - if (!mediaPickerSetup.isImagesPermissionRequired || - !mediaPickerSetup.isVideoPermissionRequired || + if (!mediaPickerSetup.isImagesPermissionRequired && + !mediaPickerSetup.isVideoPermissionRequired && !mediaPickerSetup.isAudioPermissionRequired ) { return @@ -635,6 +636,22 @@ internal class MediaPickerViewModel @Inject constructor( } private fun buildSoftAskView(softAskRequest: SoftAskRequest?): SoftAskViewUiModel { + return if (softAskRequest?.types?.contains(IMAGES) == true || + softAskRequest?.types?.contains(PermissionsRequested.VIDEO) == true || + softAskRequest?.types?.contains(PermissionsRequested.AUDIO) == true) { + if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + buildMediaSoftAskRequest(softAskRequest) + } else { + throw UnsupportedOperationException( + "Unsupported permissions for SDK < 33: ${softAskRequest.types.joinToString()}" + ) + } + } else { + buildOldSoftAskView(softAskRequest) + } + } + + private fun buildOldSoftAskView(softAskRequest: SoftAskRequest?): SoftAskViewUiModel { if (softAskRequest != null && softAskRequest.show) { val type = softAskRequest.types.first() mediaPickerTracker.trackShowPermissionsScreen( From e316ee891d72f0cd48bca53a7dcffc6755e386a8 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 13 Mar 2023 00:18:04 +0100 Subject: [PATCH 08/33] Fix the wrong permission being requested --- .../android/mediapicker/viewmodel/MediaPickerViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt index c8a74856..3637ef7b 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt @@ -727,7 +727,7 @@ internal class MediaPickerViewModel @Inject constructor( ) } val camera = permissionsHandler.getPermissionName(permission.CAMERA) - val images = permissionsHandler.getPermissionName(permission.READ_MEDIA_VIDEO) + val images = permissionsHandler.getPermissionName(permission.READ_MEDIA_IMAGES) val video = permissionsHandler.getPermissionName(permission.READ_MEDIA_VIDEO) val audio = permissionsHandler.getPermissionName(permission.READ_MEDIA_AUDIO) val permissions = From e3424131a03b57c5435ae22dd56c345d03dd2700 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 13 Mar 2023 11:37:10 +0100 Subject: [PATCH 09/33] Use a single permission to list photos & videos, since that's what it's in the settings --- .../mediapicker/util/MediaPickerPermissionUtils.kt | 4 ++-- .../android/mediapicker/viewmodel/MediaPickerViewModel.kt | 8 ++++---- mediapicker/src/main/res/values/strings.xml | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt index aab49ae5..ac740920 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt @@ -183,8 +183,8 @@ internal class MediaPickerPermissionUtils @Inject constructor( ) Manifest.permission.CAMERA -> context.getString(string.permission_camera) Manifest.permission.RECORD_AUDIO -> context.getString(string.permission_microphone) - Manifest.permission.READ_MEDIA_IMAGES -> context.getString(string.permission_images) - Manifest.permission.READ_MEDIA_VIDEO -> context.getString(string.permission_video) + Manifest.permission.READ_MEDIA_IMAGES -> context.getString(string.permission_photos_videos) + Manifest.permission.READ_MEDIA_VIDEO -> context.getString(string.permission_photos_videos) Manifest.permission.READ_MEDIA_AUDIO -> context.getString(string.permission_audio) else -> { log.w("No name for requested permission") diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt index 3637ef7b..7875455d 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt @@ -731,8 +731,8 @@ internal class MediaPickerViewModel @Inject constructor( val video = permissionsHandler.getPermissionName(permission.READ_MEDIA_VIDEO) val audio = permissionsHandler.getPermissionName(permission.READ_MEDIA_AUDIO) val permissions = - "${ - softAskRequest.types.joinToString { type -> + softAskRequest.types.map { type -> + "${ when (type) { IMAGES -> images PermissionsRequested.VIDEO -> video @@ -742,8 +742,8 @@ internal class MediaPickerViewModel @Inject constructor( "Unknown permission type: $type" ) } - } - }" + }" + }.distinct().joinToString(" & ") val label = if (softAskRequest.isAlwaysDenied) { String.format( resourceProvider.getString(string.media_picker_soft_ask_permissions_denied), diff --git a/mediapicker/src/main/res/values/strings.xml b/mediapicker/src/main/res/values/strings.xml index beb89256..f962654e 100644 --- a/mediapicker/src/main/res/values/strings.xml +++ b/mediapicker/src/main/res/values/strings.xml @@ -18,9 +18,8 @@ Files and media Camera Microphone - Images - Video - Audio + Photos and videos + Music and audio Unknown Add 0 Retry From d417adf566ddc1ad52d411ca000e837c0a0a25a9 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 13 Mar 2023 14:54:34 +0100 Subject: [PATCH 10/33] Refactor the storage permission requirement property --- .../wordpress/android/mediapicker/api/MediaPickerSetup.kt | 5 ++++- .../mediapicker/source/device/DeviceMediaPickerSetup.kt | 6 +++--- .../android/mediapicker/source/gif/GifMediaPickerSetup.kt | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/api/MediaPickerSetup.kt b/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/api/MediaPickerSetup.kt index c3d8564b..2db48f0a 100644 --- a/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/api/MediaPickerSetup.kt +++ b/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/api/MediaPickerSetup.kt @@ -13,7 +13,7 @@ data class MediaPickerSetup( val primaryDataSource: DataSource, val availableDataSources: Set, val isMultiSelectEnabled: Boolean, - val isStoragePermissionRequired: Boolean, + val needsAccessToStorage: Boolean, val allowedTypes: Set, val areResultsQueued: Boolean, val searchMode: SearchMode, @@ -27,6 +27,9 @@ data class MediaPickerSetup( HIDDEN, VISIBLE_TOGGLED, VISIBLE_UNTOGGLED } + val isStoragePermissionRequired = Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && + needsAccessToStorage + val isImagesPermissionRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && allowedTypes.contains(IMAGE) diff --git a/mediapicker/source-device/src/main/java/org/wordpress/android/mediapicker/source/device/DeviceMediaPickerSetup.kt b/mediapicker/source-device/src/main/java/org/wordpress/android/mediapicker/source/device/DeviceMediaPickerSetup.kt index a153b18e..1a3017d3 100644 --- a/mediapicker/source-device/src/main/java/org/wordpress/android/mediapicker/source/device/DeviceMediaPickerSetup.kt +++ b/mediapicker/source-device/src/main/java/org/wordpress/android/mediapicker/source/device/DeviceMediaPickerSetup.kt @@ -17,7 +17,7 @@ class DeviceMediaPickerSetup { primaryDataSource = DEVICE, availableDataSources = setOf(SYSTEM_PICKER), isMultiSelectEnabled = canMultiSelect, - isStoragePermissionRequired = true, + needsAccessToStorage = true, allowedTypes = mediaTypes.allowedTypes, areResultsQueued = false, searchMode = VISIBLE_UNTOGGLED, @@ -30,7 +30,7 @@ class DeviceMediaPickerSetup { primaryDataSource = SYSTEM_PICKER, availableDataSources = emptySet(), isMultiSelectEnabled = canMultiSelect, - isStoragePermissionRequired = false, + needsAccessToStorage = false, allowedTypes = mediaTypes.allowedTypes, areResultsQueued = false, searchMode = HIDDEN @@ -44,7 +44,7 @@ class DeviceMediaPickerSetup { primaryDataSource = CAMERA, availableDataSources = emptySet(), isMultiSelectEnabled = false, - isStoragePermissionRequired = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q, + needsAccessToStorage = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q, allowedTypes = setOf(IMAGE), areResultsQueued = false, searchMode = HIDDEN diff --git a/mediapicker/source-gif/src/main/java/org/wordpress/android/mediapicker/source/gif/GifMediaPickerSetup.kt b/mediapicker/source-gif/src/main/java/org/wordpress/android/mediapicker/source/gif/GifMediaPickerSetup.kt index 9bc35a2b..d02fac01 100644 --- a/mediapicker/source-gif/src/main/java/org/wordpress/android/mediapicker/source/gif/GifMediaPickerSetup.kt +++ b/mediapicker/source-gif/src/main/java/org/wordpress/android/mediapicker/source/gif/GifMediaPickerSetup.kt @@ -12,7 +12,7 @@ class GifMediaPickerSetup { primaryDataSource = GIF_LIBRARY, availableDataSources = emptySet(), isMultiSelectEnabled = canMultiSelect, - isStoragePermissionRequired = false, + needsAccessToStorage = false, allowedTypes = setOf(IMAGE), areResultsQueued = false, searchMode = VISIBLE_TOGGLED, From dd66adc8fb93d07a7d9c875666d083a5ed1cf956 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 13 Mar 2023 14:55:19 +0100 Subject: [PATCH 11/33] Clean up the string resources --- .../android/mediapicker/viewmodel/MediaPickerViewModel.kt | 2 +- mediapicker/src/main/res/values/strings.xml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt index 7875455d..48dfc500 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt @@ -750,7 +750,7 @@ internal class MediaPickerViewModel @Inject constructor( permissions ) } else { - resourceProvider.getString(string.media_picker_soft_ask_label_multiple, permissions) + resourceProvider.getString(string.media_picker_soft_ask_label_media_files) } val allowId = if (softAskRequest.isAlwaysDenied) { string.button_edit_permissions diff --git a/mediapicker/src/main/res/values/strings.xml b/mediapicker/src/main/res/values/strings.xml index f962654e..acdd34cf 100644 --- a/mediapicker/src/main/res/values/strings.xml +++ b/mediapicker/src/main/res/values/strings.xml @@ -7,9 +7,8 @@ Use this video Use this audio Use this media - Denied access to your media files. To fix this, edit your permissions and turn on %1$s. + Denied access to your media files. To fix this, edit your permissions and enable %1$s. We need permission to access your media files - We need permission to access your %1$s We need permission to access your camera We need permission to access your camera & media files Edit permissions From ef28bf899c91296c13b2e4fac065e622734ee90c Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 13 Mar 2023 14:55:50 +0100 Subject: [PATCH 12/33] Remove the unnecessary API requirement annotation --- .../android/mediapicker/ui/MediaPickerFragment.kt | 8 +------- .../mediapicker/util/MediaPickerPermissionUtils.kt | 9 +++------ .../mediapicker/viewmodel/MediaPickerViewModel.kt | 12 ++++++++++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt index 6d33966a..87bb654b 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/ui/MediaPickerFragment.kt @@ -126,7 +126,6 @@ internal class MediaPickerFragment : Fragment() { } } - @RequiresApi(VERSION_CODES.TIRAMISU) private val mediaPermissionRequest = registerForActivityResult( RequestMultiplePermissions() ) { permissions -> @@ -273,9 +272,7 @@ internal class MediaPickerFragment : Fragment() { } RequestCameraPermission -> requestCameraPermissions() RequestStoragePermission -> requestStoragePermission() - is RequestMediaPermissions -> if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { - requestMediaPermissions(navigationEvent.permissions) - } + is RequestMediaPermissions -> requestMediaPermissions(navigationEvent.permissions) ShowAppSettings -> permissionUtils.showAppSettings(requireActivity()) } } @@ -557,7 +554,6 @@ internal class MediaPickerFragment : Fragment() { return permissionUtils.isPermissionAlwaysDenied(requireActivity(), CAMERA) } - @RequiresApi(VERSION_CODES.TIRAMISU) private suspend fun isMediaPermissionAlwaysDenied(permissions: List): Boolean { return permissions.any { permission -> permissionUtils.isPermissionAlwaysDenied(requireActivity(), permission.toString()) @@ -585,7 +581,6 @@ internal class MediaPickerFragment : Fragment() { ) } - @RequiresApi(VERSION_CODES.TIRAMISU) private suspend fun checkMediaPermissions(permissions: List) { if (!isAdded) { return @@ -609,7 +604,6 @@ internal class MediaPickerFragment : Fragment() { cameraPermissionRequest.launch(permissions) } - @RequiresApi(VERSION_CODES.TIRAMISU) private fun requestMediaPermissions(permissions: List) { mediaPermissionRequest.launch(permissions.map { it.toString() }.toTypedArray()) } diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt index ac740920..2ad56177 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/util/MediaPickerPermissionUtils.kt @@ -71,25 +71,22 @@ internal class MediaPickerPermissionUtils @Inject constructor( ) == PackageManager.PERMISSION_GRANTED } - @RequiresApi(VERSION_CODES.TIRAMISU) fun hasImagesPermission(): Boolean { - return ContextCompat.checkSelfPermission( + return Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission( context, permission.READ_MEDIA_IMAGES ) == PackageManager.PERMISSION_GRANTED } - @RequiresApi(VERSION_CODES.TIRAMISU) fun hasAudioPermission(): Boolean { - return ContextCompat.checkSelfPermission( + return Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission( context, permission.READ_MEDIA_AUDIO ) == PackageManager.PERMISSION_GRANTED } - @RequiresApi(VERSION_CODES.TIRAMISU) fun hasVideoPermission(): Boolean { - return ContextCompat.checkSelfPermission( + return Build.VERSION.SDK_INT >= VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission( context, permission.READ_MEDIA_VIDEO ) == PackageManager.PERMISSION_GRANTED diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt index 48dfc500..f0851bf9 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt @@ -348,7 +348,14 @@ internal class MediaPickerViewModel @Inject constructor( } private fun refreshData(forceReload: Boolean) { - if (!permissionsHandler.hasReadStoragePermission()) { + if ((mediaPickerSetup.isStoragePermissionRequired && + !permissionsHandler.hasReadStoragePermission()) || + (mediaPickerSetup.isImagesPermissionRequired && + !permissionsHandler.hasImagesPermission()) || + (mediaPickerSetup.isVideoPermissionRequired && + !permissionsHandler.hasVideoPermission()) || + (mediaPickerSetup.isAudioPermissionRequired && + !permissionsHandler.hasAudioPermission())) { return } viewModelScope.launch { @@ -585,7 +592,6 @@ internal class MediaPickerViewModel @Inject constructor( } } - @RequiresApi(VERSION_CODES.TIRAMISU) fun checkMediaPermissions(permissions: List, isAlwaysDenied: Boolean) { if (!mediaPickerSetup.isImagesPermissionRequired && !mediaPickerSetup.isVideoPermissionRequired && @@ -651,6 +657,7 @@ internal class MediaPickerViewModel @Inject constructor( } } + // Requests READ_EXTERNAL_STORAGE on devices running SDK < 33 private fun buildOldSoftAskView(softAskRequest: SoftAskRequest?): SoftAskViewUiModel { if (softAskRequest != null && softAskRequest.show) { val type = softAskRequest.types.first() @@ -710,6 +717,7 @@ internal class MediaPickerViewModel @Inject constructor( } } + // Requests granular media permissions on devices running SDK >= 33 @RequiresApi(VERSION_CODES.TIRAMISU) private fun buildMediaSoftAskRequest(softAskRequest: SoftAskRequest?): SoftAskViewUiModel { if (softAskRequest != null && softAskRequest.show) { From 53172736cd25347ab764344fc25ab6a28b1f4f7c Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Wed, 22 Mar 2023 18:39:39 +0100 Subject: [PATCH 13/33] Update the changed parameter name --- .../mediapicker/source/wordpress/MediaLibraryPickerSetup.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediapicker/source-wordpress/src/main/java/org/wordpress/android/mediapicker/source/wordpress/MediaLibraryPickerSetup.kt b/mediapicker/source-wordpress/src/main/java/org/wordpress/android/mediapicker/source/wordpress/MediaLibraryPickerSetup.kt index c610bfcd..2290c84f 100644 --- a/mediapicker/source-wordpress/src/main/java/org/wordpress/android/mediapicker/source/wordpress/MediaLibraryPickerSetup.kt +++ b/mediapicker/source-wordpress/src/main/java/org/wordpress/android/mediapicker/source/wordpress/MediaLibraryPickerSetup.kt @@ -12,7 +12,7 @@ class MediaLibraryPickerSetup { primaryDataSource = WP_MEDIA_LIBRARY, availableDataSources = emptySet(), isMultiSelectEnabled = canMultiSelect, - isStoragePermissionRequired = false, + needsAccessToStorage = false, allowedTypes = mediaTypes.allowedTypes, areResultsQueued = false, searchMode = HIDDEN, From dc072fb6ac31bbbb9ad462a040a36a6307daf989 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Fri, 24 Mar 2023 14:56:44 +0100 Subject: [PATCH 14/33] Suppress warnings --- .../org/wordpress/android/mediapicker/model/UiStateModels.kt | 1 + .../android/mediapicker/viewmodel/MediaPickerViewModel.kt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/UiStateModels.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/UiStateModels.kt index 887f0251..6e8aec90 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/UiStateModels.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/model/UiStateModels.kt @@ -101,6 +101,7 @@ internal class UiStateModels { } } + @Suppress("ExceptionRaisedInUnexpectedLocation") override fun toString(): String { return when (this) { CAMERA -> Manifest.permission.CAMERA diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt index 6ab394a3..a21aeca3 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt @@ -92,6 +92,7 @@ import org.wordpress.android.mediapicker.util.distinct import org.wordpress.android.mediapicker.util.merge import javax.inject.Inject +@Suppress("LargeClass") @HiltViewModel internal class MediaPickerViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle, @@ -350,6 +351,7 @@ internal class MediaPickerViewModel @Inject constructor( return ActionModeUiModel.Visible(title) } + @Suppress("ComplexCondition") private fun refreshData(forceReload: Boolean) { if ((mediaPickerSetup.isStoragePermissionRequired && !permissionsHandler.hasReadStoragePermission()) || @@ -662,6 +664,7 @@ internal class MediaPickerViewModel @Inject constructor( } // Requests READ_EXTERNAL_STORAGE on devices running SDK < 33 + @Suppress("NestedBlockDepth") private fun buildOldSoftAskView(softAskRequest: SoftAskRequest?): SoftAskViewUiModel { if (softAskRequest != null && softAskRequest.show) { val type = softAskRequest.types.first() @@ -723,6 +726,7 @@ internal class MediaPickerViewModel @Inject constructor( // Requests granular media permissions on devices running SDK >= 33 @RequiresApi(VERSION_CODES.TIRAMISU) + @Suppress("LongMethod") private fun buildMediaSoftAskRequest(softAskRequest: SoftAskRequest?): SoftAskViewUiModel { if (softAskRequest != null && softAskRequest.show) { if (softAskRequest.types.size > 1) { From 988363d494144819e0c261de9425f6f56a2d0f5c Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Tue, 28 Mar 2023 21:37:00 +0200 Subject: [PATCH 15/33] Add all media types to device media sample --- .../org/wordpress/android/mediapicker/model/MediaType.kt | 6 +++++- .../wordpress/android/sampleapp/MediaPickerSetupFactory.kt | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/model/MediaType.kt b/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/model/MediaType.kt index 1ef9d522..ce1a041b 100644 --- a/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/model/MediaType.kt +++ b/mediapicker/domain/src/main/java/org/wordpress/android/mediapicker/model/MediaType.kt @@ -1,5 +1,6 @@ package org.wordpress.android.mediapicker.model +import org.wordpress.android.mediapicker.model.MediaType.AUDIO import org.wordpress.android.mediapicker.model.MediaType.IMAGE import org.wordpress.android.mediapicker.model.MediaType.VIDEO @@ -8,5 +9,8 @@ enum class MediaType { } enum class MediaTypes(val allowedTypes: Set) { - IMAGES(setOf(IMAGE)), VIDEOS(setOf(VIDEO)), IMAGES_AND_VIDEOS(setOf(IMAGE, VIDEO)) + IMAGES(setOf(IMAGE)), + VIDEOS(setOf(VIDEO)), + IMAGES_AND_VIDEOS(setOf(IMAGE, VIDEO)), + EVERYTHING(setOf(IMAGE, VIDEO, AUDIO)) } diff --git a/sampleapp/src/main/java/org/wordpress/android/sampleapp/MediaPickerSetupFactory.kt b/sampleapp/src/main/java/org/wordpress/android/sampleapp/MediaPickerSetupFactory.kt index 4d197aa7..b244957c 100644 --- a/sampleapp/src/main/java/org/wordpress/android/sampleapp/MediaPickerSetupFactory.kt +++ b/sampleapp/src/main/java/org/wordpress/android/sampleapp/MediaPickerSetupFactory.kt @@ -6,6 +6,7 @@ import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.CAMERA import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.DEVICE import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.GIF_LIBRARY import org.wordpress.android.mediapicker.api.MediaPickerSetup.DataSource.SYSTEM_PICKER +import org.wordpress.android.mediapicker.model.MediaTypes.EVERYTHING import org.wordpress.android.mediapicker.model.MediaTypes.IMAGES import org.wordpress.android.mediapicker.source.device.DeviceMediaPickerSetup import org.wordpress.android.mediapicker.source.gif.GifMediaPickerSetup @@ -18,7 +19,7 @@ class MediaPickerSetupFactory @Inject constructor() : MediaPickerSetup.Factory { GIF_LIBRARY -> GifMediaPickerSetup.build(canMultiSelect = true) CAMERA -> DeviceMediaPickerSetup.buildCameraPicker() DEVICE -> DeviceMediaPickerSetup.buildMediaPicker( - mediaTypes = IMAGES, + mediaTypes = EVERYTHING, canMultiSelect = true ) SYSTEM_PICKER -> DeviceMediaPickerSetup.buildSystemPicker( From 9226f6ede10afbfac051318ea60df42efc77edeb Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Thu, 30 Mar 2023 18:04:05 +0200 Subject: [PATCH 16/33] Add maxSdkVersion to READ_EXTERNAL_STORAGE permission --- mediapicker/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediapicker/src/main/AndroidManifest.xml b/mediapicker/src/main/AndroidManifest.xml index 0f9b7c86..d5ecb69c 100644 --- a/mediapicker/src/main/AndroidManifest.xml +++ b/mediapicker/src/main/AndroidManifest.xml @@ -2,10 +2,10 @@ - + From 041bd0e2774e21f214144295d679085816eb9398 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Thu, 30 Mar 2023 18:05:00 +0200 Subject: [PATCH 17/33] Remove unnecessary permission --- sampleapp/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/sampleapp/src/main/AndroidManifest.xml b/sampleapp/src/main/AndroidManifest.xml index ce8a80a0..64486e21 100644 --- a/sampleapp/src/main/AndroidManifest.xml +++ b/sampleapp/src/main/AndroidManifest.xml @@ -2,7 +2,6 @@ - Date: Thu, 30 Mar 2023 18:11:59 +0200 Subject: [PATCH 18/33] Only check the necessary permissions --- .../viewmodel/MediaPickerViewModel.kt | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt index a21aeca3..fb17c87c 100644 --- a/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt +++ b/mediapicker/src/main/java/org/wordpress/android/mediapicker/viewmodel/MediaPickerViewModel.kt @@ -598,18 +598,20 @@ internal class MediaPickerViewModel @Inject constructor( } fun checkMediaPermissions(permissions: List, isAlwaysDenied: Boolean) { - if (!mediaPickerSetup.isImagesPermissionRequired && - !mediaPickerSetup.isVideoPermissionRequired && - !mediaPickerSetup.isAudioPermissionRequired - ) { - return - } - val haveAllPermissions = permissions.all { when (it) { - IMAGES -> permissionsHandler.hasImagesPermission() - PermissionsRequested.VIDEO -> permissionsHandler.hasVideoPermission() - PermissionsRequested.AUDIO -> permissionsHandler.hasAudioPermission() + IMAGES -> { + mediaPickerSetup.isImagesPermissionRequired && + permissionsHandler.hasImagesPermission() + } + PermissionsRequested.VIDEO -> { + mediaPickerSetup.isVideoPermissionRequired && + permissionsHandler.hasVideoPermission() + } + PermissionsRequested.AUDIO -> { + mediaPickerSetup.isAudioPermissionRequired && + permissionsHandler.hasAudioPermission() + } else -> throw UnsupportedOperationException("Unsupported permission: $it") } } From c8bbfb0842ccf98d744634e1cbbc113492cd8e07 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Tue, 4 Apr 2023 00:54:18 +0200 Subject: [PATCH 19/33] Fix a warning --- .../main/java/org/wordpress/android/sampleapp/MainActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sampleapp/src/main/java/org/wordpress/android/sampleapp/MainActivity.kt b/sampleapp/src/main/java/org/wordpress/android/sampleapp/MainActivity.kt index 0798e47e..1fa91e95 100644 --- a/sampleapp/src/main/java/org/wordpress/android/sampleapp/MainActivity.kt +++ b/sampleapp/src/main/java/org/wordpress/android/sampleapp/MainActivity.kt @@ -86,8 +86,8 @@ class MainActivity : AppCompatActivity() { private fun handleMediaPickerResult(result: ActivityResult) { if (result.resultCode == RESULT_OK) { result.data?.extras?.let { extra -> - val files = (extra.get(MediaPickerConstants.EXTRA_MEDIA_URIS) as? Array<*>) - ?.map { mediaPickerUtils.getFilePath(Uri.parse(it as String)) } + val files = (extra.getStringArray(MediaPickerConstants.EXTRA_MEDIA_URIS)) + ?.map { mediaPickerUtils.getFilePath(Uri.parse(it)) } ?.joinToString("\n") ?: "" Snackbar.make(findViewById