mirror of
https://github.com/flutter/packages.git
synced 2025-06-26 03:03:23 +08:00
[camerax] Implement lockCaptureOrientation
& unlockCaptureOrientation
(#5285)
Implements `lockCaptureOrientation` & `unlockCaptureOrientation` for all camera `UseCase`s. Also fixes small bug concerning not initially setting the target rotation of the `UseCase`s to the requested sensor orientation when `createCamera` is called. Fixes https://github.com/flutter/flutter/issues/125915.
This commit is contained in:
@ -1,3 +1,7 @@
|
||||
## 0.5.0+25
|
||||
|
||||
* Implements `lockCaptureOrientation` and `unlockCaptureOrientation`.
|
||||
|
||||
## 0.5.0+24
|
||||
|
||||
* Updates example app to use non-deprecated video_player method.
|
||||
|
@ -31,10 +31,6 @@ dependencies:
|
||||
and thus, the plugin will fall back to 480p if configured with a
|
||||
`ResolutionPreset`.
|
||||
|
||||
### Locking/Unlocking capture orientation \[[Issue #125915][125915]\]
|
||||
|
||||
`lockCaptureOrientation` & `unLockCaptureOrientation` are unimplemented.
|
||||
|
||||
### Exposure mode, point, & offset configuration \[[Issue #120468][120468]\]
|
||||
|
||||
`setExposureMode`, `setExposurePoint`, & `setExposureOffset` are unimplemented.
|
||||
|
@ -27,6 +27,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity
|
||||
private ImageCaptureHostApiImpl imageCaptureHostApiImpl;
|
||||
private CameraControlHostApiImpl cameraControlHostApiImpl;
|
||||
public @Nullable SystemServicesHostApiImpl systemServicesHostApiImpl;
|
||||
public @Nullable DeviceOrientationManagerHostApiImpl deviceOrientationManagerHostApiImpl;
|
||||
|
||||
@VisibleForTesting
|
||||
public @Nullable ProcessCameraProviderHostApiImpl processCameraProviderHostApiImpl;
|
||||
@ -71,6 +72,10 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity
|
||||
systemServicesHostApiImpl =
|
||||
new SystemServicesHostApiImpl(binaryMessenger, instanceManager, context);
|
||||
GeneratedCameraXLibrary.SystemServicesHostApi.setup(binaryMessenger, systemServicesHostApiImpl);
|
||||
deviceOrientationManagerHostApiImpl =
|
||||
new DeviceOrientationManagerHostApiImpl(binaryMessenger, instanceManager);
|
||||
GeneratedCameraXLibrary.DeviceOrientationManagerHostApi.setup(
|
||||
binaryMessenger, deviceOrientationManagerHostApiImpl);
|
||||
GeneratedCameraXLibrary.PreviewHostApi.setup(
|
||||
binaryMessenger, new PreviewHostApiImpl(binaryMessenger, instanceManager, textureRegistry));
|
||||
imageCaptureHostApiImpl =
|
||||
@ -145,6 +150,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity
|
||||
systemServicesHostApiImpl.setActivity(activity);
|
||||
systemServicesHostApiImpl.setPermissionsRegistry(
|
||||
activityPluginBinding::addRequestPermissionsResultListener);
|
||||
deviceOrientationManagerHostApiImpl.setActivity(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -14,7 +14,6 @@ import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel.DeviceOrientation;
|
||||
@ -85,120 +84,6 @@ public class DeviceOrientationManager {
|
||||
broadcastReceiver = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device's photo orientation in degrees based on the sensor orientation and the last
|
||||
* known UI orientation.
|
||||
*
|
||||
* <p>Returns one of 0, 90, 180 or 270.
|
||||
*
|
||||
* @return The device's photo orientation in degrees.
|
||||
*/
|
||||
public int getPhotoOrientation() {
|
||||
return this.getPhotoOrientation(this.lastOrientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device's photo orientation in degrees based on the sensor orientation and the
|
||||
* supplied {@link PlatformChannel.DeviceOrientation} value.
|
||||
*
|
||||
* <p>Returns one of 0, 90, 180 or 270.
|
||||
*
|
||||
* @param orientation The {@link PlatformChannel.DeviceOrientation} value that is to be converted
|
||||
* into degrees.
|
||||
* @return The device's photo orientation in degrees.
|
||||
*/
|
||||
public int getPhotoOrientation(@Nullable PlatformChannel.DeviceOrientation orientation) {
|
||||
int angle = 0;
|
||||
// Fallback to device orientation when the orientation value is null.
|
||||
if (orientation == null) {
|
||||
orientation = getUIOrientation();
|
||||
}
|
||||
|
||||
switch (orientation) {
|
||||
case PORTRAIT_UP:
|
||||
angle = 90;
|
||||
break;
|
||||
case PORTRAIT_DOWN:
|
||||
angle = 270;
|
||||
break;
|
||||
case LANDSCAPE_LEFT:
|
||||
angle = isFrontFacing ? 180 : 0;
|
||||
break;
|
||||
case LANDSCAPE_RIGHT:
|
||||
angle = isFrontFacing ? 0 : 180;
|
||||
break;
|
||||
}
|
||||
|
||||
// Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X).
|
||||
// This has to be taken into account so the JPEG is rotated properly.
|
||||
// For devices with orientation of 90, this simply returns the mapping from ORIENTATIONS.
|
||||
// For devices with orientation of 270, the JPEG is rotated 180 degrees instead.
|
||||
return (angle + sensorOrientation + 270) % 360;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device's video orientation in clockwise degrees based on the sensor orientation and
|
||||
* the last known UI orientation.
|
||||
*
|
||||
* <p>Returns one of 0, 90, 180 or 270.
|
||||
*
|
||||
* @return The device's video orientation in clockwise degrees.
|
||||
*/
|
||||
public int getVideoOrientation() {
|
||||
return this.getVideoOrientation(this.lastOrientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device's video orientation in clockwise degrees based on the sensor orientation and
|
||||
* the supplied {@link PlatformChannel.DeviceOrientation} value.
|
||||
*
|
||||
* <p>Returns one of 0, 90, 180 or 270.
|
||||
*
|
||||
* <p>More details can be found in the official Android documentation:
|
||||
* https://developer.android.com/reference/android/media/MediaRecorder#setOrientationHint(int)
|
||||
*
|
||||
* <p>See also:
|
||||
* https://developer.android.com/training/camera2/camera-preview-large-screens#orientation_calculation
|
||||
*
|
||||
* @param orientation The {@link PlatformChannel.DeviceOrientation} value that is to be converted
|
||||
* into degrees.
|
||||
* @return The device's video orientation in clockwise degrees.
|
||||
*/
|
||||
public int getVideoOrientation(@Nullable PlatformChannel.DeviceOrientation orientation) {
|
||||
int angle = 0;
|
||||
|
||||
// Fallback to device orientation when the orientation value is null.
|
||||
if (orientation == null) {
|
||||
orientation = getUIOrientation();
|
||||
}
|
||||
|
||||
switch (orientation) {
|
||||
case PORTRAIT_UP:
|
||||
angle = 0;
|
||||
break;
|
||||
case PORTRAIT_DOWN:
|
||||
angle = 180;
|
||||
break;
|
||||
case LANDSCAPE_LEFT:
|
||||
angle = 270;
|
||||
break;
|
||||
case LANDSCAPE_RIGHT:
|
||||
angle = 90;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isFrontFacing) {
|
||||
angle *= -1;
|
||||
}
|
||||
|
||||
return (angle + sensorOrientation + 360) % 360;
|
||||
}
|
||||
|
||||
/** @return the last received UI orientation. */
|
||||
public @Nullable PlatformChannel.DeviceOrientation getLastUIOrientation() {
|
||||
return this.lastOrientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles orientation changes based on change events triggered by the OrientationIntentFilter.
|
||||
*
|
||||
@ -241,7 +126,7 @@ public class DeviceOrientationManager {
|
||||
@SuppressWarnings("deprecation")
|
||||
@VisibleForTesting
|
||||
PlatformChannel.DeviceOrientation getUIOrientation() {
|
||||
final int rotation = getDisplay().getRotation();
|
||||
final int rotation = getDefaultRotation();
|
||||
final int orientation = activity.getResources().getConfiguration().orientation;
|
||||
|
||||
switch (orientation) {
|
||||
@ -265,57 +150,18 @@ public class DeviceOrientationManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the sensor orientation based on the supplied angle.
|
||||
* Gets default capture rotation for CameraX {@code UseCase}s.
|
||||
*
|
||||
* <p>This method is visible for testing purposes only and should never be used outside this
|
||||
* class.
|
||||
* <p>See
|
||||
* https://developer.android.com/reference/androidx/camera/core/ImageCapture#setTargetRotation(int),
|
||||
* for instance.
|
||||
*
|
||||
* @param angle Orientation angle.
|
||||
* @return The sensor orientation based on the supplied angle.
|
||||
* @return The rotation of the screen from its "natural" orientation; one of {@code
|
||||
* Surface.ROTATION_0}, {@code Surface.ROTATION_90}, {@code Surface.ROTATION_180}, {@code
|
||||
* Surface.ROTATION_270}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
PlatformChannel.DeviceOrientation calculateSensorOrientation(int angle) {
|
||||
final int tolerance = 45;
|
||||
angle += tolerance;
|
||||
|
||||
// Orientation is 0 in the default orientation mode. This is portrait-mode for phones
|
||||
// and landscape for tablets. We have to compensate for this by calculating the default
|
||||
// orientation, and apply an offset accordingly.
|
||||
int defaultDeviceOrientation = getDeviceDefaultOrientation();
|
||||
if (defaultDeviceOrientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
angle += 90;
|
||||
}
|
||||
// Determine the orientation
|
||||
angle = angle % 360;
|
||||
return new PlatformChannel.DeviceOrientation[] {
|
||||
PlatformChannel.DeviceOrientation.PORTRAIT_UP,
|
||||
PlatformChannel.DeviceOrientation.LANDSCAPE_LEFT,
|
||||
PlatformChannel.DeviceOrientation.PORTRAIT_DOWN,
|
||||
PlatformChannel.DeviceOrientation.LANDSCAPE_RIGHT,
|
||||
}
|
||||
[angle / 90];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default orientation of the device.
|
||||
*
|
||||
* <p>This method is visible for testing purposes only and should never be used outside this
|
||||
* class.
|
||||
*
|
||||
* @return The default orientation of the device.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int getDeviceDefaultOrientation() {
|
||||
Configuration config = activity.getResources().getConfiguration();
|
||||
int rotation = getDisplay().getRotation();
|
||||
if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180)
|
||||
&& config.orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||
|| ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270)
|
||||
&& config.orientation == Configuration.ORIENTATION_PORTRAIT)) {
|
||||
return Configuration.ORIENTATION_LANDSCAPE;
|
||||
} else {
|
||||
return Configuration.ORIENTATION_PORTRAIT;
|
||||
}
|
||||
int getDefaultRotation() {
|
||||
return getDisplay().getRotation();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,20 @@
|
||||
// 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 io.flutter.plugin.common.BinaryMessenger;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.DeviceOrientationManagerFlutterApi;
|
||||
|
||||
public class DeviceOrientationManagerFlutterApiImpl extends DeviceOrientationManagerFlutterApi {
|
||||
public DeviceOrientationManagerFlutterApiImpl(@NonNull BinaryMessenger binaryMessenger) {
|
||||
super(binaryMessenger);
|
||||
}
|
||||
|
||||
public void sendDeviceOrientationChangedEvent(
|
||||
@NonNull String orientation, @NonNull Reply<Void> reply) {
|
||||
super.onDeviceOrientationChanged(orientation, reply);
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
// 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.app.Activity;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel.DeviceOrientation;
|
||||
import io.flutter.plugin.common.BinaryMessenger;
|
||||
import io.flutter.plugins.camerax.CameraPermissionsManager.PermissionsRegistry;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.DeviceOrientationManagerHostApi;
|
||||
|
||||
public class DeviceOrientationManagerHostApiImpl implements DeviceOrientationManagerHostApi {
|
||||
private final BinaryMessenger binaryMessenger;
|
||||
private final InstanceManager instanceManager;
|
||||
|
||||
@VisibleForTesting public @NonNull CameraXProxy cameraXProxy = new CameraXProxy();
|
||||
@VisibleForTesting public @Nullable DeviceOrientationManager deviceOrientationManager;
|
||||
|
||||
@VisibleForTesting
|
||||
public @NonNull DeviceOrientationManagerFlutterApiImpl deviceOrientationManagerFlutterApiImpl;
|
||||
|
||||
private Activity activity;
|
||||
private PermissionsRegistry permissionsRegistry;
|
||||
|
||||
public DeviceOrientationManagerHostApiImpl(
|
||||
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
|
||||
this.binaryMessenger = binaryMessenger;
|
||||
this.instanceManager = instanceManager;
|
||||
this.deviceOrientationManagerFlutterApiImpl =
|
||||
new DeviceOrientationManagerFlutterApiImpl(binaryMessenger);
|
||||
}
|
||||
|
||||
public void setActivity(@NonNull Activity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts listening for device orientation changes using an instance of a {@link
|
||||
* DeviceOrientationManager}.
|
||||
*
|
||||
* <p>Whenever a change in device orientation is detected by the {@code DeviceOrientationManager},
|
||||
* the {@link SystemServicesFlutterApi} will be used to notify the Dart side.
|
||||
*/
|
||||
@Override
|
||||
public void startListeningForDeviceOrientationChange(
|
||||
@NonNull Boolean isFrontFacing, @NonNull Long sensorOrientation) {
|
||||
deviceOrientationManager =
|
||||
cameraXProxy.createDeviceOrientationManager(
|
||||
activity,
|
||||
isFrontFacing,
|
||||
sensorOrientation.intValue(),
|
||||
(DeviceOrientation newOrientation) -> {
|
||||
deviceOrientationManagerFlutterApiImpl.sendDeviceOrientationChangedEvent(
|
||||
serializeDeviceOrientation(newOrientation), reply -> {});
|
||||
});
|
||||
deviceOrientationManager.start();
|
||||
}
|
||||
|
||||
/** Serializes {@code DeviceOrientation} into a String that the Dart side is able to recognize. */
|
||||
String serializeDeviceOrientation(DeviceOrientation orientation) {
|
||||
return orientation.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the {@code deviceOrientationManager} to stop listening for orientation updates.
|
||||
*
|
||||
* <p>Has no effect if the {@code deviceOrientationManager} was never created to listen for device
|
||||
* orientation updates.
|
||||
*/
|
||||
@Override
|
||||
public void stopListeningForDeviceOrientationChange() {
|
||||
if (deviceOrientationManager != null) {
|
||||
deviceOrientationManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets default capture rotation for CameraX {@code UseCase}s.
|
||||
*
|
||||
* <p>The default capture rotation for CameraX is the rotation of default {@code Display} at the
|
||||
* time that a {@code UseCase} is bound, but the default {@code Display} does not change in this
|
||||
* plugin, so this value is {@code Display}-agnostic.
|
||||
*
|
||||
* <p>See
|
||||
* https://developer.android.com/reference/androidx/camera/core/ImageCapture#setTargetRotation(int)
|
||||
* for instance for more information on how this default value is used.
|
||||
*/
|
||||
@Override
|
||||
public @NonNull Long getDefaultDisplayRotation() {
|
||||
int defaultRotation;
|
||||
try {
|
||||
defaultRotation = deviceOrientationManager.getDefaultRotation();
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalStateException(
|
||||
"startListeningForDeviceOrientationChange must first be called to subscribe to device orientation changes in order to retrieve default rotation.");
|
||||
}
|
||||
|
||||
return Long.valueOf(defaultRotation);
|
||||
}
|
||||
}
|
@ -1256,11 +1256,6 @@ public class GeneratedCameraXLibrary {
|
||||
void requestCameraPermissions(
|
||||
@NonNull Boolean enableAudio, @NonNull Result<CameraPermissionsErrorData> result);
|
||||
|
||||
void startListeningForDeviceOrientationChange(
|
||||
@NonNull Boolean isFrontFacing, @NonNull Long sensorOrientation);
|
||||
|
||||
void stopListeningForDeviceOrientationChange();
|
||||
|
||||
@NonNull
|
||||
String getTempFilePath(@NonNull String prefix, @NonNull String suffix);
|
||||
|
||||
@ -1305,57 +1300,6 @@ public class GeneratedCameraXLibrary {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange",
|
||||
getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Boolean isFrontFacingArg = (Boolean) args.get(0);
|
||||
Number sensorOrientationArg = (Number) args.get(1);
|
||||
try {
|
||||
api.startListeningForDeviceOrientationChange(
|
||||
isFrontFacingArg,
|
||||
(sensorOrientationArg == null) ? null : sensorOrientationArg.longValue());
|
||||
wrapped.add(0, null);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange",
|
||||
getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
try {
|
||||
api.stopListeningForDeviceOrientationChange();
|
||||
wrapped.add(0, null);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
@ -1402,18 +1346,6 @@ public class GeneratedCameraXLibrary {
|
||||
return new StandardMessageCodec();
|
||||
}
|
||||
|
||||
public void onDeviceOrientationChanged(
|
||||
@NonNull String orientationArg, @NonNull Reply<Void> callback) {
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged",
|
||||
getCodec());
|
||||
channel.send(
|
||||
new ArrayList<Object>(Collections.singletonList(orientationArg)),
|
||||
channelReply -> callback.reply(null));
|
||||
}
|
||||
|
||||
public void onCameraError(@NonNull String errorDescriptionArg, @NonNull Reply<Void> callback) {
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
@ -1425,6 +1357,133 @@ public class GeneratedCameraXLibrary {
|
||||
channelReply -> callback.reply(null));
|
||||
}
|
||||
}
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
public interface DeviceOrientationManagerHostApi {
|
||||
|
||||
void startListeningForDeviceOrientationChange(
|
||||
@NonNull Boolean isFrontFacing, @NonNull Long sensorOrientation);
|
||||
|
||||
void stopListeningForDeviceOrientationChange();
|
||||
|
||||
@NonNull
|
||||
Long getDefaultDisplayRotation();
|
||||
|
||||
/** The codec used by DeviceOrientationManagerHostApi. */
|
||||
static @NonNull MessageCodec<Object> getCodec() {
|
||||
return new StandardMessageCodec();
|
||||
}
|
||||
/**
|
||||
* Sets up an instance of `DeviceOrientationManagerHostApi` to handle messages through the
|
||||
* `binaryMessenger`.
|
||||
*/
|
||||
static void setup(
|
||||
@NonNull BinaryMessenger binaryMessenger, @Nullable DeviceOrientationManagerHostApi api) {
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.DeviceOrientationManagerHostApi.startListeningForDeviceOrientationChange",
|
||||
getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Boolean isFrontFacingArg = (Boolean) args.get(0);
|
||||
Number sensorOrientationArg = (Number) args.get(1);
|
||||
try {
|
||||
api.startListeningForDeviceOrientationChange(
|
||||
isFrontFacingArg,
|
||||
(sensorOrientationArg == null) ? null : sensorOrientationArg.longValue());
|
||||
wrapped.add(0, null);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.DeviceOrientationManagerHostApi.stopListeningForDeviceOrientationChange",
|
||||
getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
try {
|
||||
api.stopListeningForDeviceOrientationChange();
|
||||
wrapped.add(0, null);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.DeviceOrientationManagerHostApi.getDefaultDisplayRotation",
|
||||
getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
try {
|
||||
Long output = api.getDefaultDisplayRotation();
|
||||
wrapped.add(0, output);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
|
||||
public static class DeviceOrientationManagerFlutterApi {
|
||||
private final @NonNull BinaryMessenger binaryMessenger;
|
||||
|
||||
public DeviceOrientationManagerFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) {
|
||||
this.binaryMessenger = argBinaryMessenger;
|
||||
}
|
||||
|
||||
/** Public interface for sending reply. */
|
||||
@SuppressWarnings("UnknownNullness")
|
||||
public interface Reply<T> {
|
||||
void reply(T reply);
|
||||
}
|
||||
/** The codec used by DeviceOrientationManagerFlutterApi. */
|
||||
static @NonNull MessageCodec<Object> getCodec() {
|
||||
return new StandardMessageCodec();
|
||||
}
|
||||
|
||||
public void onDeviceOrientationChanged(
|
||||
@NonNull String orientationArg, @NonNull Reply<Void> callback) {
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.DeviceOrientationManagerFlutterApi.onDeviceOrientationChanged",
|
||||
getCodec());
|
||||
channel.send(
|
||||
new ArrayList<Object>(Collections.singletonList(orientationArg)),
|
||||
channelReply -> callback.reply(null));
|
||||
}
|
||||
}
|
||||
|
||||
private static class PreviewHostApiCodec extends StandardMessageCodec {
|
||||
public static final PreviewHostApiCodec INSTANCE = new PreviewHostApiCodec();
|
||||
@ -1466,6 +1525,8 @@ public class GeneratedCameraXLibrary {
|
||||
@NonNull
|
||||
ResolutionInfo getResolutionInfo(@NonNull Long identifier);
|
||||
|
||||
void setTargetRotation(@NonNull Long identifier, @NonNull Long rotation);
|
||||
|
||||
/** The codec used by PreviewHostApi. */
|
||||
static @NonNull MessageCodec<Object> getCodec() {
|
||||
return PreviewHostApiCodec.INSTANCE;
|
||||
@ -1577,6 +1638,32 @@ public class GeneratedCameraXLibrary {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger, "dev.flutter.pigeon.PreviewHostApi.setTargetRotation", getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Number identifierArg = (Number) args.get(0);
|
||||
Number rotationArg = (Number) args.get(1);
|
||||
try {
|
||||
api.setTargetRotation(
|
||||
(identifierArg == null) ? null : identifierArg.longValue(),
|
||||
(rotationArg == null) ? null : rotationArg.longValue());
|
||||
wrapped.add(0, null);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
@ -1588,6 +1675,8 @@ public class GeneratedCameraXLibrary {
|
||||
@NonNull
|
||||
Long getOutput(@NonNull Long identifier);
|
||||
|
||||
void setTargetRotation(@NonNull Long identifier, @NonNull Long rotation);
|
||||
|
||||
/** The codec used by VideoCaptureHostApi. */
|
||||
static @NonNull MessageCodec<Object> getCodec() {
|
||||
return new StandardMessageCodec();
|
||||
@ -1646,6 +1735,34 @@ public class GeneratedCameraXLibrary {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.VideoCaptureHostApi.setTargetRotation",
|
||||
getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Number identifierArg = (Number) args.get(0);
|
||||
Number rotationArg = (Number) args.get(1);
|
||||
try {
|
||||
api.setTargetRotation(
|
||||
(identifierArg == null) ? null : identifierArg.longValue(),
|
||||
(rotationArg == null) ? null : rotationArg.longValue());
|
||||
wrapped.add(0, null);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
|
||||
@ -2055,12 +2172,17 @@ public class GeneratedCameraXLibrary {
|
||||
public interface ImageCaptureHostApi {
|
||||
|
||||
void create(
|
||||
@NonNull Long identifier, @Nullable Long flashMode, @Nullable Long resolutionSelectorId);
|
||||
@NonNull Long identifier,
|
||||
@Nullable Long targetRotation,
|
||||
@Nullable Long flashMode,
|
||||
@Nullable Long resolutionSelectorId);
|
||||
|
||||
void setFlashMode(@NonNull Long identifier, @NonNull Long flashMode);
|
||||
|
||||
void takePicture(@NonNull Long identifier, @NonNull Result<String> result);
|
||||
|
||||
void setTargetRotation(@NonNull Long identifier, @NonNull Long rotation);
|
||||
|
||||
/** The codec used by ImageCaptureHostApi. */
|
||||
static @NonNull MessageCodec<Object> getCodec() {
|
||||
return new StandardMessageCodec();
|
||||
@ -2080,11 +2202,13 @@ public class GeneratedCameraXLibrary {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Number identifierArg = (Number) args.get(0);
|
||||
Number flashModeArg = (Number) args.get(1);
|
||||
Number resolutionSelectorIdArg = (Number) args.get(2);
|
||||
Number targetRotationArg = (Number) args.get(1);
|
||||
Number flashModeArg = (Number) args.get(2);
|
||||
Number resolutionSelectorIdArg = (Number) args.get(3);
|
||||
try {
|
||||
api.create(
|
||||
(identifierArg == null) ? null : identifierArg.longValue(),
|
||||
(targetRotationArg == null) ? null : targetRotationArg.longValue(),
|
||||
(flashModeArg == null) ? null : flashModeArg.longValue(),
|
||||
(resolutionSelectorIdArg == null)
|
||||
? null
|
||||
@ -2156,6 +2280,34 @@ public class GeneratedCameraXLibrary {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.ImageCaptureHostApi.setTargetRotation",
|
||||
getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Number identifierArg = (Number) args.get(0);
|
||||
Number rotationArg = (Number) args.get(1);
|
||||
try {
|
||||
api.setTargetRotation(
|
||||
(identifierArg == null) ? null : identifierArg.longValue(),
|
||||
(rotationArg == null) ? null : rotationArg.longValue());
|
||||
wrapped.add(0, null);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2486,12 +2638,17 @@ public class GeneratedCameraXLibrary {
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
public interface ImageAnalysisHostApi {
|
||||
|
||||
void create(@NonNull Long identifier, @Nullable Long resolutionSelectorId);
|
||||
void create(
|
||||
@NonNull Long identifier,
|
||||
@Nullable Long targetRotation,
|
||||
@Nullable Long resolutionSelectorId);
|
||||
|
||||
void setAnalyzer(@NonNull Long identifier, @NonNull Long analyzerIdentifier);
|
||||
|
||||
void clearAnalyzer(@NonNull Long identifier);
|
||||
|
||||
void setTargetRotation(@NonNull Long identifier, @NonNull Long rotation);
|
||||
|
||||
/** The codec used by ImageAnalysisHostApi. */
|
||||
static @NonNull MessageCodec<Object> getCodec() {
|
||||
return new StandardMessageCodec();
|
||||
@ -2512,10 +2669,12 @@ public class GeneratedCameraXLibrary {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Number identifierArg = (Number) args.get(0);
|
||||
Number resolutionSelectorIdArg = (Number) args.get(1);
|
||||
Number targetRotationArg = (Number) args.get(1);
|
||||
Number resolutionSelectorIdArg = (Number) args.get(2);
|
||||
try {
|
||||
api.create(
|
||||
(identifierArg == null) ? null : identifierArg.longValue(),
|
||||
(targetRotationArg == null) ? null : targetRotationArg.longValue(),
|
||||
(resolutionSelectorIdArg == null)
|
||||
? null
|
||||
: resolutionSelectorIdArg.longValue());
|
||||
@ -2581,6 +2740,34 @@ public class GeneratedCameraXLibrary {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger,
|
||||
"dev.flutter.pigeon.ImageAnalysisHostApi.setTargetRotation",
|
||||
getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||
Number identifierArg = (Number) args.get(0);
|
||||
Number rotationArg = (Number) args.get(1);
|
||||
try {
|
||||
api.setTargetRotation(
|
||||
(identifierArg == null) ? null : identifierArg.longValue(),
|
||||
(rotationArg == null) ? null : rotationArg.longValue());
|
||||
wrapped.add(0, null);
|
||||
} catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
}
|
||||
reply.reply(wrapped);
|
||||
});
|
||||
} else {
|
||||
channel.setMessageHandler(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
|
@ -41,9 +41,13 @@ public class ImageAnalysisHostApiImpl implements ImageAnalysisHostApi {
|
||||
|
||||
/** Creates an {@link ImageAnalysis} instance with the target resolution if specified. */
|
||||
@Override
|
||||
public void create(@NonNull Long identifier, @Nullable Long resolutionSelectorId) {
|
||||
public void create(
|
||||
@NonNull Long identifier, @Nullable Long rotation, @Nullable Long resolutionSelectorId) {
|
||||
ImageAnalysis.Builder imageAnalysisBuilder = cameraXProxy.createImageAnalysisBuilder();
|
||||
|
||||
if (rotation != null) {
|
||||
imageAnalysisBuilder.setTargetRotation(rotation.intValue());
|
||||
}
|
||||
if (resolutionSelectorId != null) {
|
||||
ResolutionSelector resolutionSelector =
|
||||
Objects.requireNonNull(instanceManager.getInstance(resolutionSelectorId));
|
||||
@ -75,6 +79,13 @@ public class ImageAnalysisHostApiImpl implements ImageAnalysisHostApi {
|
||||
imageAnalysis.clearAnalyzer();
|
||||
}
|
||||
|
||||
/** Dynamically sets the target rotation of the {@link ImageAnalysis}. */
|
||||
@Override
|
||||
public void setTargetRotation(@NonNull Long identifier, @NonNull Long rotation) {
|
||||
ImageAnalysis imageAnalysis = getImageAnalysisInstance(identifier);
|
||||
imageAnalysis.setTargetRotation(rotation.intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieives the {@link ImageAnalysis} instance associated with the specified {@code identifier}.
|
||||
*/
|
||||
|
@ -53,9 +53,15 @@ public class ImageCaptureHostApiImpl implements ImageCaptureHostApi {
|
||||
*/
|
||||
@Override
|
||||
public void create(
|
||||
@NonNull Long identifier, @Nullable Long flashMode, @Nullable Long resolutionSelectorId) {
|
||||
@NonNull Long identifier,
|
||||
@Nullable Long rotation,
|
||||
@Nullable Long flashMode,
|
||||
@Nullable Long resolutionSelectorId) {
|
||||
ImageCapture.Builder imageCaptureBuilder = cameraXProxy.createImageCaptureBuilder();
|
||||
|
||||
if (rotation != null) {
|
||||
imageCaptureBuilder.setTargetRotation(rotation.intValue());
|
||||
}
|
||||
if (flashMode != null) {
|
||||
// This sets the requested flash mode, but may fail silently.
|
||||
imageCaptureBuilder.setFlashMode(flashMode.intValue());
|
||||
@ -73,8 +79,7 @@ public class ImageCaptureHostApiImpl implements ImageCaptureHostApi {
|
||||
/** Sets the flash mode of the {@link ImageCapture} instance with the specified identifier. */
|
||||
@Override
|
||||
public void setFlashMode(@NonNull Long identifier, @NonNull Long flashMode) {
|
||||
ImageCapture imageCapture =
|
||||
(ImageCapture) Objects.requireNonNull(instanceManager.getInstance(identifier));
|
||||
ImageCapture imageCapture = getImageCaptureInstance(identifier);
|
||||
imageCapture.setFlashMode(flashMode.intValue());
|
||||
}
|
||||
|
||||
@ -82,8 +87,7 @@ public class ImageCaptureHostApiImpl implements ImageCaptureHostApi {
|
||||
@Override
|
||||
public void takePicture(
|
||||
@NonNull Long identifier, @NonNull GeneratedCameraXLibrary.Result<String> result) {
|
||||
ImageCapture imageCapture =
|
||||
(ImageCapture) Objects.requireNonNull(instanceManager.getInstance(identifier));
|
||||
ImageCapture imageCapture = getImageCaptureInstance(identifier);
|
||||
final File outputDir = context.getCacheDir();
|
||||
File temporaryCaptureFile;
|
||||
try {
|
||||
@ -118,4 +122,18 @@ public class ImageCaptureHostApiImpl implements ImageCaptureHostApi {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Dynamically sets the target rotation of the {@link ImageCapture}. */
|
||||
@Override
|
||||
public void setTargetRotation(@NonNull Long identifier, @NonNull Long rotation) {
|
||||
ImageCapture imageCapture = getImageCaptureInstance(identifier);
|
||||
imageCapture.setTargetRotation(rotation.intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the {@link ImageCapture} instance associated with the specified {@code identifier}.
|
||||
*/
|
||||
private ImageCapture getImageCaptureInstance(@NonNull Long identifier) {
|
||||
return Objects.requireNonNull(instanceManager.getInstance(identifier));
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,14 @@ public class PendingRecordingHostApiImpl implements PendingRecordingHostApi {
|
||||
if (event instanceof VideoRecordEvent.Finalize) {
|
||||
VideoRecordEvent.Finalize castedEvent = (VideoRecordEvent.Finalize) event;
|
||||
if (castedEvent.hasError()) {
|
||||
systemServicesFlutterApi.sendCameraError(castedEvent.getCause().toString(), reply -> {});
|
||||
String cameraErrorMessage;
|
||||
if (castedEvent.getCause() != null) {
|
||||
cameraErrorMessage = castedEvent.getCause().toString();
|
||||
} else {
|
||||
cameraErrorMessage =
|
||||
"Error code " + castedEvent.getError() + ": An error occurred while recording video.";
|
||||
}
|
||||
systemServicesFlutterApi.sendCameraError(cameraErrorMessage, reply -> {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public class PreviewHostApiImpl implements PreviewHostApi {
|
||||
*/
|
||||
@Override
|
||||
public @NonNull Long setSurfaceProvider(@NonNull Long identifier) {
|
||||
Preview preview = (Preview) Objects.requireNonNull(instanceManager.getInstance(identifier));
|
||||
Preview preview = getPreviewInstance(identifier);
|
||||
flutterSurfaceTexture = textureRegistry.createSurfaceTexture();
|
||||
SurfaceTexture surfaceTexture = flutterSurfaceTexture.surfaceTexture();
|
||||
Preview.SurfaceProvider surfaceProvider = createSurfaceProvider(surfaceTexture);
|
||||
@ -142,7 +142,7 @@ public class PreviewHostApiImpl implements PreviewHostApi {
|
||||
@Override
|
||||
public @NonNull GeneratedCameraXLibrary.ResolutionInfo getResolutionInfo(
|
||||
@NonNull Long identifier) {
|
||||
Preview preview = (Preview) Objects.requireNonNull(instanceManager.getInstance(identifier));
|
||||
Preview preview = getPreviewInstance(identifier);
|
||||
Size resolution = preview.getResolutionInfo().getResolution();
|
||||
|
||||
GeneratedCameraXLibrary.ResolutionInfo.Builder resolutionInfo =
|
||||
@ -151,4 +151,16 @@ public class PreviewHostApiImpl implements PreviewHostApi {
|
||||
.setHeight(Long.valueOf(resolution.getHeight()));
|
||||
return resolutionInfo.build();
|
||||
}
|
||||
|
||||
/** Dynamically sets the target rotation of the {@link Preview}. */
|
||||
@Override
|
||||
public void setTargetRotation(@NonNull Long identifier, @NonNull Long rotation) {
|
||||
Preview preview = getPreviewInstance(identifier);
|
||||
preview.setTargetRotation(rotation.intValue());
|
||||
}
|
||||
|
||||
/** Retrieves the {@link Preview} instance associated with the specified {@code identifier}. */
|
||||
private Preview getPreviewInstance(@NonNull Long identifier) {
|
||||
return Objects.requireNonNull(instanceManager.getInstance(identifier));
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,6 @@ public class SystemServicesFlutterApiImpl extends SystemServicesFlutterApi {
|
||||
super(binaryMessenger);
|
||||
}
|
||||
|
||||
public void sendDeviceOrientationChangedEvent(
|
||||
@NonNull String orientation, @NonNull Reply<Void> reply) {
|
||||
super.onDeviceOrientationChanged(orientation, reply);
|
||||
}
|
||||
|
||||
public void sendCameraError(@NonNull String errorDescription, @NonNull Reply<Void> reply) {
|
||||
super.onCameraError(errorDescription, reply);
|
||||
}
|
||||
|
@ -7,14 +7,11 @@ package io.flutter.plugins.camerax;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel.DeviceOrientation;
|
||||
import io.flutter.plugin.common.BinaryMessenger;
|
||||
import io.flutter.plugins.camerax.CameraPermissionsManager.PermissionsRegistry;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraPermissionsErrorData;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.Result;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.SystemServicesFlutterApi;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.SystemServicesHostApi;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -25,7 +22,6 @@ public class SystemServicesHostApiImpl implements SystemServicesHostApi {
|
||||
private Context context;
|
||||
|
||||
@VisibleForTesting public @NonNull CameraXProxy cameraXProxy = new CameraXProxy();
|
||||
@VisibleForTesting public @Nullable DeviceOrientationManager deviceOrientationManager;
|
||||
@VisibleForTesting public @NonNull SystemServicesFlutterApiImpl systemServicesFlutterApi;
|
||||
|
||||
private Activity activity;
|
||||
@ -84,46 +80,6 @@ public class SystemServicesHostApiImpl implements SystemServicesHostApi {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts listening for device orientation changes using an instance of a {@link
|
||||
* DeviceOrientationManager}.
|
||||
*
|
||||
* <p>Whenever a change in device orientation is detected by the {@code DeviceOrientationManager},
|
||||
* the {@link SystemServicesFlutterApi} will be used to notify the Dart side.
|
||||
*/
|
||||
@Override
|
||||
public void startListeningForDeviceOrientationChange(
|
||||
@NonNull Boolean isFrontFacing, @NonNull Long sensorOrientation) {
|
||||
deviceOrientationManager =
|
||||
cameraXProxy.createDeviceOrientationManager(
|
||||
activity,
|
||||
isFrontFacing,
|
||||
sensorOrientation.intValue(),
|
||||
(DeviceOrientation newOrientation) -> {
|
||||
systemServicesFlutterApi.sendDeviceOrientationChangedEvent(
|
||||
serializeDeviceOrientation(newOrientation), reply -> {});
|
||||
});
|
||||
deviceOrientationManager.start();
|
||||
}
|
||||
|
||||
/** Serializes {@code DeviceOrientation} into a String that the Dart side is able to recognize. */
|
||||
String serializeDeviceOrientation(DeviceOrientation orientation) {
|
||||
return orientation.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the {@code deviceOrientationManager} to stop listening for orientation updates.
|
||||
*
|
||||
* <p>Has no effect if the {@code deviceOrientationManager} was never created to listen for device
|
||||
* orientation updates.
|
||||
*/
|
||||
@Override
|
||||
public void stopListeningForDeviceOrientationChange() {
|
||||
if (deviceOrientationManager != null) {
|
||||
deviceOrientationManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a path to be used to create a temp file in the current cache directory. */
|
||||
@Override
|
||||
@NonNull
|
||||
|
@ -37,8 +37,7 @@ public class VideoCaptureHostApiImpl implements VideoCaptureHostApi {
|
||||
@Override
|
||||
@NonNull
|
||||
public Long getOutput(@NonNull Long identifier) {
|
||||
VideoCapture<Recorder> videoCapture =
|
||||
Objects.requireNonNull(instanceManager.getInstance(identifier));
|
||||
VideoCapture<Recorder> videoCapture = getVideoCaptureInstance(identifier);
|
||||
Recorder recorder = videoCapture.getOutput();
|
||||
return Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(recorder));
|
||||
}
|
||||
@ -49,4 +48,18 @@ public class VideoCaptureHostApiImpl implements VideoCaptureHostApi {
|
||||
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
|
||||
return new VideoCaptureFlutterApiImpl(binaryMessenger, instanceManager);
|
||||
}
|
||||
|
||||
/** Dynamically sets the target rotation of the {@link VideoCapture}. */
|
||||
@Override
|
||||
public void setTargetRotation(@NonNull Long identifier, @NonNull Long rotation) {
|
||||
VideoCapture<Recorder> videoCapture = getVideoCaptureInstance(identifier);
|
||||
videoCapture.setTargetRotation(rotation.intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the {@link VideoCapture} instance associated with the specified {@code identifier}.
|
||||
*/
|
||||
private VideoCapture<Recorder> getVideoCaptureInstance(@NonNull Long identifier) {
|
||||
return Objects.requireNonNull(instanceManager.getInstance(identifier));
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ public class CameraAndroidCameraxPluginTest {
|
||||
plugin.processCameraProviderHostApiImpl = mockProcessCameraProviderHostApiImpl;
|
||||
plugin.liveDataHostApiImpl = mockLiveDataHostApiImpl;
|
||||
plugin.systemServicesHostApiImpl = mock(SystemServicesHostApiImpl.class);
|
||||
plugin.deviceOrientationManagerHostApiImpl = mock(DeviceOrientationManagerHostApiImpl.class);
|
||||
|
||||
plugin.onAttachedToEngine(flutterPluginBinding);
|
||||
plugin.onAttachedToActivity(activityPluginBinding);
|
||||
@ -68,6 +69,7 @@ public class CameraAndroidCameraxPluginTest {
|
||||
plugin.processCameraProviderHostApiImpl = mockProcessCameraProviderHostApiImpl;
|
||||
plugin.liveDataHostApiImpl = mockLiveDataHostApiImpl;
|
||||
plugin.systemServicesHostApiImpl = mock(SystemServicesHostApiImpl.class);
|
||||
plugin.deviceOrientationManagerHostApiImpl = mock(DeviceOrientationManagerHostApiImpl.class);
|
||||
|
||||
plugin.onAttachedToEngine(flutterPluginBinding);
|
||||
plugin.onAttachedToActivity(activityPluginBinding);
|
||||
|
@ -50,108 +50,6 @@ public class DeviceOrientationManagerTest {
|
||||
new DeviceOrientationManager(mockActivity, false, 0, mockDeviceOrientationChangeCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVideoOrientation_whenNaturalScreenOrientationEqualsPortraitUp() {
|
||||
int degreesPortraitUp =
|
||||
deviceOrientationManager.getVideoOrientation(DeviceOrientation.PORTRAIT_UP);
|
||||
int degreesPortraitDown =
|
||||
deviceOrientationManager.getVideoOrientation(DeviceOrientation.PORTRAIT_DOWN);
|
||||
int degreesLandscapeLeft =
|
||||
deviceOrientationManager.getVideoOrientation(DeviceOrientation.LANDSCAPE_LEFT);
|
||||
int degreesLandscapeRight =
|
||||
deviceOrientationManager.getVideoOrientation(DeviceOrientation.LANDSCAPE_RIGHT);
|
||||
|
||||
assertEquals(0, degreesPortraitUp);
|
||||
assertEquals(270, degreesLandscapeLeft);
|
||||
assertEquals(180, degreesPortraitDown);
|
||||
assertEquals(90, degreesLandscapeRight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVideoOrientation_whenNaturalScreenOrientationEqualsLandscapeLeft() {
|
||||
DeviceOrientationManager orientationManager =
|
||||
new DeviceOrientationManager(mockActivity, false, 90, mockDeviceOrientationChangeCallback);
|
||||
|
||||
int degreesPortraitUp = orientationManager.getVideoOrientation(DeviceOrientation.PORTRAIT_UP);
|
||||
int degreesPortraitDown =
|
||||
orientationManager.getVideoOrientation(DeviceOrientation.PORTRAIT_DOWN);
|
||||
int degreesLandscapeLeft =
|
||||
orientationManager.getVideoOrientation(DeviceOrientation.LANDSCAPE_LEFT);
|
||||
int degreesLandscapeRight =
|
||||
orientationManager.getVideoOrientation(DeviceOrientation.LANDSCAPE_RIGHT);
|
||||
|
||||
assertEquals(90, degreesPortraitUp);
|
||||
assertEquals(0, degreesLandscapeLeft);
|
||||
assertEquals(270, degreesPortraitDown);
|
||||
assertEquals(180, degreesLandscapeRight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVideoOrientation_fallbackToPortraitSensorOrientationWhenOrientationIsNull() {
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_0);
|
||||
|
||||
int degrees = deviceOrientationManager.getVideoOrientation(null);
|
||||
|
||||
assertEquals(0, degrees);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVideoOrientation_fallbackToLandscapeSensorOrientationWhenOrientationIsNull() {
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_0);
|
||||
|
||||
DeviceOrientationManager orientationManager =
|
||||
new DeviceOrientationManager(mockActivity, false, 90, mockDeviceOrientationChangeCallback);
|
||||
|
||||
int degrees = orientationManager.getVideoOrientation(null);
|
||||
|
||||
assertEquals(0, degrees);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPhotoOrientation_whenNaturalScreenOrientationEqualsPortraitUp() {
|
||||
int degreesPortraitUp =
|
||||
deviceOrientationManager.getPhotoOrientation(DeviceOrientation.PORTRAIT_UP);
|
||||
int degreesPortraitDown =
|
||||
deviceOrientationManager.getPhotoOrientation(DeviceOrientation.PORTRAIT_DOWN);
|
||||
int degreesLandscapeLeft =
|
||||
deviceOrientationManager.getPhotoOrientation(DeviceOrientation.LANDSCAPE_LEFT);
|
||||
int degreesLandscapeRight =
|
||||
deviceOrientationManager.getPhotoOrientation(DeviceOrientation.LANDSCAPE_RIGHT);
|
||||
|
||||
assertEquals(0, degreesPortraitUp);
|
||||
assertEquals(90, degreesLandscapeRight);
|
||||
assertEquals(180, degreesPortraitDown);
|
||||
assertEquals(270, degreesLandscapeLeft);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPhotoOrientation_whenNaturalScreenOrientationEqualsLandscapeLeft() {
|
||||
DeviceOrientationManager orientationManager =
|
||||
new DeviceOrientationManager(mockActivity, false, 90, mockDeviceOrientationChangeCallback);
|
||||
|
||||
int degreesPortraitUp = orientationManager.getPhotoOrientation(DeviceOrientation.PORTRAIT_UP);
|
||||
int degreesPortraitDown =
|
||||
orientationManager.getPhotoOrientation(DeviceOrientation.PORTRAIT_DOWN);
|
||||
int degreesLandscapeLeft =
|
||||
orientationManager.getPhotoOrientation(DeviceOrientation.LANDSCAPE_LEFT);
|
||||
int degreesLandscapeRight =
|
||||
orientationManager.getPhotoOrientation(DeviceOrientation.LANDSCAPE_RIGHT);
|
||||
|
||||
assertEquals(90, degreesPortraitUp);
|
||||
assertEquals(180, degreesLandscapeRight);
|
||||
assertEquals(270, degreesPortraitDown);
|
||||
assertEquals(0, degreesLandscapeLeft);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPhotoOrientation_shouldFallbackToCurrentOrientationWhenOrientationIsNull() {
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_0);
|
||||
|
||||
int degrees = deviceOrientationManager.getPhotoOrientation(null);
|
||||
|
||||
assertEquals(270, degrees);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUIOrientationChange_shouldSendMessageWhenSensorAccessIsAllowed() {
|
||||
try (MockedStatic<Settings.System> mockedSystem = mockStatic(Settings.System.class)) {
|
||||
@ -239,60 +137,6 @@ public class DeviceOrientationManagerTest {
|
||||
assertEquals(DeviceOrientation.PORTRAIT_UP, uiOrientation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDeviceDefaultOrientation() {
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_0);
|
||||
int orientation = deviceOrientationManager.getDeviceDefaultOrientation();
|
||||
assertEquals(Configuration.ORIENTATION_PORTRAIT, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_180);
|
||||
orientation = deviceOrientationManager.getDeviceDefaultOrientation();
|
||||
assertEquals(Configuration.ORIENTATION_PORTRAIT, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_90);
|
||||
orientation = deviceOrientationManager.getDeviceDefaultOrientation();
|
||||
assertEquals(Configuration.ORIENTATION_LANDSCAPE, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_270);
|
||||
orientation = deviceOrientationManager.getDeviceDefaultOrientation();
|
||||
assertEquals(Configuration.ORIENTATION_LANDSCAPE, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_0);
|
||||
orientation = deviceOrientationManager.getDeviceDefaultOrientation();
|
||||
assertEquals(Configuration.ORIENTATION_LANDSCAPE, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_180);
|
||||
orientation = deviceOrientationManager.getDeviceDefaultOrientation();
|
||||
assertEquals(Configuration.ORIENTATION_LANDSCAPE, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_90);
|
||||
orientation = deviceOrientationManager.getDeviceDefaultOrientation();
|
||||
assertEquals(Configuration.ORIENTATION_PORTRAIT, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_270);
|
||||
orientation = deviceOrientationManager.getDeviceDefaultOrientation();
|
||||
assertEquals(Configuration.ORIENTATION_PORTRAIT, orientation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateSensorOrientation() {
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_0);
|
||||
DeviceOrientation orientation = deviceOrientationManager.calculateSensorOrientation(0);
|
||||
assertEquals(DeviceOrientation.PORTRAIT_UP, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_0);
|
||||
orientation = deviceOrientationManager.calculateSensorOrientation(90);
|
||||
assertEquals(DeviceOrientation.LANDSCAPE_LEFT, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_0);
|
||||
orientation = deviceOrientationManager.calculateSensorOrientation(180);
|
||||
assertEquals(DeviceOrientation.PORTRAIT_DOWN, orientation);
|
||||
|
||||
setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_0);
|
||||
orientation = deviceOrientationManager.calculateSensorOrientation(270);
|
||||
assertEquals(DeviceOrientation.LANDSCAPE_RIGHT, orientation);
|
||||
}
|
||||
|
||||
private void setUpUIOrientationMocks(int orientation, int rotation) {
|
||||
Resources mockResources = mock(Resources.class);
|
||||
Configuration mockConfiguration = mock(Configuration.class);
|
||||
@ -304,6 +148,16 @@ public class DeviceOrientationManagerTest {
|
||||
when(mockResources.getConfiguration()).thenReturn(mockConfiguration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDefaultRotation_returnsExpectedValue() {
|
||||
final int expectedRotation = 90;
|
||||
when(mockDisplay.getRotation()).thenReturn(expectedRotation);
|
||||
|
||||
final int defaultRotation = deviceOrientationManager.getDefaultRotation();
|
||||
|
||||
assertEquals(defaultRotation, expectedRotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisplayTest() {
|
||||
Display display = deviceOrientationManager.getDisplay();
|
||||
|
@ -0,0 +1,96 @@
|
||||
// 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 static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel.DeviceOrientation;
|
||||
import io.flutter.plugin.common.BinaryMessenger;
|
||||
import io.flutter.plugins.camerax.DeviceOrientationManager.DeviceOrientationChangeCallback;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.DeviceOrientationManagerFlutterApi.Reply;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
public class DeviceOrientationManagerWrapperTest {
|
||||
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
|
||||
|
||||
@Mock DeviceOrientationManager mockDeviceOrientationManager;
|
||||
@Mock public BinaryMessenger mockBinaryMessenger;
|
||||
@Mock public InstanceManager mockInstanceManager;
|
||||
|
||||
@Test
|
||||
public void deviceOrientationManagerWrapper_handlesDeviceOrientationChangesAsExpected() {
|
||||
final DeviceOrientationManagerHostApiImpl hostApi =
|
||||
new DeviceOrientationManagerHostApiImpl(mockBinaryMessenger, mockInstanceManager);
|
||||
final CameraXProxy mockCameraXProxy = mock(CameraXProxy.class);
|
||||
final Activity mockActivity = mock(Activity.class);
|
||||
final Boolean isFrontFacing = true;
|
||||
final int sensorOrientation = 90;
|
||||
|
||||
DeviceOrientationManagerFlutterApiImpl flutterApi =
|
||||
mock(DeviceOrientationManagerFlutterApiImpl.class);
|
||||
hostApi.deviceOrientationManagerFlutterApiImpl = flutterApi;
|
||||
|
||||
hostApi.cameraXProxy = mockCameraXProxy;
|
||||
hostApi.setActivity(mockActivity);
|
||||
when(mockCameraXProxy.createDeviceOrientationManager(
|
||||
eq(mockActivity),
|
||||
eq(isFrontFacing),
|
||||
eq(sensorOrientation),
|
||||
any(DeviceOrientationChangeCallback.class)))
|
||||
.thenReturn(mockDeviceOrientationManager);
|
||||
|
||||
final ArgumentCaptor<DeviceOrientationChangeCallback> deviceOrientationChangeCallbackCaptor =
|
||||
ArgumentCaptor.forClass(DeviceOrientationChangeCallback.class);
|
||||
|
||||
hostApi.startListeningForDeviceOrientationChange(
|
||||
isFrontFacing, Long.valueOf(sensorOrientation));
|
||||
|
||||
// Test callback method defined in Flutter API is called when device orientation changes.
|
||||
verify(mockCameraXProxy)
|
||||
.createDeviceOrientationManager(
|
||||
eq(mockActivity),
|
||||
eq(isFrontFacing),
|
||||
eq(sensorOrientation),
|
||||
deviceOrientationChangeCallbackCaptor.capture());
|
||||
DeviceOrientationChangeCallback deviceOrientationChangeCallback =
|
||||
deviceOrientationChangeCallbackCaptor.getValue();
|
||||
|
||||
deviceOrientationChangeCallback.onChange(DeviceOrientation.PORTRAIT_DOWN);
|
||||
verify(flutterApi)
|
||||
.sendDeviceOrientationChangedEvent(
|
||||
eq(DeviceOrientation.PORTRAIT_DOWN.toString()), ArgumentMatchers.<Reply<Void>>any());
|
||||
|
||||
// Test that the DeviceOrientationManager starts listening for device orientation changes.
|
||||
verify(mockDeviceOrientationManager).start();
|
||||
|
||||
// Test that the DeviceOrientationManager can stop listening for device orientation changes.
|
||||
hostApi.stopListeningForDeviceOrientationChange();
|
||||
verify(mockDeviceOrientationManager).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDefaultDisplayRotation_returnsExpectedRotation() {
|
||||
final DeviceOrientationManagerHostApiImpl hostApi =
|
||||
new DeviceOrientationManagerHostApiImpl(mockBinaryMessenger, mockInstanceManager);
|
||||
final int defaultRotation = 180;
|
||||
|
||||
hostApi.deviceOrientationManager = mockDeviceOrientationManager;
|
||||
when(mockDeviceOrientationManager.getDefaultRotation()).thenReturn(defaultRotation);
|
||||
|
||||
assertEquals(hostApi.getDefaultDisplayRotation(), Long.valueOf(defaultRotation));
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Surface;
|
||||
import androidx.camera.core.ImageAnalysis;
|
||||
import androidx.camera.core.resolutionselector.ResolutionSelector;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@ -56,6 +57,7 @@ public class ImageAnalysisTest {
|
||||
final ResolutionSelector mockResolutionSelector = mock(ResolutionSelector.class);
|
||||
final long instanceIdentifier = 0;
|
||||
final long mockResolutionSelectorId = 25;
|
||||
final int targetRotation = Surface.ROTATION_90;
|
||||
|
||||
hostApi.cameraXProxy = mockCameraXProxy;
|
||||
instanceManager.addDartCreatedInstance(mockResolutionSelector, mockResolutionSelectorId);
|
||||
@ -63,8 +65,9 @@ public class ImageAnalysisTest {
|
||||
when(mockCameraXProxy.createImageAnalysisBuilder()).thenReturn(mockImageAnalysisBuilder);
|
||||
when(mockImageAnalysisBuilder.build()).thenReturn(mockImageAnalysis);
|
||||
|
||||
hostApi.create(instanceIdentifier, mockResolutionSelectorId);
|
||||
hostApi.create(instanceIdentifier, Long.valueOf(targetRotation), mockResolutionSelectorId);
|
||||
|
||||
verify(mockImageAnalysisBuilder).setTargetRotation(targetRotation);
|
||||
verify(mockImageAnalysisBuilder).setResolutionSelector(mockResolutionSelector);
|
||||
assertEquals(instanceManager.getInstance(instanceIdentifier), mockImageAnalysis);
|
||||
}
|
||||
@ -98,4 +101,18 @@ public class ImageAnalysisTest {
|
||||
|
||||
verify(mockImageAnalysis).clearAnalyzer();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTargetRotation_makesCallToSetTargetRotation() {
|
||||
final ImageAnalysisHostApiImpl hostApi =
|
||||
new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager, context);
|
||||
final long instanceIdentifier = 32;
|
||||
final int targetRotation = Surface.ROTATION_180;
|
||||
|
||||
instanceManager.addDartCreatedInstance(mockImageAnalysis, instanceIdentifier);
|
||||
|
||||
hostApi.setTargetRotation(instanceIdentifier, Long.valueOf(targetRotation));
|
||||
|
||||
verify(mockImageAnalysis).setTargetRotation(targetRotation);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Surface;
|
||||
import androidx.camera.core.ImageCapture;
|
||||
import androidx.camera.core.ImageCaptureException;
|
||||
import androidx.camera.core.resolutionselector.ResolutionSelector;
|
||||
@ -63,18 +64,24 @@ public class ImageCaptureTest {
|
||||
new ImageCaptureHostApiImpl(mockBinaryMessenger, testInstanceManager, context);
|
||||
final ImageCapture.Builder mockImageCaptureBuilder = mock(ImageCapture.Builder.class);
|
||||
final Long imageCaptureIdentifier = 74L;
|
||||
final Long flashMode = Long.valueOf(ImageCapture.FLASH_MODE_ON);
|
||||
final int flashMode = ImageCapture.FLASH_MODE_ON;
|
||||
final ResolutionSelector mockResolutionSelector = mock(ResolutionSelector.class);
|
||||
final long mockResolutionSelectorId = 77;
|
||||
final int targetRotation = Surface.ROTATION_270;
|
||||
|
||||
imageCaptureHostApiImpl.cameraXProxy = mockCameraXProxy;
|
||||
testInstanceManager.addDartCreatedInstance(mockResolutionSelector, mockResolutionSelectorId);
|
||||
when(mockCameraXProxy.createImageCaptureBuilder()).thenReturn(mockImageCaptureBuilder);
|
||||
when(mockImageCaptureBuilder.build()).thenReturn(mockImageCapture);
|
||||
|
||||
imageCaptureHostApiImpl.create(imageCaptureIdentifier, flashMode, mockResolutionSelectorId);
|
||||
imageCaptureHostApiImpl.create(
|
||||
imageCaptureIdentifier,
|
||||
Long.valueOf(targetRotation),
|
||||
Long.valueOf(flashMode),
|
||||
mockResolutionSelectorId);
|
||||
|
||||
verify(mockImageCaptureBuilder).setFlashMode(flashMode.intValue());
|
||||
verify(mockImageCaptureBuilder).setTargetRotation(targetRotation);
|
||||
verify(mockImageCaptureBuilder).setFlashMode(flashMode);
|
||||
verify(mockImageCaptureBuilder).setResolutionSelector(mockResolutionSelector);
|
||||
verify(mockImageCaptureBuilder).build();
|
||||
verify(testInstanceManager).addDartCreatedInstance(mockImageCapture, imageCaptureIdentifier);
|
||||
@ -197,4 +204,18 @@ public class ImageCaptureTest {
|
||||
|
||||
verify(mockResult).error(mockException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTargetRotation_makesCallToSetTargetRotation() {
|
||||
final ImageCaptureHostApiImpl hostApi =
|
||||
new ImageCaptureHostApiImpl(mockBinaryMessenger, testInstanceManager, context);
|
||||
final long instanceIdentifier = 42;
|
||||
final int targetRotation = Surface.ROTATION_90;
|
||||
|
||||
testInstanceManager.addDartCreatedInstance(mockImageCapture, instanceIdentifier);
|
||||
|
||||
hostApi.setTargetRotation(instanceIdentifier, Long.valueOf(targetRotation));
|
||||
|
||||
verify(mockImageCapture).setTargetRotation(targetRotation);
|
||||
}
|
||||
}
|
||||
|
@ -217,4 +217,18 @@ public class PreviewTest {
|
||||
assertEquals(resolutionInfo.getWidth(), Long.valueOf(resolutionWidth));
|
||||
assertEquals(resolutionInfo.getHeight(), Long.valueOf(resolutionHeight));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTargetRotation_makesCallToSetTargetRotation() {
|
||||
final PreviewHostApiImpl hostApi =
|
||||
new PreviewHostApiImpl(mockBinaryMessenger, testInstanceManager, mockTextureRegistry);
|
||||
final long instanceIdentifier = 52;
|
||||
final int targetRotation = Surface.ROTATION_180;
|
||||
|
||||
testInstanceManager.addDartCreatedInstance(mockPreview, instanceIdentifier);
|
||||
|
||||
hostApi.setTargetRotation(instanceIdentifier, Long.valueOf(targetRotation));
|
||||
|
||||
verify(mockPreview).setTargetRotation(targetRotation);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ package io.flutter.plugins.camerax;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
@ -16,20 +15,16 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformChannel.DeviceOrientation;
|
||||
import io.flutter.plugin.common.BinaryMessenger;
|
||||
import io.flutter.plugins.camerax.CameraPermissionsManager.PermissionsRegistry;
|
||||
import io.flutter.plugins.camerax.CameraPermissionsManager.ResultCallback;
|
||||
import io.flutter.plugins.camerax.DeviceOrientationManager.DeviceOrientationChangeCallback;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraPermissionsErrorData;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.Result;
|
||||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.SystemServicesFlutterApi.Reply;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
@ -96,55 +91,6 @@ public class SystemServicesTest {
|
||||
assertEquals(cameraPermissionsErrorData.getDescription(), testErrorDescription);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deviceOrientationChangeTest() {
|
||||
final SystemServicesHostApiImpl systemServicesHostApi =
|
||||
new SystemServicesHostApiImpl(mockBinaryMessenger, mockInstanceManager, mockContext);
|
||||
final CameraXProxy mockCameraXProxy = mock(CameraXProxy.class);
|
||||
final Activity mockActivity = mock(Activity.class);
|
||||
final DeviceOrientationManager mockDeviceOrientationManager =
|
||||
mock(DeviceOrientationManager.class);
|
||||
final Boolean isFrontFacing = true;
|
||||
final int sensorOrientation = 90;
|
||||
|
||||
SystemServicesFlutterApiImpl systemServicesFlutterApi =
|
||||
mock(SystemServicesFlutterApiImpl.class);
|
||||
systemServicesHostApi.systemServicesFlutterApi = systemServicesFlutterApi;
|
||||
|
||||
systemServicesHostApi.cameraXProxy = mockCameraXProxy;
|
||||
systemServicesHostApi.setActivity(mockActivity);
|
||||
when(mockCameraXProxy.createDeviceOrientationManager(
|
||||
eq(mockActivity),
|
||||
eq(isFrontFacing),
|
||||
eq(sensorOrientation),
|
||||
any(DeviceOrientationChangeCallback.class)))
|
||||
.thenReturn(mockDeviceOrientationManager);
|
||||
|
||||
final ArgumentCaptor<DeviceOrientationChangeCallback> deviceOrientationChangeCallbackCaptor =
|
||||
ArgumentCaptor.forClass(DeviceOrientationChangeCallback.class);
|
||||
|
||||
systemServicesHostApi.startListeningForDeviceOrientationChange(
|
||||
isFrontFacing, Long.valueOf(sensorOrientation));
|
||||
|
||||
// Test callback method defined in Flutter API is called when device orientation changes.
|
||||
verify(mockCameraXProxy)
|
||||
.createDeviceOrientationManager(
|
||||
eq(mockActivity),
|
||||
eq(isFrontFacing),
|
||||
eq(sensorOrientation),
|
||||
deviceOrientationChangeCallbackCaptor.capture());
|
||||
DeviceOrientationChangeCallback deviceOrientationChangeCallback =
|
||||
deviceOrientationChangeCallbackCaptor.getValue();
|
||||
|
||||
deviceOrientationChangeCallback.onChange(DeviceOrientation.PORTRAIT_DOWN);
|
||||
verify(systemServicesFlutterApi)
|
||||
.sendDeviceOrientationChangedEvent(
|
||||
eq(DeviceOrientation.PORTRAIT_DOWN.toString()), ArgumentMatchers.<Reply<Void>>any());
|
||||
|
||||
// Test that the DeviceOrientationManager starts listening for device orientation changes.
|
||||
verify(mockDeviceOrientationManager).start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTempFilePath_returnsCorrectPath() {
|
||||
final SystemServicesHostApiImpl systemServicesHostApi =
|
||||
|
@ -10,6 +10,7 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.view.Surface;
|
||||
import androidx.camera.video.Recorder;
|
||||
import androidx.camera.video.VideoCapture;
|
||||
import io.flutter.plugin.common.BinaryMessenger;
|
||||
@ -78,6 +79,20 @@ public class VideoCaptureTest {
|
||||
testInstanceManager.remove(videoCaptureId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTargetRotation_makesCallToSetTargetRotation() {
|
||||
final VideoCaptureHostApiImpl hostApi =
|
||||
new VideoCaptureHostApiImpl(mockBinaryMessenger, testInstanceManager);
|
||||
final long instanceIdentifier = 62;
|
||||
final int targetRotation = Surface.ROTATION_270;
|
||||
|
||||
testInstanceManager.addDartCreatedInstance(mockVideoCapture, instanceIdentifier);
|
||||
|
||||
hostApi.setTargetRotation(instanceIdentifier, Long.valueOf(targetRotation));
|
||||
|
||||
verify(mockVideoCapture).setTargetRotation(targetRotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flutterApiCreateTest() {
|
||||
final VideoCaptureFlutterApiImpl spyVideoCaptureFlutterApi =
|
||||
|
@ -296,14 +296,16 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
||||
IconButton(
|
||||
icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute),
|
||||
color: Colors.blue,
|
||||
onPressed: () {}, // TODO(camsim99): Add functionality back here.
|
||||
onPressed: controller != null ? onAudioModeButtonPressed : null,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(controller?.value.isCaptureOrientationLocked ?? false
|
||||
? Icons.screen_lock_rotation
|
||||
: Icons.screen_rotation),
|
||||
color: Colors.blue,
|
||||
onPressed: () {}, // TODO(camsim99): Add functionality back here.
|
||||
onPressed: controller != null
|
||||
? onCaptureOrientationLockButtonPressed
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -6,6 +6,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:camera_platform_interface/camera_platform_interface.dart';
|
||||
import 'package:flutter/services.dart' show DeviceOrientation;
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
|
||||
@ -17,6 +18,7 @@ import 'camera_selector.dart';
|
||||
import 'camera_state.dart';
|
||||
import 'camerax_library.g.dart';
|
||||
import 'camerax_proxy.dart';
|
||||
import 'device_orientation_manager.dart';
|
||||
import 'exposure_state.dart';
|
||||
import 'fallback_strategy.dart';
|
||||
import 'image_analysis.dart';
|
||||
@ -155,6 +157,24 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
/// set for the camera in use.
|
||||
static const String zoomStateNotSetErrorCode = 'zoomStateNotSet';
|
||||
|
||||
/// Whether or not the capture orientation is locked.
|
||||
///
|
||||
/// Indicates a new target rotation should not be set as it has been locked by
|
||||
/// [lockCaptureOrientation].
|
||||
@visibleForTesting
|
||||
bool captureOrientationLocked = false;
|
||||
|
||||
/// Whether or not the default rotation for [UseCase]s needs to be set
|
||||
/// manually because the capture orientation was previously locked.
|
||||
///
|
||||
/// Currently, CameraX provides no way to unset target rotations for
|
||||
/// [UseCase]s, so once they are set and unset, this plugin must start setting
|
||||
/// the default orientation manually.
|
||||
///
|
||||
/// See https://developer.android.com/reference/androidx/camera/core/ImageCapture#setTargetRotation(int)
|
||||
/// for an example on how setting target rotations for [UseCase]s works.
|
||||
bool shouldSetDefaultRotation = false;
|
||||
|
||||
/// Returns list of all available cameras and their descriptions.
|
||||
@override
|
||||
Future<List<CameraDescription>> availableCameras() async {
|
||||
@ -240,20 +260,19 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
processCameraProvider!.unbindAll();
|
||||
|
||||
// Configure Preview instance.
|
||||
final int targetRotation =
|
||||
_getTargetRotation(cameraDescription.sensorOrientation);
|
||||
preview = proxy.createPreview(
|
||||
targetRotation: targetRotation,
|
||||
resolutionSelector: presetResolutionSelector);
|
||||
preview = proxy.createPreview(presetResolutionSelector,
|
||||
/* use CameraX default target rotation */ null);
|
||||
final int flutterSurfaceTextureId =
|
||||
await proxy.setPreviewSurfaceProvider(preview!);
|
||||
|
||||
// Configure ImageCapture instance.
|
||||
imageCapture = proxy.createImageCapture(presetResolutionSelector);
|
||||
imageCapture = proxy.createImageCapture(presetResolutionSelector,
|
||||
/* use CameraX default target rotation */ null);
|
||||
|
||||
// Configure ImageAnalysis instance.
|
||||
// Defaults to YUV_420_888 image format.
|
||||
imageAnalysis = proxy.createImageAnalysis(presetResolutionSelector);
|
||||
imageAnalysis = proxy.createImageAnalysis(presetResolutionSelector,
|
||||
/* use CameraX default target rotation */ null);
|
||||
|
||||
// Configure VideoCapture and Recorder instances.
|
||||
recorder = proxy.createRecorder(presetQualitySelector);
|
||||
@ -370,6 +389,35 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
return _cameraEvents(cameraId).whereType<VideoRecordedEvent>();
|
||||
}
|
||||
|
||||
/// Locks the capture orientation.
|
||||
@override
|
||||
Future<void> lockCaptureOrientation(
|
||||
int cameraId,
|
||||
DeviceOrientation orientation,
|
||||
) async {
|
||||
// Flag that (1) default rotation for UseCases will need to be set manually
|
||||
// if orientation is ever unlocked and (2) the capture orientation is locked
|
||||
// and should not be changed until unlocked.
|
||||
shouldSetDefaultRotation = true;
|
||||
captureOrientationLocked = true;
|
||||
|
||||
// Get target rotation based on locked orientation.
|
||||
final int targetLockedRotation =
|
||||
_getRotationConstantFromDeviceOrientation(orientation);
|
||||
|
||||
// Update UseCases to use target device orientation.
|
||||
await imageCapture!.setTargetRotation(targetLockedRotation);
|
||||
await imageAnalysis!.setTargetRotation(targetLockedRotation);
|
||||
await videoCapture!.setTargetRotation(targetLockedRotation);
|
||||
}
|
||||
|
||||
/// Unlocks the capture orientation.
|
||||
@override
|
||||
Future<void> unlockCaptureOrientation(int cameraId) async {
|
||||
// Flag that default rotation should be set for UseCases as needed.
|
||||
captureOrientationLocked = false;
|
||||
}
|
||||
|
||||
/// Gets the minimum supported exposure offset for the selected camera in EV units.
|
||||
///
|
||||
/// [cameraId] not used.
|
||||
@ -449,7 +497,8 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
/// The ui orientation changed.
|
||||
@override
|
||||
Stream<DeviceOrientationChangedEvent> onDeviceOrientationChanged() {
|
||||
return SystemServices.deviceOrientationChangedStreamController.stream;
|
||||
return DeviceOrientationManager
|
||||
.deviceOrientationChangedStreamController.stream;
|
||||
}
|
||||
|
||||
/// Pause the active preview on the current frame for the selected camera.
|
||||
@ -493,6 +542,7 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
/// [cameraId] is not used.
|
||||
@override
|
||||
Future<XFile> takePicture(int cameraId) async {
|
||||
// Set flash mode.
|
||||
if (_currentFlashMode != null) {
|
||||
await imageCapture!.setFlashMode(_currentFlashMode!);
|
||||
} else if (torchEnabled) {
|
||||
@ -500,6 +550,14 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
// been enabled.
|
||||
await imageCapture!.setFlashMode(ImageCapture.flashModeOff);
|
||||
}
|
||||
|
||||
// Set target rotation to default CameraX rotation only if capture
|
||||
// orientation not locked.
|
||||
if (!captureOrientationLocked && shouldSetDefaultRotation) {
|
||||
await imageCapture!
|
||||
.setTargetRotation(await proxy.getDefaultDisplayRotation());
|
||||
}
|
||||
|
||||
final String picturePath = await imageCapture!.takePicture();
|
||||
return XFile(picturePath);
|
||||
}
|
||||
@ -582,6 +640,13 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
.bindToLifecycle(cameraSelector!, <UseCase>[videoCapture!]);
|
||||
}
|
||||
|
||||
// Set target rotation to default CameraX rotation only if capture
|
||||
// orientation not locked.
|
||||
if (!captureOrientationLocked && shouldSetDefaultRotation) {
|
||||
await videoCapture!
|
||||
.setTargetRotation(await proxy.getDefaultDisplayRotation());
|
||||
}
|
||||
|
||||
videoOutputPath =
|
||||
await SystemServices.getTempFilePath(videoPrefix, '.temp');
|
||||
pendingRecording = await recorder!.prepareRecording(videoOutputPath!);
|
||||
@ -654,7 +719,7 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
Stream<CameraImageData> onStreamedFrameAvailable(int cameraId,
|
||||
{CameraImageStreamOptions? options}) {
|
||||
cameraImageDataStreamController = StreamController<CameraImageData>(
|
||||
onListen: () => _onFrameStreamListen(cameraId),
|
||||
onListen: () => _configureImageAnalysis(cameraId),
|
||||
onCancel: _onFrameStreamCancel,
|
||||
);
|
||||
return cameraImageDataStreamController!.stream;
|
||||
@ -683,7 +748,14 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
|
||||
/// Configures the [imageAnalysis] instance for image streaming.
|
||||
Future<void> _configureImageAnalysis(int cameraId) async {
|
||||
// Create Analyzer that can read image data for image streaming.
|
||||
// Set target rotation to default CameraX rotation only if capture
|
||||
// orientation not locked.
|
||||
if (!captureOrientationLocked && shouldSetDefaultRotation) {
|
||||
await imageAnalysis!
|
||||
.setTargetRotation(await proxy.getDefaultDisplayRotation());
|
||||
}
|
||||
|
||||
// Create and set Analyzer that can read image data for image streaming.
|
||||
final WeakReference<AndroidCameraCameraX> weakThis =
|
||||
WeakReference<AndroidCameraCameraX>(this);
|
||||
Future<void> analyze(ImageProxy imageProxy) async {
|
||||
@ -708,7 +780,7 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
width: imageProxy.width);
|
||||
|
||||
weakThis.target!.cameraImageDataStreamController!.add(cameraImageData);
|
||||
unawaited(imageProxy.close());
|
||||
await imageProxy.close();
|
||||
}
|
||||
|
||||
final Analyzer analyzer = proxy.createAnalyzer(analyze);
|
||||
@ -728,12 +800,6 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
|
||||
// Methods for configuring image streaming:
|
||||
|
||||
/// The [onListen] callback for the stream controller used for image
|
||||
/// streaming.
|
||||
Future<void> _onFrameStreamListen(int cameraId) async {
|
||||
await _configureImageAnalysis(cameraId);
|
||||
}
|
||||
|
||||
/// The [onCancel] callback for the stream controller used for image
|
||||
/// streaming.
|
||||
///
|
||||
@ -816,21 +882,19 @@ class AndroidCameraCameraX extends CameraPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [Surface] target rotation constant that maps to specified sensor
|
||||
/// orientation.
|
||||
int _getTargetRotation(int sensorOrientation) {
|
||||
switch (sensorOrientation) {
|
||||
case 90:
|
||||
return Surface.ROTATION_90;
|
||||
case 180:
|
||||
return Surface.ROTATION_180;
|
||||
case 270:
|
||||
return Surface.ROTATION_270;
|
||||
case 0:
|
||||
/// Returns [Surface] constant for counter-clockwise degrees of rotation from
|
||||
/// [DeviceOrientation.portraitUp] required to reach the specified
|
||||
/// [DeviceOrientation].
|
||||
int _getRotationConstantFromDeviceOrientation(DeviceOrientation orientation) {
|
||||
switch (orientation) {
|
||||
case DeviceOrientation.portraitUp:
|
||||
return Surface.ROTATION_0;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'"$sensorOrientation" is not a valid sensor orientation value');
|
||||
case DeviceOrientation.landscapeLeft:
|
||||
return Surface.ROTATION_90;
|
||||
case DeviceOrientation.portraitDown:
|
||||
return Surface.ROTATION_180;
|
||||
case DeviceOrientation.landscapeRight:
|
||||
return Surface.ROTATION_270;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import 'camera_selector.dart';
|
||||
import 'camera_state.dart';
|
||||
import 'camera_state_error.dart';
|
||||
import 'camerax_library.g.dart';
|
||||
import 'device_orientation_manager.dart';
|
||||
import 'exposure_state.dart';
|
||||
import 'image_proxy.dart';
|
||||
import 'java_object.dart';
|
||||
@ -34,6 +35,8 @@ class AndroidCameraXCameraFlutterApis {
|
||||
CameraSelectorFlutterApiImpl? cameraSelectorFlutterApiImpl,
|
||||
ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApiImpl,
|
||||
SystemServicesFlutterApiImpl? systemServicesFlutterApiImpl,
|
||||
DeviceOrientationManagerFlutterApiImpl?
|
||||
deviceOrientationManagerFlutterApiImpl,
|
||||
CameraStateErrorFlutterApiImpl? cameraStateErrorFlutterApiImpl,
|
||||
CameraStateFlutterApiImpl? cameraStateFlutterApiImpl,
|
||||
PendingRecordingFlutterApiImpl? pendingRecordingFlutterApiImpl,
|
||||
@ -60,6 +63,9 @@ class AndroidCameraXCameraFlutterApis {
|
||||
this.cameraFlutterApiImpl = cameraFlutterApiImpl ?? CameraFlutterApiImpl();
|
||||
this.systemServicesFlutterApiImpl =
|
||||
systemServicesFlutterApiImpl ?? SystemServicesFlutterApiImpl();
|
||||
this.deviceOrientationManagerFlutterApiImpl =
|
||||
deviceOrientationManagerFlutterApiImpl ??
|
||||
DeviceOrientationManagerFlutterApiImpl();
|
||||
this.cameraStateErrorFlutterApiImpl =
|
||||
cameraStateErrorFlutterApiImpl ?? CameraStateErrorFlutterApiImpl();
|
||||
this.cameraStateFlutterApiImpl =
|
||||
@ -117,6 +123,10 @@ class AndroidCameraXCameraFlutterApis {
|
||||
/// Flutter Api implementation for [SystemServices].
|
||||
late final SystemServicesFlutterApiImpl systemServicesFlutterApiImpl;
|
||||
|
||||
/// Flutter Api implementation for [DeviceOrientationManager].
|
||||
late final DeviceOrientationManagerFlutterApiImpl
|
||||
deviceOrientationManagerFlutterApiImpl;
|
||||
|
||||
/// Flutter Api implementation for [CameraStateError].
|
||||
late final CameraStateErrorFlutterApiImpl? cameraStateErrorFlutterApiImpl;
|
||||
|
||||
@ -169,6 +179,8 @@ class AndroidCameraXCameraFlutterApis {
|
||||
processCameraProviderFlutterApiImpl);
|
||||
CameraFlutterApi.setup(cameraFlutterApiImpl);
|
||||
SystemServicesFlutterApi.setup(systemServicesFlutterApiImpl);
|
||||
DeviceOrientationManagerFlutterApi.setup(
|
||||
deviceOrientationManagerFlutterApiImpl);
|
||||
CameraStateErrorFlutterApi.setup(cameraStateErrorFlutterApiImpl);
|
||||
CameraStateFlutterApi.setup(cameraStateFlutterApiImpl);
|
||||
PendingRecordingFlutterApi.setup(pendingRecordingFlutterApiImpl);
|
||||
|
@ -894,53 +894,6 @@ class SystemServicesHostApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startListeningForDeviceOrientationChange(
|
||||
bool arg_isFrontFacing, int arg_sensorOrientation) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange',
|
||||
codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_isFrontFacing, arg_sensorOrientation])
|
||||
as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stopListeningForDeviceOrientationChange() async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange',
|
||||
codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getTempFilePath(String arg_prefix, String arg_suffix) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.SystemServicesHostApi.getTempFilePath', codec,
|
||||
@ -972,32 +925,10 @@ class SystemServicesHostApi {
|
||||
abstract class SystemServicesFlutterApi {
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
void onDeviceOrientationChanged(String orientation);
|
||||
|
||||
void onCameraError(String errorDescription);
|
||||
|
||||
static void setup(SystemServicesFlutterApi? api,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged',
|
||||
codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
channel.setMessageHandler(null);
|
||||
} else {
|
||||
channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final String? arg_orientation = (args[0] as String?);
|
||||
assert(arg_orientation != null,
|
||||
'Argument for dev.flutter.pigeon.SystemServicesFlutterApi.onDeviceOrientationChanged was null, expected non-null String.');
|
||||
api.onDeviceOrientationChanged(arg_orientation!);
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.SystemServicesFlutterApi.onCameraError', codec,
|
||||
@ -1020,6 +951,121 @@ abstract class SystemServicesFlutterApi {
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceOrientationManagerHostApi {
|
||||
/// Constructor for [DeviceOrientationManagerHostApi]. The [binaryMessenger] named argument is
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
DeviceOrientationManagerHostApi({BinaryMessenger? binaryMessenger})
|
||||
: _binaryMessenger = binaryMessenger;
|
||||
final BinaryMessenger? _binaryMessenger;
|
||||
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
Future<void> startListeningForDeviceOrientationChange(
|
||||
bool arg_isFrontFacing, int arg_sensorOrientation) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.DeviceOrientationManagerHostApi.startListeningForDeviceOrientationChange',
|
||||
codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_isFrontFacing, arg_sensorOrientation])
|
||||
as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stopListeningForDeviceOrientationChange() async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.DeviceOrientationManagerHostApi.stopListeningForDeviceOrientationChange',
|
||||
codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> getDefaultDisplayRotation() async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.DeviceOrientationManagerHostApi.getDefaultDisplayRotation',
|
||||
codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else if (replyList[0] == null) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (replyList[0] as int?)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DeviceOrientationManagerFlutterApi {
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
void onDeviceOrientationChanged(String orientation);
|
||||
|
||||
static void setup(DeviceOrientationManagerFlutterApi? api,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.DeviceOrientationManagerFlutterApi.onDeviceOrientationChanged',
|
||||
codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
channel.setMessageHandler(null);
|
||||
} else {
|
||||
channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.DeviceOrientationManagerFlutterApi.onDeviceOrientationChanged was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final String? arg_orientation = (args[0] as String?);
|
||||
assert(arg_orientation != null,
|
||||
'Argument for dev.flutter.pigeon.DeviceOrientationManagerFlutterApi.onDeviceOrientationChanged was null, expected non-null String.');
|
||||
api.onDeviceOrientationChanged(arg_orientation!);
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _PreviewHostApiCodec extends StandardMessageCodec {
|
||||
const _PreviewHostApiCodec();
|
||||
@override
|
||||
@ -1151,6 +1197,28 @@ class PreviewHostApi {
|
||||
return (replyList[0] as ResolutionInfo?)!;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setTargetRotation(int arg_identifier, int arg_rotation) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.PreviewHostApi.setTargetRotation', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel
|
||||
.send(<Object?>[arg_identifier, arg_rotation]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VideoCaptureHostApi {
|
||||
@ -1216,6 +1284,28 @@ class VideoCaptureHostApi {
|
||||
return (replyList[0] as int?)!;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setTargetRotation(int arg_identifier, int arg_rotation) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.VideoCaptureHostApi.setTargetRotation', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel
|
||||
.send(<Object?>[arg_identifier, arg_rotation]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class VideoCaptureFlutterApi {
|
||||
@ -1603,14 +1693,17 @@ class ImageCaptureHostApi {
|
||||
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
Future<void> create(int arg_identifier, int? arg_flashMode,
|
||||
int? arg_resolutionSelectorId) async {
|
||||
Future<void> create(int arg_identifier, int? arg_targetRotation,
|
||||
int? arg_flashMode, int? arg_resolutionSelectorId) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.ImageCaptureHostApi.create', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel.send(
|
||||
<Object?>[arg_identifier, arg_flashMode, arg_resolutionSelectorId])
|
||||
as List<Object?>?;
|
||||
final List<Object?>? replyList = await channel.send(<Object?>[
|
||||
arg_identifier,
|
||||
arg_targetRotation,
|
||||
arg_flashMode,
|
||||
arg_resolutionSelectorId
|
||||
]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
@ -1675,6 +1768,28 @@ class ImageCaptureHostApi {
|
||||
return (replyList[0] as String?)!;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setTargetRotation(int arg_identifier, int arg_rotation) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.ImageCaptureHostApi.setTargetRotation', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel
|
||||
.send(<Object?>[arg_identifier, arg_rotation]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _ResolutionStrategyHostApiCodec extends StandardMessageCodec {
|
||||
@ -1974,13 +2089,16 @@ class ImageAnalysisHostApi {
|
||||
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
Future<void> create(int arg_identifier, int? arg_resolutionSelectorId) async {
|
||||
Future<void> create(int arg_identifier, int? arg_targetRotation,
|
||||
int? arg_resolutionSelectorId) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.ImageAnalysisHostApi.create', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_identifier, arg_resolutionSelectorId])
|
||||
as List<Object?>?;
|
||||
final List<Object?>? replyList = await channel.send(<Object?>[
|
||||
arg_identifier,
|
||||
arg_targetRotation,
|
||||
arg_resolutionSelectorId
|
||||
]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
@ -2042,6 +2160,28 @@ class ImageAnalysisHostApi {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setTargetRotation(int arg_identifier, int arg_rotation) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.ImageAnalysisHostApi.setTargetRotation', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel
|
||||
.send(<Object?>[arg_identifier, arg_rotation]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnalyzerHostApi {
|
||||
|
@ -8,6 +8,7 @@ import 'analyzer.dart';
|
||||
import 'camera_selector.dart';
|
||||
import 'camera_state.dart';
|
||||
import 'camerax_library.g.dart';
|
||||
import 'device_orientation_manager.dart';
|
||||
import 'fallback_strategy.dart';
|
||||
import 'image_analysis.dart';
|
||||
import 'image_capture.dart';
|
||||
@ -47,6 +48,7 @@ class CameraXProxy {
|
||||
this.startListeningForDeviceOrientationChange =
|
||||
_startListeningForDeviceOrientationChange,
|
||||
this.setPreviewSurfaceProvider = _setPreviewSurfaceProvider,
|
||||
this.getDefaultDisplayRotation = _getDefaultDisplayRotation,
|
||||
});
|
||||
|
||||
/// Returns a [ProcessCameraProvider] instance.
|
||||
@ -58,12 +60,14 @@ class CameraXProxy {
|
||||
/// Returns a [Preview] configured with the specified target rotation and
|
||||
/// specified [ResolutionSelector].
|
||||
Preview Function(
|
||||
{required int targetRotation,
|
||||
ResolutionSelector? resolutionSelector}) createPreview;
|
||||
ResolutionSelector? resolutionSelector,
|
||||
int? targetRotation,
|
||||
) createPreview;
|
||||
|
||||
/// Returns an [ImageCapture] configured with specified flash mode and
|
||||
/// the specified [ResolutionSelector].
|
||||
ImageCapture Function(ResolutionSelector? resolutionSelector)
|
||||
ImageCapture Function(
|
||||
ResolutionSelector? resolutionSelector, int? targetRotation)
|
||||
createImageCapture;
|
||||
|
||||
/// Returns a [Recorder] for use in video capture configured with the
|
||||
@ -75,7 +79,8 @@ class CameraXProxy {
|
||||
|
||||
/// Returns an [ImageAnalysis] configured with the specified
|
||||
/// [ResolutionSelector].
|
||||
ImageAnalysis Function(ResolutionSelector? resolutionSelector)
|
||||
ImageAnalysis Function(
|
||||
ResolutionSelector? resolutionSelector, int? targetRotation)
|
||||
createImageAnalysis;
|
||||
|
||||
/// Returns an [Analyzer] configured with the specified callback for
|
||||
@ -128,6 +133,10 @@ class CameraXProxy {
|
||||
/// the ID corresponding to the surface it will provide.
|
||||
Future<int> Function(Preview preview) setPreviewSurfaceProvider;
|
||||
|
||||
/// Returns default rotation for [UseCase]s in terms of one of the [Surface]
|
||||
/// rotation constants.
|
||||
Future<int> Function() getDefaultDisplayRotation;
|
||||
|
||||
static Future<ProcessCameraProvider> _getProcessCameraProvider() {
|
||||
return ProcessCameraProvider.getInstance();
|
||||
}
|
||||
@ -145,14 +154,17 @@ class CameraXProxy {
|
||||
}
|
||||
|
||||
static Preview _createAttachedPreview(
|
||||
{required int targetRotation, ResolutionSelector? resolutionSelector}) {
|
||||
ResolutionSelector? resolutionSelector, int? targetRotation) {
|
||||
return Preview(
|
||||
targetRotation: targetRotation, resolutionSelector: resolutionSelector);
|
||||
initialTargetRotation: targetRotation,
|
||||
resolutionSelector: resolutionSelector);
|
||||
}
|
||||
|
||||
static ImageCapture _createAttachedImageCapture(
|
||||
ResolutionSelector? resolutionSelector) {
|
||||
return ImageCapture(resolutionSelector: resolutionSelector);
|
||||
ResolutionSelector? resolutionSelector, int? targetRotation) {
|
||||
return ImageCapture(
|
||||
resolutionSelector: resolutionSelector,
|
||||
initialTargetRotation: targetRotation);
|
||||
}
|
||||
|
||||
static Recorder _createAttachedRecorder(QualitySelector? qualitySelector) {
|
||||
@ -165,8 +177,10 @@ class CameraXProxy {
|
||||
}
|
||||
|
||||
static ImageAnalysis _createAttachedImageAnalysis(
|
||||
ResolutionSelector? resolutionSelector) {
|
||||
return ImageAnalysis(resolutionSelector: resolutionSelector);
|
||||
ResolutionSelector? resolutionSelector, int? targetRotation) {
|
||||
return ImageAnalysis(
|
||||
resolutionSelector: resolutionSelector,
|
||||
initialTargetRotation: targetRotation);
|
||||
}
|
||||
|
||||
static Analyzer _createAttachedAnalyzer(
|
||||
@ -214,11 +228,15 @@ class CameraXProxy {
|
||||
|
||||
static void _startListeningForDeviceOrientationChange(
|
||||
bool cameraIsFrontFacing, int sensorOrientation) {
|
||||
SystemServices.startListeningForDeviceOrientationChange(
|
||||
DeviceOrientationManager.startListeningForDeviceOrientationChange(
|
||||
cameraIsFrontFacing, sensorOrientation);
|
||||
}
|
||||
|
||||
static Future<int> _setPreviewSurfaceProvider(Preview preview) async {
|
||||
return preview.setSurfaceProvider();
|
||||
}
|
||||
|
||||
static Future<int> _getDefaultDisplayRotation() async {
|
||||
return DeviceOrientationManager.getDefaultDisplayRotation();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,121 @@
|
||||
// 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.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:camera_platform_interface/camera_platform_interface.dart'
|
||||
show DeviceOrientationChangedEvent;
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'android_camera_camerax_flutter_api_impls.dart';
|
||||
import 'camerax_library.g.dart';
|
||||
|
||||
// Ignoring lint indicating this class only contains static members
|
||||
// as this class is a wrapper for various Android system services.
|
||||
// ignore_for_file: avoid_classes_with_only_static_members
|
||||
|
||||
/// Utility class that offers access to Android system services needed for
|
||||
/// camera usage and other informational streams.
|
||||
class DeviceOrientationManager {
|
||||
/// Stream that emits the device orientation whenever it is changed.
|
||||
///
|
||||
/// Values may start being added to the stream once
|
||||
/// `startListeningForDeviceOrientationChange(...)` is called.
|
||||
static final StreamController<DeviceOrientationChangedEvent>
|
||||
deviceOrientationChangedStreamController =
|
||||
StreamController<DeviceOrientationChangedEvent>.broadcast();
|
||||
|
||||
/// Requests that [deviceOrientationChangedStreamController] start
|
||||
/// emitting values for any change in device orientation.
|
||||
static void startListeningForDeviceOrientationChange(
|
||||
bool isFrontFacing, int sensorOrientation,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
AndroidCameraXCameraFlutterApis.instance.ensureSetUp();
|
||||
final DeviceOrientationManagerHostApi api =
|
||||
DeviceOrientationManagerHostApi(binaryMessenger: binaryMessenger);
|
||||
|
||||
api.startListeningForDeviceOrientationChange(
|
||||
isFrontFacing, sensorOrientation);
|
||||
}
|
||||
|
||||
/// Stops the [deviceOrientationChangedStreamController] from emitting values
|
||||
/// for changes in device orientation.
|
||||
static void stopListeningForDeviceOrientationChange(
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
final DeviceOrientationManagerHostApi api =
|
||||
DeviceOrientationManagerHostApi(binaryMessenger: binaryMessenger);
|
||||
|
||||
api.stopListeningForDeviceOrientationChange();
|
||||
}
|
||||
|
||||
/// Retrieves the default rotation that CameraX uses for [UseCase]s in terms
|
||||
/// of one of the [Surface] rotation constants.
|
||||
///
|
||||
/// The default rotation that CameraX uses is the rotation of the default
|
||||
/// display at the time of binding a particular [UseCase], but the default
|
||||
/// display does not change in the plugin, so this default value is
|
||||
/// display-agnostic.
|
||||
///
|
||||
/// [startListeningForDeviceOrientationChange] must be called before calling
|
||||
/// this method.
|
||||
static Future<int> getDefaultDisplayRotation(
|
||||
{BinaryMessenger? binaryMessenger}) async {
|
||||
final DeviceOrientationManagerHostApi api =
|
||||
DeviceOrientationManagerHostApi(binaryMessenger: binaryMessenger);
|
||||
|
||||
return api.getDefaultDisplayRotation();
|
||||
}
|
||||
|
||||
/// Serializes [DeviceOrientation] into a [String].
|
||||
static String serializeDeviceOrientation(DeviceOrientation orientation) {
|
||||
switch (orientation) {
|
||||
case DeviceOrientation.landscapeLeft:
|
||||
return 'LANDSCAPE_LEFT';
|
||||
case DeviceOrientation.landscapeRight:
|
||||
return 'LANDSCAPE_RIGHT';
|
||||
case DeviceOrientation.portraitDown:
|
||||
return 'PORTRAIT_DOWN';
|
||||
case DeviceOrientation.portraitUp:
|
||||
return 'PORTRAIT_UP';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Flutter API implementation of [DeviceOrientationManager].
|
||||
class DeviceOrientationManagerFlutterApiImpl
|
||||
implements DeviceOrientationManagerFlutterApi {
|
||||
/// Constructs an [DeviceOrientationManagerFlutterApiImpl].
|
||||
DeviceOrientationManagerFlutterApiImpl();
|
||||
|
||||
/// Callback method for any changes in device orientation.
|
||||
///
|
||||
/// Will only be called if
|
||||
/// `DeviceOrientationManager.startListeningForDeviceOrientationChange(...)` was called
|
||||
/// to start listening for device orientation updates.
|
||||
@override
|
||||
void onDeviceOrientationChanged(String orientation) {
|
||||
final DeviceOrientation deviceOrientation =
|
||||
deserializeDeviceOrientation(orientation);
|
||||
DeviceOrientationManager.deviceOrientationChangedStreamController
|
||||
.add(DeviceOrientationChangedEvent(deviceOrientation));
|
||||
}
|
||||
|
||||
/// Deserializes device orientation in [String] format into a
|
||||
/// [DeviceOrientation].
|
||||
DeviceOrientation deserializeDeviceOrientation(String orientation) {
|
||||
switch (orientation) {
|
||||
case 'LANDSCAPE_LEFT':
|
||||
return DeviceOrientation.landscapeLeft;
|
||||
case 'LANDSCAPE_RIGHT':
|
||||
return DeviceOrientation.landscapeRight;
|
||||
case 'PORTRAIT_DOWN':
|
||||
return DeviceOrientation.portraitDown;
|
||||
case 'PORTRAIT_UP':
|
||||
return DeviceOrientation.portraitUp;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'"$orientation" is not a valid DeviceOrientation value');
|
||||
}
|
||||
}
|
||||
}
|
@ -24,20 +24,23 @@ class ImageAnalysis extends UseCase {
|
||||
ImageAnalysis(
|
||||
{BinaryMessenger? binaryMessenger,
|
||||
InstanceManager? instanceManager,
|
||||
this.initialTargetRotation,
|
||||
this.resolutionSelector})
|
||||
: super.detached(
|
||||
binaryMessenger: binaryMessenger,
|
||||
instanceManager: instanceManager) {
|
||||
_api = _ImageAnalysisHostApiImpl(
|
||||
binaryMessenger: binaryMessenger, instanceManager: instanceManager);
|
||||
_api.createfromInstances(this, resolutionSelector);
|
||||
_api.createFromInstances(this, initialTargetRotation, resolutionSelector);
|
||||
AndroidCameraXCameraFlutterApis.instance.ensureSetUp();
|
||||
}
|
||||
|
||||
/// Constructs an [ImageAnalysis] that is not automatically attached to a native object.
|
||||
/// Constructs an [ImageAnalysis] that is not automatically attached to a
|
||||
/// native object.
|
||||
ImageAnalysis.detached(
|
||||
{BinaryMessenger? binaryMessenger,
|
||||
InstanceManager? instanceManager,
|
||||
this.initialTargetRotation,
|
||||
this.resolutionSelector})
|
||||
: super.detached(
|
||||
binaryMessenger: binaryMessenger,
|
||||
@ -49,18 +52,34 @@ class ImageAnalysis extends UseCase {
|
||||
|
||||
late final _ImageAnalysisHostApiImpl _api;
|
||||
|
||||
/// Initial target rotation of the camera used for the preview stream.
|
||||
///
|
||||
/// Should be specified in terms of one of the [Surface]
|
||||
/// rotation constants that represents the counter-clockwise degrees of
|
||||
/// rotation relative to [DeviceOrientation.portraitUp].
|
||||
// TODO(camsim99): Remove this parameter. https://github.com/flutter/flutter/issues/140664
|
||||
final int? initialTargetRotation;
|
||||
|
||||
/// Target resolution of the camera preview stream.
|
||||
///
|
||||
/// If not set, this [UseCase] will default to the behavior described in:
|
||||
/// https://developer.android.com/reference/androidx/camera/core/ImageAnalysis.Builder#setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector).
|
||||
final ResolutionSelector? resolutionSelector;
|
||||
|
||||
/// Dynamically sets the target rotation of this instance.
|
||||
///
|
||||
/// [rotation] should be specified in terms of one of the [Surface]
|
||||
/// rotation constants that represents the counter-clockwise degrees of
|
||||
/// rotation relative to [DeviceOrientation.portraitUp].
|
||||
Future<void> setTargetRotation(int rotation) =>
|
||||
_api.setTargetRotationFromInstances(this, rotation);
|
||||
|
||||
/// Sets an [Analyzer] to receive and analyze images.
|
||||
Future<void> setAnalyzer(Analyzer analyzer) =>
|
||||
_api.setAnalyzerfromInstances(this, analyzer);
|
||||
_api.setAnalyzerFromInstances(this, analyzer);
|
||||
|
||||
/// Removes a previously set [Analyzer].
|
||||
Future<void> clearAnalyzer() => _api.clearAnalyzerfromInstances(this);
|
||||
Future<void> clearAnalyzer() => _api.clearAnalyzerFromInstances(this);
|
||||
}
|
||||
|
||||
/// Host API implementation of [ImageAnalysis].
|
||||
@ -85,27 +104,37 @@ class _ImageAnalysisHostApiImpl extends ImageAnalysisHostApi {
|
||||
|
||||
/// Creates an [ImageAnalysis] instance with the specified target resolution
|
||||
/// on the native side.
|
||||
Future<void> createfromInstances(
|
||||
Future<void> createFromInstances(
|
||||
ImageAnalysis instance,
|
||||
int? targetRotation,
|
||||
ResolutionSelector? resolutionSelector,
|
||||
) {
|
||||
return create(
|
||||
instanceManager.addDartCreatedInstance(
|
||||
instance,
|
||||
onCopy: (ImageAnalysis original) => ImageAnalysis.detached(
|
||||
initialTargetRotation: original.initialTargetRotation,
|
||||
resolutionSelector: original.resolutionSelector,
|
||||
binaryMessenger: binaryMessenger,
|
||||
instanceManager: instanceManager,
|
||||
),
|
||||
),
|
||||
targetRotation,
|
||||
resolutionSelector == null
|
||||
? null
|
||||
: instanceManager.getIdentifier(resolutionSelector),
|
||||
);
|
||||
}
|
||||
|
||||
/// Dynamically sets the target rotation of [instance] to [rotation].
|
||||
Future<void> setTargetRotationFromInstances(
|
||||
ImageAnalysis instance, int rotation) {
|
||||
return setTargetRotation(
|
||||
instanceManager.getIdentifier(instance)!, rotation);
|
||||
}
|
||||
|
||||
/// Sets the [analyzer] to receive and analyze images on the [instance].
|
||||
Future<void> setAnalyzerfromInstances(
|
||||
Future<void> setAnalyzerFromInstances(
|
||||
ImageAnalysis instance,
|
||||
Analyzer analyzer,
|
||||
) {
|
||||
@ -116,7 +145,7 @@ class _ImageAnalysisHostApiImpl extends ImageAnalysisHostApi {
|
||||
}
|
||||
|
||||
/// Removes a previously set analyzer from the [instance].
|
||||
Future<void> clearAnalyzerfromInstances(
|
||||
Future<void> clearAnalyzerFromInstances(
|
||||
ImageAnalysis instance,
|
||||
) {
|
||||
return clearAnalyzer(
|
||||
|
@ -20,6 +20,7 @@ class ImageCapture extends UseCase {
|
||||
ImageCapture({
|
||||
BinaryMessenger? binaryMessenger,
|
||||
InstanceManager? instanceManager,
|
||||
this.initialTargetRotation,
|
||||
this.targetFlashMode,
|
||||
this.resolutionSelector,
|
||||
}) : super.detached(
|
||||
@ -28,13 +29,16 @@ class ImageCapture extends UseCase {
|
||||
) {
|
||||
_api = ImageCaptureHostApiImpl(
|
||||
binaryMessenger: binaryMessenger, instanceManager: instanceManager);
|
||||
_api.createFromInstance(this, targetFlashMode, resolutionSelector);
|
||||
_api.createFromInstance(
|
||||
this, initialTargetRotation, targetFlashMode, resolutionSelector);
|
||||
}
|
||||
|
||||
/// Constructs a [ImageCapture] that is not automatically attached to a native object.
|
||||
/// Constructs an [ImageCapture] that is not automatically attached to a
|
||||
/// native object.
|
||||
ImageCapture.detached({
|
||||
BinaryMessenger? binaryMessenger,
|
||||
InstanceManager? instanceManager,
|
||||
this.initialTargetRotation,
|
||||
this.targetFlashMode,
|
||||
this.resolutionSelector,
|
||||
}) : super.detached(
|
||||
@ -47,6 +51,15 @@ class ImageCapture extends UseCase {
|
||||
|
||||
late final ImageCaptureHostApiImpl _api;
|
||||
|
||||
/// Initial target rotation of the camera used for the preview stream.
|
||||
///
|
||||
/// Should be specified in terms of one of the [Surface]
|
||||
/// rotation constants that represents the counter-clockwise degrees of
|
||||
/// rotation relative to [DeviceOrientation.portraitUp].
|
||||
///
|
||||
// TODO(camsim99): Remove this parameter. https://github.com/flutter/flutter/issues/140664
|
||||
final int? initialTargetRotation;
|
||||
|
||||
/// Flash mode used to take a picture.
|
||||
final int? targetFlashMode;
|
||||
|
||||
@ -71,9 +84,17 @@ class ImageCapture extends UseCase {
|
||||
/// See https://developer.android.com/reference/androidx/camera/core/ImageCapture#FLASH_MODE_OFF().
|
||||
static const int flashModeOff = 2;
|
||||
|
||||
/// Dynamically sets the target rotation of this instance.
|
||||
///
|
||||
/// [rotation] should be specified in terms of one of the [Surface]
|
||||
/// rotation constants that represents the counter-clockwise degrees of
|
||||
/// rotation relative to [DeviceOrientation.portraitUp].
|
||||
Future<void> setTargetRotation(int rotation) =>
|
||||
_api.setTargetRotationFromInstances(this, rotation);
|
||||
|
||||
/// Sets the flash mode to use for image capture.
|
||||
Future<void> setFlashMode(int newFlashMode) async {
|
||||
return _api.setFlashModeFromInstance(this, newFlashMode);
|
||||
return _api.setFlashModeFromInstances(this, newFlashMode);
|
||||
}
|
||||
|
||||
/// Takes a picture and returns the absolute path of where the capture image
|
||||
@ -94,7 +115,7 @@ class ImageCapture extends UseCase {
|
||||
/// See https://developer.android.com/reference/androidx/camera/core/ImageCapture
|
||||
/// for more information.
|
||||
Future<String> takePicture() async {
|
||||
return _api.takePictureFromInstance(this);
|
||||
return _api.takePictureFromInstances(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,27 +145,36 @@ class ImageCaptureHostApiImpl extends ImageCaptureHostApi {
|
||||
|
||||
/// Creates an [ImageCapture] instance with the flash mode and target resolution
|
||||
/// if specified.
|
||||
void createFromInstance(ImageCapture instance, int? targetFlashMode,
|
||||
ResolutionSelector? resolutionSelector) {
|
||||
void createFromInstance(ImageCapture instance, int? targetRotation,
|
||||
int? targetFlashMode, ResolutionSelector? resolutionSelector) {
|
||||
final int identifier = instanceManager.addDartCreatedInstance(instance,
|
||||
onCopy: (ImageCapture original) {
|
||||
return ImageCapture.detached(
|
||||
binaryMessenger: binaryMessenger,
|
||||
instanceManager: instanceManager,
|
||||
initialTargetRotation: original.initialTargetRotation,
|
||||
targetFlashMode: original.targetFlashMode,
|
||||
resolutionSelector: original.resolutionSelector);
|
||||
});
|
||||
create(
|
||||
identifier,
|
||||
targetRotation,
|
||||
targetFlashMode,
|
||||
resolutionSelector == null
|
||||
? null
|
||||
: instanceManager.getIdentifier(resolutionSelector));
|
||||
}
|
||||
|
||||
/// Dynamically sets the target rotation of [instance] to [rotation].
|
||||
Future<void> setTargetRotationFromInstances(
|
||||
ImageCapture instance, int rotation) {
|
||||
return setTargetRotation(
|
||||
instanceManager.getIdentifier(instance)!, rotation);
|
||||
}
|
||||
|
||||
/// Sets the flash mode for the specified [ImageCapture] instance to take
|
||||
/// a picture with.
|
||||
Future<void> setFlashModeFromInstance(
|
||||
Future<void> setFlashModeFromInstances(
|
||||
ImageCapture instance, int flashMode) async {
|
||||
final int? identifier = instanceManager.getIdentifier(instance);
|
||||
assert(identifier != null,
|
||||
@ -154,7 +184,7 @@ class ImageCaptureHostApiImpl extends ImageCaptureHostApi {
|
||||
}
|
||||
|
||||
/// Takes a picture with the specified [ImageCapture] instance.
|
||||
Future<String> takePictureFromInstance(ImageCapture instance) async {
|
||||
Future<String> takePictureFromInstances(ImageCapture instance) async {
|
||||
final int? identifier = instanceManager.getIdentifier(instance);
|
||||
assert(identifier != null,
|
||||
'No ImageCapture has the identifer of that requested to get the resolution information for.');
|
||||
|
@ -20,21 +20,21 @@ class Preview extends UseCase {
|
||||
Preview(
|
||||
{BinaryMessenger? binaryMessenger,
|
||||
InstanceManager? instanceManager,
|
||||
this.targetRotation,
|
||||
this.initialTargetRotation,
|
||||
this.resolutionSelector})
|
||||
: super.detached(
|
||||
binaryMessenger: binaryMessenger,
|
||||
instanceManager: instanceManager) {
|
||||
_api = PreviewHostApiImpl(
|
||||
binaryMessenger: binaryMessenger, instanceManager: instanceManager);
|
||||
_api.createFromInstance(this, targetRotation, resolutionSelector);
|
||||
_api.createFromInstance(this, initialTargetRotation, resolutionSelector);
|
||||
}
|
||||
|
||||
/// Constructs a [Preview] that is not automatically attached to a native object.
|
||||
Preview.detached(
|
||||
{BinaryMessenger? binaryMessenger,
|
||||
InstanceManager? instanceManager,
|
||||
this.targetRotation,
|
||||
this.initialTargetRotation,
|
||||
this.resolutionSelector})
|
||||
: super.detached(
|
||||
binaryMessenger: binaryMessenger,
|
||||
@ -46,7 +46,13 @@ class Preview extends UseCase {
|
||||
late final PreviewHostApiImpl _api;
|
||||
|
||||
/// Target rotation of the camera used for the preview stream.
|
||||
final int? targetRotation;
|
||||
///
|
||||
/// Should be specified in terms of one of the [Surface]
|
||||
/// rotation constants that represents the counter-clockwise degrees of
|
||||
/// rotation relative to [DeviceOrientation.portraitUp].
|
||||
///
|
||||
// TODO(camsim99): Remove this parameter. https://github.com/flutter/flutter/issues/140664
|
||||
final int? initialTargetRotation;
|
||||
|
||||
/// Target resolution of the camera preview stream.
|
||||
///
|
||||
@ -54,6 +60,14 @@ class Preview extends UseCase {
|
||||
/// https://developer.android.com/reference/androidx/camera/core/Preview.Builder#setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector).
|
||||
final ResolutionSelector? resolutionSelector;
|
||||
|
||||
/// Dynamically sets the target rotation of this instance.
|
||||
///
|
||||
/// [rotation] should be specified in terms of one of the [Surface]
|
||||
/// rotation constants that represents the counter-clockwise degrees of
|
||||
/// rotation relative to [DeviceOrientation.portraitUp].
|
||||
Future<void> setTargetRotation(int rotation) =>
|
||||
_api.setTargetRotationFromInstances(this, rotation);
|
||||
|
||||
/// Sets the surface provider for the preview stream.
|
||||
///
|
||||
/// Returns the ID of the FlutterSurfaceTextureEntry used on the native end
|
||||
@ -103,7 +117,7 @@ class PreviewHostApiImpl extends PreviewHostApi {
|
||||
return Preview.detached(
|
||||
binaryMessenger: binaryMessenger,
|
||||
instanceManager: instanceManager,
|
||||
targetRotation: original.targetRotation,
|
||||
initialTargetRotation: original.initialTargetRotation,
|
||||
resolutionSelector: original.resolutionSelector);
|
||||
});
|
||||
create(
|
||||
@ -114,6 +128,12 @@ class PreviewHostApiImpl extends PreviewHostApi {
|
||||
: instanceManager.getIdentifier(resolutionSelector));
|
||||
}
|
||||
|
||||
/// Dynamically sets the target rotation of [instance] to [rotation].
|
||||
Future<void> setTargetRotationFromInstances(Preview instance, int rotation) {
|
||||
return setTargetRotation(
|
||||
instanceManager.getIdentifier(instance)!, rotation);
|
||||
}
|
||||
|
||||
/// Sets the surface provider of the specified [Preview] instance and returns
|
||||
/// the ID corresponding to the surface it will provide.
|
||||
Future<int> setSurfaceProviderFromInstance(Preview instance) async {
|
||||
|
@ -5,10 +5,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:camera_platform_interface/camera_platform_interface.dart'
|
||||
show CameraException, DeviceOrientationChangedEvent;
|
||||
show CameraException;
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'android_camera_camerax_flutter_api_impls.dart';
|
||||
import 'camerax_library.g.dart';
|
||||
|
||||
// Ignoring lint indicating this class only contains static members
|
||||
@ -18,14 +17,6 @@ import 'camerax_library.g.dart';
|
||||
/// Utility class that offers access to Android system services needed for
|
||||
/// camera usage and other informational streams.
|
||||
class SystemServices {
|
||||
/// Stream that emits the device orientation whenever it is changed.
|
||||
///
|
||||
/// Values may start being added to the stream once
|
||||
/// `startListeningForDeviceOrientationChange(...)` is called.
|
||||
static final StreamController<DeviceOrientationChangedEvent>
|
||||
deviceOrientationChangedStreamController =
|
||||
StreamController<DeviceOrientationChangedEvent>.broadcast();
|
||||
|
||||
/// Stream that emits the errors caused by camera usage on the native side.
|
||||
static final StreamController<String> cameraErrorStreamController =
|
||||
StreamController<String>.broadcast();
|
||||
@ -39,29 +30,6 @@ class SystemServices {
|
||||
return api.sendCameraPermissionsRequest(enableAudio);
|
||||
}
|
||||
|
||||
/// Requests that [deviceOrientationChangedStreamController] start
|
||||
/// emitting values for any change in device orientation.
|
||||
static void startListeningForDeviceOrientationChange(
|
||||
bool isFrontFacing, int sensorOrientation,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
AndroidCameraXCameraFlutterApis.instance.ensureSetUp();
|
||||
final SystemServicesHostApi api =
|
||||
SystemServicesHostApi(binaryMessenger: binaryMessenger);
|
||||
|
||||
api.startListeningForDeviceOrientationChange(
|
||||
isFrontFacing, sensorOrientation);
|
||||
}
|
||||
|
||||
/// Stops the [deviceOrientationChangedStreamController] from emitting values
|
||||
/// for changes in device orientation.
|
||||
static void stopListeningForDeviceOrientationChange(
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
final SystemServicesHostApi api =
|
||||
SystemServicesHostApi(binaryMessenger: binaryMessenger);
|
||||
|
||||
api.stopListeningForDeviceOrientationChange();
|
||||
}
|
||||
|
||||
/// Returns a file path which was used to create a temporary file.
|
||||
/// Prefix is a part of the file name, and suffix is the file extension.
|
||||
///
|
||||
@ -116,37 +84,6 @@ class SystemServicesFlutterApiImpl implements SystemServicesFlutterApi {
|
||||
/// Constructs an [SystemServicesFlutterApiImpl].
|
||||
SystemServicesFlutterApiImpl();
|
||||
|
||||
/// Callback method for any changes in device orientation.
|
||||
///
|
||||
/// Will only be called if
|
||||
/// `SystemServices.startListeningForDeviceOrientationChange(...)` was called
|
||||
/// to start listening for device orientation updates.
|
||||
@override
|
||||
void onDeviceOrientationChanged(String orientation) {
|
||||
final DeviceOrientation deviceOrientation =
|
||||
deserializeDeviceOrientation(orientation);
|
||||
SystemServices.deviceOrientationChangedStreamController
|
||||
.add(DeviceOrientationChangedEvent(deviceOrientation));
|
||||
}
|
||||
|
||||
/// Deserializes device orientation in [String] format into a
|
||||
/// [DeviceOrientation].
|
||||
DeviceOrientation deserializeDeviceOrientation(String orientation) {
|
||||
switch (orientation) {
|
||||
case 'LANDSCAPE_LEFT':
|
||||
return DeviceOrientation.landscapeLeft;
|
||||
case 'LANDSCAPE_RIGHT':
|
||||
return DeviceOrientation.landscapeRight;
|
||||
case 'PORTRAIT_DOWN':
|
||||
return DeviceOrientation.portraitDown;
|
||||
case 'PORTRAIT_UP':
|
||||
return DeviceOrientation.portraitUp;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'"$orientation" is not a valid DeviceOrientation value');
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback method for any errors caused by camera usage on the Java side.
|
||||
@override
|
||||
void onCameraError(String errorDescription) {
|
||||
|
@ -17,7 +17,8 @@ import 'use_case.dart';
|
||||
/// See https://developer.android.com/reference/androidx/camera/video/VideoCapture.
|
||||
@immutable
|
||||
class VideoCapture extends UseCase {
|
||||
/// Creates a VideoCapture that is not automatically attached to a native object.
|
||||
/// Creates a [VideoCapture] that is not automatically attached to a native
|
||||
/// object.
|
||||
VideoCapture.detached(
|
||||
{BinaryMessenger? binaryMessenger, InstanceManager? instanceManager})
|
||||
: super.detached(
|
||||
@ -28,6 +29,8 @@ class VideoCapture extends UseCase {
|
||||
AndroidCameraXCameraFlutterApis.instance.ensureSetUp();
|
||||
}
|
||||
|
||||
late final VideoCaptureHostApiImpl _api;
|
||||
|
||||
/// Creates a [VideoCapture] associated with the given [Recorder].
|
||||
static Future<VideoCapture> withOutput(Recorder recorder,
|
||||
{BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) {
|
||||
@ -38,12 +41,18 @@ class VideoCapture extends UseCase {
|
||||
return api.withOutputFromInstance(recorder);
|
||||
}
|
||||
|
||||
/// Dynamically sets the target rotation of this instance.
|
||||
///
|
||||
/// [rotation] should be specified in terms of one of the [Surface]
|
||||
/// rotation constants that represents the counter-clockwise degrees of
|
||||
/// rotation relative to [DeviceOrientation.portraitUp].
|
||||
Future<void> setTargetRotation(int rotation) =>
|
||||
_api.setTargetRotationFromInstances(this, rotation);
|
||||
|
||||
/// Gets the [Recorder] associated with this VideoCapture.
|
||||
Future<Recorder> getOutput() {
|
||||
return _api.getOutputFromInstance(this);
|
||||
}
|
||||
|
||||
late final VideoCaptureHostApiImpl _api;
|
||||
}
|
||||
|
||||
/// Host API implementation of [VideoCapture].
|
||||
@ -76,6 +85,13 @@ class VideoCaptureHostApiImpl extends VideoCaptureHostApi {
|
||||
.getInstanceWithWeakReference<VideoCapture>(videoCaptureId)!;
|
||||
}
|
||||
|
||||
/// Dynamically sets the target rotation of [instance] to [rotation].
|
||||
Future<void> setTargetRotationFromInstances(
|
||||
VideoCapture instance, int rotation) {
|
||||
return setTargetRotation(
|
||||
instanceManager.getIdentifier(instance)!, rotation);
|
||||
}
|
||||
|
||||
/// Gets the [Recorder] associated with the provided [VideoCapture] instance.
|
||||
Future<Recorder> getOutputFromInstance(VideoCapture instance) async {
|
||||
final int? identifier = instanceManager.getIdentifier(instance);
|
||||
|
@ -211,21 +211,29 @@ abstract class SystemServicesHostApi {
|
||||
@async
|
||||
CameraPermissionsErrorData? requestCameraPermissions(bool enableAudio);
|
||||
|
||||
void startListeningForDeviceOrientationChange(
|
||||
bool isFrontFacing, int sensorOrientation);
|
||||
|
||||
void stopListeningForDeviceOrientationChange();
|
||||
|
||||
String getTempFilePath(String prefix, String suffix);
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class SystemServicesFlutterApi {
|
||||
void onDeviceOrientationChanged(String orientation);
|
||||
|
||||
void onCameraError(String errorDescription);
|
||||
}
|
||||
|
||||
@HostApi(dartHostTestHandler: 'TestDeviceOrientationManagerHostApi')
|
||||
abstract class DeviceOrientationManagerHostApi {
|
||||
void startListeningForDeviceOrientationChange(
|
||||
bool isFrontFacing, int sensorOrientation);
|
||||
|
||||
void stopListeningForDeviceOrientationChange();
|
||||
|
||||
int getDefaultDisplayRotation();
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class DeviceOrientationManagerFlutterApi {
|
||||
void onDeviceOrientationChanged(String orientation);
|
||||
}
|
||||
|
||||
@HostApi(dartHostTestHandler: 'TestPreviewHostApi')
|
||||
abstract class PreviewHostApi {
|
||||
void create(int identifier, int? rotation, int? resolutionSelectorId);
|
||||
@ -235,6 +243,8 @@ abstract class PreviewHostApi {
|
||||
void releaseFlutterSurfaceTexture();
|
||||
|
||||
ResolutionInfo getResolutionInfo(int identifier);
|
||||
|
||||
void setTargetRotation(int identifier, int rotation);
|
||||
}
|
||||
|
||||
@HostApi(dartHostTestHandler: 'TestVideoCaptureHostApi')
|
||||
@ -242,6 +252,8 @@ abstract class VideoCaptureHostApi {
|
||||
int withOutput(int videoOutputId);
|
||||
|
||||
int getOutput(int identifier);
|
||||
|
||||
void setTargetRotation(int identifier, int rotation);
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
@ -294,12 +306,15 @@ abstract class RecordingFlutterApi {
|
||||
|
||||
@HostApi(dartHostTestHandler: 'TestImageCaptureHostApi')
|
||||
abstract class ImageCaptureHostApi {
|
||||
void create(int identifier, int? flashMode, int? resolutionSelectorId);
|
||||
void create(int identifier, int? targetRotation, int? flashMode,
|
||||
int? resolutionSelectorId);
|
||||
|
||||
void setFlashMode(int identifier, int flashMode);
|
||||
|
||||
@async
|
||||
String takePicture(int identifier);
|
||||
|
||||
void setTargetRotation(int identifier, int rotation);
|
||||
}
|
||||
|
||||
@HostApi(dartHostTestHandler: 'TestResolutionStrategyHostApi')
|
||||
@ -341,11 +356,13 @@ abstract class ZoomStateFlutterApi {
|
||||
|
||||
@HostApi(dartHostTestHandler: 'TestImageAnalysisHostApi')
|
||||
abstract class ImageAnalysisHostApi {
|
||||
void create(int identifier, int? resolutionSelectorId);
|
||||
void create(int identifier, int? targetRotation, int? resolutionSelectorId);
|
||||
|
||||
void setAnalyzer(int identifier, int analyzerIdentifier);
|
||||
|
||||
void clearAnalyzer(int identifier);
|
||||
|
||||
void setTargetRotation(int identifier, int rotation);
|
||||
}
|
||||
|
||||
@HostApi(dartHostTestHandler: 'TestAnalyzerHostApi')
|
||||
|
@ -2,7 +2,7 @@ name: camera_android_camerax
|
||||
description: Android implementation of the camera plugin using the CameraX library.
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
|
||||
version: 0.5.0+24
|
||||
version: 0.5.0+25
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
@ -15,6 +15,7 @@ import 'package:camera_android_camerax/src/camera_state.dart';
|
||||
import 'package:camera_android_camerax/src/camera_state_error.dart';
|
||||
import 'package:camera_android_camerax/src/camerax_library.g.dart';
|
||||
import 'package:camera_android_camerax/src/camerax_proxy.dart';
|
||||
import 'package:camera_android_camerax/src/device_orientation_manager.dart';
|
||||
import 'package:camera_android_camerax/src/exposure_state.dart';
|
||||
import 'package:camera_android_camerax/src/fallback_strategy.dart';
|
||||
import 'package:camera_android_camerax/src/image_analysis.dart';
|
||||
@ -31,6 +32,7 @@ import 'package:camera_android_camerax/src/recorder.dart';
|
||||
import 'package:camera_android_camerax/src/recording.dart';
|
||||
import 'package:camera_android_camerax/src/resolution_selector.dart';
|
||||
import 'package:camera_android_camerax/src/resolution_strategy.dart';
|
||||
import 'package:camera_android_camerax/src/surface.dart';
|
||||
import 'package:camera_android_camerax/src/system_services.dart';
|
||||
import 'package:camera_android_camerax/src/use_case.dart';
|
||||
import 'package:camera_android_camerax/src/video_capture.dart';
|
||||
@ -229,14 +231,11 @@ void main() {
|
||||
return mockBackCameraSelector;
|
||||
}
|
||||
},
|
||||
createPreview: (
|
||||
{required int targetRotation,
|
||||
ResolutionSelector? resolutionSelector}) =>
|
||||
mockPreview,
|
||||
createImageCapture: (_) => mockImageCapture,
|
||||
createPreview: (_, __) => mockPreview,
|
||||
createImageCapture: (_, __) => mockImageCapture,
|
||||
createRecorder: (_) => mockRecorder,
|
||||
createVideoCapture: (_) => Future<VideoCapture>.value(mockVideoCapture),
|
||||
createImageAnalysis: (_) => mockImageAnalysis,
|
||||
createImageAnalysis: (_, __) => mockImageAnalysis,
|
||||
createResolutionStrategy: (
|
||||
{bool highestAvailable = false,
|
||||
Size? boundSize,
|
||||
@ -347,14 +346,11 @@ void main() {
|
||||
return mockBackCameraSelector;
|
||||
}
|
||||
},
|
||||
createPreview: (
|
||||
{required int targetRotation,
|
||||
ResolutionSelector? resolutionSelector}) =>
|
||||
mockPreview,
|
||||
createImageCapture: (_) => mockImageCapture,
|
||||
createPreview: (_, __) => mockPreview,
|
||||
createImageCapture: (_, __) => mockImageCapture,
|
||||
createRecorder: (_) => mockRecorder,
|
||||
createVideoCapture: (_) => Future<VideoCapture>.value(mockVideoCapture),
|
||||
createImageAnalysis: (_) => mockImageAnalysis,
|
||||
createImageAnalysis: (_, __) => mockImageAnalysis,
|
||||
createResolutionStrategy: (
|
||||
{bool highestAvailable = false,
|
||||
Size? boundSize,
|
||||
@ -431,18 +427,23 @@ void main() {
|
||||
return mockBackCameraSelector;
|
||||
}
|
||||
},
|
||||
createPreview: (
|
||||
{required int targetRotation,
|
||||
ResolutionSelector? resolutionSelector}) =>
|
||||
Preview.detached(
|
||||
targetRotation: targetRotation,
|
||||
resolutionSelector: resolutionSelector),
|
||||
createImageCapture: (ResolutionSelector? resolutionSelector) =>
|
||||
ImageCapture.detached(resolutionSelector: resolutionSelector),
|
||||
createPreview:
|
||||
(ResolutionSelector? resolutionSelector, int? targetRotation) =>
|
||||
Preview.detached(
|
||||
initialTargetRotation: targetRotation,
|
||||
resolutionSelector: resolutionSelector),
|
||||
createImageCapture:
|
||||
(ResolutionSelector? resolutionSelector, int? targetRotation) =>
|
||||
ImageCapture.detached(
|
||||
resolutionSelector: resolutionSelector,
|
||||
initialTargetRotation: targetRotation),
|
||||
createRecorder: (_) => mockRecorder,
|
||||
createVideoCapture: (_) => Future<VideoCapture>.value(mockVideoCapture),
|
||||
createImageAnalysis: (ResolutionSelector? resolutionSelector) =>
|
||||
ImageAnalysis.detached(resolutionSelector: resolutionSelector),
|
||||
createImageAnalysis:
|
||||
(ResolutionSelector? resolutionSelector, int? targetRotation) =>
|
||||
ImageAnalysis.detached(
|
||||
resolutionSelector: resolutionSelector,
|
||||
initialTargetRotation: targetRotation),
|
||||
createResolutionStrategy: (
|
||||
{bool highestAvailable = false, Size? boundSize, int? fallbackRule}) {
|
||||
if (highestAvailable) {
|
||||
@ -581,15 +582,12 @@ void main() {
|
||||
return mockBackCameraSelector;
|
||||
}
|
||||
},
|
||||
createPreview: (
|
||||
{required int targetRotation,
|
||||
ResolutionSelector? resolutionSelector}) =>
|
||||
mockPreview,
|
||||
createImageCapture: (_) => mockImageCapture,
|
||||
createPreview: (_, __) => mockPreview,
|
||||
createImageCapture: (_, __) => mockImageCapture,
|
||||
createRecorder: (QualitySelector? qualitySelector) =>
|
||||
Recorder.detached(qualitySelector: qualitySelector),
|
||||
createVideoCapture: (_) => Future<VideoCapture>.value(mockVideoCapture),
|
||||
createImageAnalysis: (_) => mockImageAnalysis,
|
||||
createImageAnalysis: (_, __) => mockImageAnalysis,
|
||||
createResolutionStrategy: (
|
||||
{bool highestAvailable = false,
|
||||
Size? boundSize,
|
||||
@ -716,14 +714,11 @@ void main() {
|
||||
return mockBackCameraSelector;
|
||||
}
|
||||
},
|
||||
createPreview: (
|
||||
{required int targetRotation,
|
||||
ResolutionSelector? resolutionSelector}) =>
|
||||
mockPreview,
|
||||
createImageCapture: (_) => mockImageCapture,
|
||||
createPreview: (_, __) => mockPreview,
|
||||
createImageCapture: (_, __) => mockImageCapture,
|
||||
createRecorder: (QualitySelector? qualitySelector) => MockRecorder(),
|
||||
createVideoCapture: (_) => Future<VideoCapture>.value(MockVideoCapture()),
|
||||
createImageAnalysis: (_) => mockImageAnalysis,
|
||||
createImageAnalysis: (_, __) => mockImageAnalysis,
|
||||
createResolutionStrategy: (
|
||||
{bool highestAvailable = false,
|
||||
Size? boundSize,
|
||||
@ -870,7 +865,8 @@ void main() {
|
||||
const DeviceOrientationChangedEvent testEvent =
|
||||
DeviceOrientationChangedEvent(DeviceOrientation.portraitDown);
|
||||
|
||||
SystemServices.deviceOrientationChangedStreamController.add(testEvent);
|
||||
DeviceOrientationManager.deviceOrientationChangedStreamController
|
||||
.add(testEvent);
|
||||
|
||||
expect(await streamQueue.next, testEvent);
|
||||
await streamQueue.cancel();
|
||||
@ -1084,6 +1080,9 @@ void main() {
|
||||
camera.videoCapture = MockVideoCapture();
|
||||
camera.cameraSelector = MockCameraSelector();
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
const int cameraId = 17;
|
||||
const String outputPath = '/temp/MOV123.temp';
|
||||
|
||||
@ -1124,6 +1123,9 @@ void main() {
|
||||
camera.videoCapture = MockVideoCapture();
|
||||
camera.cameraSelector = MockCameraSelector();
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
const int cameraId = 17;
|
||||
const String outputPath = '/temp/MOV123.temp';
|
||||
|
||||
@ -1178,6 +1180,9 @@ void main() {
|
||||
MockTestSystemServicesHostApi();
|
||||
TestSystemServicesHostApi.setup(mockSystemServicesApi);
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
// Tell plugin to create detached Analyzer for testing.
|
||||
camera.proxy = CameraXProxy(
|
||||
createAnalyzer:
|
||||
@ -1214,6 +1219,73 @@ void main() {
|
||||
await camera.cameraImageDataStreamController!.close();
|
||||
});
|
||||
|
||||
test(
|
||||
'startVideoCapturing sets VideoCapture target rotation to current video orientation if orientation unlocked',
|
||||
() async {
|
||||
// Set up mocks and constants.
|
||||
final AndroidCameraCameraX camera = AndroidCameraCameraX();
|
||||
final MockPendingRecording mockPendingRecording = MockPendingRecording();
|
||||
final MockRecording mockRecording = MockRecording();
|
||||
final MockVideoCapture mockVideoCapture = MockVideoCapture();
|
||||
final TestSystemServicesHostApi mockSystemServicesApi =
|
||||
MockTestSystemServicesHostApi();
|
||||
TestSystemServicesHostApi.setup(mockSystemServicesApi);
|
||||
const int defaultTargetRotation = Surface.ROTATION_270;
|
||||
|
||||
// Set directly for test versus calling createCamera.
|
||||
camera.processCameraProvider = MockProcessCameraProvider();
|
||||
camera.camera = MockCamera();
|
||||
camera.recorder = MockRecorder();
|
||||
camera.videoCapture = mockVideoCapture;
|
||||
camera.cameraSelector = MockCameraSelector();
|
||||
|
||||
// Tell plugin to mock call to get current video orientation.
|
||||
camera.proxy = CameraXProxy(
|
||||
getDefaultDisplayRotation: () =>
|
||||
Future<int>.value(defaultTargetRotation));
|
||||
|
||||
const int cameraId = 87;
|
||||
const String outputPath = '/temp/MOV123.temp';
|
||||
|
||||
// Mock method calls.
|
||||
when(mockSystemServicesApi.getTempFilePath(camera.videoPrefix, '.temp'))
|
||||
.thenReturn(outputPath);
|
||||
when(camera.recorder!.prepareRecording(outputPath))
|
||||
.thenAnswer((_) async => mockPendingRecording);
|
||||
when(mockPendingRecording.start()).thenAnswer((_) async => mockRecording);
|
||||
when(camera.processCameraProvider!.isBound(camera.videoCapture!))
|
||||
.thenAnswer((_) async => true);
|
||||
|
||||
// Orientation is unlocked and plugin does not need to set default target
|
||||
// rotation manually.
|
||||
camera.recording = null;
|
||||
await camera.startVideoCapturing(const VideoCaptureOptions(cameraId));
|
||||
verifyNever(mockVideoCapture.setTargetRotation(any));
|
||||
|
||||
// Orientation is locked and plugin does not need to set default target
|
||||
// rotation manually.
|
||||
camera.recording = null;
|
||||
camera.captureOrientationLocked = true;
|
||||
await camera.startVideoCapturing(const VideoCaptureOptions(cameraId));
|
||||
verifyNever(mockVideoCapture.setTargetRotation(any));
|
||||
|
||||
// Orientation is locked and plugin does need to set default target
|
||||
// rotation manually.
|
||||
camera.recording = null;
|
||||
camera.captureOrientationLocked = true;
|
||||
camera.shouldSetDefaultRotation = true;
|
||||
await camera.startVideoCapturing(const VideoCaptureOptions(cameraId));
|
||||
verifyNever(mockVideoCapture.setTargetRotation(any));
|
||||
|
||||
// Orientation is unlocked and plugin does need to set default target
|
||||
// rotation manually.
|
||||
camera.recording = null;
|
||||
camera.captureOrientationLocked = false;
|
||||
camera.shouldSetDefaultRotation = true;
|
||||
await camera.startVideoCapturing(const VideoCaptureOptions(cameraId));
|
||||
verify(mockVideoCapture.setTargetRotation(defaultTargetRotation));
|
||||
});
|
||||
|
||||
test('pauseVideoRecording pauses the recording', () async {
|
||||
final AndroidCameraCameraX camera = AndroidCameraCameraX();
|
||||
final MockRecording recording = MockRecording();
|
||||
@ -1321,6 +1393,9 @@ void main() {
|
||||
// Set directly for test versus calling createCamera.
|
||||
camera.imageCapture = MockImageCapture();
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
when(camera.imageCapture!.takePicture())
|
||||
.thenAnswer((_) async => testPicturePath);
|
||||
|
||||
@ -1329,6 +1404,51 @@ void main() {
|
||||
expect(imageFile.path, equals(testPicturePath));
|
||||
});
|
||||
|
||||
test(
|
||||
'takePicture sets ImageCapture target rotation to currrent photo rotation when orientation unlocked',
|
||||
() async {
|
||||
final AndroidCameraCameraX camera = AndroidCameraCameraX();
|
||||
final MockImageCapture mockImageCapture = MockImageCapture();
|
||||
const int cameraId = 3;
|
||||
const int defaultTargetRotation = Surface.ROTATION_180;
|
||||
|
||||
// Set directly for test versus calling createCamera.
|
||||
camera.imageCapture = mockImageCapture;
|
||||
|
||||
// Tell plugin to mock call to get current photo orientation.
|
||||
camera.proxy = CameraXProxy(
|
||||
getDefaultDisplayRotation: () =>
|
||||
Future<int>.value(defaultTargetRotation));
|
||||
|
||||
when(camera.imageCapture!.takePicture())
|
||||
.thenAnswer((_) async => 'test/absolute/path/to/picture');
|
||||
|
||||
// Orientation is unlocked and plugin does not need to set default target
|
||||
// rotation manually.
|
||||
await camera.takePicture(cameraId);
|
||||
verifyNever(mockImageCapture.setTargetRotation(any));
|
||||
|
||||
// Orientation is locked and plugin does not need to set default target
|
||||
// rotation manually.
|
||||
camera.captureOrientationLocked = true;
|
||||
await camera.takePicture(cameraId);
|
||||
verifyNever(mockImageCapture.setTargetRotation(any));
|
||||
|
||||
// Orientation is locked and plugin does need to set default target
|
||||
// rotation manually.
|
||||
camera.captureOrientationLocked = true;
|
||||
camera.shouldSetDefaultRotation = true;
|
||||
await camera.takePicture(cameraId);
|
||||
verifyNever(mockImageCapture.setTargetRotation(any));
|
||||
|
||||
// Orientation is unlocked and plugin does need to set default target
|
||||
// rotation manually.
|
||||
camera.captureOrientationLocked = false;
|
||||
camera.shouldSetDefaultRotation = true;
|
||||
await camera.takePicture(cameraId);
|
||||
verify(mockImageCapture.setTargetRotation(defaultTargetRotation));
|
||||
});
|
||||
|
||||
test('takePicture turns non-torch flash mode off when torch mode enabled',
|
||||
() async {
|
||||
final AndroidCameraCameraX camera = AndroidCameraCameraX();
|
||||
@ -1339,6 +1459,9 @@ void main() {
|
||||
camera.imageCapture = MockImageCapture();
|
||||
camera.camera = MockCamera();
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
when(camera.camera!.getCameraControl())
|
||||
.thenAnswer((_) async => mockCameraControl);
|
||||
|
||||
@ -1358,6 +1481,9 @@ void main() {
|
||||
camera.imageCapture = MockImageCapture();
|
||||
camera.camera = MockCamera();
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
when(camera.camera!.getCameraControl())
|
||||
.thenAnswer((_) async => mockCameraControl);
|
||||
|
||||
@ -1568,6 +1694,9 @@ void main() {
|
||||
camera.cameraSelector = MockCameraSelector();
|
||||
camera.imageAnalysis = MockImageAnalysis();
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
when(mockProcessCameraProvider.bindToLifecycle(any, any))
|
||||
.thenAnswer((_) => Future<Camera>.value(mockCamera));
|
||||
when(mockCamera.getCameraInfo())
|
||||
@ -1608,6 +1737,9 @@ void main() {
|
||||
camera.cameraSelector = MockCameraSelector();
|
||||
camera.imageAnalysis = MockImageAnalysis();
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
when(mockProcessCameraProvider.bindToLifecycle(any, any))
|
||||
.thenAnswer((_) => Future<Camera>.value(mockCamera));
|
||||
when(mockCamera.getCameraInfo())
|
||||
@ -1669,6 +1801,9 @@ void main() {
|
||||
camera.cameraSelector = mockCameraSelector;
|
||||
camera.imageAnalysis = mockImageAnalysis;
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
when(mockProcessCameraProvider.isBound(mockImageAnalysis))
|
||||
.thenAnswer((_) async => Future<bool>.value(false));
|
||||
when(mockProcessCameraProvider
|
||||
@ -1725,6 +1860,12 @@ void main() {
|
||||
// Set directly for test versus calling createCamera.
|
||||
camera.imageAnalysis = mockImageAnalysis;
|
||||
|
||||
// Ignore setting target rotation for this test; tested seprately.
|
||||
camera.captureOrientationLocked = true;
|
||||
|
||||
// Tell plugin to create a detached analyzer for testing purposes.
|
||||
camera.proxy = CameraXProxy(createAnalyzer: (_) => MockAnalyzer());
|
||||
|
||||
final StreamSubscription<CameraImageData> imageStreamSubscription = camera
|
||||
.onStreamedFrameAvailable(cameraId)
|
||||
.listen((CameraImageData data) {});
|
||||
@ -1733,4 +1874,117 @@ void main() {
|
||||
|
||||
verify(mockImageAnalysis.clearAnalyzer());
|
||||
});
|
||||
|
||||
test(
|
||||
'onStreamedFrameAvailable sets ImageAnalysis target rotation to current photo orientation when orientation unlocked',
|
||||
() async {
|
||||
final AndroidCameraCameraX camera = AndroidCameraCameraX();
|
||||
const int cameraId = 35;
|
||||
const int defaultTargetRotation = Surface.ROTATION_90;
|
||||
final MockImageAnalysis mockImageAnalysis = MockImageAnalysis();
|
||||
|
||||
// Set directly for test versus calling createCamera.
|
||||
camera.imageAnalysis = mockImageAnalysis;
|
||||
|
||||
// Tell plugin to create a detached analyzer for testing purposes and mock
|
||||
// call to get current photo orientation.
|
||||
camera.proxy = CameraXProxy(
|
||||
createAnalyzer: (_) => MockAnalyzer(),
|
||||
getDefaultDisplayRotation: () =>
|
||||
Future<int>.value(defaultTargetRotation));
|
||||
|
||||
// Orientation is unlocked and plugin does not need to set default target
|
||||
// rotation manually.
|
||||
StreamSubscription<CameraImageData> imageStreamSubscription = camera
|
||||
.onStreamedFrameAvailable(cameraId)
|
||||
.listen((CameraImageData data) {});
|
||||
await untilCalled(mockImageAnalysis.setAnalyzer(any));
|
||||
verifyNever(mockImageAnalysis.setTargetRotation(any));
|
||||
await imageStreamSubscription.cancel();
|
||||
|
||||
// Orientation is locked and plugin does not need to set default target
|
||||
// rotation manually.
|
||||
camera.captureOrientationLocked = true;
|
||||
imageStreamSubscription = camera
|
||||
.onStreamedFrameAvailable(cameraId)
|
||||
.listen((CameraImageData data) {});
|
||||
await untilCalled(mockImageAnalysis.setAnalyzer(any));
|
||||
verifyNever(mockImageAnalysis.setTargetRotation(any));
|
||||
await imageStreamSubscription.cancel();
|
||||
|
||||
// Orientation is locked and plugin does need to set default target
|
||||
// rotation manually.
|
||||
camera.captureOrientationLocked = true;
|
||||
camera.shouldSetDefaultRotation = true;
|
||||
imageStreamSubscription = camera
|
||||
.onStreamedFrameAvailable(cameraId)
|
||||
.listen((CameraImageData data) {});
|
||||
await untilCalled(mockImageAnalysis.setAnalyzer(any));
|
||||
verifyNever(mockImageAnalysis.setTargetRotation(any));
|
||||
await imageStreamSubscription.cancel();
|
||||
|
||||
// Orientation is unlocked and plugin does need to set default target
|
||||
// rotation manually.
|
||||
camera.captureOrientationLocked = false;
|
||||
camera.shouldSetDefaultRotation = true;
|
||||
imageStreamSubscription = camera
|
||||
.onStreamedFrameAvailable(cameraId)
|
||||
.listen((CameraImageData data) {});
|
||||
await untilCalled(
|
||||
mockImageAnalysis.setTargetRotation(defaultTargetRotation));
|
||||
await imageStreamSubscription.cancel();
|
||||
});
|
||||
|
||||
test(
|
||||
'lockCaptureOrientation sets capture-related use case target rotations to correct orientation',
|
||||
() async {
|
||||
final AndroidCameraCameraX camera = AndroidCameraCameraX();
|
||||
const int cameraId = 44;
|
||||
|
||||
final MockImageAnalysis mockImageAnalysis = MockImageAnalysis();
|
||||
final MockImageCapture mockImageCapture = MockImageCapture();
|
||||
final MockVideoCapture mockVideoCapture = MockVideoCapture();
|
||||
|
||||
// Set directly for test versus calling createCamera.
|
||||
camera.imageAnalysis = mockImageAnalysis;
|
||||
camera.imageCapture = mockImageCapture;
|
||||
camera.videoCapture = mockVideoCapture;
|
||||
|
||||
for (final DeviceOrientation orientation in DeviceOrientation.values) {
|
||||
int? expectedTargetRotation;
|
||||
switch (orientation) {
|
||||
case DeviceOrientation.portraitUp:
|
||||
expectedTargetRotation = Surface.ROTATION_0;
|
||||
case DeviceOrientation.landscapeLeft:
|
||||
expectedTargetRotation = Surface.ROTATION_90;
|
||||
case DeviceOrientation.portraitDown:
|
||||
expectedTargetRotation = Surface.ROTATION_180;
|
||||
case DeviceOrientation.landscapeRight:
|
||||
expectedTargetRotation = Surface.ROTATION_270;
|
||||
}
|
||||
|
||||
await camera.lockCaptureOrientation(cameraId, orientation);
|
||||
|
||||
verify(mockImageAnalysis.setTargetRotation(expectedTargetRotation));
|
||||
verify(mockImageCapture.setTargetRotation(expectedTargetRotation));
|
||||
verify(mockVideoCapture.setTargetRotation(expectedTargetRotation));
|
||||
expect(camera.captureOrientationLocked, isTrue);
|
||||
expect(camera.shouldSetDefaultRotation, isTrue);
|
||||
|
||||
// Reset flags for testing.
|
||||
camera.captureOrientationLocked = false;
|
||||
camera.shouldSetDefaultRotation = false;
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
'unlockCaptureOrientation sets capture-related use case target rotations to current photo/video orientation',
|
||||
() async {
|
||||
final AndroidCameraCameraX camera = AndroidCameraCameraX();
|
||||
const int cameraId = 57;
|
||||
|
||||
camera.captureOrientationLocked = true;
|
||||
await camera.unlockCaptureOrientation(cameraId);
|
||||
expect(camera.captureOrientationLocked, isFalse);
|
||||
});
|
||||
}
|
||||
|
@ -516,6 +516,16 @@ class MockFallbackStrategy extends _i1.Mock implements _i21.FallbackStrategy {
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
// ignore: must_be_immutable
|
||||
class MockImageAnalysis extends _i1.Mock implements _i22.ImageAnalysis {
|
||||
@override
|
||||
_i16.Future<void> setTargetRotation(int? rotation) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#setTargetRotation,
|
||||
[rotation],
|
||||
),
|
||||
returnValue: _i16.Future<void>.value(),
|
||||
returnValueForMissingStub: _i16.Future<void>.value(),
|
||||
) as _i16.Future<void>);
|
||||
|
||||
@override
|
||||
_i16.Future<void> setAnalyzer(_i15.Analyzer? analyzer) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@ -542,6 +552,16 @@ class MockImageAnalysis extends _i1.Mock implements _i22.ImageAnalysis {
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
// ignore: must_be_immutable
|
||||
class MockImageCapture extends _i1.Mock implements _i23.ImageCapture {
|
||||
@override
|
||||
_i16.Future<void> setTargetRotation(int? rotation) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#setTargetRotation,
|
||||
[rotation],
|
||||
),
|
||||
returnValue: _i16.Future<void>.value(),
|
||||
returnValueForMissingStub: _i16.Future<void>.value(),
|
||||
) as _i16.Future<void>);
|
||||
|
||||
@override
|
||||
_i16.Future<void> setFlashMode(int? newFlashMode) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@ -708,6 +728,16 @@ class MockPlaneProxy extends _i1.Mock implements _i25.PlaneProxy {
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
// ignore: must_be_immutable
|
||||
class MockPreview extends _i1.Mock implements _i28.Preview {
|
||||
@override
|
||||
_i16.Future<void> setTargetRotation(int? rotation) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#setTargetRotation,
|
||||
[rotation],
|
||||
),
|
||||
returnValue: _i16.Future<void>.value(),
|
||||
returnValueForMissingStub: _i16.Future<void>.value(),
|
||||
) as _i16.Future<void>);
|
||||
|
||||
@override
|
||||
_i16.Future<int> setSurfaceProvider() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@ -944,6 +974,16 @@ class MockRecording extends _i1.Mock implements _i8.Recording {
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
// ignore: must_be_immutable
|
||||
class MockVideoCapture extends _i1.Mock implements _i34.VideoCapture {
|
||||
@override
|
||||
_i16.Future<void> setTargetRotation(int? rotation) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#setTargetRotation,
|
||||
[rotation],
|
||||
),
|
||||
returnValue: _i16.Future<void>.value(),
|
||||
returnValueForMissingStub: _i16.Future<void>.value(),
|
||||
) as _i16.Future<void>);
|
||||
|
||||
@override
|
||||
_i16.Future<_i11.Recorder> getOutput() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@ -1185,31 +1225,6 @@ class MockTestSystemServicesHostApi extends _i1.Mock
|
||||
_i16.Future<_i7.CameraPermissionsErrorData?>.value(),
|
||||
) as _i16.Future<_i7.CameraPermissionsErrorData?>);
|
||||
|
||||
@override
|
||||
void startListeningForDeviceOrientationChange(
|
||||
bool? isFrontFacing,
|
||||
int? sensorOrientation,
|
||||
) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#startListeningForDeviceOrientationChange,
|
||||
[
|
||||
isFrontFacing,
|
||||
sensorOrientation,
|
||||
],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void stopListeningForDeviceOrientationChange() => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#stopListeningForDeviceOrientationChange,
|
||||
[],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String getTempFilePath(
|
||||
String? prefix,
|
||||
|
@ -0,0 +1,85 @@
|
||||
// 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.
|
||||
|
||||
import 'package:camera_android_camerax/src/device_orientation_manager.dart';
|
||||
import 'package:camera_android_camerax/src/surface.dart';
|
||||
import 'package:camera_platform_interface/camera_platform_interface.dart'
|
||||
show DeviceOrientationChangedEvent;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'device_orientation_manager_test.mocks.dart';
|
||||
import 'test_camerax_library.g.dart';
|
||||
|
||||
@GenerateMocks(
|
||||
<Type>[TestInstanceManagerHostApi, TestDeviceOrientationManagerHostApi])
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Mocks the call to clear the native InstanceManager.
|
||||
TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi());
|
||||
|
||||
group('DeviceOrientationManager', () {
|
||||
tearDown(() => TestProcessCameraProviderHostApi.setup(null));
|
||||
|
||||
test(
|
||||
'startListeningForDeviceOrientationChange makes request to start listening for new device orientations',
|
||||
() async {
|
||||
final MockTestDeviceOrientationManagerHostApi mockApi =
|
||||
MockTestDeviceOrientationManagerHostApi();
|
||||
TestDeviceOrientationManagerHostApi.setup(mockApi);
|
||||
|
||||
DeviceOrientationManager.startListeningForDeviceOrientationChange(
|
||||
true, 90);
|
||||
verify(mockApi.startListeningForDeviceOrientationChange(true, 90));
|
||||
});
|
||||
|
||||
test(
|
||||
'stopListeningForDeviceOrientationChange makes request to stop listening for new device orientations',
|
||||
() async {
|
||||
final MockTestDeviceOrientationManagerHostApi mockApi =
|
||||
MockTestDeviceOrientationManagerHostApi();
|
||||
TestDeviceOrientationManagerHostApi.setup(mockApi);
|
||||
|
||||
DeviceOrientationManager.stopListeningForDeviceOrientationChange();
|
||||
verify(mockApi.stopListeningForDeviceOrientationChange());
|
||||
});
|
||||
|
||||
test('getDefaultDisplayRotation retrieves expected rotation', () async {
|
||||
final MockTestDeviceOrientationManagerHostApi mockApi =
|
||||
MockTestDeviceOrientationManagerHostApi();
|
||||
TestDeviceOrientationManagerHostApi.setup(mockApi);
|
||||
const int expectedRotation = Surface.ROTATION_180;
|
||||
|
||||
when(mockApi.getDefaultDisplayRotation()).thenReturn(expectedRotation);
|
||||
|
||||
expect(await DeviceOrientationManager.getDefaultDisplayRotation(),
|
||||
equals(expectedRotation));
|
||||
verify(mockApi.getDefaultDisplayRotation());
|
||||
});
|
||||
|
||||
test('onDeviceOrientationChanged adds new orientation to stream', () {
|
||||
DeviceOrientationManager.deviceOrientationChangedStreamController.stream
|
||||
.listen((DeviceOrientationChangedEvent event) {
|
||||
expect(event.orientation, equals(DeviceOrientation.landscapeLeft));
|
||||
});
|
||||
DeviceOrientationManagerFlutterApiImpl()
|
||||
.onDeviceOrientationChanged('LANDSCAPE_LEFT');
|
||||
});
|
||||
|
||||
test(
|
||||
'onDeviceOrientationChanged throws error if new orientation is invalid',
|
||||
() {
|
||||
expect(
|
||||
() => DeviceOrientationManagerFlutterApiImpl()
|
||||
.onDeviceOrientationChanged('FAKE_ORIENTATION'),
|
||||
throwsA(isA<ArgumentError>().having(
|
||||
(ArgumentError e) => e.message,
|
||||
'message',
|
||||
'"FAKE_ORIENTATION" is not a valid DeviceOrientation value')));
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
// Mocks generated by Mockito 5.4.3 from annotations
|
||||
// in camera_android_camerax/test/device_orientation_manager_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
|
||||
import 'test_camerax_library.g.dart' as _i2;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
// ignore_for_file: avoid_setters_without_getters
|
||||
// ignore_for_file: comment_references
|
||||
// ignore_for_file: deprecated_member_use
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
// ignore_for_file: camel_case_types
|
||||
// ignore_for_file: subtype_of_sealed_class
|
||||
|
||||
/// A class which mocks [TestInstanceManagerHostApi].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockTestInstanceManagerHostApi extends _i1.Mock
|
||||
implements _i2.TestInstanceManagerHostApi {
|
||||
MockTestInstanceManagerHostApi() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void clear() => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#clear,
|
||||
[],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [TestDeviceOrientationManagerHostApi].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockTestDeviceOrientationManagerHostApi extends _i1.Mock
|
||||
implements _i2.TestDeviceOrientationManagerHostApi {
|
||||
MockTestDeviceOrientationManagerHostApi() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void startListeningForDeviceOrientationChange(
|
||||
bool? isFrontFacing,
|
||||
int? sensorOrientation,
|
||||
) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#startListeningForDeviceOrientationChange,
|
||||
[
|
||||
isFrontFacing,
|
||||
sensorOrientation,
|
||||
],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void stopListeningForDeviceOrientationChange() => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#stopListeningForDeviceOrientationChange,
|
||||
[],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
int getDefaultDisplayRotation() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getDefaultDisplayRotation,
|
||||
[],
|
||||
),
|
||||
returnValue: 0,
|
||||
) as int);
|
||||
}
|
@ -7,6 +7,7 @@ import 'package:camera_android_camerax/src/image_analysis.dart';
|
||||
import 'package:camera_android_camerax/src/image_proxy.dart';
|
||||
import 'package:camera_android_camerax/src/instance_manager.dart';
|
||||
import 'package:camera_android_camerax/src/resolution_selector.dart';
|
||||
import 'package:camera_android_camerax/src/surface.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
@ -30,6 +31,24 @@ void main() {
|
||||
TestImageAnalysisHostApi.setup(null);
|
||||
});
|
||||
|
||||
test('detached create does not call create on the Java side', () {
|
||||
final MockTestImageAnalysisHostApi mockApi =
|
||||
MockTestImageAnalysisHostApi();
|
||||
TestImageAnalysisHostApi.setup(mockApi);
|
||||
|
||||
final InstanceManager instanceManager = InstanceManager(
|
||||
onWeakReferenceRemoved: (_) {},
|
||||
);
|
||||
|
||||
ImageAnalysis.detached(
|
||||
initialTargetRotation: Surface.ROTATION_270,
|
||||
resolutionSelector: MockResolutionSelector(),
|
||||
instanceManager: instanceManager,
|
||||
);
|
||||
|
||||
verifyNever(mockApi.create(argThat(isA<int>()), argThat(isA<int>()),
|
||||
argThat(isA<ResolutionSelector>())));
|
||||
});
|
||||
test('create calls create on the Java side', () {
|
||||
final MockTestImageAnalysisHostApi mockApi =
|
||||
MockTestImageAnalysisHostApi();
|
||||
@ -39,6 +58,7 @@ void main() {
|
||||
onWeakReferenceRemoved: (_) {},
|
||||
);
|
||||
|
||||
const int targetRotation = Surface.ROTATION_90;
|
||||
final MockResolutionSelector mockResolutionSelector =
|
||||
MockResolutionSelector();
|
||||
const int mockResolutionSelectorId = 24;
|
||||
@ -50,15 +70,43 @@ void main() {
|
||||
});
|
||||
|
||||
final ImageAnalysis instance = ImageAnalysis(
|
||||
initialTargetRotation: targetRotation,
|
||||
resolutionSelector: mockResolutionSelector,
|
||||
instanceManager: instanceManager,
|
||||
);
|
||||
|
||||
verify(mockApi.create(
|
||||
argThat(equals(instanceManager.getIdentifier(instance))),
|
||||
argThat(equals(targetRotation)),
|
||||
argThat(equals(mockResolutionSelectorId))));
|
||||
});
|
||||
|
||||
test(
|
||||
'setTargetRotation makes call to set target rotation for ImageAnalysis instance',
|
||||
() async {
|
||||
final MockTestImageAnalysisHostApi mockApi =
|
||||
MockTestImageAnalysisHostApi();
|
||||
TestImageAnalysisHostApi.setup(mockApi);
|
||||
|
||||
final InstanceManager instanceManager = InstanceManager(
|
||||
onWeakReferenceRemoved: (_) {},
|
||||
);
|
||||
const int targetRotation = Surface.ROTATION_180;
|
||||
final ImageAnalysis imageAnalysis = ImageAnalysis.detached(
|
||||
instanceManager: instanceManager,
|
||||
);
|
||||
instanceManager.addHostCreatedInstance(
|
||||
imageAnalysis,
|
||||
0,
|
||||
onCopy: (_) => ImageAnalysis.detached(instanceManager: instanceManager),
|
||||
);
|
||||
|
||||
await imageAnalysis.setTargetRotation(targetRotation);
|
||||
|
||||
verify(mockApi.setTargetRotation(
|
||||
instanceManager.getIdentifier(imageAnalysis), targetRotation));
|
||||
});
|
||||
|
||||
test('setAnalyzer makes call to set analyzer on ImageAnalysis instance',
|
||||
() async {
|
||||
final MockTestImageAnalysisHostApi mockApi =
|
||||
|
@ -33,6 +33,7 @@ class MockTestImageAnalysisHostApi extends _i1.Mock
|
||||
@override
|
||||
void create(
|
||||
int? identifier,
|
||||
int? targetRotation,
|
||||
int? resolutionSelectorId,
|
||||
) =>
|
||||
super.noSuchMethod(
|
||||
@ -40,6 +41,7 @@ class MockTestImageAnalysisHostApi extends _i1.Mock
|
||||
#create,
|
||||
[
|
||||
identifier,
|
||||
targetRotation,
|
||||
resolutionSelectorId,
|
||||
],
|
||||
),
|
||||
@ -70,6 +72,22 @@ class MockTestImageAnalysisHostApi extends _i1.Mock
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void setTargetRotation(
|
||||
int? identifier,
|
||||
int? rotation,
|
||||
) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#setTargetRotation,
|
||||
[
|
||||
identifier,
|
||||
rotation,
|
||||
],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [TestInstanceManagerHostApi].
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'package:camera_android_camerax/src/image_capture.dart';
|
||||
import 'package:camera_android_camerax/src/instance_manager.dart';
|
||||
import 'package:camera_android_camerax/src/resolution_selector.dart';
|
||||
import 'package:camera_android_camerax/src/surface.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
@ -35,12 +36,13 @@ void main() {
|
||||
);
|
||||
ImageCapture.detached(
|
||||
instanceManager: instanceManager,
|
||||
initialTargetRotation: Surface.ROTATION_180,
|
||||
targetFlashMode: ImageCapture.flashModeOn,
|
||||
resolutionSelector: MockResolutionSelector(),
|
||||
);
|
||||
|
||||
verifyNever(mockApi.create(argThat(isA<int>()), argThat(isA<int>()),
|
||||
argThat(isA<ResolutionSelector>())));
|
||||
argThat(isA<ResolutionSelector>()), argThat(isA<int>())));
|
||||
});
|
||||
|
||||
test('create calls create on the Java side', () async {
|
||||
@ -50,6 +52,8 @@ void main() {
|
||||
final InstanceManager instanceManager = InstanceManager(
|
||||
onWeakReferenceRemoved: (_) {},
|
||||
);
|
||||
|
||||
const int targetRotation = Surface.ROTATION_270;
|
||||
const int targetFlashMode = ImageCapture.flashModeAuto;
|
||||
final MockResolutionSelector mockResolutionSelector =
|
||||
MockResolutionSelector();
|
||||
@ -63,12 +67,14 @@ void main() {
|
||||
|
||||
ImageCapture(
|
||||
instanceManager: instanceManager,
|
||||
initialTargetRotation: targetRotation,
|
||||
targetFlashMode: targetFlashMode,
|
||||
resolutionSelector: mockResolutionSelector,
|
||||
);
|
||||
|
||||
verify(mockApi.create(
|
||||
argThat(isA<int>()),
|
||||
argThat(equals(targetRotation)),
|
||||
argThat(equals(targetFlashMode)),
|
||||
argThat(equals(mockResolutionSelectorId))));
|
||||
});
|
||||
@ -97,6 +103,31 @@ void main() {
|
||||
instanceManager.getIdentifier(imageCapture), flashMode));
|
||||
});
|
||||
|
||||
test(
|
||||
'setTargetRotation makes call to set target rotation for ImageCapture instance',
|
||||
() async {
|
||||
final MockTestImageCaptureHostApi mockApi = MockTestImageCaptureHostApi();
|
||||
TestImageCaptureHostApi.setup(mockApi);
|
||||
|
||||
final InstanceManager instanceManager = InstanceManager(
|
||||
onWeakReferenceRemoved: (_) {},
|
||||
);
|
||||
const int targetRotation = Surface.ROTATION_180;
|
||||
final ImageCapture imageCapture = ImageCapture.detached(
|
||||
instanceManager: instanceManager,
|
||||
);
|
||||
instanceManager.addHostCreatedInstance(
|
||||
imageCapture,
|
||||
0,
|
||||
onCopy: (_) => ImageCapture.detached(instanceManager: instanceManager),
|
||||
);
|
||||
|
||||
await imageCapture.setTargetRotation(targetRotation);
|
||||
|
||||
verify(mockApi.setTargetRotation(
|
||||
instanceManager.getIdentifier(imageCapture), targetRotation));
|
||||
});
|
||||
|
||||
test('takePicture makes call to capture still image', () async {
|
||||
final MockTestImageCaptureHostApi mockApi = MockTestImageCaptureHostApi();
|
||||
TestImageCaptureHostApi.setup(mockApi);
|
||||
|
@ -36,6 +36,7 @@ class MockTestImageCaptureHostApi extends _i1.Mock
|
||||
@override
|
||||
void create(
|
||||
int? identifier,
|
||||
int? targetRotation,
|
||||
int? flashMode,
|
||||
int? resolutionSelectorId,
|
||||
) =>
|
||||
@ -44,6 +45,7 @@ class MockTestImageCaptureHostApi extends _i1.Mock
|
||||
#create,
|
||||
[
|
||||
identifier,
|
||||
targetRotation,
|
||||
flashMode,
|
||||
resolutionSelectorId,
|
||||
],
|
||||
@ -81,6 +83,22 @@ class MockTestImageCaptureHostApi extends _i1.Mock
|
||||
),
|
||||
)),
|
||||
) as _i3.Future<String>);
|
||||
|
||||
@override
|
||||
void setTargetRotation(
|
||||
int? identifier,
|
||||
int? rotation,
|
||||
) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#setTargetRotation,
|
||||
[
|
||||
identifier,
|
||||
rotation,
|
||||
],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [TestInstanceManagerHostApi].
|
||||
|
@ -6,6 +6,7 @@ import 'package:camera_android_camerax/src/camerax_library.g.dart';
|
||||
import 'package:camera_android_camerax/src/instance_manager.dart';
|
||||
import 'package:camera_android_camerax/src/preview.dart';
|
||||
import 'package:camera_android_camerax/src/resolution_selector.dart';
|
||||
import 'package:camera_android_camerax/src/surface.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
@ -33,7 +34,7 @@ void main() {
|
||||
);
|
||||
Preview.detached(
|
||||
instanceManager: instanceManager,
|
||||
targetRotation: 90,
|
||||
initialTargetRotation: Surface.ROTATION_90,
|
||||
resolutionSelector: MockResolutionSelector(),
|
||||
);
|
||||
|
||||
@ -48,7 +49,7 @@ void main() {
|
||||
final InstanceManager instanceManager = InstanceManager(
|
||||
onWeakReferenceRemoved: (_) {},
|
||||
);
|
||||
const int targetRotation = 90;
|
||||
const int targetRotation = Surface.ROTATION_90;
|
||||
final MockResolutionSelector mockResolutionSelector =
|
||||
MockResolutionSelector();
|
||||
const int mockResolutionSelectorId = 24;
|
||||
@ -61,7 +62,7 @@ void main() {
|
||||
|
||||
Preview(
|
||||
instanceManager: instanceManager,
|
||||
targetRotation: targetRotation,
|
||||
initialTargetRotation: targetRotation,
|
||||
resolutionSelector: mockResolutionSelector,
|
||||
);
|
||||
|
||||
@ -71,6 +72,31 @@ void main() {
|
||||
argThat(equals(mockResolutionSelectorId))));
|
||||
});
|
||||
|
||||
test(
|
||||
'setTargetRotation makes call to set target rotation for Preview instance',
|
||||
() async {
|
||||
final MockTestPreviewHostApi mockApi = MockTestPreviewHostApi();
|
||||
TestPreviewHostApi.setup(mockApi);
|
||||
|
||||
final InstanceManager instanceManager = InstanceManager(
|
||||
onWeakReferenceRemoved: (_) {},
|
||||
);
|
||||
const int targetRotation = Surface.ROTATION_180;
|
||||
final Preview preview = Preview.detached(
|
||||
instanceManager: instanceManager,
|
||||
);
|
||||
instanceManager.addHostCreatedInstance(
|
||||
preview,
|
||||
0,
|
||||
onCopy: (_) => Preview.detached(instanceManager: instanceManager),
|
||||
);
|
||||
|
||||
await preview.setTargetRotation(targetRotation);
|
||||
|
||||
verify(mockApi.setTargetRotation(
|
||||
instanceManager.getIdentifier(preview), targetRotation));
|
||||
});
|
||||
|
||||
test(
|
||||
'setSurfaceProvider makes call to set surface provider for preview instance',
|
||||
() async {
|
||||
|
@ -111,6 +111,22 @@ class MockTestPreviewHostApi extends _i1.Mock
|
||||
),
|
||||
),
|
||||
) as _i2.ResolutionInfo);
|
||||
|
||||
@override
|
||||
void setTargetRotation(
|
||||
int? identifier,
|
||||
int? rotation,
|
||||
) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#setTargetRotation,
|
||||
[
|
||||
identifier,
|
||||
rotation,
|
||||
],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [ResolutionSelector].
|
||||
|
@ -6,8 +6,7 @@ import 'package:camera_android_camerax/src/camerax_library.g.dart'
|
||||
show CameraPermissionsErrorData;
|
||||
import 'package:camera_android_camerax/src/system_services.dart';
|
||||
import 'package:camera_platform_interface/camera_platform_interface.dart'
|
||||
show CameraException, DeviceOrientationChangedEvent;
|
||||
import 'package:flutter/services.dart';
|
||||
show CameraException;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
@ -62,45 +61,6 @@ void main() {
|
||||
verify(mockApi.requestCameraPermissions(true));
|
||||
});
|
||||
|
||||
test('startListeningForDeviceOrientationChangeTest', () async {
|
||||
final MockTestSystemServicesHostApi mockApi =
|
||||
MockTestSystemServicesHostApi();
|
||||
TestSystemServicesHostApi.setup(mockApi);
|
||||
|
||||
SystemServices.startListeningForDeviceOrientationChange(true, 90);
|
||||
verify(mockApi.startListeningForDeviceOrientationChange(true, 90));
|
||||
});
|
||||
|
||||
test('stopListeningForDeviceOrientationChangeTest', () async {
|
||||
final MockTestSystemServicesHostApi mockApi =
|
||||
MockTestSystemServicesHostApi();
|
||||
TestSystemServicesHostApi.setup(mockApi);
|
||||
|
||||
SystemServices.stopListeningForDeviceOrientationChange();
|
||||
verify(mockApi.stopListeningForDeviceOrientationChange());
|
||||
});
|
||||
|
||||
test('onDeviceOrientationChanged adds new orientation to stream', () {
|
||||
SystemServices.deviceOrientationChangedStreamController.stream
|
||||
.listen((DeviceOrientationChangedEvent event) {
|
||||
expect(event.orientation, equals(DeviceOrientation.landscapeLeft));
|
||||
});
|
||||
SystemServicesFlutterApiImpl()
|
||||
.onDeviceOrientationChanged('LANDSCAPE_LEFT');
|
||||
});
|
||||
|
||||
test(
|
||||
'onDeviceOrientationChanged throws error if new orientation is invalid',
|
||||
() {
|
||||
expect(
|
||||
() => SystemServicesFlutterApiImpl()
|
||||
.onDeviceOrientationChanged('FAKE_ORIENTATION'),
|
||||
throwsA(isA<ArgumentError>().having(
|
||||
(ArgumentError e) => e.message,
|
||||
'message',
|
||||
'"FAKE_ORIENTATION" is not a valid DeviceOrientation value')));
|
||||
});
|
||||
|
||||
test('onCameraError adds new error to stream', () {
|
||||
const String testErrorDescription = 'Test error description!';
|
||||
SystemServices.cameraErrorStreamController.stream
|
||||
|
@ -63,31 +63,6 @@ class MockTestSystemServicesHostApi extends _i1.Mock
|
||||
returnValue: _i3.Future<_i4.CameraPermissionsErrorData?>.value(),
|
||||
) as _i3.Future<_i4.CameraPermissionsErrorData?>);
|
||||
|
||||
@override
|
||||
void startListeningForDeviceOrientationChange(
|
||||
bool? isFrontFacing,
|
||||
int? sensorOrientation,
|
||||
) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#startListeningForDeviceOrientationChange,
|
||||
[
|
||||
isFrontFacing,
|
||||
sensorOrientation,
|
||||
],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
void stopListeningForDeviceOrientationChange() => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#stopListeningForDeviceOrientationChange,
|
||||
[],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String getTempFilePath(
|
||||
String? prefix,
|
||||
|
@ -508,11 +508,6 @@ abstract class TestSystemServicesHostApi {
|
||||
Future<CameraPermissionsErrorData?> requestCameraPermissions(
|
||||
bool enableAudio);
|
||||
|
||||
void startListeningForDeviceOrientationChange(
|
||||
bool isFrontFacing, int sensorOrientation);
|
||||
|
||||
void stopListeningForDeviceOrientationChange();
|
||||
|
||||
String getTempFilePath(String prefix, String suffix);
|
||||
|
||||
static void setup(TestSystemServicesHostApi? api,
|
||||
@ -541,51 +536,6 @@ abstract class TestSystemServicesHostApi {
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange',
|
||||
codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final bool? arg_isFrontFacing = (args[0] as bool?);
|
||||
assert(arg_isFrontFacing != null,
|
||||
'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null, expected non-null bool.');
|
||||
final int? arg_sensorOrientation = (args[1] as int?);
|
||||
assert(arg_sensorOrientation != null,
|
||||
'Argument for dev.flutter.pigeon.SystemServicesHostApi.startListeningForDeviceOrientationChange was null, expected non-null int.');
|
||||
api.startListeningForDeviceOrientationChange(
|
||||
arg_isFrontFacing!, arg_sensorOrientation!);
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.SystemServicesHostApi.stopListeningForDeviceOrientationChange',
|
||||
codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
// ignore message
|
||||
api.stopListeningForDeviceOrientationChange();
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.SystemServicesHostApi.getTempFilePath', codec,
|
||||
@ -614,6 +564,86 @@ abstract class TestSystemServicesHostApi {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TestDeviceOrientationManagerHostApi {
|
||||
static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding =>
|
||||
TestDefaultBinaryMessengerBinding.instance;
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
void startListeningForDeviceOrientationChange(
|
||||
bool isFrontFacing, int sensorOrientation);
|
||||
|
||||
void stopListeningForDeviceOrientationChange();
|
||||
|
||||
int getDefaultDisplayRotation();
|
||||
|
||||
static void setup(TestDeviceOrientationManagerHostApi? api,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.DeviceOrientationManagerHostApi.startListeningForDeviceOrientationChange',
|
||||
codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.DeviceOrientationManagerHostApi.startListeningForDeviceOrientationChange was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final bool? arg_isFrontFacing = (args[0] as bool?);
|
||||
assert(arg_isFrontFacing != null,
|
||||
'Argument for dev.flutter.pigeon.DeviceOrientationManagerHostApi.startListeningForDeviceOrientationChange was null, expected non-null bool.');
|
||||
final int? arg_sensorOrientation = (args[1] as int?);
|
||||
assert(arg_sensorOrientation != null,
|
||||
'Argument for dev.flutter.pigeon.DeviceOrientationManagerHostApi.startListeningForDeviceOrientationChange was null, expected non-null int.');
|
||||
api.startListeningForDeviceOrientationChange(
|
||||
arg_isFrontFacing!, arg_sensorOrientation!);
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.DeviceOrientationManagerHostApi.stopListeningForDeviceOrientationChange',
|
||||
codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
// ignore message
|
||||
api.stopListeningForDeviceOrientationChange();
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.DeviceOrientationManagerHostApi.getDefaultDisplayRotation',
|
||||
codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
// ignore message
|
||||
final int output = api.getDefaultDisplayRotation();
|
||||
return <Object?>[output];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _TestPreviewHostApiCodec extends StandardMessageCodec {
|
||||
const _TestPreviewHostApiCodec();
|
||||
@override
|
||||
@ -650,6 +680,8 @@ abstract class TestPreviewHostApi {
|
||||
|
||||
ResolutionInfo getResolutionInfo(int identifier);
|
||||
|
||||
void setTargetRotation(int identifier, int rotation);
|
||||
|
||||
static void setup(TestPreviewHostApi? api,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
@ -738,6 +770,31 @@ abstract class TestPreviewHostApi {
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.PreviewHostApi.setTargetRotation', codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.PreviewHostApi.setTargetRotation was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_identifier = (args[0] as int?);
|
||||
assert(arg_identifier != null,
|
||||
'Argument for dev.flutter.pigeon.PreviewHostApi.setTargetRotation was null, expected non-null int.');
|
||||
final int? arg_rotation = (args[1] as int?);
|
||||
assert(arg_rotation != null,
|
||||
'Argument for dev.flutter.pigeon.PreviewHostApi.setTargetRotation was null, expected non-null int.');
|
||||
api.setTargetRotation(arg_identifier!, arg_rotation!);
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -750,6 +807,8 @@ abstract class TestVideoCaptureHostApi {
|
||||
|
||||
int getOutput(int identifier);
|
||||
|
||||
void setTargetRotation(int identifier, int rotation);
|
||||
|
||||
static void setup(TestVideoCaptureHostApi? api,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
@ -796,6 +855,31 @@ abstract class TestVideoCaptureHostApi {
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.VideoCaptureHostApi.setTargetRotation', codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.VideoCaptureHostApi.setTargetRotation was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_identifier = (args[0] as int?);
|
||||
assert(arg_identifier != null,
|
||||
'Argument for dev.flutter.pigeon.VideoCaptureHostApi.setTargetRotation was null, expected non-null int.');
|
||||
final int? arg_rotation = (args[1] as int?);
|
||||
assert(arg_rotation != null,
|
||||
'Argument for dev.flutter.pigeon.VideoCaptureHostApi.setTargetRotation was null, expected non-null int.');
|
||||
api.setTargetRotation(arg_identifier!, arg_rotation!);
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1059,12 +1143,15 @@ abstract class TestImageCaptureHostApi {
|
||||
TestDefaultBinaryMessengerBinding.instance;
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
void create(int identifier, int? flashMode, int? resolutionSelectorId);
|
||||
void create(int identifier, int? targetRotation, int? flashMode,
|
||||
int? resolutionSelectorId);
|
||||
|
||||
void setFlashMode(int identifier, int flashMode);
|
||||
|
||||
Future<String> takePicture(int identifier);
|
||||
|
||||
void setTargetRotation(int identifier, int rotation);
|
||||
|
||||
static void setup(TestImageCaptureHostApi? api,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
@ -1084,9 +1171,11 @@ abstract class TestImageCaptureHostApi {
|
||||
final int? arg_identifier = (args[0] as int?);
|
||||
assert(arg_identifier != null,
|
||||
'Argument for dev.flutter.pigeon.ImageCaptureHostApi.create was null, expected non-null int.');
|
||||
final int? arg_flashMode = (args[1] as int?);
|
||||
final int? arg_resolutionSelectorId = (args[2] as int?);
|
||||
api.create(arg_identifier!, arg_flashMode, arg_resolutionSelectorId);
|
||||
final int? arg_targetRotation = (args[1] as int?);
|
||||
final int? arg_flashMode = (args[2] as int?);
|
||||
final int? arg_resolutionSelectorId = (args[3] as int?);
|
||||
api.create(arg_identifier!, arg_targetRotation, arg_flashMode,
|
||||
arg_resolutionSelectorId);
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
@ -1138,6 +1227,31 @@ abstract class TestImageCaptureHostApi {
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.ImageCaptureHostApi.setTargetRotation', codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.ImageCaptureHostApi.setTargetRotation was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_identifier = (args[0] as int?);
|
||||
assert(arg_identifier != null,
|
||||
'Argument for dev.flutter.pigeon.ImageCaptureHostApi.setTargetRotation was null, expected non-null int.');
|
||||
final int? arg_rotation = (args[1] as int?);
|
||||
assert(arg_rotation != null,
|
||||
'Argument for dev.flutter.pigeon.ImageCaptureHostApi.setTargetRotation was null, expected non-null int.');
|
||||
api.setTargetRotation(arg_identifier!, arg_rotation!);
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1285,12 +1399,14 @@ abstract class TestImageAnalysisHostApi {
|
||||
TestDefaultBinaryMessengerBinding.instance;
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
void create(int identifier, int? resolutionSelectorId);
|
||||
void create(int identifier, int? targetRotation, int? resolutionSelectorId);
|
||||
|
||||
void setAnalyzer(int identifier, int analyzerIdentifier);
|
||||
|
||||
void clearAnalyzer(int identifier);
|
||||
|
||||
void setTargetRotation(int identifier, int rotation);
|
||||
|
||||
static void setup(TestImageAnalysisHostApi? api,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
@ -1310,8 +1426,10 @@ abstract class TestImageAnalysisHostApi {
|
||||
final int? arg_identifier = (args[0] as int?);
|
||||
assert(arg_identifier != null,
|
||||
'Argument for dev.flutter.pigeon.ImageAnalysisHostApi.create was null, expected non-null int.');
|
||||
final int? arg_resolutionSelectorId = (args[1] as int?);
|
||||
api.create(arg_identifier!, arg_resolutionSelectorId);
|
||||
final int? arg_targetRotation = (args[1] as int?);
|
||||
final int? arg_resolutionSelectorId = (args[2] as int?);
|
||||
api.create(
|
||||
arg_identifier!, arg_targetRotation, arg_resolutionSelectorId);
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
@ -1363,6 +1481,31 @@ abstract class TestImageAnalysisHostApi {
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.ImageAnalysisHostApi.setTargetRotation', codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel, null);
|
||||
} else {
|
||||
_testBinaryMessengerBinding!.defaultBinaryMessenger
|
||||
.setMockDecodedMessageHandler<Object?>(channel,
|
||||
(Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.ImageAnalysisHostApi.setTargetRotation was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_identifier = (args[0] as int?);
|
||||
assert(arg_identifier != null,
|
||||
'Argument for dev.flutter.pigeon.ImageAnalysisHostApi.setTargetRotation was null, expected non-null int.');
|
||||
final int? arg_rotation = (args[1] as int?);
|
||||
assert(arg_rotation != null,
|
||||
'Argument for dev.flutter.pigeon.ImageAnalysisHostApi.setTargetRotation was null, expected non-null int.');
|
||||
api.setTargetRotation(arg_identifier!, arg_rotation!);
|
||||
return <Object?>[];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'package:camera_android_camerax/src/camerax_library.g.dart';
|
||||
import 'package:camera_android_camerax/src/instance_manager.dart';
|
||||
import 'package:camera_android_camerax/src/recorder.dart';
|
||||
import 'package:camera_android_camerax/src/surface.dart';
|
||||
import 'package:camera_android_camerax/src/video_capture.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
@ -50,6 +51,31 @@ void main() {
|
||||
verify(mockApi.withOutput(mockRecorderId));
|
||||
});
|
||||
|
||||
test(
|
||||
'setTargetRotation makes call to set target rotation for VideoCapture instance',
|
||||
() async {
|
||||
final MockTestVideoCaptureHostApi mockApi = MockTestVideoCaptureHostApi();
|
||||
TestVideoCaptureHostApi.setup(mockApi);
|
||||
|
||||
final InstanceManager instanceManager = InstanceManager(
|
||||
onWeakReferenceRemoved: (_) {},
|
||||
);
|
||||
const int targetRotation = Surface.ROTATION_180;
|
||||
final VideoCapture videoCapture = VideoCapture.detached(
|
||||
instanceManager: instanceManager,
|
||||
);
|
||||
instanceManager.addHostCreatedInstance(
|
||||
videoCapture,
|
||||
0,
|
||||
onCopy: (_) => VideoCapture.detached(instanceManager: instanceManager),
|
||||
);
|
||||
|
||||
await videoCapture.setTargetRotation(targetRotation);
|
||||
|
||||
verify(mockApi.setTargetRotation(
|
||||
instanceManager.getIdentifier(videoCapture), targetRotation));
|
||||
});
|
||||
|
||||
test('getOutput calls the Java side and returns correct Recorder', () async {
|
||||
final MockTestVideoCaptureHostApi mockApi = MockTestVideoCaptureHostApi();
|
||||
TestVideoCaptureHostApi.setup(mockApi);
|
||||
|
@ -61,6 +61,22 @@ class MockTestVideoCaptureHostApi extends _i1.Mock
|
||||
),
|
||||
returnValue: 0,
|
||||
) as int);
|
||||
|
||||
@override
|
||||
void setTargetRotation(
|
||||
int? identifier,
|
||||
int? rotation,
|
||||
) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#setTargetRotation,
|
||||
[
|
||||
identifier,
|
||||
rotation,
|
||||
],
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [TestInstanceManagerHostApi].
|
||||
|
Reference in New Issue
Block a user