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 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
475f4ab
Replicated existing code and migrated references to new embedding, ad…
matthew-carroll Sep 24, 2019
8cde03b
Introduced CameraPreviewDisplay and CameraImageStream to remove Flutt…
matthew-carroll Sep 24, 2019
2081f42
Introduced a CameraEventListener to completely decouple Camera from E…
matthew-carroll Sep 24, 2019
b78bfcb
Removed all references of Result from Camera by introducing a combina…
matthew-carroll Sep 24, 2019
b45ae68
Refactored Camera such that getFlutterTexture() no longer needs to ex…
matthew-carroll Sep 24, 2019
6780abf
Moved ResolutionPreset to standalone class and made Camera package pr…
matthew-carroll Sep 24, 2019
8b93f68
Re-organized Camera code order to clearly separate preview images, fr…
matthew-carroll Sep 24, 2019
c85fcc2
Removed Activity reference from Camera.
matthew-carroll Sep 24, 2019
e04653b
Refactored CameraPermissions to eliminate Activity, ActivityCompat, C…
matthew-carroll Sep 24, 2019
60c1d20
Refactored CameraPermissions into an Interface and AndroidCameraPermi…
matthew-carroll Sep 25, 2019
13e4303
Separated the CameraPlugin class from the concept of the comms Camera…
matthew-carroll Sep 25, 2019
d5da9ce
Introduced CameraDetails data structure & wrote unit tests for Camera…
matthew-carroll Sep 25, 2019
436d9ca
Added unit tests for CameraPluginProtocol and reached 100% coverage f…
matthew-carroll Sep 25, 2019
3dcc0ab
Moved CameraPreviewDisplay and CameraImageStream implementations into…
matthew-carroll Sep 26, 2019
02ab397
Extracted per-camera channel construction out into CameraPlugin for t…
matthew-carroll Sep 26, 2019
e5408d9
Refactored so that CameraSystem and AndroidCameraSystem could be redu…
matthew-carroll Sep 26, 2019
6ea1b4f
Deleted CameraUtils, and continued to cleanup relationships.
matthew-carroll Sep 26, 2019
4669cb4
Added tests for CameraSystem - at 93% line coverage.
matthew-carroll Sep 26, 2019
410d0ac
Added tests to ensure that CameraPlugin does nothing without an Activ…
matthew-carroll Sep 26, 2019
0bd6522
Added tests for ChannelCameraEventHandler - now at CameraSystem (93%)…
matthew-carroll Sep 26, 2019
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
Prev Previous commit
Next Next commit
Separated the CameraPlugin class from the concept of the comms Camera…
…PluginProtocol, which resulted in the simplest possible implementation of CameraPlugin. Also added copyright to new files.
  • Loading branch information
matthew-carroll committed Sep 25, 2019
commit 13e4303fc8991915d77287f1e62ce72b4f1b26c7
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// 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.Manifest;
Expand Down Expand Up @@ -52,7 +56,7 @@ public void requestPermissions(boolean enableAudio, CameraPermissions.ResultCall
CAMERA_REQUEST_ID);
} else {
// Permissions already exist. Call the callback with success.
callback.onResult(null, null);
callback.onSuccess();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// 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.content.Context;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.EventChannel;
import io.flutter.view.TextureRegistry;

/* package */ class AndroidCameraSystem implements CameraSystem {
@NonNull
private final FlutterPlugin.FlutterPluginBinding pluginBinding;
@NonNull
private final ActivityPluginBinding activityBinding;
@NonNull
private final CameraPermissions cameraPermissions;
@NonNull
private final EventChannel imageStreamChannel;
@Nullable
private Camera camera;

/* package */ AndroidCameraSystem(
@NonNull FlutterPlugin.FlutterPluginBinding pluginBinding,
@NonNull ActivityPluginBinding activityBinding,
@NonNull CameraPermissions cameraPermissions,
@NonNull EventChannel imageStreamChannel
) {
this.pluginBinding = pluginBinding;
this.activityBinding = activityBinding;
this.cameraPermissions = cameraPermissions;
this.imageStreamChannel = imageStreamChannel;
}

@Override
public List<Map<String, Object>> getAvailableCameras() throws CameraAccessException {
return CameraUtils.getAvailableCameras(activityBinding.getActivity());
}

@Override
public void initialize(
@NonNull CameraConfigurationRequest request,
@NonNull OnCameraInitializationCallback callback
) {
if (camera != null) {
camera.close();
}

cameraPermissions.requestPermissions(
request.getEnableAudio(),
new CameraPermissions.ResultCallback() {
@Override
public void onSuccess() {
try {
instantiateCamera(request, callback);
} catch (Exception error) {
callback.onError("CameraAccess", error.getMessage());
}
}

@Override
public void onResult(String errorCode, String errorDescription) {
callback.onCameraPermissionError(errorCode, errorDescription);
}
});
}

private void instantiateCamera(
@NonNull CameraConfigurationRequest request,
@NonNull OnCameraInitializationCallback callback
) throws CameraAccessException {
TextureRegistry.SurfaceTextureEntry textureEntry = pluginBinding
.getFlutterEngine()
.getRenderer()
.createSurfaceTexture();

camera = new Camera(
activityBinding.getActivity(),
(CameraManager) activityBinding.getActivity().getSystemService(Context.CAMERA_SERVICE),
textureEntry,
request.getCameraName(),
request.getResolutionPreset(),
request.getEnableAudio()
);

camera.open(new Camera.OnCameraOpenedCallback() {
@Override
public void onCameraOpened(long textureId, int previewWidth, int previewHeight) {
callback.onSuccess(textureId, previewWidth, previewHeight);
}

@Override
public void onCameraOpenFailed(@NonNull String message) {
callback.onError("CameraAccess", message);
}
});

EventChannel cameraEventChannel = new EventChannel(
pluginBinding.getFlutterEngine().getDartExecutor(),
"flutter.io/cameraPlugin/cameraEvents" + textureEntry.id()
);
cameraEventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object o, final EventChannel.EventSink eventSink) {
final Camera.CameraEventHandler cameraEventHandler = new Camera.CameraEventHandler() {
@Override
public void onError(String description) {
Map<String, String> event = new HashMap<>();
event.put("eventType", "error");
event.put("errorDescription", description);
eventSink.success(event);
}

@Override
public void onCameraClosed() {
Map<String, String> event = new HashMap<>();
event.put("eventType", "camera_closing");
eventSink.success(event);
}
};

camera.setCameraEventHandler(cameraEventHandler);
}

@Override
public void onCancel(Object o) {
camera.setCameraEventHandler(null);
}
});
}

