Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[image_picker] fix camera on Android 11 #3194

Merged
Merged
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
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();
pedromassango marked this conversation as resolved.
Show resolved Hide resolved
} 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.");
Bubu marked this conversation as resolved.
Show resolved Hide resolved
}
}

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