Skip to content

Commit

Permalink
image_picker: don't require QUERY_ALL_PACKAGES on sdk30
Browse files Browse the repository at this point in the history
resolveActivity(intent) might return false on android 11+_even though startActivity(intent) would succeed.
Instead of introducing a <queries> entry we can just try to start the
activity and catch the exception if that doesn't work.

Properly fixes flutter/flutter#62669.

Ref: https://cketti.de/2020/09/03/avoid-intent-resolveactivity/
  • Loading branch information
Bubu committed Aug 2, 2021
1 parent a3accd7 commit 9f64f31
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 53 deletions.
18 changes: 3 additions & 15 deletions packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
## NEXT

* Move `ImagePickerFromLimitedGalleryUITests` to `RunnerUITests` target.

## 0.8.2

* Added new methods that return `package:cross_file` `XFile` instances. [Docs](https://pub.dev/documentation/cross_file/latest/index.html).
* Deprecate methods that return `PickedFile` instances:
* `getImage`: use **`pickImage`** instead.
* `getVideo`: use **`pickVideo`** instead.
* `getMultiImage`: use **`pickMultiImage`** instead.
* `getLostData`: use **`retrieveLostData`** instead.

## 0.8.1+4

* Fixes an issue where `preferredCameraDevice` option is not working for `getVideo` method.
* Refactor unit tests that were device-only before.
* Fix using Camera as image source on Android 11+

## 0.8.1+3

* Fix image picker causing a crash when the cache directory is deleted.

## 0.8.1+2

* Update the example app to support the multi-image feature.

## 0.8.1+1
Expand Down Expand Up @@ -65,6 +52,7 @@ will no longer be able to access images or videos captured unless they are moved
* Localize `UIAlertController` strings.

## 0.7.5+2

* Implement `UIAlertController` with a preferredStyle of `UIAlertControllerStyleAlert` since `UIAlertView` is deprecated.

## 0.7.5+1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
Expand Down Expand Up @@ -88,7 +89,6 @@ public class ImagePickerDelegate
private final ImageResizer imageResizer;
private final ImagePickerCache cache;
private final PermissionManager permissionManager;
private final IntentResolver intentResolver;
private final FileUriResolver fileUriResolver;
private final FileUtils fileUtils;
private CameraDevice cameraDevice;
Expand All @@ -101,10 +101,6 @@ interface PermissionManager {
boolean needRequestCameraPermission();
}

interface IntentResolver {
boolean resolveActivity(Intent intent);
}

interface FileUriResolver {
Uri resolveFileProviderUriForFile(String fileProviderName, File imageFile);

Expand Down Expand Up @@ -148,12 +144,6 @@ public boolean needRequestCameraPermission() {
return ImagePickerUtils.needRequestCameraPermission(activity);
}
},
new IntentResolver() {
@Override
public boolean resolveActivity(Intent intent) {
return intent.resolveActivity(activity.getPackageManager()) != null;
}
},
new FileUriResolver() {
@Override
public Uri resolveFileProviderUriForFile(String fileProviderName, File file) {
Expand Down Expand Up @@ -190,7 +180,6 @@ public void onScanCompleted(String path, Uri uri) {
final MethodCall methodCall,
final ImagePickerCache cache,
final PermissionManager permissionManager,
final IntentResolver intentResolver,
final FileUriResolver fileUriResolver,
final FileUtils fileUtils) {
this.activity = activity;
Expand All @@ -200,7 +189,6 @@ public void onScanCompleted(String path, Uri uri) {
this.pendingResult = result;
this.methodCall = methodCall;
this.permissionManager = permissionManager;
this.intentResolver = intentResolver;
this.fileUriResolver = fileUriResolver;
this.fileUtils = fileUtils;
this.cache = cache;
Expand Down Expand Up @@ -291,21 +279,18 @@ private void launchTakeVideoWithCameraIntent() {
useFrontCamera(intent);
}

boolean canTakePhotos = intentResolver.resolveActivity(intent);

if (!canTakePhotos) {
finishWithError("no_available_camera", "No cameras available for taking pictures.");
return;
}

File videoFile = createTemporaryWritableVideoFile();
pendingCameraMediaUri = Uri.parse("file:" + videoFile.getAbsolutePath());

Uri videoUri = fileUriResolver.resolveFileProviderUriForFile(fileProviderName, videoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, videoUri);
grantUriPermissions(intent, videoUri);

activity.startActivityForResult(intent, REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA);
try {
activity.startActivityForResult(intent, REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA);
} catch (ActivityNotFoundException e) {
4 finishWithError("no_available_camera", "No cameras available for taking pictures.");
}
}

public void chooseImageFromGallery(MethodCall methodCall, MethodChannel.Result result) {
Expand Down Expand Up @@ -371,21 +356,18 @@ private void launchTakeImageWithCameraIntent() {
useFrontCamera(intent);
}

boolean canTakePhotos = intentResolver.resolveActivity(intent);

if (!canTakePhotos) {
finishWithError("no_available_camera", "No cameras available for taking pictures.");
return;
}

File imageFile = createTemporaryWritableImageFile();
pendingCameraMediaUri = Uri.parse("file:" + imageFile.getAbsolutePath());

Uri imageUri = fileUriResolver.resolveFileProviderUriForFile(fileProviderName, imageFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
grantUriPermissions(intent, imageUri);

activity.startActivityForResult(intent, REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA);
try {
activity.startActivityForResult(intent, REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA);
} catch (ActivityNotFoundException e) {
finishWithError("no_available_camera", "No cameras available for taking pictures.");
}
}

private File createTemporaryWritableImageFile() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
Expand All @@ -16,6 +18,7 @@

import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
Expand All @@ -42,7 +45,6 @@ public class ImagePickerDelegateTest {
@Mock MethodCall mockMethodCall;
@Mock MethodChannel.Result mockResult;
@Mock ImagePickerDelegate.PermissionManager mockPermissionManager;
@Mock ImagePickerDelegate.IntentResolver mockIntentResolver;
@Mock FileUtils mockFileUtils;
@Mock Intent mockIntent;
@Mock ImagePickerCache cache;
Expand Down Expand Up @@ -164,7 +166,6 @@ public void takeImageWithCamera_WhenHasNoCameraPermission_RequestsForPermission(
@Test
public void takeImageWithCamera_WhenCameraPermissionNotPresent_RequestsForPermission() {
when(mockPermissionManager.needRequestCameraPermission()).thenReturn(false);
when(mockIntentResolver.resolveActivity(any(Intent.class))).thenReturn(true);

ImagePickerDelegate delegate = createDelegate();
delegate.takeImageWithCamera(mockMethodCall, mockResult);
Expand All @@ -178,7 +179,6 @@ public void takeImageWithCamera_WhenCameraPermissionNotPresent_RequestsForPermis
public void
takeImageWithCamera_WhenHasCameraPermission_AndAnActivityCanHandleCameraIntent_LaunchesTakeWithCameraIntent() {
when(mockPermissionManager.isPermissionGranted(Manifest.permission.CAMERA)).thenReturn(true);
when(mockIntentResolver.resolveActivity(any(Intent.class))).thenReturn(true);

ImagePickerDelegate delegate = createDelegate();
delegate.takeImageWithCamera(mockMethodCall, mockResult);
Expand All @@ -192,8 +192,9 @@ public void takeImageWithCamera_WhenCameraPermissionNotPresent_RequestsForPermis
public void
takeImageWithCamera_WhenHasCameraPermission_AndNoActivityToHandleCameraIntent_FinishesWithNoCamerasAvailableError() {
when(mockPermissionManager.isPermissionGranted(Manifest.permission.CAMERA)).thenReturn(true);
when(mockIntentResolver.resolveActivity(any(Intent.class))).thenReturn(false);

doThrow(ActivityNotFoundException.class)
.when(mockActivity)
.startActivityForResult(any(Intent.class), anyInt());
ImagePickerDelegate delegate = createDelegate();
delegate.takeImageWithCamera(mockMethodCall, mockResult);

Expand Down Expand Up @@ -231,7 +232,6 @@ public void onRequestPermissionsResult_WhenCameraPermissionDenied_FinishesWithEr
@Test
public void
onRequestTakeVideoPermissionsResult_WhenCameraPermissionGranted_LaunchesTakeVideoWithCameraIntent() {
when(mockIntentResolver.resolveActivity(any(Intent.class))).thenReturn(true);

ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall();
delegate.onRequestPermissionsResult(
Expand All @@ -247,7 +247,6 @@ public void onRequestPermissionsResult_WhenCameraPermissionDenied_FinishesWithEr
@Test
public void
onRequestTakeImagePermissionsResult_WhenCameraPermissionGranted_LaunchesTakeWithCameraIntent() {
when(mockIntentResolver.resolveActivity(any(Intent.class))).thenReturn(true);

ImagePickerDelegate delegate = createDelegateWithPendingResultAndMethodCall();
delegate.onRequestPermissionsResult(
Expand Down Expand Up @@ -379,7 +378,6 @@ private ImagePickerDelegate createDelegate() {
null,
cache,
mockPermissionManager,
mockIntentResolver,
mockFileUriResolver,
mockFileUtils);
}
Expand All @@ -393,7 +391,6 @@ private ImagePickerDelegate createDelegateWithPendingResultAndMethodCall() {
mockMethodCall,
cache,
mockPermissionManager,
mockIntentResolver,
mockFileUriResolver,
mockFileUtils);
}
Expand Down

0 comments on commit 9f64f31

Please sign in to comment.