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
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2019 The Chromium 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 dev.flutter.plugins.camera;

import android.hardware.camera2.CameraAccessException;
import android.os.Build;

import androidx.annotation.NonNull;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.camera.Camera;
import io.flutter.plugins.camera.CameraChannelHandler;
import io.flutter.plugins.camera.CameraPermissions;
import io.flutter.plugins.camera.CameraUtils;

public class CameraPlugin implements FlutterPlugin, ActivityAware {

private FlutterPluginBinding pluginBinding;
private ActivityPluginBinding activityBinding;

private final CameraPermissions cameraPermissions = new CameraPermissions();
private EventChannel imageStreamChannel;
private Camera camera;

@Override
public void onAttachedToEngine(FlutterPluginBinding flutterPluginBinding) {
this.pluginBinding = flutterPluginBinding;
this.imageStreamChannel = new EventChannel(
this.pluginBinding.getFlutterEngine().getDartExecutor(),
"plugins.flutter.io/camera/imageStream"
);
}

@Override
public void onDetachedFromEngine(FlutterPluginBinding flutterPluginBinding) {
this.pluginBinding = null;
}

@Override
public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) {
// Setup
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// When a background flutter view tries to register the plugin, the registrar has no activity.
// We stop the registration process as this plugin is foreground only. Also, if the sdk is
// less than 21 (min sdk for Camera2) we don't register the plugin.
return;
}

this.activityBinding = activityPluginBinding;

final MethodChannel channel =
new MethodChannel(pluginBinding.getFlutterEngine().getDartExecutor(), "plugins.flutter.io/camera");

channel.setMethodCallHandler(new CameraChannelHandler(
activityPluginBinding.getActivity(),
pluginBinding.getFlutterEngine().getRenderer(),
pluginBinding.getFlutterEngine().getDartExecutor(),
new EventChannel(pluginBinding.getFlutterEngine().getDartExecutor(), "plugins.flutter.io/camera/imageStream"),
new NewEmbeddingPermissions(activityPluginBinding)
));
}

@Override
public void onDetachedFromActivityForConfigChanges() {
// Ignore
}

@Override
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) {
// Ignore
}

@Override
public void onDetachedFromActivity() {
// Teardown
this.activityBinding = null;
}

