From 36a41e352d7071452f65dcd97c6281ecbe3c07aa Mon Sep 17 00:00:00 2001 From: Nguyen Hoang Lam Date: Sat, 26 Nov 2022 17:54:43 +0700 Subject: [PATCH] Fix permission error on API 33 --- README.md | 44 ++++++------ build.gradle | 6 +- example/build.gradle | 4 +- imagepicker/build.gradle | 16 +++-- imagepicker/src/main/AndroidManifest.xml | 11 ++- .../imagepicker/helper/Constants.kt | 4 +- .../imagepicker/helper/DeviceHelper.kt | 2 + .../imagepicker/helper/PermissionHelper.kt | 2 +- .../imagepicker/ui/camera/CameraActivity.kt | 13 ++-- .../ui/imagepicker/ImagePickerActivity.kt | 68 ++++++++++++------- imagepicker/src/main/res/values/strings.xml | 4 +- 11 files changed, 102 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 4c70b27..de8e661 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ Preview Changelog -------- -- Fix empty images bug. -- Upgrade some dependency versions: Kotlin v1.7.0, Glide v4.13.2 ... +- Fix permission error when running on Android 13. +- Upgrade some dependency versions. Installation -------- @@ -33,7 +33,7 @@ dependencyResolutionManagement { Add the following dependency in app build.gradle: ``` dependencies { - implementation 'com.github.nguyenhoanglam:ImagePicker:1.5.3' + implementation 'com.github.nguyenhoanglam:ImagePicker:1.5.4' } ``` @@ -51,14 +51,14 @@ Usage Define an `ActivityResultLauncher` class variable in `Activity` or `Fragment`. ```java private val launcher = registerImagePicker { images -> - // Selected images are ready to use - if(images.isNotEmpty()){ + // Selected images are ready to use + if(images.isNotEmpty()){ val sampleImage = images[0] Glide.with(this@MainActivity) - .load(sampleImage.uri) - .into(imageView) - } -} + .load(sampleImage.uri) + .into(imageView) + } + } ``` Then, launch image picker when needed. @@ -69,19 +69,19 @@ launcher.launch() - With customize configuration: ```java val config = ImagePickerConfig( - statusBarColor = "#000000", - isLightStatusBar = false, - isFolderMode = true, - isMultipleMode = true, - maxSize = 10, - rootDirectory = Config.ROOT_DIR_DOWNLOAD, - subDirectory = "Photos", - folderGridCount = GridCount(2, 4), - imageGridCount = GridCount(3, 5), - // See more at configuration attributes table below -) - -launcher.launch(config) + statusBarColor = "#000000", + isLightStatusBar = false, + isFolderMode = true, + isMultipleMode = true, + maxSize = 10, + rootDirectory = Config.ROOT_DIR_DOWNLOAD, + subDirectory = "Photos", + folderGridCount = GridCount(2, 4), + imageGridCount = GridCount(3, 5), + // See more at configuration attributes table below + ) + + launcher.launch(config) ``` Configuration attributes diff --git a/build.gradle b/build.gradle index 7043012..f1b7653 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.7.0' + ext.kotlin_version = '1.7.20' repositories { mavenCentral() google() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle:7.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -15,7 +15,7 @@ buildscript { } plugins { - id 'org.jetbrains.kotlin.jvm' version '1.7.0' + id 'org.jetbrains.kotlin.jvm' version '1.7.20' } allprojects { diff --git a/example/build.gradle b/example/build.gradle index 5c267b9..43404f3 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -28,8 +28,8 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.4.2' + implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.recyclerview:recyclerview:1.2.1' - implementation 'com.github.bumptech.glide:glide:4.13.2' + implementation 'com.github.bumptech.glide:glide:4.14.2' implementation project(path: ':imagepicker') } diff --git a/imagepicker/build.gradle b/imagepicker/build.gradle index ebbee83..663d8fa 100644 --- a/imagepicker/build.gradle +++ b/imagepicker/build.gradle @@ -24,14 +24,16 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.4.2' + implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation "androidx.activity:activity:1.7.0-alpha02" implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0' - implementation 'androidx.core:core-ktx:1.8.0' - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0' - implementation 'com.google.android.material:material:1.6.1' - implementation 'com.github.bumptech.glide:glide:4.13.2' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' + implementation 'androidx.core:core-ktx:1.9.0' + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20' + implementation 'com.google.android.material:material:1.7.0' + implementation 'com.github.bumptech.glide:glide:4.14.2' + annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2' } afterEvaluate { @@ -45,7 +47,7 @@ afterEvaluate { // You can then customize attributes of the publication as shown below. groupId = 'com.github.nguyenhoanglam' artifactId = 'final' - version = '1.5.3' + version = '1.5.4' } } } diff --git a/imagepicker/src/main/AndroidManifest.xml b/imagepicker/src/main/AndroidManifest.xml index 9c7d5aa..b76bb98 100644 --- a/imagepicker/src/main/AndroidManifest.xml +++ b/imagepicker/src/main/AndroidManifest.xml @@ -1,8 +1,12 @@ - + + @@ -13,7 +17,9 @@ - + = Build.VERSION_CODES.Q + val isMinSdk33 get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + fun checkCameraAvailability(context: Context): Boolean { val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) val isAvailable = intent.resolveActivity(context.packageManager) != null diff --git a/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/helper/PermissionHelper.kt b/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/helper/PermissionHelper.kt index 13c5801..337f207 100644 --- a/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/helper/PermissionHelper.kt +++ b/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/helper/PermissionHelper.kt @@ -112,7 +112,7 @@ object PermissionHelper { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M } - fun shouldShowRequestPermissionRationale(activity: Activity?, permission: String): Boolean { + private fun shouldShowRequestPermissionRationale(activity: Activity?, permission: String): Boolean { if (activity != null) { return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission) } diff --git a/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/ui/camera/CameraActivity.kt b/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/ui/camera/CameraActivity.kt index 9207190..a7f9d49 100644 --- a/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/ui/camera/CameraActivity.kt +++ b/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/ui/camera/CameraActivity.kt @@ -23,7 +23,6 @@ import com.nguyenhoanglam.imagepicker.helper.PermissionHelper.openAppSettings import com.nguyenhoanglam.imagepicker.helper.ToastHelper import com.nguyenhoanglam.imagepicker.model.Image import com.nguyenhoanglam.imagepicker.model.ImagePickerConfig -import java.util.* class CameraActivity : AppCompatActivity() { @@ -64,7 +63,11 @@ class CameraActivity : AppCompatActivity() { return } - config = intent.getParcelableExtra(Constants.EXTRA_CONFIG)!! + @Suppress("DEPRECATION") + config = if (DeviceHelper.isMinSdk33) intent.getParcelableExtra( + Constants.EXTRA_CONFIG, + ImagePickerConfig::class.java + )!! else intent.getParcelableExtra(Constants.EXTRA_CONFIG)!! config.initDefaultValues(this@CameraActivity) binding = ImagepickerActivityCameraBinding.inflate(layoutInflater) @@ -93,7 +96,7 @@ class CameraActivity : AppCompatActivity() { PermissionHelper.requestAllPermissions( this@CameraActivity, permissions, - Constants.RC_WRITE_EXTERNAL_STORAGE_PERMISSION + Constants.RC_WRITE_PERMISSION ) } @@ -101,7 +104,7 @@ class CameraActivity : AppCompatActivity() { PermissionHelper.requestAllPermissions( this@CameraActivity, permissions, - Constants.RC_WRITE_EXTERNAL_STORAGE_PERMISSION + Constants.RC_WRITE_PERMISSION ) } @@ -159,7 +162,7 @@ class CameraActivity : AppCompatActivity() { grantResults: IntArray ) { when (requestCode) { - Constants.RC_WRITE_EXTERNAL_STORAGE_PERMISSION -> { + Constants.RC_WRITE_PERMISSION -> { if (hasGranted(grantResults)) { captureImage() } else { diff --git a/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/ui/imagepicker/ImagePickerActivity.kt b/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/ui/imagepicker/ImagePickerActivity.kt index f47d7f4..8a61b5c 100644 --- a/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/ui/imagepicker/ImagePickerActivity.kt +++ b/imagepicker/src/main/java/com/nguyenhoanglam/imagepicker/ui/imagepicker/ImagePickerActivity.kt @@ -12,6 +12,7 @@ import android.graphics.Color import android.os.Build import android.os.Bundle import android.view.View +import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat @@ -36,7 +37,7 @@ class ImagePickerActivity : AppCompatActivity(), OnFolderClickListener, OnImageS private lateinit var viewModel: ImagePickerViewModel private val cameraModule = CameraModule() - private val backClickListener = View.OnClickListener { onBackPressed() } + private val backClickListener = View.OnClickListener { handleBackPress() } private val cameraClickListener = View.OnClickListener { captureImageWithPermission() } private val doneClickListener = View.OnClickListener { onDone() } @@ -65,7 +66,12 @@ class ImagePickerActivity : AppCompatActivity(), OnFolderClickListener, OnImageS return } - config = intent.getParcelableExtra(Constants.EXTRA_CONFIG)!! + @Suppress("DEPRECATION") + config = + if (DeviceHelper.isMinSdk33) intent.getParcelableExtra( + Constants.EXTRA_CONFIG, + ImagePickerConfig::class.java + )!! else intent.getParcelableExtra(Constants.EXTRA_CONFIG)!! config.initDefaultValues(this@ImagePickerActivity) // Setup status bar theme @@ -79,15 +85,22 @@ class ImagePickerActivity : AppCompatActivity(), OnFolderClickListener, OnImageS binding = ImagepickerActivityImagepickerBinding.inflate(layoutInflater) setContentView(binding.root) - viewModel = ViewModelProvider(this, ImagePickerViewModelFactory(this.application)).get( - ImagePickerViewModel::class.java - ) + viewModel = ViewModelProvider( + this, + ImagePickerViewModelFactory(this.application) + )[ImagePickerViewModel::class.java] viewModel.setConfig(config) - viewModel.selectedImages.observe(this, { + viewModel.selectedImages.observe(this) { binding.toolbar.showDoneButton(config.isAlwaysShowDoneButton || it.isNotEmpty()) - }) + } setupViews() + + onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + handleBackPress() + } + }) } override fun onResume() { @@ -112,26 +125,39 @@ class ImagePickerActivity : AppCompatActivity(), OnFolderClickListener, OnImageS .commit() } + private fun handleBackPress() { + if (supportFragmentManager.backStackEntryCount > 0) { + supportFragmentManager.popBackStackImmediate() + val fragment = supportFragmentManager.findFragmentById(R.id.fragmentContainer) + if (fragment != null && fragment is FolderFragment) { + binding.toolbar.setTitle(config.folderTitle) + } + } else { + finish() + } + } private fun fetchDataWithPermission() { - val permissions = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) + val readPermission = + if (DeviceHelper.isMinSdk33) Manifest.permission.READ_MEDIA_IMAGES else Manifest.permission.READ_EXTERNAL_STORAGE + PermissionHelper.checkPermission( this, - Manifest.permission.READ_EXTERNAL_STORAGE, + readPermission, object : PermissionHelper.PermissionAskListener { override fun onNeedPermission() { PermissionHelper.requestAllPermissions( this@ImagePickerActivity, - permissions, - Constants.RC_READ_EXTERNAL_STORAGE_PERMISSION + arrayOf(readPermission), + Constants.RC_READ_PERMISSION ) } override fun onPermissionPreviouslyDenied() { PermissionHelper.requestAllPermissions( this@ImagePickerActivity, - permissions, - Constants.RC_READ_EXTERNAL_STORAGE_PERMISSION + arrayOf(readPermission), + Constants.RC_READ_PERMISSION ) } @@ -155,14 +181,14 @@ class ImagePickerActivity : AppCompatActivity(), OnFolderClickListener, OnImageS grantResults: IntArray ) { when (requestCode) { - Constants.RC_READ_EXTERNAL_STORAGE_PERMISSION -> { + Constants.RC_READ_PERMISSION -> { if (PermissionHelper.hasGranted(grantResults)) { fetchData() } else { finish() } } - Constants.RC_WRITE_EXTERNAL_STORAGE_PERMISSION -> { + Constants.RC_WRITE_PERMISSION -> { if (PermissionHelper.hasGranted(grantResults)) { captureImage() } @@ -177,14 +203,6 @@ class ImagePickerActivity : AppCompatActivity(), OnFolderClickListener, OnImageS viewModel.fetchImages() } - override fun onBackPressed() { - super.onBackPressed() - val fragment = supportFragmentManager.findFragmentById(R.id.fragmentContainer) - if (fragment != null && fragment is FolderFragment) { - binding.toolbar.setTitle(config.folderTitle) - } - } - private fun onDone() { val selectedImages = viewModel.selectedImages.value finishPickImages(selectedImages ?: arrayListOf()) @@ -206,7 +224,7 @@ class ImagePickerActivity : AppCompatActivity(), OnFolderClickListener, OnImageS PermissionHelper.requestAllPermissions( this@ImagePickerActivity, permissions, - Constants.RC_WRITE_EXTERNAL_STORAGE_PERMISSION + Constants.RC_WRITE_PERMISSION ) } @@ -214,7 +232,7 @@ class ImagePickerActivity : AppCompatActivity(), OnFolderClickListener, OnImageS PermissionHelper.requestAllPermissions( this@ImagePickerActivity, permissions, - Constants.RC_WRITE_EXTERNAL_STORAGE_PERMISSION + Constants.RC_WRITE_PERMISSION ) } diff --git a/imagepicker/src/main/res/values/strings.xml b/imagepicker/src/main/res/values/strings.xml index c3744bc..abc37c1 100644 --- a/imagepicker/src/main/res/values/strings.xml +++ b/imagepicker/src/main/res/values/strings.xml @@ -13,7 +13,7 @@ Could not open camera. No image available. - To continue, please allow app to access photos and media on this device. - Limit selection to %d images. + Please allow permission to access photos and media. + You can select up to %d images.