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
Show all changes
42 commits
Select commit Hold shift + click to select a range
db7ffa2
Start draft
camsim99 Oct 26, 2022
a20af04
Dev
camsim99 Oct 27, 2022
aa56e6f
Dev
camsim99 Oct 27, 2022
63c661c
Dev
camsim99 Oct 31, 2022
7ff9593
Dev
camsim99 Nov 7, 2022
9f2cbfa
Dev
camsim99 Nov 7, 2022
849c881
Merge remote-tracking branch 'upstream/main' into preview_poc
camsim99 Nov 7, 2022
7ccb3cd
Dev
camsim99 Nov 9, 2022
ffa132d
Get preview running
camsim99 Nov 10, 2022
33afdb6
Get preview to show up
camsim99 Nov 15, 2022
53c3aa3
Add pause and resume preview
camsim99 Nov 16, 2022
b13b8bc
Merge remote-tracking branch 'upstream/main' into preview_poc
camsim99 Nov 22, 2022
4f435cd
Clean up example
camsim99 Nov 28, 2022
689b4f0
Format and clean up
camsim99 Nov 30, 2022
56f6ab6
Test changes from meeting with Xi
camsim99 Dec 6, 2022
a07c4f4
Add resolution, rotation customization
camsim99 Dec 7, 2022
f5d27c6
Review async test
camsim99 Dec 7, 2022
3204bf5
Add surface.dart
camsim99 Dec 7, 2022
4561c64
Dev
camsim99 Dec 13, 2022
02f6f10
Undo changes
camsim99 Dec 13, 2022
edc4dab
dev
camsim99 Dec 20, 2022
491cfe5
Add new files
camsim99 Dec 20, 2022
2860101
Start fixing permissions
camsim99 Dec 20, 2022
b366939
Get simplified camerapermissions code working
camsim99 Dec 21, 2022
5e14d68
Add enableAudio parameter
camsim99 Dec 21, 2022
3dc2d10
Add device orientation changed event steam
camsim99 Dec 22, 2022
47ee54a
Add deice orientation manager
camsim99 Jan 3, 2023
b731ea7
Merge remote-tracking branch 'upstream/main' into preview_poc
camsim99 Jan 4, 2023
4cc11c6
Sub in camera_android example app
camsim99 Jan 5, 2023
f034221
Fix pause/resume preview
camsim99 Jan 5, 2023
8c6e1d8
Connect permissions
camsim99 Jan 5, 2023
22f19a9
Connet device orientation manager
camsim99 Jan 5, 2023
94ae5bb
Fix hot reload issue with process camera provider
camsim99 Jan 5, 2023
1dadb78
Change use case cast
camsim99 Jan 5, 2023
ed67888
Add map for resolution info
camsim99 Jan 5, 2023
ffb954e
Add todos for adding back unimplemented functionality eventually
camsim99 Jan 5, 2023
90488b9
Fix aspect ratio and preview cast
camsim99 Jan 9, 2023
13ff42f
Remove comment
camsim99 Jan 11, 2023
a5c6e55
Debugging hot reload issue
camsim99 Jan 17, 2023
5ecd7a4
Add S1 fix
camsim99 Jan 23, 2023
2e07da0
Play with results codes
camsim99 Jan 31, 2023
6a831fd
Correct releases
camsim99 Jan 31, 2023
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
Expand Up @@ -108,7 +108,7 @@ class Camera
static {
supportedImageFormats = new HashMap<>();
supportedImageFormats.put("yuv420", ImageFormat.YUV_420_888);
supportedImageFormats.put("jpeg", ImageFormat.JPEG);
supportedImageFormats.put("jpeg", ImageFormat.JPEG); // may not be supported by image streaming
}