@Override
public void takePicture(@NonNull String filePath, @NonNull Camera.OnPictureTakenCallback callback) {
// TODO(mattcarroll): determine desired behavior when no camera is active
camera.takePicture(filePath, callback);
}

@Override
public void startVideoRecording(@NonNull String filePath, @NonNull OnStartVideoRecordingCallback callback) {
// TODO(mattcarroll): determine desired behavior when no camera is active
try {
camera.startVideoRecording(filePath);
} catch (IllegalStateException e) {
callback.onFileAlreadyExists(filePath);
} catch (CameraAccessException | IOException e) {
callback.onVideoRecordingFailed(e.getMessage());
}
}

@Override
public void stopVideoRecording(@NonNull OnVideoRecordingCommandCallback callback) {
// TODO(mattcarroll): determine desired behavior when no camera is active
try {
camera.stopVideoRecording();
callback.onSuccess();
} catch (CameraAccessException | IllegalStateException e) {
callback.onVideoRecordingFailed(e.getMessage());
}
}

@Override
public void pauseVideoRecording(@NonNull OnApiDependentVideoRecordingCommandCallback callback) {
// TODO(mattcarroll): determine desired behavior when no camera is active
try {
camera.pauseVideoRecording();
} catch (UnsupportedOperationException e) {
callback.onUnsupportedOperation();
} catch (IllegalStateException e) {
callback.onVideoRecordingFailed(e.getMessage());
}
}

@Override
public void resumeVideoRecording(@NonNull OnApiDependentVideoRecordingCommandCallback callback) {
// TODO(mattcarroll): determine desired behavior when no camera is active
try {
camera.resumeVideoRecording();
} catch (UnsupportedOperationException e) {
callback.onUnsupportedOperation();
} catch (IllegalStateException e) {
callback.onVideoRecordingFailed(e.getMessage());
}
}

@Override
public void startImageStream(@NonNull OnCameraAccessCommandCallback callback) {
// TODO(mattcarroll): determine desired behavior when no camera is active
try {
CameraPreviewDisplay previewDisplay = new CameraPreviewDisplay(imageStreamChannel);
camera.startPreviewWithImageStream(previewDisplay);
callback.success();
} catch (CameraAccessException e) {
callback.onCameraAccessFailure(e.getMessage());
}
}

@Override
public void stopImageStream(@NonNull OnCameraAccessCommandCallback callback) {
// TODO(mattcarroll): determine desired behavior when no camera is active
try {
camera.startPreview();
callback.success();
} catch (CameraAccessException e) {
callback.onCameraAccessFailure(e.getMessage());
}
}

@Override
public void dispose() {
if (camera != null) {
camera.dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,9 @@ private void writeToFile(ByteBuffer buffer, File file) throws IOException {
//------ End: Take picture with Camera -------

//------ Start: Video recording with Camera ----
public void startVideoRecording(String filePath) throws IOException, CameraAccessException, IllegalStateException {
public void startVideoRecording(@NonNull String filePath) throws IOException, CameraAccessException, IllegalStateException {
if (new File(filePath).exists()) {
throw new IllegalStateException("File " + filePath + " already exists.");
throw new IOException("File " + filePath + " already exists.");
}

prepareMediaRecorder(filePath);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// 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.media.Image;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// 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 androidx.annotation.NonNull;
Expand Down
Loading