blob: 893785f1a58fed513634e47332ea72f650a43813 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.plugins.camera;
import android.app.Activity;
import android.hardware.camera2.CameraAccessException;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.camera.CameraPermissions.PermissionsRegistry;
import io.flutter.plugins.camera.features.CameraFeatureFactoryImpl;
import io.flutter.plugins.camera.features.Point;
import io.flutter.plugins.camera.features.autofocus.FocusMode;
import io.flutter.plugins.camera.features.exposurelock.ExposureMode;
import io.flutter.plugins.camera.features.flash.FlashMode;
import io.flutter.plugins.camera.features.resolution.ResolutionPreset;
import io.flutter.view.TextureRegistry;
import java.util.HashMap;
import java.util.Map;
final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler, LifecycleObserver {
private final Activity activity;
private final BinaryMessenger messenger;
private final CameraPermissions cameraPermissions;
private final PermissionsRegistry permissionsRegistry;
private final TextureRegistry textureRegistry;
private final MethodChannel methodChannel;
private final EventChannel imageStreamChannel;
private final Lifecycle lifecycle;
private @Nullable Camera camera;
MethodCallHandlerImpl(
Activity activity,
BinaryMessenger messenger,
CameraPermissions cameraPermissions,
PermissionsRegistry permissionsAdder,
TextureRegistry textureRegistry,
@Nullable Lifecycle lifecycle) {
this.activity = activity;
this.messenger = messenger;
this.cameraPermissions = cameraPermissions;
this.permissionsRegistry = permissionsAdder;
this.textureRegistry = textureRegistry;
this.lifecycle = lifecycle;
methodChannel = new MethodChannel(messenger, "plugins.flutter.io/camera");
imageStreamChannel = new EventChannel(messenger, "plugins.flutter.io/camera/imageStream");
methodChannel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) {
switch (call.method) {
case "availableCameras":
try {
result.success(CameraUtils.getAvailableCameras(activity));
} catch (Exception e) {
handleException(e, result);
}
break;
case "create":
{
if (camera != null) {
camera.close();
}
cameraPermissions.requestPermissions(
activity,
permissionsRegistry,
call.argument("enableAudio"),
(String errCode, String errDesc) -> {
if (errCode == null) {
try {
instantiateCamera(call, result);
} catch (Exception e) {
handleException(e, result);
}
} else {
result.error(errCode, errDesc, null);
}
});
break;
}
case "initialize":
{
if (camera != null) {
try {
camera.open(call.argument("imageFormatGroup"));
result.success(null);
} catch (Exception e) {
handleException(e, result);
}
} else {
result.error(
"cameraNotFound",
"Camera not found. Please call the 'create' method before calling 'initialize'.",
null);
}
break;
}
case "takePicture":
{
camera.takePicture(result);
break;
}
case "prepareForVideoRecording":
{
// This optimization is not required for Android.
result.success(null);
break;
}
case "startVideoRecording":
{
camera.startVideoRecording(result);
break;
}
case "stopVideoRecording":
{
camera.stopVideoRecording(result);
break;
}
case "pauseVideoRecording":
{
camera.pauseVideoRecording(result);
break;
}
case "resumeVideoRecording":
{
camera.resumeVideoRecording(result);
break;
}
case "setFlashMode":
{
String modeStr = call.argument("mode");
FlashMode mode = FlashMode.getValueForString(modeStr);
if (mode == null) {
result.error("setFlashModeFailed", "Unknown flash mode " + modeStr, null);
return;
}
try {
camera.setFlashMode(result, mode);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "setExposureMode":
{
String modeStr = call.argument("mode");
ExposureMode mode = ExposureMode.getValueForString(modeStr);
if (mode == null) {
result.error("setExposureModeFailed", "Unknown exposure mode " + modeStr, null);
return;
}
try {
camera.setExposureMode(result, mode);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "setExposurePoint":
{
Boolean reset = call.argument("reset");
Double x = null;
Double y = null;
if (reset == null || !reset) {
x = call.argument("x");
y = call.argument("y");
}
try {
camera.setExposurePoint(result, new Point(x, y));
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "getMinExposureOffset":
{
try {
result.success(camera.getMinExposureOffset());
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "getMaxExposureOffset":
{
try {
result.success(camera.getMaxExposureOffset());
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "getExposureOffsetStepSize":
{
try {
result.success(camera.getExposureOffsetStepSize());
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "setExposureOffset":
{
try {
camera.setExposureOffset(result, call.argument("offset"));
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "setFocusMode":
{
String modeStr = call.argument("mode");
FocusMode mode = FocusMode.getValueForString(modeStr);
if (mode == null) {
result.error("setFocusModeFailed", "Unknown focus mode " + modeStr, null);
return;
}
try {
camera.setFocusMode(result, mode);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "setFocusPoint":
{
Boolean reset = call.argument("reset");
Double x = null;
Double y = null;
if (reset == null || !reset) {
x = call.argument("x");
y = call.argument("y");
}
try {
camera.setFocusPoint(result, new Point(x, y));
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "startImageStream":
{
try {
camera.startPreviewWithImageStream(imageStreamChannel);
result.success(null);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "stopImageStream":
{
try {
camera.startPreview();
result.success(null);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "getMaxZoomLevel":
{
assert camera != null;
try {
float maxZoomLevel = camera.getMaxZoomLevel();
result.success(maxZoomLevel);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "getMinZoomLevel":
{
assert camera != null;
try {
float minZoomLevel = camera.getMinZoomLevel();
result.success(minZoomLevel);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "setZoomLevel":
{
assert camera != null;
Double zoom = call.argument("zoom");
if (zoom == null) {
result.error(
"ZOOM_ERROR", "setZoomLevel is called without specifying a zoom level.", null);
return;
}
try {
camera.setZoomLevel(result, zoom.floatValue());
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "lockCaptureOrientation":
{
PlatformChannel.DeviceOrientation orientation =
CameraUtils.deserializeDeviceOrientation(call.argument("orientation"));
try {
camera.lockCaptureOrientation(orientation);
result.success(null);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "unlockCaptureOrientation":
{
try {
camera.unlockCaptureOrientation();
result.success(null);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "dispose":
{
if (camera != null) {
camera.dispose();
}
result.success(null);
break;
}
default:
result.notImplemented();
break;
}
}
void stopListening() {
methodChannel.setMethodCallHandler(null);
}
private void instantiateCamera(MethodCall call, Result result) throws CameraAccessException {
String cameraName = call.argument("cameraName");
String preset = call.argument("resolutionPreset");
boolean enableAudio = call.argument("enableAudio");
TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture =
textureRegistry.createSurfaceTexture();
DartMessenger dartMessenger =
new DartMessenger(
messenger, flutterSurfaceTexture.id(), new Handler(Looper.getMainLooper()));
CameraProperties cameraProperties =
new CameraPropertiesImpl(cameraName, CameraUtils.getCameraManager(activity));
ResolutionPreset resolutionPreset = ResolutionPreset.valueOf(preset);
if (camera != null && lifecycle != null) {
lifecycle.removeObserver(camera);
}
camera =
new Camera(
activity,
flutterSurfaceTexture,
new CameraFeatureFactoryImpl(),
dartMessenger,
cameraProperties,
resolutionPreset,
enableAudio);
if (lifecycle != null) {
lifecycle.addObserver(camera);
}
Map<String, Object> reply = new HashMap<>();
reply.put("cameraId", flutterSurfaceTexture.id());
result.success(reply);
}
// We move catching CameraAccessException out of onMethodCall because it causes a crash
// on plugin registration for sdks incompatible with Camera2 (< 21). We want this plugin to
// to be able to compile with <21 sdks for apps that want the camera and support earlier version.
@SuppressWarnings("ConstantConditions")
private void handleException(Exception exception, Result result) {
if (exception instanceof CameraAccessException) {
result.error("CameraAccess", exception.getMessage(), null);
return;
}
// CameraAccessException can not be cast to a RuntimeException.
throw (RuntimeException) exception;
}
}