Skip to content

Commit 7ec6a77

Browse files
authored
[image_picker_android] Adds Android 13 photo picker functionality (flutter#3267)
[image_picker_android] Adds Android 13 photo picker functionality
1 parent 588c5f4 commit 7ec6a77

File tree

8 files changed

+162
-13
lines changed

8 files changed

+162
-13
lines changed

packages/image_picker/image_picker_android/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.8.5+8
2+
3+
* Adds Android 13 photo picker functionality if SDK version is at least 33.
4+
* Bumps compileSdkVersion from 31 to 33
5+
16
## 0.8.5+7
27

38
* Updates links for the merge of flutter/plugins into flutter/packages.

packages/image_picker/image_picker_android/android/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ rootProject.allprojects {
2222
apply plugin: 'com.android.library'
2323

2424
android {
25-
compileSdkVersion 31
25+
compileSdkVersion 33
2626

2727
defaultConfig {
2828
minSdkVersion 16
@@ -35,6 +35,7 @@ android {
3535
implementation 'androidx.core:core:1.8.0'
3636
implementation 'androidx.annotation:annotation:1.3.0'
3737
implementation 'androidx.exifinterface:exifinterface:1.3.3'
38+
implementation 'androidx.activity:activity:1.6.1'
3839

3940
testImplementation 'junit:junit:4.13.2'
4041
testImplementation 'org.mockito:mockito-core:5.1.1'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## For more details on how to configure your build environment visit
2+
# http://www.gradle.org/docs/current/userguide/build_environment.html
3+
#
4+
# Specifies the JVM arguments used for the daemon process.
5+
# The setting is particularly useful for tweaking memory settings.
6+
# Default value: -Xmx1024m -XX:MaxPermSize=256m
7+
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
8+
#
9+
# When configured, Gradle will run in incubating parallel mode.
10+
# This option should only be used with decoupled projects. More details, visit
11+
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
12+
# org.gradle.parallel=true
13+
#Fri Jan 27 08:52:19 CST 2023
14+
org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4+
zipStoreBase=GRADLE_USER_HOME
5+
zipStorePath=wrapper/dists

packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import android.net.Uri;
1616
import android.os.Build;
1717
import android.provider.MediaStore;
18+
import androidx.activity.result.PickVisualMediaRequest;
19+
import androidx.activity.result.contract.ActivityResultContracts;
1820
import androidx.annotation.Nullable;
1921
import androidx.annotation.VisibleForTesting;
2022
import androidx.core.app.ActivityCompat;
@@ -79,6 +81,7 @@ public class ImagePickerDelegate
7981
@VisibleForTesting static final int REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA = 2343;
8082
@VisibleForTesting static final int REQUEST_CAMERA_IMAGE_PERMISSION = 2345;
8183
@VisibleForTesting static final int REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY = 2346;
84+
8285
@VisibleForTesting static final int REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY = 2352;
8386
@VisibleForTesting static final int REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA = 2353;
8487
@VisibleForTesting static final int REQUEST_CAMERA_VIDEO_PERMISSION = 2355;
@@ -253,8 +256,19 @@ public void chooseVideoFromGallery(MethodCall methodCall, MethodChannel.Result r
253256
}
254257

255258
private void launchPickVideoFromGalleryIntent() {
256-
Intent pickVideoIntent = new Intent(Intent.ACTION_GET_CONTENT);
257-
pickVideoIntent.setType("video/*");
259+
Intent pickVideoIntent;
260+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
261+
pickVideoIntent =
262+
new ActivityResultContracts.PickVisualMedia()
263+
.createIntent(
264+
activity,
265+
new PickVisualMediaRequest.Builder()
266+
.setMediaType(ActivityResultContracts.PickVisualMedia.VideoOnly.INSTANCE)
267+
.build());
268+
} else {
269+
pickVideoIntent = new Intent(Intent.ACTION_GET_CONTENT);
270+
pickVideoIntent.setType("video/*");
271+
}
258272

259273
activity.startActivityForResult(pickVideoIntent, REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY);
260274
}
@@ -325,20 +339,42 @@ public void chooseMultiImageFromGallery(MethodCall methodCall, MethodChannel.Res
325339
}
326340

327341
private void launchPickImageFromGalleryIntent() {
328-
Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
329-
pickImageIntent.setType("image/*");
342+
Intent pickImageIntent;
343+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
344+
pickImageIntent =
345+
new ActivityResultContracts.PickVisualMedia()
346+
.createIntent(
347+
activity,
348+
new PickVisualMediaRequest.Builder()
349+
.setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE)
350+
.build());
351+
} else {
352+
pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
353+
pickImageIntent.setType("image/*");
354+
}
330355

331356
activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY);
332357
}
333358

334359
private void launchMultiPickImageFromGalleryIntent() {
335-
Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
336-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
337-
pickImageIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
360+
Intent pickMultiImageIntent;
361+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
362+
pickMultiImageIntent =
363+
new ActivityResultContracts.PickMultipleVisualMedia()
364+
.createIntent(
365+
activity,
366+
new PickVisualMediaRequest.Builder()
367+
.setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE)
368+
.build());
369+
} else {
370+
pickMultiImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
371+
pickMultiImageIntent.setType("image/*");
372+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
373+
pickMultiImageIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
374+
}
338375
}
339-
pickImageIntent.setType("image/*");
340-
341-
activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY);
376+
activity.startActivityForResult(
377+
pickMultiImageIntent, REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY);
342378
}
343379