private static class NewEmbeddingPermissions implements CameraPermissions.Permissions {
private ActivityPluginBinding activityPluginBinding;

private NewEmbeddingPermissions(@NonNull ActivityPluginBinding activityPluginBinding) {
this.activityPluginBinding = activityPluginBinding;
}

@Override
public PluginRegistry.Registrar addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener listener) {
activityPluginBinding.addRequestPermissionsResultListener(listener);

return null; // <- this is a breaking change.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.view.FlutterView;
import io.flutter.view.TextureRegistry;
import io.flutter.view.TextureRegistry.SurfaceTextureEntry;
import java.io.File;
import java.io.FileOutputStream;
Expand Down Expand Up @@ -74,7 +75,7 @@ public enum ResolutionPreset {

public Camera(
final Activity activity,
final FlutterView flutterView,
final TextureRegistry textureRegistry,
final String cameraName,
final String resolutionPreset,
final boolean enableAudio)
Expand All @@ -85,7 +86,7 @@ public Camera(

this.cameraName = cameraName;
this.enableAudio = enableAudio;
this.flutterTexture = flutterView.createSurfaceTexture();
this.flutterTexture = textureRegistry.createSurfaceTexture();
this.cameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
orientationEventListener =
new OrientationEventListener(activity.getApplicationContext()) {
Expand Down Expand Up @@ -233,7 +234,7 @@ private void writeToFile(ByteBuffer buffer, File file) throws IOException {
}
}

SurfaceTextureEntry getFlutterTexture() {
public SurfaceTextureEntry getFlutterTexture() {
return flutterTexture;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package io.flutter.plugins.camera;

import android.app.Activity;
import android.hardware.camera2.CameraAccessException;

import androidx.annotation.NonNull;

import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
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.view.TextureRegistry;

public class CameraChannelHandler implements MethodChannel.MethodCallHandler {

private final CameraPermissions cameraPermissions = new CameraPermissions();
private final Activity activity;
private final TextureRegistry textureRegistry;
private final BinaryMessenger messenger;
private final EventChannel imageStreamChannel;
private final CameraPermissions.Permissions permissions;
private Camera camera;

public CameraChannelHandler(
@NonNull Activity activity,
@NonNull TextureRegistry textureRegistry,
@NonNull BinaryMessenger binaryMessenger,
@NonNull EventChannel imageStreamChannel,
@NonNull CameraPermissions.Permissions permissions
) {
this.activity = activity;
this.textureRegistry = textureRegistry;
this.messenger = binaryMessenger;
this.imageStreamChannel = imageStreamChannel;
this.permissions = permissions;
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull final MethodChannel.Result result) {
switch (call.method) {
case "availableCameras":
try {
result.success(CameraUtils.getAvailableCameras(activity));
} catch (Exception e) {
handleException(e, result);
}
break;
case "initialize":
{
if (camera != null) {
camera.close();
}
cameraPermissions.requestPermissions(
activity,
permissions,
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 "takePicture":
{
camera.takePicture(call.argument("path"), result);
break;
}
case "prepareForVideoRecording":
{
// This optimization is not required for Android.
result.success(null);
break;
}
case "startVideoRecording":
{
camera.startVideoRecording(call.argument("filePath"), result);
break;
}
case "stopVideoRecording":
{
camera.stopVideoRecording(result);
break;
}
case "pauseVideoRecording":
{
camera.pauseVideoRecording(result);
break;
}
case "resumeVideoRecording":
{
camera.resumeVideoRecording(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 "dispose":
{
if (camera != null) {
camera.dispose();
}
result.success(null);
break;
}
default:
result.notImplemented();
break;
}
}

private void instantiateCamera(MethodCall call, MethodChannel.Result result) throws CameraAccessException {
String cameraName = call.argument("cameraName");
String resolutionPreset = call.argument("resolutionPreset");
boolean enableAudio = call.argument("enableAudio");
camera = new Camera(activity, textureRegistry, cameraName, resolutionPreset, enableAudio);

EventChannel cameraEventChannel =
new EventChannel(
messenger,
"flutter.io/cameraPlugin/cameraEvents" + camera.getFlutterTexture().id());
camera.setupCameraEventChannel(cameraEventChannel);

camera.open(result);
}

// 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, MethodChannel.Result result) {
if (exception instanceof CameraAccessException) {
result.error("CameraAccess", exception.getMessage(), null);
}

throw (RuntimeException) exception;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.Registrar;

Expand All @@ -14,13 +16,12 @@ public class CameraPermissions {
private boolean ongoing = false;

public void requestPermissions(
Registrar registrar, boolean enableAudio, ResultCallback callback) {
Activity activity, Permissions permissions, boolean enableAudio, ResultCallback callback) {
if (ongoing) {
callback.onResult("cameraPermission", "Camera permission request ongoing");
}
Activity activity = registrar.activity();
if (!hasCameraPermission(activity) || (enableAudio && !hasAudioPermission(activity))) {
registrar.addRequestPermissionsResultListener(
permissions.addRequestPermissionsResultListener(
new CameraRequestPermissionsListener(
(String errorCode, String errorDescription) -> {
ongoing = false;
Expand Down Expand Up @@ -74,7 +75,13 @@ public boolean onRequestPermissionsResult(int id, String[] permissions, int[] gr
}
}

interface ResultCallback {
public interface Permissions {
Registrar addRequestPermissionsResultListener(
PluginRegistry.RequestPermissionsResultListener listener
);
}

public interface ResultCallback {
void onResult(String errorCode, String errorDescription);
}
}
Loading