/**
Expand Down Expand Up @@ -399,6 +399,7 @@ private void createCaptureSession(
// Build Flutter surface to render to.
ResolutionFeature resolutionFeature = cameraFeatures.getResolution();
SurfaceTexture surfaceTexture = flutterTexture.surfaceTexture();
// you shouldn't be able to set arbitrary size
surfaceTexture.setDefaultBufferSize(
resolutionFeature.getPreviewSize().getWidth(),
resolutionFeature.getPreviewSize().getHeight());
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_android/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1090,4 +1090,4 @@ Future<void> main() async {
/// We use this so that APIs that have become non-nullable can still be used
/// with `!` and `?` on the stable branch.
// TODO(ianh): Remove this once we roll stable in late 2021.
T? _ambiguate<T>(T? value) => value;
T? _ambiguate<T>(T? value) => value;
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.flutter.plugins.camerax">
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
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.BinaryMessenger;
import io.flutter.view.TextureRegistry;

/** Platform implementation of the camera_plugin implemented with the CameraX library. */
public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware {
private InstanceManager instanceManager;
private FlutterPluginBinding pluginBinding;
private ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
public ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
public SystemServicesHostApiImpl systemServicesHostApi;

/**
* Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment.
Expand All @@ -24,7 +27,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity
*/
public CameraAndroidCameraxPlugin() {}

void setUp(BinaryMessenger binaryMessenger, Context context) {
void setUp(BinaryMessenger binaryMessenger, Context context, TextureRegistry textureRegistry) {
// Set up instance manager.
instanceManager =
InstanceManager.open(
Expand All @@ -34,25 +37,30 @@ void setUp(BinaryMessenger binaryMessenger, Context context) {
});

// Set up Host APIs.
// TODO(camsim99): Alphabetize these.
GeneratedCameraXLibrary.CameraInfoHostApi.setup(
binaryMessenger, new CameraInfoHostApiImpl(instanceManager));
GeneratedCameraXLibrary.JavaObjectHostApi.setup(
binaryMessenger, new JavaObjectHostApiImpl(instanceManager));
GeneratedCameraXLibrary.CameraSelectorHostApi.setup(
binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager));
GeneratedCameraXLibrary.CameraHostApi.setup(
binaryMessenger, new CameraHostApiImpl(binaryMessenger, instanceManager));
GeneratedCameraXLibrary.CameraControlHostApi.setup(
binaryMessenger, new CameraControlHostApiImpl(binaryMessenger, instanceManager));
GeneratedCameraXLibrary.PreviewHostApi.setup(
binaryMessenger, new PreviewHostApiImpl(binaryMessenger, instanceManager, textureRegistry));
processCameraProviderHostApi =
new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context);
GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup(
binaryMessenger, processCameraProviderHostApi);
systemServicesHostApi = new SystemServicesHostApiImpl(binaryMessenger, instanceManager);
GeneratedCameraXLibrary.SystemServicesHostApi.setup(binaryMessenger, systemServicesHostApi);
}

@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
pluginBinding = flutterPluginBinding;
(new CameraAndroidCameraxPlugin())
.setUp(
flutterPluginBinding.getBinaryMessenger(),
flutterPluginBinding.getApplicationContext());
}

@Override
Expand All @@ -66,7 +74,16 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {

@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
updateContext(activityPluginBinding.getActivity());
this.setUp(
pluginBinding.getBinaryMessenger(),
pluginBinding.getApplicationContext(),
pluginBinding.getTextureRegistry());
this.updateContext(pluginBinding.getApplicationContext());
this.processCameraProviderHostApi.setLifecycleOwner(
(LifecycleOwner) activityPluginBinding.getActivity());
this.systemServicesHostApi.setActivity(activityPluginBinding.getActivity());
this.systemServicesHostApi.setPermissionsRegistry(
activityPluginBinding::addRequestPermissionsResultListener);
}