344380
public void takeImageWithCamera(MethodCall methodCall, MethodChannel.Result result) {

packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,16 @@
3535
import org.junit.After;
3636
import org.junit.Before;
3737
import org.junit.Test;
38+
import org.junit.runner.RunWith;
3839
import org.mockito.ArgumentCaptor;
3940
import org.mockito.Mock;
4041
import org.mockito.MockedStatic;
4142
import org.mockito.Mockito;
4243
import org.mockito.MockitoAnnotations;
44+
import org.robolectric.RobolectricTestRunner;
45+
import org.robolectric.annotation.Config;
4346

47+
@RunWith(RobolectricTestRunner.class)
4448
public class ImagePickerDelegateTest {
4549
private static final Double WIDTH = 10.0;
4650
private static final Double HEIGHT = 10.0;
@@ -134,6 +138,8 @@ public void chooseMultiImageFromGallery_WhenPendingResultExists_FinishesWithAlre
134138
verifyNoMoreInteractions(mockResult);
135139
}
136140

141+
@Test
142+
@Config(sdk = 30)
137143
public void
138144
chooseImageFromGallery_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() {
139145
when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE))
@@ -147,6 +153,83 @@ public void chooseMultiImageFromGallery_WhenPendingResultExists_FinishesWithAlre
147153
any(Intent.class), eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY));
148154
}
149155

