diff --git a/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml b/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml
index ea4275c757cf..52012aaa6915 100644
--- a/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml
+++ b/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml
@@ -1,3 +1,8 @@
+
+
+
+
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
index 7ee7263f7779..c35394f01d82 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
@@ -11,12 +11,14 @@
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.view.TextureRegistry;
/** Platform implementation of the camera_plugin implemented with the CameraX library. */
public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware {
private InstanceManager instanceManager;
private FlutterPluginBinding pluginBinding;
- public ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
+ private ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
+ public SystemServicesHostApiImpl systemServicesHostApi;
/**
* Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment.
@@ -25,7 +27,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity
*/
public CameraAndroidCameraxPlugin() {}
- void setUp(BinaryMessenger binaryMessenger, Context context) {
+ void setUp(BinaryMessenger binaryMessenger, Context context, TextureRegistry textureRegistry) {
// Set up instance manager.
instanceManager =
InstanceManager.open(
@@ -45,6 +47,8 @@ void setUp(BinaryMessenger binaryMessenger, Context context) {
new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context);
GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup(
binaryMessenger, processCameraProviderHostApi);
+ systemServicesHostApi = new SystemServicesHostApiImpl(binaryMessenger, instanceManager);
+ GeneratedCameraXLibrary.SystemServicesHostApi.setup(binaryMessenger, systemServicesHostApi);
}
@Override
@@ -63,10 +67,16 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
- setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext());
+ setUp(
+ pluginBinding.getBinaryMessenger(),
+ pluginBinding.getApplicationContext(),
+ pluginBinding.getTextureRegistry());
updateContext(pluginBinding.getApplicationContext());
processCameraProviderHostApi.setLifecycleOwner(
(LifecycleOwner) activityPluginBinding.getActivity());
+ systemServicesHostApi.setActivity(activityPluginBinding.getActivity());
+ systemServicesHostApi.setPermissionsRegistry(
+ activityPluginBinding::addRequestPermissionsResultListener);
}
@Override
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraPermissionsManager.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraPermissionsManager.java
new file mode 100644
index 000000000000..19b1ee569a9b
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraPermissionsManager.java
@@ -0,0 +1,120 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camerax;
+
+import android.Manifest;
+import android.Manifest.permission;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+final class CameraPermissionsManager {
+ interface PermissionsRegistry {
+ @SuppressWarnings("deprecation")
+ void addListener(
+ io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener handler);
+ }
+
+ interface ResultCallback {
+ void onResult(String errorCode, String errorDescription);
+ }
+
+ /**
+ * Camera access permission errors handled when camera is created. See {@code MethodChannelCamera}
+ * in {@code camera/camera_platform_interface} for details.
+ */
+ private static final String CAMERA_PERMISSIONS_REQUEST_ONGOING =
+ "CameraPermissionsRequestOngoing";
+
+ private static final String CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE =
+ "Another request is ongoing and multiple requests cannot be handled at once.";
+ private static final String CAMERA_ACCESS_DENIED = "CameraAccessDenied";
+ private static final String CAMERA_ACCESS_DENIED_MESSAGE = "Camera access permission was denied.";
+ private static final String AUDIO_ACCESS_DENIED = "AudioAccessDenied";
+ private static final String AUDIO_ACCESS_DENIED_MESSAGE = "Audio access permission was denied.";
+
+ private static final int CAMERA_REQUEST_ID = 9796;
+ @VisibleForTesting boolean ongoing = false;
+
+ void requestPermissions(
+ Activity activity,
+ PermissionsRegistry permissionsRegistry,
+ boolean enableAudio,
+ ResultCallback callback) {
+ if (ongoing) {
+ callback.onResult(
+ CAMERA_PERMISSIONS_REQUEST_ONGOING, CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE);
+ return;
+ }
+ if (!hasCameraPermission(activity) || (enableAudio && !hasAudioPermission(activity))) {
+ permissionsRegistry.addListener(
+ new CameraRequestPermissionsListener(
+ (String errorCode, String errorDescription) -> {
+ ongoing = false;
+ callback.onResult(errorCode, errorDescription);
+ }));
+ ongoing = true;
+ ActivityCompat.requestPermissions(
+ activity,
+ enableAudio
+ ? new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}
+ : new String[] {Manifest.permission.CAMERA},
+ CAMERA_REQUEST_ID);
+ } else {
+ // Permissions already exist. Call the callback with success.
+ callback.onResult(null, null);
+ }
+ }
+
+ private boolean hasCameraPermission(Activity activity) {
+ return ContextCompat.checkSelfPermission(activity, permission.CAMERA)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean hasAudioPermission(Activity activity) {
+ return ContextCompat.checkSelfPermission(activity, permission.RECORD_AUDIO)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @VisibleForTesting
+ @SuppressWarnings("deprecation")
+ static final class CameraRequestPermissionsListener
+ implements io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener {
+
+ // There's no way to unregister permission listeners in the v1 embedding, so we'll be called
+ // duplicate times in cases where the user denies and then grants a permission. Keep track of if
+ // we've responded before and bail out of handling the callback manually if this is a repeat
+ // call.
+ boolean alreadyCalled = false;
+
+ final ResultCallback callback;
+
+ @VisibleForTesting
+ CameraRequestPermissionsListener(ResultCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public boolean onRequestPermissionsResult(int id, String[] permissions, int[] grantResults) {
+ if (alreadyCalled || id != CAMERA_REQUEST_ID) {
+ return false;
+ }
+
+ alreadyCalled = true;
+ // grantResults could be empty if the permissions request with the user is interrupted
+ // https://developer.android.com/reference/android/app/Activity#onRequestPermissionsResult(int,%20java.lang.String[],%20int[])
+ if (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ callback.onResult(CAMERA_ACCESS_DENIED, CAMERA_ACCESS_DENIED_MESSAGE);
+ } else if (grantResults.length > 1 && grantResults[1] != PackageManager.PERMISSION_GRANTED) {
+ callback.onResult(AUDIO_ACCESS_DENIED, AUDIO_ACCESS_DENIED_MESSAGE);
+ } else {
+ callback.onResult(null, null);
+ }
+ return true;
+ }
+ }
+}
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java
index 8063866d2fc6..83c43a9d55d4 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java
@@ -4,10 +4,23 @@
package io.flutter.plugins.camerax;
+import android.app.Activity;
import androidx.camera.core.CameraSelector;
public class CameraXProxy {
public CameraSelector.Builder createCameraSelectorBuilder() {
return new CameraSelector.Builder();
}
+
+ public CameraPermissionsManager createCameraPermissionsManager() {
+ return new CameraPermissionsManager();
+ }
+
+ public DeviceOrientationManager createDeviceOrientationManager(
+ Activity activity,
+ Boolean isFrontFacing,
+ int sensorOrientation,
+ DeviceOrientationManager.DeviceOrientationChangeCallback callback) {
+ return new DeviceOrientationManager(activity, isFrontFacing, sensorOrientation, callback);
+ }
}
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java
new file mode 100644
index 000000000000..ebcb86433f65
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java
@@ -0,0 +1,329 @@
+// 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 android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.view.Display;
+import android.view.Surface;
+import android.view.WindowManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import io.flutter.embedding.engine.systemchannels.PlatformChannel;
+import io.flutter.embedding.engine.systemchannels.PlatformChannel.DeviceOrientation;
+
+/**
+ * Support class to help to determine the media orientation based on the orientation of the device.
+ */
+public class DeviceOrientationManager {
+
+ interface DeviceOrientationChangeCallback {
+ void onChange(DeviceOrientation newOrientation);
+ }
+
+ private static final IntentFilter orientationIntentFilter =
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
+
+ private final Activity activity;
+ private final boolean isFrontFacing;
+ private final int sensorOrientation;
+ private final DeviceOrientationChangeCallback deviceOrientationChangeCallback;
+ private PlatformChannel.DeviceOrientation lastOrientation;
+ private BroadcastReceiver broadcastReceiver;
+
+ DeviceOrientationManager(
+ @NonNull Activity activity,
+ boolean isFrontFacing,
+ int sensorOrientation,
+ DeviceOrientationChangeCallback callback) {
+ this.activity = activity;
+ this.isFrontFacing = isFrontFacing;
+ this.sensorOrientation = sensorOrientation;
+ this.deviceOrientationChangeCallback = callback;
+ }
+
+ /**
+ * Starts listening to the device's sensors or UI for orientation updates.
+ *
+ *
When orientation information is updated, the callback method of the {@link
+ * DeviceOrientationChangeCallback} is called with the new orientation. This latest value can also
+ * be retrieved through the {@link #getVideoOrientation()} accessor.
+ *
+ *
If the device's ACCELEROMETER_ROTATION setting is enabled the {@link
+ * DeviceOrientationManager} will report orientation updates based on the sensor information. If
+ * the ACCELEROMETER_ROTATION is disabled the {@link DeviceOrientationManager} will fallback to
+ * the deliver orientation updates based on the UI orientation.
+ */
+ public void start() {
+ if (broadcastReceiver != null) {
+ return;
+ }
+ broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleUIOrientationChange();
+ }
+ };
+ activity.registerReceiver(broadcastReceiver, orientationIntentFilter);
+ broadcastReceiver.onReceive(activity, null);
+ }
+
+ /** Stops listening for orientation updates. */
+ public void stop() {
+ if (broadcastReceiver == null) {
+ return;
+ }
+ activity.unregisterReceiver(broadcastReceiver);
+ broadcastReceiver = null;
+ }
+
+ /**
+ * Returns the device's photo orientation in degrees based on the sensor orientation and the last
+ * known UI orientation.
+ *
+ *
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.
+ *
+ *
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(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.
+ *
+ *
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.
+ *
+ *
Returns one of 0, 90, 180 or 270.
+ *
+ *
More details can be found in the official Android documentation:
+ * https://developer.android.com/reference/android/media/MediaRecorder#setOrientationHint(int)
+ *
+ *
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(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 PlatformChannel.DeviceOrientation getLastUIOrientation() {
+ return this.lastOrientation;
+ }
+
+ /**
+ * Handles orientation changes based on change events triggered by the OrientationIntentFilter.
+ *
+ *
This method is visible for testing purposes only and should never be used outside this
+ * class.
+ */
+ @VisibleForTesting
+ void handleUIOrientationChange() {
+ PlatformChannel.DeviceOrientation orientation = getUIOrientation();
+ handleOrientationChange(orientation, lastOrientation, deviceOrientationChangeCallback);
+ lastOrientation = orientation;
+ }
+
+ /**
+ * Handles orientation changes coming from either the device's sensors or the
+ * OrientationIntentFilter.
+ *
+ *
This method is visible for testing purposes only and should never be used outside this
+ * class.
+ */
+ @VisibleForTesting
+ static void handleOrientationChange(
+ DeviceOrientation newOrientation,
+ DeviceOrientation previousOrientation,
+ DeviceOrientationChangeCallback callback) {
+ if (!newOrientation.equals(previousOrientation)) {
+ callback.onChange(newOrientation);
+ }
+ }
+
+ /**
+ * Gets the current user interface orientation.
+ *
+ *
This method is visible for testing purposes only and should never be used outside this
+ * class.
+ *
+ * @return The current user interface orientation.
+ */
+ @VisibleForTesting
+ PlatformChannel.DeviceOrientation getUIOrientation() {
+ final int rotation = getDisplay().getRotation();
+ final int orientation = activity.getResources().getConfiguration().orientation;
+
+ switch (orientation) {
+ case Configuration.ORIENTATION_PORTRAIT:
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
+ return PlatformChannel.DeviceOrientation.PORTRAIT_UP;
+ } else {
+ return PlatformChannel.DeviceOrientation.PORTRAIT_DOWN;
+ }
+ case Configuration.ORIENTATION_LANDSCAPE:
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
+ return PlatformChannel.DeviceOrientation.LANDSCAPE_LEFT;
+ } else {
+ return PlatformChannel.DeviceOrientation.LANDSCAPE_RIGHT;
+ }
+ default:
+ return PlatformChannel.DeviceOrientation.PORTRAIT_UP;
+ }
+ }
+
+ /**
+ * Calculates the sensor orientation based on the supplied angle.
+ *
+ *
This method is visible for testing purposes only and should never be used outside this
+ * class.
+ *
+ * @param angle Orientation angle.
+ * @return The sensor orientation based on the supplied angle.
+ */
+ @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.
+ *
+ *
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;
+ }
+ }
+
+ /**
+ * Gets an instance of the Android {@link android.view.Display}.
+ *
+ *
This method is visible for testing purposes only and should never be used outside this
+ * class.
+ *
+ * @return An instance of the Android {@link android.view.Display}.
+ */
+ @SuppressWarnings("deprecation")
+ @VisibleForTesting
+ Display getDisplay() {
+ return ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+ }
+}
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java
index 8c42a7911768..528870cc749c 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java
@@ -13,6 +13,8 @@
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.common.StandardMessageCodec;
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -23,6 +25,78 @@
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})
public class GeneratedCameraXLibrary {
+ /** Generated class from Pigeon that represents data sent in messages. */
+ public static class CameraPermissionsErrorData {
+ private @NonNull String errorCode;
+
+ public @NonNull String getErrorCode() {
+ return errorCode;
+ }
+
+ public void setErrorCode(@NonNull String setterArg) {
+ if (setterArg == null) {
+ throw new IllegalStateException("Nonnull field \"errorCode\" is null.");
+ }
+ this.errorCode = setterArg;
+ }
+
+ private @NonNull String description;
+
+ public @NonNull String getDescription() {
+ return description;
+ }
+
+ public void setDescription(@NonNull String setterArg) {
+ if (setterArg == null) {
+ throw new IllegalStateException("Nonnull field \"description\" is null.");
+ }
+ this.description = setterArg;
+ }
+
+ /** Constructor is private to enforce null safety; use Builder. */
+ private CameraPermissionsErrorData() {}
+
+ public static final class Builder {
+ private @Nullable String errorCode;
+
+ public @NonNull Builder setErrorCode(@NonNull String setterArg) {
+ this.errorCode = setterArg;
+ return this;
+ }
+
+ private @Nullable String description;
+
+ public @NonNull Builder setDescription(@NonNull String setterArg) {
+ this.description = setterArg;
+ return this;
+ }
+
+ public @NonNull CameraPermissionsErrorData build() {
+ CameraPermissionsErrorData pigeonReturn = new CameraPermissionsErrorData();
+ pigeonReturn.setErrorCode(errorCode);
+ pigeonReturn.setDescription(description);
+ return pigeonReturn;
+ }
+ }
+
+ @NonNull
+ Map toMap() {
+ Map toMapResult = new HashMap<>();
+ toMapResult.put("errorCode", errorCode);
+ toMapResult.put("description", description);
+ return toMapResult;
+ }
+
+ static @NonNull CameraPermissionsErrorData fromMap(@NonNull Map map) {
+ CameraPermissionsErrorData pigeonResult = new CameraPermissionsErrorData();
+ Object errorCode = map.get("errorCode");
+ pigeonResult.setErrorCode((String) errorCode);
+ Object description = map.get("description");
+ pigeonResult.setDescription((String) description);
+ return pigeonResult;
+ }
+ }
+
public interface Result {
void success(T result);
@@ -590,6 +664,187 @@ public void create(@NonNull Long identifierArg, Reply callback) {
}
}
+ private static class SystemServicesHostApiCodec extends StandardMessageCodec {
+ public static final SystemServicesHostApiCodec INSTANCE = new SystemServicesHostApiCodec();
+
+ private SystemServicesHostApiCodec() {}
+
+ @Override
+ protected Object readValueOfType(byte type, ByteBuffer buffer) {
+ switch (type) {
+ case (byte) 128:
+ return CameraPermissionsErrorData.fromMap((Map) readValue(buffer));
+
+ default:
+ return super.readValueOfType(type, buffer);
+ }
+ }
+
+ @Override
+ protected void writeValue(ByteArrayOutputStream stream, Object value) {
+ if (value instanceof CameraPermissionsErrorData) {
+ stream.write(128);
+ writeValue(stream, ((CameraPermissionsErrorData) value).toMap());
+ } else {
+ super.writeValue(stream, value);
+ }
+ }
+ }
+
+ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+ public interface SystemServicesHostApi {
+ void requestCameraPermissions(
+ @NonNull Boolean enableAudio, Result result);
+
+ void startListeningForDeviceOrientationChange(
+ @NonNull Boolean isFrontFacing, @NonNull Long sensorOrientation);
+
+ void stopListeningForDeviceOrientationChange();
+
+ /** The codec used by SystemServicesHostApi. */
+ static MessageCodec