@Override
Expand All @@ -89,7 +106,7 @@ public void onDetachedFromActivity() {
* Updates context that is used to fetch the corresponding instance of a {@code
* ProcessCameraProvider}.
*/
private void updateContext(Context context) {
public void updateContext(Context context) {
if (processCameraProviderHostApi != null) {
processCameraProviderHostApi.setContext(context);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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.camerax;

import androidx.camera.core.CameraControl;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlFlutterApi;

public class CameraControlFlutterApiImpl extends CameraControlFlutterApi {
private final InstanceManager instanceManager;

public CameraControlFlutterApiImpl(
BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
super(binaryMessenger);
this.instanceManager = instanceManager;
}

void create(CameraControl cameraControl, Reply<Void> reply) {
create(instanceManager.addHostCreatedInstance(cameraControl), reply);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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.camerax;

import androidx.annotation.NonNull;
import androidx.camera.core.CameraControl;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlHostApi;

public class CameraControlHostApiImpl implements CameraControlHostApi {
private final BinaryMessenger binaryMessenger;
private final InstanceManager instanceManager;

public CameraControlHostApiImpl(
BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
}

@Override
public void setZoomRatio(@NonNull Long identifier, @NonNull Long ratio) {
CameraControl cameraControl = (CameraControl) instanceManager.getInstance(identifier);
cameraControl.setZoomRatio(ratio);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.camerax;

import androidx.camera.core.Camera;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraFlutterApi;

public class CameraFlutterApiImpl extends CameraFlutterApi {
private final InstanceManager instanceManager;

public CameraFlutterApiImpl(BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
super(binaryMessenger);
this.instanceManager = instanceManager;
}

void create(Camera camera, Reply<Void> reply) {
create(instanceManager.addHostCreatedInstance(camera), reply);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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.camerax;

import androidx.annotation.NonNull;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraControl;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraHostApi;

public class CameraHostApiImpl implements CameraHostApi {
private final BinaryMessenger binaryMessenger;
private final InstanceManager instanceManager;

public CameraHostApiImpl(BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
}

@Override
public Long getCameraControl(@NonNull Long identifier) {
Camera camera = (Camera) instanceManager.getInstance(identifier);
CameraControl cameraControl = camera.getCameraControl();

final CameraControlFlutterApiImpl cameraControlFlutterApiImpl =
new CameraControlFlutterApiImpl(binaryMessenger, instanceManager);
cameraControlFlutterApiImpl.create(cameraControl, result -> {});
return instanceManager.getIdentifierForStrongReference(cameraControl);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// 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.camerax;

import android.Manifest;
import android.Manifest.permission;
import android.app.Activity;
import android.content.pm.PackageManager;
import androidx.annotation.VisibleForTesting;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

final class CameraPermissions {
interface PermissionsRegistry {
@SuppressWarnings("deprecation")
void addListener(
io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener handler);
}

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

/**
* Camera access permission errors handled when camera is created. See {@code MethodChannelCamera}
* in {@code camera/camera_platform_interface} for details.
*/
private static final String CAMERA_PERMISSIONS_REQUEST_ONGOING =
"CameraPermissionsRequestOngoing";

private static final String CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE =
"Another request is ongoing and multiple requests cannot be handled at once.";
private static final String CAMERA_ACCESS_DENIED = "CameraAccessDenied";
private static final String CAMERA_ACCESS_DENIED_MESSAGE = "Camera access permission was denied.";
private static final String AUDIO_ACCESS_DENIED = "AudioAccessDenied";
private static final String AUDIO_ACCESS_DENIED_MESSAGE = "Audio access permission was denied.";

private static final int CAMERA_REQUEST_ID = 9796;
@VisibleForTesting boolean ongoing = false;

void requestPermissions(
Activity activity,
PermissionsRegistry permissionsRegistry,
boolean enableAudio,
ResultCallback callback) {
if (ongoing) {
callback.onResult(
CAMERA_PERMISSIONS_REQUEST_ONGOING, CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE);
return;
}
if (!hasCameraPermission(activity) || (enableAudio && !hasAudioPermission(activity))) {
permissionsRegistry.addListener(
new CameraRequestPermissionsListener(
(String errorCode, String errorDescription) -> {
ongoing = false;
callback.onResult(errorCode, errorDescription);
}));
ongoing = true;
ActivityCompat.requestPermissions(
activity,
enableAudio
? new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}
: new String[] {Manifest.permission.CAMERA},
CAMERA_REQUEST_ID);
} else {
// Permissions already exist. Call the callback with success.
callback.onResult(null, null);
}
}

private boolean hasCameraPermission(Activity activity) {
return ContextCompat.checkSelfPermission(activity, permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}

private boolean hasAudioPermission(Activity activity) {
return ContextCompat.checkSelfPermission(activity, permission.RECORD_AUDIO)
== PackageManager.PERMISSION_GRANTED;
}

@VisibleForTesting
@SuppressWarnings("deprecation")
static final class CameraRequestPermissionsListener
implements io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener {

// There's no way to unregister permission listeners in the v1 embedding, so we'll be called
// duplicate times in cases where the user denies and then grants a permission. Keep track of if
// we've responded before and bail out of handling the callback manually if this is a repeat
// call.
boolean alreadyCalled = false;

final ResultCallback callback;

@VisibleForTesting
CameraRequestPermissionsListener(ResultCallback callback) {
this.callback = callback;
}

@Override
public boolean onRequestPermissionsResult(int id, String[] permissions, int[] grantResults) {
if (alreadyCalled || id != CAMERA_REQUEST_ID) {
return false;
}

alreadyCalled = true;
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
callback.onResult(CAMERA_ACCESS_DENIED, CAMERA_ACCESS_DENIED_MESSAGE);
} else if (grantResults.length > 1 && grantResults[1] != PackageManager.PERMISSION_GRANTED) {
callback.onResult(AUDIO_ACCESS_DENIED, AUDIO_ACCESS_DENIED_MESSAGE);
} else {
callback.onResult(null, null);
}
return true;
}
}
}
Loading