Skip to content

Commit

Permalink
[image_picker] fix camera on Android 11 (flutter#3194)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bubu authored and amantoux committed Sep 27, 2021
1 parent eef7c0b commit 267ae23
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 40 deletions.
5 changes: 5 additions & 0 deletions packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.8.3+2

* Fix using Camera as image source on Android 11+

## 0.8.3+1

* Fixed README Example.
Expand Down Expand Up @@ -26,6 +30,7 @@
* 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
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,25 @@ 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) {
try {
// If we can't delete the file again here, there's not really anything we can do about it.
//noinspection ResultOfMethodCallIgnored
videoFile.delete();
} catch (SecurityException exception) {
exception.printStackTrace();
}
finishWithError("no_available_camera", "No cameras available for taking pictures.");
}
}

public void chooseImageFromGallery(MethodCall methodCall, MethodChannel.Result result) {
Expand Down Expand Up @@ -371,21 +363,25 @@ 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) {
try {
// If we can't delete the file again here, there's not really anything we can do about it.
//noinspection ResultOfMethodCallIgnored
imageFile.delete();
} catch (SecurityException exception) {
exception.printStackTrace();
}
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 All @@ -205,7 +206,6 @@ public void takeImageWithCamera_WhenCameraPermissionNotPresent_RequestsForPermis
@Test
public void takeImageWithCamera_WritesImageToCacheDirectory() {
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 @@ -231,7 +231,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 +246,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 +377,6 @@ private ImagePickerDelegate createDelegate() {
null,
cache,
mockPermissionManager,
mockIntentResolver,
mockFileUriResolver,
mockFileUtils);
}
Expand All @@ -393,7 +390,6 @@ private ImagePickerDelegate createDelegateWithPendingResultAndMethodCall() {
mockMethodCall,
cache,
mockPermissionManager,
mockIntentResolver,
mockFileUriResolver,
mockFileUtils);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/image_picker/image_picker/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image
library, and taking new pictures with the camera.
repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
version: 0.8.3+1
version: 0.8.3+2

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

0 comments on commit 267ae23

Please sign in to comment.