156+
@Test
157+
@Config(minSdk = 33)
158+
public void
159+
chooseImageFromGallery_WithPhotoPicker_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() {
160+
when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE))
161+
.thenReturn(true);
162+
163+
ImagePickerDelegate delegate = createDelegate();
164+
delegate.chooseImageFromGallery(mockMethodCall, mockResult);
165+
166+
verify(mockActivity)
167+
.startActivityForResult(
168+
any(Intent.class), eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY));
169+
}
170+
171+
@Test
172+
@Config(sdk = 30)
173+
public void
174+
chooseMultiImageFromGallery_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() {
175+
when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE))
176+
.thenReturn(true);
177+
178+
ImagePickerDelegate delegate = createDelegate();
179+
delegate.chooseMultiImageFromGallery(mockMethodCall, mockResult);
180+
181+
verify(mockActivity)
182+
.startActivityForResult(
183+
any(Intent.class),
184+
eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY));
185+
}
186+
187+
@Test
188+
@Config(minSdk = 33)
189+
public void
190+
chooseMultiImageFromGallery_WithPhotoPicker_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() {
191+
when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE))
192+
.thenReturn(true);
193+
194+
ImagePickerDelegate delegate = createDelegate();
195+
delegate.chooseMultiImageFromGallery(mockMethodCall, mockResult);
196+
197+
verify(mockActivity)
198+
.startActivityForResult(
199+
any(Intent.class),
200+
eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY));
201+
}
202+
203+
@Test
204+
@Config(sdk = 30)
205+
public void
206+
chooseVideoFromGallery_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() {
207+
when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE))
208+
.thenReturn(true);
209+
210+
ImagePickerDelegate delegate = createDelegate();
211+
delegate.chooseVideoFromGallery(mockMethodCall, mockResult);
212+
213+
verify(mockActivity)
214+
.startActivityForResult(
215+
any(Intent.class), eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY));
216+
}
217+
218+
@Test
219+
@Config(minSdk = 33)
220+
public void
221+
chooseVideoFromGallery_WithPhotoPicker_WhenHasExternalStoragePermission_LaunchesChooseFromGalleryIntent() {
222+
when(mockPermissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE))
223+
.thenReturn(true);
224+
225+
ImagePickerDelegate delegate = createDelegate();
226+
delegate.chooseVideoFromGallery(mockMethodCall, mockResult);
227+
228+
verify(mockActivity)
229+
.startActivityForResult(
230+
any(Intent.class), eq(ImagePickerDelegate.REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY));
231+
}
232+
150233
@Test
151234
public void takeImageWithCamera_WhenPendingResultExists_FinishesWithAlreadyActiveError() {
152235
ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall();
@@ -350,6 +433,7 @@ public void onActivityResult_WhenTakeImageWithCameraCanceled_FinishesWithNull()
350433
@Test
351434
public void onActivityResult_WhenImageTakenWithCamera_AndNoResizeNeeded_FinishesWithImagePath() {
352435
ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall();
436+
when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString");
353437

354438
delegate.onActivityResult(
355439
ImagePickerDelegate.REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA, Activity.RESULT_OK, mockIntent);
@@ -362,6 +446,7 @@ public void onActivityResult_WhenImageTakenWithCamera_AndNoResizeNeeded_Finishes
362446
public void
363447
onActivityResult_WhenImageTakenWithCamera_AndResizeNeeded_FinishesWithScaledImagePath() {
364448
when(mockMethodCall.argument("maxWidth")).thenReturn(WIDTH);
449+
when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString");
365450

366451
ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall();
367452
delegate.onActivityResult(
@@ -375,6 +460,7 @@ public void onActivityResult_WhenImageTakenWithCamera_AndNoResizeNeeded_Finishes
375460
public void
376461
onActivityResult_WhenVideoTakenWithCamera_AndResizeParametersSupplied_FinishesWithFilePath() {
377462
when(mockMethodCall.argument("maxWidth")).thenReturn(WIDTH);
463+
when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString");
378464

379465
ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall();
380466
delegate.onActivityResult(
@@ -388,6 +474,7 @@ public void onActivityResult_WhenImageTakenWithCamera_AndNoResizeNeeded_Finishes
388474
public void
389475
onActivityResult_WhenVideoTakenWithCamera_AndMaxDurationParametersSupplied_FinishesWithFilePath() {
390476
when(mockMethodCall.argument("maxDuration")).thenReturn(MAX_DURATION);
477+
when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString");
391478

392479
ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall();
393480
delegate.onActivityResult(

packages/image_picker/image_picker_android/example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
2525
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
2626

2727
android {
28-
compileSdkVersion 31
28+
compileSdkVersion 33
2929
testOptions.unitTests.includeAndroidResources = true
3030

3131
lintOptions {

packages/image_picker/image_picker_android/pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ name: image_picker_android
22
description: Android implementation of the image_picker plugin.
33
repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_android
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
5-
version: 0.8.5+7
5+
6+
version: 0.8.5+8
67

78
environment:
89
sdk: ">=2.14.0 <3.0.0"

0 commit comments

Comments
 (0)