From d351ca878c7e008ec8ba3c8a1f7c7e4f197b2f57 Mon Sep 17 00:00:00 2001 From: Preston Schwartz Date: Wed, 22 Feb 2023 12:21:16 -0600 Subject: [PATCH 1/7] Recreating PR from flutter/plugins --- .../image_picker_android/CHANGELOG.md | 4 +- .../image_picker_android/android/build.gradle | 2 +- .../android/gradle.properties | 14 +++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../imagepicker/ImagePickerDelegate.java | 54 +++++++++-- .../plugins/imagepicker/ImagePickerUtils.java | 22 +++++ .../imagepicker/ImagePickerDelegateTest.java | 91 +++++++++++++++++++ .../example/android/app/build.gradle | 2 +- .../image_picker_android/pubspec.yaml | 2 +- 9 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 packages/image_picker/image_picker_android/android/gradle.properties create mode 100644 packages/image_picker/image_picker_android/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 1ab21108d70f..f150028e6590 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,5 +1,7 @@ -## 0.8.5+6 +## 0.8.6 +* Adds Android 13 photo picker functionality if SDK version is at least 33. +* Bumps compileSdkVersion from 31 to 33 * Updates minimum Flutter version to 3.0. * Fixes names of picked files to match original filenames where possible. diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index e61f3161d0f5..8329016d5323 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 31 + compileSdkVersion 33 defaultConfig { minSdkVersion 16 diff --git a/packages/image_picker/image_picker_android/android/gradle.properties b/packages/image_picker/image_picker_android/android/gradle.properties new file mode 100644 index 000000000000..15eca541fbad --- /dev/null +++ b/packages/image_picker/image_picker_android/android/gradle.properties @@ -0,0 +1,14 @@ +## For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx1024m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +#Fri Jan 27 08:52:19 CST 2023 +org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M" diff --git a/packages/image_picker/image_picker_android/android/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker_android/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..41dfb87909a8 --- /dev/null +++ b/packages/image_picker/image_picker_android/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index cb4beacf9df4..bfb9611c455d 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -75,10 +75,20 @@ enum CameraDevice { public class ImagePickerDelegate implements PluginRegistry.ActivityResultListener, PluginRegistry.RequestPermissionsResultListener { + @VisibleForTesting + static final int REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER = 2341; + @VisibleForTesting static final int REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY = 2342; @VisibleForTesting static final int REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA = 2343; @VisibleForTesting static final int REQUEST_CAMERA_IMAGE_PERMISSION = 2345; @VisibleForTesting static final int REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY = 2346; + + @VisibleForTesting + static final int REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER = 2347; + + @VisibleForTesting + static final int REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY_USING_PHOTO_PICKER = 2351; + @VisibleForTesting static final int REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY = 2352; @VisibleForTesting static final int REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA = 2353; @VisibleForTesting static final int REQUEST_CAMERA_VIDEO_PERMISSION = 2355; @@ -253,10 +263,18 @@ public void chooseVideoFromGallery(MethodCall methodCall, MethodChannel.Result r } private void launchPickVideoFromGalleryIntent() { - Intent pickVideoIntent = new Intent(Intent.ACTION_GET_CONTENT); + boolean isPhotoPickerAvailable = ImagePickerUtils.isPhotoPickerAvailable(); + + Intent pickVideoIntent = + new Intent( + isPhotoPickerAvailable ? MediaStore.ACTION_PICK_IMAGES : Intent.ACTION_GET_CONTENT); pickVideoIntent.setType("video/*"); + int requestCode = + isPhotoPickerAvailable + ? REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY_USING_PHOTO_PICKER + : REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY; - activity.startActivityForResult(pickVideoIntent, REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY); + activity.startActivityForResult(pickVideoIntent, requestCode); } public void takeVideoWithCamera(MethodCall methodCall, MethodChannel.Result result) { @@ -325,20 +343,38 @@ public void chooseMultiImageFromGallery(MethodCall methodCall, MethodChannel.Res } private void launchPickImageFromGalleryIntent() { - Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT); + boolean isPhotoPickerAvailable = ImagePickerUtils.isPhotoPickerAvailable(); + Intent pickImageIntent = + new Intent( + isPhotoPickerAvailable ? MediaStore.ACTION_PICK_IMAGES : Intent.ACTION_GET_CONTENT); + int requestCode = + isPhotoPickerAvailable + ? REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER + : REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY; pickImageIntent.setType("image/*"); - - activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY); + activity.startActivityForResult(pickImageIntent, requestCode); } private void launchMultiPickImageFromGalleryIntent() { - Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT); + boolean isPhotoPickerAvailable = ImagePickerUtils.isPhotoPickerAvailable(); + Intent pickImageIntent = + new Intent( + isPhotoPickerAvailable ? MediaStore.ACTION_PICK_IMAGES : Intent.ACTION_GET_CONTENT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - pickImageIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + if (isPhotoPickerAvailable) { + pickImageIntent.putExtra( + MediaStore.EXTRA_PICK_IMAGES_MAX, ImagePickerUtils.getPickImagesMaxLimit()); + } else { + pickImageIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + } } pickImageIntent.setType("image/*"); - - activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY); + int requestCode = + isPhotoPickerAvailable + ? REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER + : REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY; + activity.startActivityForResult(pickImageIntent, requestCode); } public void takeImageWithCamera(MethodCall methodCall, MethodChannel.Result result) { diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java index ba9878925575..b9143c46d2fb 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java @@ -4,11 +4,14 @@ package io.flutter.plugins.imagepicker; +import static android.os.ext.SdkExtensions.getExtensionVersion; + import android.Manifest; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; +import android.provider.MediaStore; import java.util.Arrays; final class ImagePickerUtils { @@ -27,6 +30,25 @@ private static boolean isPermissionPresentInManifest(Context context, String per } } + static boolean isPhotoPickerAvailable() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + return getExtensionVersion(Build.VERSION_CODES.R) >= 2; + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + return true; + } else { + return false; + } + } + + static int getPickImagesMaxLimit() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + return MediaStore.getPickImagesMaxLimit(); + } else { + return 0; + } + } + /** * Camera permission need request if it present in manifest, because for M or great for take Photo * ar Video by intent need it permission, even if the camera permission is not used. diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java index 6d1e73c49eb9..01272031fd90 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java @@ -35,12 +35,16 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +@RunWith(RobolectricTestRunner.class) public class ImagePickerDelegateTest { private static final Double WIDTH = 10.0; private static final Double HEIGHT = 10.0; @@ -134,6 +138,8 @@ public void chooseMultiImageFromGallery_WhenPendingResultExists_FinishesWithAlre verifyNoMoreInteractions(mockResult); } + @Test + @Config(sdk = 30) public void chooseImageFromGallery_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() { when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE)) @@ -147,6 +153,87 @@ public void chooseMultiImageFromGallery_WhenPendingResultExists_FinishesWithAlre any(Intent.class), eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY)); } + @Test + @Config(minSdk = 33) + public void + chooseImageFromGallery_WithPhotoPicker_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() { + when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE)) + .thenReturn(true); + + ImagePickerDelegate delegate = createDelegate(); + delegate.chooseImageFromGallery(mockMethodCall, mockResult); + + verify(mockActivity) + .startActivityForResult( + any(Intent.class), + eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER)); + } + + @Test + @Config(sdk = 30) + public void + chooseMultiImageFromGallery_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() { + when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE)) + .thenReturn(true); + + ImagePickerDelegate delegate = createDelegate(); + delegate.chooseMultiImageFromGallery(mockMethodCall, mockResult); + + verify(mockActivity) + .startActivityForResult( + any(Intent.class), + eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY)); + } + + @Test + @Config(minSdk = 33) + public void + chooseMultiImageFromGallery_WithPhotoPicker_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() { + when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE)) + .thenReturn(true); + + ImagePickerDelegate delegate = createDelegate(); + delegate.chooseMultiImageFromGallery(mockMethodCall, mockResult); + + verify(mockActivity) + .startActivityForResult( + any(Intent.class), + eq( + ImagePickerDelegate + .REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER)); + } + + @Test + @Config(sdk = 30) + public void + chooseVideoFromGallery_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() { + when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE)) + .thenReturn(true); + + ImagePickerDelegate delegate = createDelegate(); + delegate.chooseVideoFromGallery(mockMethodCall, mockResult); + + verify(mockActivity) + .startActivityForResult( + any(Intent.class), eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY)); + } + + @Test + @Config(minSdk = 33) + public void + chooseVideoFromGallery_WithPhotoPicker_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() { + when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE)) + .thenReturn(true); + + ImagePickerDelegate delegate = createDelegate(); + delegate.chooseVideoFromGallery(mockMethodCall, mockResult); + + verify(mockActivity) + .startActivityForResult( + any(Intent.class), + eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY_USING_PHOTO_PICKER)); + } + @Test public void takeImageWithCamera_WhenPendingResultExists_FinishesWithAlreadyActiveError() { ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall(); @@ -350,6 +437,7 @@ public void onActivityResult_WhenTakeImageWithCameraCanceled_FinishesWithNull() @Test public void onActivityResult_WhenImageTakenWithCamera_AndNoResizeNeeded_FinishesWithImagePath() { ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall(); + when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString"); delegate.onActivityResult( ImagePickerDelegate.REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA, Activity.RESULT_OK, mockIntent); @@ -362,6 +450,7 @@ public void onActivityResult_WhenImageTakenWithCamera_AndNoResizeNeeded_Finishes public void onActivityResult_WhenImageTakenWithCamera_AndResizeNeeded_FinishesWithScaledImagePath() { when(mockMethodCall.argument("maxWidth")).thenReturn(WIDTH); + when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString"); ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall(); delegate.onActivityResult( @@ -375,6 +464,7 @@ public void onActivityResult_WhenImageTakenWithCamera_AndNoResizeNeeded_Finishes public void onActivityResult_WhenVideoTakenWithCamera_AndResizeParametersSupplied_FinishesWithFilePath() { when(mockMethodCall.argument("maxWidth")).thenReturn(WIDTH); + when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString"); ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall(); delegate.onActivityResult( @@ -388,6 +478,7 @@ public void onActivityResult_WhenImageTakenWithCamera_AndNoResizeNeeded_Finishes public void onActivityResult_WhenVideoTakenWithCamera_AndMaxDurationParametersSupplied_FinishesWithFilePath() { when(mockMethodCall.argument("maxDuration")).thenReturn(MAX_DURATION); + when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString"); ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall(); delegate.onActivityResult( diff --git a/packages/image_picker/image_picker_android/example/android/app/build.gradle b/packages/image_picker/image_picker_android/example/android/app/build.gradle index f8487c7959f1..f667a7b625c8 100755 --- a/packages/image_picker/image_picker_android/example/android/app/build.gradle +++ b/packages/image_picker/image_picker_android/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + compileSdkVersion 33 testOptions.unitTests.includeAndroidResources = true lintOptions { diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index a0516685964c..ddb888b35309 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+6 +version: 0.8.6 environment: sdk: ">=2.14.0 <3.0.0" From 5e1a03575aad16f787d410c7a0a877a3e7e1e4ef Mon Sep 17 00:00:00 2001 From: Preston Schwartz Date: Wed, 22 Feb 2023 14:21:45 -0600 Subject: [PATCH 2/7] Removes duplicate intent tags, uses android.x.activity ActivityResultContracts to decide which photo_picker should be used based on SDK version instead of handling the checks manually. --- .../image_picker_android/android/build.gradle | 1 + .../imagepicker/ImagePickerDelegate.java | 70 ++++++------------- .../plugins/imagepicker/ImagePickerUtils.java | 19 ----- .../imagepicker/ImagePickerDelegateTest.java | 10 +-- 4 files changed, 27 insertions(+), 73 deletions(-) diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index 8329016d5323..d7fdf141bbe1 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -35,6 +35,7 @@ android { implementation 'androidx.core:core:1.8.0' implementation 'androidx.annotation:annotation:1.3.0' implementation 'androidx.exifinterface:exifinterface:1.3.3' + implementation 'androidx.activity:activity:1.6.1' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.1.1' diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index bfb9611c455d..b2d0acbe427f 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -15,6 +15,8 @@ import android.net.Uri; import android.os.Build; import android.provider.MediaStore; +import androidx.activity.result.PickVisualMediaRequest; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.app.ActivityCompat; @@ -75,20 +77,11 @@ enum CameraDevice { public class ImagePickerDelegate implements PluginRegistry.ActivityResultListener, PluginRegistry.RequestPermissionsResultListener { - @VisibleForTesting - static final int REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER = 2341; - @VisibleForTesting static final int REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY = 2342; @VisibleForTesting static final int REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA = 2343; @VisibleForTesting static final int REQUEST_CAMERA_IMAGE_PERMISSION = 2345; @VisibleForTesting static final int REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY = 2346; - @VisibleForTesting - static final int REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER = 2347; - - @VisibleForTesting - static final int REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY_USING_PHOTO_PICKER = 2351; - @VisibleForTesting static final int REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY = 2352; @VisibleForTesting static final int REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA = 2353; @VisibleForTesting static final int REQUEST_CAMERA_VIDEO_PERMISSION = 2355; @@ -263,18 +256,14 @@ public void chooseVideoFromGallery(MethodCall methodCall, MethodChannel.Result r } private void launchPickVideoFromGalleryIntent() { - boolean isPhotoPickerAvailable = ImagePickerUtils.isPhotoPickerAvailable(); - Intent pickVideoIntent = - new Intent( - isPhotoPickerAvailable ? MediaStore.ACTION_PICK_IMAGES : Intent.ACTION_GET_CONTENT); - pickVideoIntent.setType("video/*"); - int requestCode = - isPhotoPickerAvailable - ? REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY_USING_PHOTO_PICKER - : REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY; - - activity.startActivityForResult(pickVideoIntent, requestCode); + new ActivityResultContracts.PickVisualMedia() + .createIntent( + activity, + new PickVisualMediaRequest.Builder() + .setMediaType(ActivityResultContracts.PickVisualMedia.VideoOnly.INSTANCE) + .build()); + activity.startActivityForResult(pickVideoIntent, REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY); } public void takeVideoWithCamera(MethodCall methodCall, MethodChannel.Result result) { @@ -343,38 +332,25 @@ public void chooseMultiImageFromGallery(MethodCall methodCall, MethodChannel.Res } private void launchPickImageFromGalleryIntent() { - boolean isPhotoPickerAvailable = ImagePickerUtils.isPhotoPickerAvailable(); Intent pickImageIntent = - new Intent( - isPhotoPickerAvailable ? MediaStore.ACTION_PICK_IMAGES : Intent.ACTION_GET_CONTENT); - int requestCode = - isPhotoPickerAvailable - ? REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER - : REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY; - pickImageIntent.setType("image/*"); - activity.startActivityForResult(pickImageIntent, requestCode); + new ActivityResultContracts.PickVisualMedia() + .createIntent( + activity, + new PickVisualMediaRequest.Builder() + .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE) + .build()); + activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY); } private void launchMultiPickImageFromGalleryIntent() { - boolean isPhotoPickerAvailable = ImagePickerUtils.isPhotoPickerAvailable(); Intent pickImageIntent = - new Intent( - isPhotoPickerAvailable ? MediaStore.ACTION_PICK_IMAGES : Intent.ACTION_GET_CONTENT); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - if (isPhotoPickerAvailable) { - pickImageIntent.putExtra( - MediaStore.EXTRA_PICK_IMAGES_MAX, ImagePickerUtils.getPickImagesMaxLimit()); - } else { - pickImageIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - } - } - pickImageIntent.setType("image/*"); - int requestCode = - isPhotoPickerAvailable - ? REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER - : REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY; - activity.startActivityForResult(pickImageIntent, requestCode); + new ActivityResultContracts.PickMultipleVisualMedia() + .createIntent( + activity, + new PickVisualMediaRequest.Builder() + .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE) + .build()); + activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY); } public void takeImageWithCamera(MethodCall methodCall, MethodChannel.Result result) { diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java index b9143c46d2fb..a73748d51af0 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java @@ -30,25 +30,6 @@ private static boolean isPermissionPresentInManifest(Context context, String per } } - static boolean isPhotoPickerAvailable() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - return getExtensionVersion(Build.VERSION_CODES.R) >= 2; - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - return true; - } else { - return false; - } - } - - static int getPickImagesMaxLimit() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - return MediaStore.getPickImagesMaxLimit(); - } else { - return 0; - } - } - /** * Camera permission need request if it present in manifest, because for M or great for take Photo * ar Video by intent need it permission, even if the camera permission is not used. diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java index 01272031fd90..3a6fd0c48c01 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java @@ -165,8 +165,7 @@ public void chooseMultiImageFromGallery_WhenPendingResultExists_FinishesWithAlre verify(mockActivity) .startActivityForResult( - any(Intent.class), - eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER)); + any(Intent.class), eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY)); } @Test @@ -198,9 +197,7 @@ public void chooseMultiImageFromGallery_WhenPendingResultExists_FinishesWithAlre verify(mockActivity) .startActivityForResult( any(Intent.class), - eq( - ImagePickerDelegate - .REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY_USING_PHOTO_PICKER)); + eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY)); } @Test @@ -230,8 +227,7 @@ public void chooseMultiImageFromGallery_WhenPendingResultExists_FinishesWithAlre verify(mockActivity) .startActivityForResult( - any(Intent.class), - eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY_USING_PHOTO_PICKER)); + any(Intent.class), eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY)); } @Test From da4b4d01ad628dea77ced9f82b905ed45304c3e7 Mon Sep 17 00:00:00 2001 From: Preston Schwartz Date: Wed, 22 Feb 2023 14:24:11 -0600 Subject: [PATCH 3/7] Formats java file --- .../java/io/flutter/plugins/imagepicker/ImagePickerUtils.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java index a73748d51af0..ba9878925575 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java @@ -4,14 +4,11 @@ package io.flutter.plugins.imagepicker; -import static android.os.ext.SdkExtensions.getExtensionVersion; - import android.Manifest; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; -import android.provider.MediaStore; import java.util.Arrays; final class ImagePickerUtils { From f41bd09d7200b8188b93bcbd356427bdc08c56bb Mon Sep 17 00:00:00 2001 From: Preston Schwartz Date: Fri, 24 Feb 2023 15:33:59 -0600 Subject: [PATCH 4/7] Bumps minSdkVersion from 16 to 19. --- packages/image_picker/image_picker_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index d7fdf141bbe1..104b51864801 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -25,7 +25,7 @@ android { compileSdkVersion 33 defaultConfig { - minSdkVersion 16 + minSdkVersion 19 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { From 16530a8f4a1a4f5a9a153a2f62b107e5401478b4 Mon Sep 17 00:00:00 2001 From: Preston Schwartz Date: Tue, 28 Feb 2023 14:17:17 -0600 Subject: [PATCH 5/7] Adds SDK version check to decide if the newer PickVisualMedia can be used, otherwise defaults to old Intent.ACTION_GET_CONTENT behavior. --- .../image_picker_android/android/build.gradle | 2 +- .../imagepicker/ImagePickerDelegate.java | 68 +++++++++++++------ 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index 104b51864801..d7fdf141bbe1 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -25,7 +25,7 @@ android { compileSdkVersion 33 defaultConfig { - minSdkVersion 19 + minSdkVersion 16 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index b2d0acbe427f..b95fc69ff6b3 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -256,13 +256,20 @@ public void chooseVideoFromGallery(MethodCall methodCall, MethodChannel.Result r } private void launchPickVideoFromGalleryIntent() { - Intent pickVideoIntent = - new ActivityResultContracts.PickVisualMedia() - .createIntent( - activity, - new PickVisualMediaRequest.Builder() - .setMediaType(ActivityResultContracts.PickVisualMedia.VideoOnly.INSTANCE) - .build()); + Intent pickVideoIntent; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + pickVideoIntent = + new ActivityResultContracts.PickVisualMedia() + .createIntent( + activity, + new PickVisualMediaRequest.Builder() + .setMediaType(ActivityResultContracts.PickVisualMedia.VideoOnly.INSTANCE) + .build()); + } else { + pickVideoIntent = new Intent(Intent.ACTION_GET_CONTENT); + pickVideoIntent.setType("video/*"); + } + activity.startActivityForResult(pickVideoIntent, REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY); } @@ -332,25 +339,42 @@ public void chooseMultiImageFromGallery(MethodCall methodCall, MethodChannel.Res } private void launchPickImageFromGalleryIntent() { - Intent pickImageIntent = - new ActivityResultContracts.PickVisualMedia() - .createIntent( - activity, - new PickVisualMediaRequest.Builder() - .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE) - .build()); + Intent pickImageIntent; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + pickImageIntent = + new ActivityResultContracts.PickVisualMedia() + .createIntent( + activity, + new PickVisualMediaRequest.Builder() + .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE) + .build()); + } else { + pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT); + pickImageIntent.setType("image/*"); + } + activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY); } private void launchMultiPickImageFromGalleryIntent() { - Intent pickImageIntent = - new ActivityResultContracts.PickMultipleVisualMedia() - .createIntent( - activity, - new PickVisualMediaRequest.Builder() - .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE) - .build()); - activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY); + Intent pickMultiImageIntent; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + pickMultiImageIntent = + new ActivityResultContracts.PickMultipleVisualMedia() + .createIntent( + activity, + new PickVisualMediaRequest.Builder() + .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE) + .build()); + } else { + pickMultiImageIntent = new Intent(Intent.ACTION_GET_CONTENT); + pickMultiImageIntent.setType("image/*"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + pickMultiImageIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + } + } + activity.startActivityForResult( + pickMultiImageIntent, REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY); } public void takeImageWithCamera(MethodCall methodCall, MethodChannel.Result result) { From dad609492ed62c936ae45de0bd3bdb6827b82457 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 28 Feb 2023 16:46:18 -0800 Subject: [PATCH 6/7] fix changelog --- packages/image_picker/image_picker_android/CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 93f7b05d42f2..48067658e667 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,11 +1,14 @@ +## 0.8.5+8 + +* Adds Android 13 photo picker functionality if SDK version is at least 33. +* Bumps compileSdkVersion from 31 to 33 + ## 0.8.5+7 * Updates links for the merge of flutter/plugins into flutter/packages. ## 0.8.5+6 -* Adds Android 13 photo picker functionality if SDK version is at least 33. -* Bumps compileSdkVersion from 31 to 33 * Updates minimum Flutter version to 3.0. * Fixes names of picked files to match original filenames where possible. From 28be6bdbdaef5c5ec7063d91ac52dcfa7dae8639 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 28 Feb 2023 17:04:02 -0800 Subject: [PATCH 7/7] pubspec --- packages/image_picker/image_picker_android/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 57cbbb60740a..cd606752e31a 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+7 +version: 0.8.5+8 environment: sdk: ">=2.14.0 <3.0.0"