Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/image_picker/image_picker_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## NEXT
## 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.

## 0.8.5+5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ rootProject.allprojects {
apply plugin: 'com.android.library'

android {
compileSdkVersion 31
compileSdkVersion 33

defaultConfig {
minSdkVersion 16
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Comment on lines +272 to +275
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for these to be different from each other. What is needed is just that they're different from anything else (any request codes anywhere in the app — not sure what the practice is among Flutter plugins for avoiding accidental conflicts between them), and that this plugin's onActivityResult handles them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok perfect!

I initially made these different so the tests could confirm that the correct intent tag was being used based on the given Android SDK.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


activity.startActivityForResult(pickVideoIntent, REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY);
activity.startActivityForResult(pickVideoIntent, requestCode);
}

public void takeVideoWithCamera(MethodCall methodCall, MethodChannel.Result result) {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion packages/image_picker/image_picker_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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+5
version: 0.8.6

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down