From 93e5cc08b1e8a1a806cb27b38db55795414e45f2 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Mon, 30 Sep 2024 16:23:36 -0400 Subject: [PATCH 01/13] Define initial pigeon --- .../camera_android/pigeons/copyright.txt | 3 +++ .../camera_android/pigeons/messages.dart | 25 +++++++++++++++++++ packages/camera/camera_android/pubspec.yaml | 1 + 3 files changed, 29 insertions(+) create mode 100644 packages/camera/camera_android/pigeons/copyright.txt create mode 100644 packages/camera/camera_android/pigeons/messages.dart diff --git a/packages/camera/camera_android/pigeons/copyright.txt b/packages/camera/camera_android/pigeons/copyright.txt new file mode 100644 index 00000000000..fb682b1ab96 --- /dev/null +++ b/packages/camera/camera_android/pigeons/copyright.txt @@ -0,0 +1,3 @@ +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. \ No newline at end of file diff --git a/packages/camera/camera_android/pigeons/messages.dart b/packages/camera/camera_android/pigeons/messages.dart new file mode 100644 index 00000000000..7a02763b0a6 --- /dev/null +++ b/packages/camera/camera_android/pigeons/messages.dart @@ -0,0 +1,25 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + javaOptions: JavaOptions(package: 'io.flutter.plugins.camera'), + javaOut: 'android/src/main/java/io/flutter/plugins/camera/Messages.java', + copyrightHeader: 'pigeons/copyright.txt', +)) + +enum PlatformCameraLensDirection { + front, + back, + external, +} + +class PlatformCameraDescription { + PlatformCameraDescription({required this.name, required this.lensDirection}); + final String name; + final PlatformCameraLensDirection lensDirection; +} + +@HostApi() +abstract class CameraApi { + List getAvailableCameras(); +} diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 4ddd429c519..9a5efb1769e 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -23,6 +23,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 + pigeon: ^22.4.1 stream_transform: ^2.0.0 dev_dependencies: From 56969c280054829e0f8fe4580aff8c3b4c8de4d2 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Mon, 30 Sep 2024 16:23:53 -0400 Subject: [PATCH 02/13] Generate pigeons --- .../io/flutter/plugins/camera/Messages.java | 245 ++++++++++++++++++ .../camera_android/lib/src/messages.g.dart | 125 +++++++++ 2 files changed, 370 insertions(+) create mode 100644 packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java create mode 100644 packages/camera/camera_android/lib/src/messages.g.dart diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java new file mode 100644 index 00000000000..40cfe9fde0a --- /dev/null +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java @@ -0,0 +1,245 @@ +// 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. +// Autogenerated from Pigeon (v22.4.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package io.flutter.plugins.camera; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.CLASS; + +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MessageCodec; +import io.flutter.plugin.common.StandardMessageCodec; +import java.io.ByteArrayOutputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** Generated class from Pigeon. */ +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) +public class Messages { + + /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ + public static class FlutterError extends RuntimeException { + + /** The error code. */ + public final String code; + + /** The error details. Must be a datatype supported by the api codec. */ + public final Object details; + + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) + { + super(message); + this.code = code; + this.details = details; + } + } + + @NonNull + protected static ArrayList wrapError(@NonNull Throwable exception) { + ArrayList errorList = new ArrayList<>(3); + if (exception instanceof FlutterError) { + FlutterError error = (FlutterError) exception; + errorList.add(error.code); + errorList.add(error.getMessage()); + errorList.add(error.details); + } else { + errorList.add(exception.toString()); + errorList.add(exception.getClass().getSimpleName()); + errorList.add( + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + } + return errorList; + } + + @Target(METHOD) + @Retention(CLASS) + @interface CanIgnoreReturnValue {} + + public enum PlatformCameraLensDirection { + FRONT(0), + BACK(1), + EXTERNAL(2); + + final int index; + + PlatformCameraLensDirection(final int index) { + this.index = index; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class PlatformCameraDescription { + private @NonNull String name; + + public @NonNull String getName() { + return name; + } + + public void setName(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"name\" is null."); + } + this.name = setterArg; + } + + private @NonNull PlatformCameraLensDirection lensDirection; + + public @NonNull PlatformCameraLensDirection getLensDirection() { + return lensDirection; + } + + public void setLensDirection(@NonNull PlatformCameraLensDirection setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"lensDirection\" is null."); + } + this.lensDirection = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + PlatformCameraDescription() {} + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + PlatformCameraDescription that = (PlatformCameraDescription) o; + return name.equals(that.name) && lensDirection.equals(that.lensDirection); + } + + @Override + public int hashCode() { + return Objects.hash(name, lensDirection); + } + + public static final class Builder { + + private @Nullable String name; + + @CanIgnoreReturnValue + public @NonNull Builder setName(@NonNull String setterArg) { + this.name = setterArg; + return this; + } + + private @Nullable PlatformCameraLensDirection lensDirection; + + @CanIgnoreReturnValue + public @NonNull Builder setLensDirection(@NonNull PlatformCameraLensDirection setterArg) { + this.lensDirection = setterArg; + return this; + } + + public @NonNull PlatformCameraDescription build() { + PlatformCameraDescription pigeonReturn = new PlatformCameraDescription(); + pigeonReturn.setName(name); + pigeonReturn.setLensDirection(lensDirection); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(name); + toListResult.add(lensDirection); + return toListResult; + } + + static @NonNull PlatformCameraDescription fromList(@NonNull ArrayList pigeonVar_list) { + PlatformCameraDescription pigeonResult = new PlatformCameraDescription(); + Object name = pigeonVar_list.get(0); + pigeonResult.setName((String) name); + Object lensDirection = pigeonVar_list.get(1); + pigeonResult.setLensDirection((PlatformCameraLensDirection) lensDirection); + return pigeonResult; + } + } + + private static class PigeonCodec extends StandardMessageCodec { + public static final PigeonCodec INSTANCE = new PigeonCodec(); + + private PigeonCodec() {} + + @Override + protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { + switch (type) { + case (byte) 129: { + Object value = readValue(buffer); + return value == null ? null : PlatformCameraLensDirection.values()[((Long) value).intValue()]; + } + case (byte) 130: + return PlatformCameraDescription.fromList((ArrayList) readValue(buffer)); + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { + if (value instanceof PlatformCameraLensDirection) { + stream.write(129); + writeValue(stream, value == null ? null : ((PlatformCameraLensDirection) value).index); + } else if (value instanceof PlatformCameraDescription) { + stream.write(130); + writeValue(stream, ((PlatformCameraDescription) value).toList()); + } else { + super.writeValue(stream, value); + } + } + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface CameraApi { + + @NonNull + List getAvailableCameras(); + + /** The codec used by CameraApi. */ + static @NonNull MessageCodec getCodec() { + return PigeonCodec.INSTANCE; + } + /**Sets up an instance of `CameraApi` to handle messages through the `binaryMessenger`. */ + static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable CameraApi api) { + setUp(binaryMessenger, "", api); + } + static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String messageChannelSuffix, @Nullable CameraApi api) { + messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.camera_android.CameraApi.getAvailableCameras" + messageChannelSuffix, getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + try { + List output = api.getAvailableCameras(); + wrapped.add(0, output); + } + catch (Throwable exception) { + wrapped = wrapError(exception); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } +} diff --git a/packages/camera/camera_android/lib/src/messages.g.dart b/packages/camera/camera_android/lib/src/messages.g.dart new file mode 100644 index 00000000000..7189f26bc21 --- /dev/null +++ b/packages/camera/camera_android/lib/src/messages.g.dart @@ -0,0 +1,125 @@ +// 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. +// Autogenerated from Pigeon (v22.4.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +enum PlatformCameraLensDirection { + front, + back, + external, +} + +class PlatformCameraDescription { + PlatformCameraDescription({ + required this.name, + required this.lensDirection, + }); + + String name; + + PlatformCameraLensDirection lensDirection; + + Object encode() { + return [ + name, + lensDirection, + ]; + } + + static PlatformCameraDescription decode(Object result) { + result as List; + return PlatformCameraDescription( + name: result[0]! as String, + lensDirection: result[1]! as PlatformCameraLensDirection, + ); + } +} + + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is PlatformCameraLensDirection) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is PlatformCameraDescription) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformCameraLensDirection.values[value]; + case 130: + return PlatformCameraDescription.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class CameraApi { + /// Constructor for [CameraApi]. 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. + CameraApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future> getAvailableCameras() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.camera_android.CameraApi.getAvailableCameras$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)!.cast(); + } + } +} From 469c28103200ba5e44deaa14af61348ccfa8bf09 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Mon, 30 Sep 2024 16:34:55 -0400 Subject: [PATCH 03/13] Convert dart lib --- .../io/flutter/plugins/camera/Messages.java | 31 +++++++++++++++++-- .../lib/src/android_camera.dart | 21 +++++-------- .../camera_android/lib/src/messages.g.dart | 5 +++ .../camera/camera_android/lib/src/utils.dart | 10 ++++++ .../camera_android/pigeons/messages.dart | 3 +- 5 files changed, 53 insertions(+), 17 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java index 40cfe9fde0a..27d87022926 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java @@ -110,6 +110,19 @@ public void setLensDirection(@NonNull PlatformCameraLensDirection setterArg) { this.lensDirection = setterArg; } + private @NonNull Long sensorOrientation; + + public @NonNull Long getSensorOrientation() { + return sensorOrientation; + } + + public void setSensorOrientation(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"sensorOrientation\" is null."); + } + this.sensorOrientation = setterArg; + } + /** Constructor is non-public to enforce null safety; use Builder. */ PlatformCameraDescription() {} @@ -118,12 +131,12 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PlatformCameraDescription that = (PlatformCameraDescription) o; - return name.equals(that.name) && lensDirection.equals(that.lensDirection); + return name.equals(that.name) && lensDirection.equals(that.lensDirection) && sensorOrientation.equals(that.sensorOrientation); } @Override public int hashCode() { - return Objects.hash(name, lensDirection); + return Objects.hash(name, lensDirection, sensorOrientation); } public static final class Builder { @@ -144,19 +157,29 @@ public static final class Builder { return this; } + private @Nullable Long sensorOrientation; + + @CanIgnoreReturnValue + public @NonNull Builder setSensorOrientation(@NonNull Long setterArg) { + this.sensorOrientation = setterArg; + return this; + } + public @NonNull PlatformCameraDescription build() { PlatformCameraDescription pigeonReturn = new PlatformCameraDescription(); pigeonReturn.setName(name); pigeonReturn.setLensDirection(lensDirection); + pigeonReturn.setSensorOrientation(sensorOrientation); return pigeonReturn; } } @NonNull ArrayList toList() { - ArrayList toListResult = new ArrayList<>(2); + ArrayList toListResult = new ArrayList<>(3); toListResult.add(name); toListResult.add(lensDirection); + toListResult.add(sensorOrientation); return toListResult; } @@ -166,6 +189,8 @@ ArrayList toList() { pigeonResult.setName((String) name); Object lensDirection = pigeonVar_list.get(1); pigeonResult.setLensDirection((PlatformCameraLensDirection) lensDirection); + Object sensorOrientation = pigeonVar_list.get(2); + pigeonResult.setSensorOrientation((Long) sensorOrientation); return pigeonResult; } } diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 9cd1df45191..9ba522ef55e 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -11,6 +11,7 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'messages.g.dart'; import 'type_conversion.dart'; import 'utils.dart'; @@ -26,6 +27,8 @@ class AndroidCamera extends CameraPlatform { final Map _channels = {}; + final CameraApi _hostApi = CameraApi(); + /// The name of the channel that device events from the platform side are /// sent on. @visibleForTesting @@ -71,20 +74,12 @@ class AndroidCamera extends CameraPlatform { @override Future> availableCameras() async { try { - final List>? cameras = await _channel - .invokeListMethod>('availableCameras'); - - if (cameras == null) { - return []; - } - - return cameras.map((Map camera) { + final List cameraDescriptions = await _hostApi.getAvailableCameras(); + return cameraDescriptions.map((PlatformCameraDescription cameraDescription) { return CameraDescription( - name: camera['name']! as String, - lensDirection: - parseCameraLensDirection(camera['lensFacing']! as String), - sensorOrientation: camera['sensorOrientation']! as int, - ); + name: cameraDescription.name, + lensDirection: cameraLensDirectionFromPlatform(cameraDescription.lensDirection), + sensorOrientation: cameraDescription.sensorOrientation); }).toList(); } on PlatformException catch (e) { throw CameraException(e.code, e.message); diff --git a/packages/camera/camera_android/lib/src/messages.g.dart b/packages/camera/camera_android/lib/src/messages.g.dart index 7189f26bc21..8b8bcca6755 100644 --- a/packages/camera/camera_android/lib/src/messages.g.dart +++ b/packages/camera/camera_android/lib/src/messages.g.dart @@ -28,16 +28,20 @@ class PlatformCameraDescription { PlatformCameraDescription({ required this.name, required this.lensDirection, + required this.sensorOrientation, }); String name; PlatformCameraLensDirection lensDirection; + int sensorOrientation; + Object encode() { return [ name, lensDirection, + sensorOrientation, ]; } @@ -46,6 +50,7 @@ class PlatformCameraDescription { return PlatformCameraDescription( name: result[0]! as String, lensDirection: result[1]! as PlatformCameraLensDirection, + sensorOrientation: result[2]! as int, ); } } diff --git a/packages/camera/camera_android/lib/src/utils.dart b/packages/camera/camera_android/lib/src/utils.dart index 8d58f7fe129..c65d41da9b8 100644 --- a/packages/camera/camera_android/lib/src/utils.dart +++ b/packages/camera/camera_android/lib/src/utils.dart @@ -5,6 +5,8 @@ import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/services.dart'; +import 'messages.g.dart'; + /// Parses a string into a corresponding CameraLensDirection. CameraLensDirection parseCameraLensDirection(String string) { switch (string) { @@ -18,6 +20,14 @@ CameraLensDirection parseCameraLensDirection(String string) { throw ArgumentError('Unknown CameraLensDirection value'); } +CameraLensDirection cameraLensDirectionFromPlatform(PlatformCameraLensDirection direction) { + return switch (direction) { + PlatformCameraLensDirection.front => CameraLensDirection.front, + PlatformCameraLensDirection.back => CameraLensDirection.back, + PlatformCameraLensDirection.external => CameraLensDirection.external, + }; +} + /// Returns the device orientation as a String. String serializeDeviceOrientation(DeviceOrientation orientation) { switch (orientation) { diff --git a/packages/camera/camera_android/pigeons/messages.dart b/packages/camera/camera_android/pigeons/messages.dart index 7a02763b0a6..8e64b3089d0 100644 --- a/packages/camera/camera_android/pigeons/messages.dart +++ b/packages/camera/camera_android/pigeons/messages.dart @@ -14,9 +14,10 @@ enum PlatformCameraLensDirection { } class PlatformCameraDescription { - PlatformCameraDescription({required this.name, required this.lensDirection}); + PlatformCameraDescription({required this.name, required this.lensDirection, required this.sensorOrientation}); final String name; final PlatformCameraLensDirection lensDirection; + final int sensorOrientation; } @HostApi() From a426ba83ceaa3362a31baf42b76ec489c19eb945 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Tue, 1 Oct 2024 12:56:31 -0400 Subject: [PATCH 04/13] Convert Java --- .../flutter/plugins/camera/CameraPlugin.java | 21 +++++++- .../flutter/plugins/camera/CameraUtils.java | 43 +++++++++------- .../io/flutter/plugins/camera/Messages.java | 50 +++++++++++-------- .../plugins/camera/MethodCallHandlerImpl.java | 7 --- .../plugins/camera/CameraUtilsTest.java | 18 ++++--- .../lib/src/android_camera.dart | 9 ++-- .../camera_android/lib/src/messages.g.dart | 24 +++++---- .../camera/camera_android/lib/src/utils.dart | 3 +- .../camera_android/pigeons/messages.dart | 6 ++- 9 files changed, 110 insertions(+), 71 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java index aff225e2c35..85ed614720e 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java @@ -5,6 +5,7 @@ package io.flutter.plugins.camera; import android.app.Activity; +import android.hardware.camera2.CameraAccessException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.flutter.embedding.engine.plugins.FlutterPlugin; @@ -13,6 +14,8 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.camera.CameraPermissions.PermissionsRegistry; import io.flutter.view.TextureRegistry; +import java.util.Collections; +import java.util.List; /** * Platform implementation of the camera_plugin. @@ -20,11 +23,12 @@ *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. * See {@code io.flutter.plugins.camera.MainActivity} for an example. */ -public final class CameraPlugin implements FlutterPlugin, ActivityAware { +public final class CameraPlugin implements FlutterPlugin, ActivityAware, Messages.CameraApi { private static final String TAG = "CameraPlugin"; private @Nullable FlutterPluginBinding flutterPluginBinding; private @Nullable MethodCallHandlerImpl methodCallHandler; + private @Nullable Activity activity; /** * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. @@ -76,8 +80,23 @@ private void maybeStartListening( BinaryMessenger messenger, PermissionsRegistry permissionsRegistry, TextureRegistry textureRegistry) { + this.activity = activity; methodCallHandler = new MethodCallHandlerImpl( activity, messenger, new CameraPermissions(), permissionsRegistry, textureRegistry); + Messages.CameraApi.setUp(messenger, this); + } + + @NonNull + @Override + public List getAvailableCameras() { + if (activity == null) { + return Collections.emptyList(); + } + try { + return CameraUtils.getAvailableCameras(activity); + } catch (CameraAccessException e) { + throw new RuntimeException(e); + } } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java index d98984cbf2f..9ac4430d703 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java @@ -13,9 +13,7 @@ import androidx.annotation.NonNull; import io.flutter.embedding.engine.systemchannels.PlatformChannel; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** Provides various utilities for camera. */ public final class CameraUtils { @@ -85,6 +83,20 @@ static PlatformChannel.DeviceOrientation deserializeDeviceOrientation(String ori } } + static Messages.PlatformCameraLensDirection lensDirectionFromInteger(int lensDirection) { + switch (lensDirection) { + case CameraMetadata.LENS_FACING_FRONT: + return Messages.PlatformCameraLensDirection.FRONT; + case CameraMetadata.LENS_FACING_BACK: + return Messages.PlatformCameraLensDirection.BACK; + case CameraMetadata.LENS_FACING_EXTERNAL: + return Messages.PlatformCameraLensDirection.EXTERNAL; + } + // CameraMetadata is defined in the Android API. In the event that a new value is added, a + // default fallback value of FRONT is returned. + return Messages.PlatformCameraLensDirection.FRONT; + } + /** * Gets all the available cameras for the device. * @@ -93,11 +105,11 @@ static PlatformChannel.DeviceOrientation deserializeDeviceOrientation(String ori * @throws CameraAccessException when the camera could not be accessed. */ @NonNull - public static List> getAvailableCameras(@NonNull Activity activity) - throws CameraAccessException { + public static List getAvailableCameras( + @NonNull Activity activity) throws CameraAccessException { CameraManager cameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); String[] cameraNames = cameraManager.getCameraIdList(); - List> cameras = new ArrayList<>(); + List cameras = new ArrayList<>(); for (String cameraName : cameraNames) { int cameraId; try { @@ -109,24 +121,17 @@ public static List> getAvailableCameras(@NonNull Activity ac continue; } - HashMap details = new HashMap<>(); CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraName); - details.put("name", cameraName); int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); - details.put("sensorOrientation", sensorOrientation); int lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING); - switch (lensFacing) { - case CameraMetadata.LENS_FACING_FRONT: - details.put("lensFacing", "front"); - break; - case CameraMetadata.LENS_FACING_BACK: - details.put("lensFacing", "back"); - break; - case CameraMetadata.LENS_FACING_EXTERNAL: - details.put("lensFacing", "external"); - break; - } + Messages.PlatformCameraLensDirection lensDirection = lensDirectionFromInteger(lensFacing); + Messages.PlatformCameraDescription details = + new Messages.PlatformCameraDescription.Builder() + .setName(cameraName) + .setSensorOrientation((long) sensorOrientation) + .setLensDirection(lensDirection) + .build(); cameras.add(details); } return cameras; diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java index 27d87022926..2944bcd65eb 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java @@ -21,11 +21,7 @@ import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; /** Generated class from Pigeon. */ @@ -41,8 +37,7 @@ public static class FlutterError extends RuntimeException { /** The error details. Must be a datatype supported by the api codec. */ public final Object details; - public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) - { + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) { super(message); this.code = code; this.details = details; @@ -61,7 +56,7 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { errorList.add(exception.toString()); errorList.add(exception.getClass().getSimpleName()); errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); } return errorList; } @@ -128,10 +123,16 @@ public void setSensorOrientation(@NonNull Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } PlatformCameraDescription that = (PlatformCameraDescription) o; - return name.equals(that.name) && lensDirection.equals(that.lensDirection) && sensorOrientation.equals(that.sensorOrientation); + return name.equals(that.name) + && lensDirection.equals(that.lensDirection) + && sensorOrientation.equals(that.sensorOrientation); } @Override @@ -203,10 +204,13 @@ private PigeonCodec() {} @Override protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte) 129: { - Object value = readValue(buffer); - return value == null ? null : PlatformCameraLensDirection.values()[((Long) value).intValue()]; - } + case (byte) 129: + { + Object value = readValue(buffer); + return value == null + ? null + : PlatformCameraLensDirection.values()[((Long) value).intValue()]; + } case (byte) 130: return PlatformCameraDescription.fromList((ArrayList) readValue(buffer)); default: @@ -231,23 +235,30 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface CameraApi { - @NonNull + @NonNull List getAvailableCameras(); /** The codec used by CameraApi. */ static @NonNull MessageCodec getCodec() { return PigeonCodec.INSTANCE; } - /**Sets up an instance of `CameraApi` to handle messages through the `binaryMessenger`. */ + /** Sets up an instance of `CameraApi` to handle messages through the `binaryMessenger`. */ static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable CameraApi api) { setUp(binaryMessenger, "", api); } - static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String messageChannelSuffix, @Nullable CameraApi api) { + + static void setUp( + @NonNull BinaryMessenger binaryMessenger, + @NonNull String messageChannelSuffix, + @Nullable CameraApi api) { messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.camera_android.CameraApi.getAvailableCameras" + messageChannelSuffix, getCodec()); + binaryMessenger, + "dev.flutter.pigeon.camera_android.CameraApi.getAvailableCameras" + + messageChannelSuffix, + getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -255,8 +266,7 @@ static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String mess try { List output = api.getAvailableCameras(); wrapped.add(0, output); - } - catch (Throwable exception) { + } catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index eae8c650fe6..0a8ae7b9730 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -60,13 +60,6 @@ final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { @Override public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) { switch (call.method) { - case "availableCameras": - try { - result.success(CameraUtils.getAvailableCameras(activity)); - } catch (Exception e) { - handleException(e, result); - } - break; case "create": { if (camera != null) { diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java index e59b05bf4fe..bf6e6c4d203 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java @@ -18,7 +18,6 @@ import android.hardware.camera2.CameraMetadata; import io.flutter.embedding.engine.systemchannels.PlatformChannel; import java.util.List; -import java.util.Map; import org.junit.Test; public class CameraUtilsTest { @@ -87,14 +86,17 @@ public void getAvailableCameras_retrievesValidCameras() .thenReturn(mockSensorOrientation2) .thenReturn(mockLensFacing2); - List> availableCameras = CameraUtils.getAvailableCameras(mockActivity); + List availableCameras = + CameraUtils.getAvailableCameras(mockActivity); assertEquals(availableCameras.size(), 2); - assertEquals(availableCameras.get(0).get("name"), "1394902"); - assertEquals(availableCameras.get(0).get("sensorOrientation"), mockSensorOrientation0); - assertEquals(availableCameras.get(0).get("lensFacing"), "front"); - assertEquals(availableCameras.get(1).get("name"), "0283835"); - assertEquals(availableCameras.get(1).get("sensorOrientation"), mockSensorOrientation2); - assertEquals(availableCameras.get(1).get("lensFacing"), "external"); + assertEquals(availableCameras.get(0).getName(), "1394902"); + assertEquals(availableCameras.get(0).getSensorOrientation().intValue(), mockSensorOrientation0); + assertEquals( + availableCameras.get(0).getLensDirection(), Messages.PlatformCameraLensDirection.FRONT); + assertEquals(availableCameras.get(1).getName(), "0283835"); + assertEquals(availableCameras.get(1).getSensorOrientation().intValue(), mockSensorOrientation2); + assertEquals( + availableCameras.get(1).getLensDirection(), Messages.PlatformCameraLensDirection.EXTERNAL); } } diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 9ba522ef55e..3345d7665fa 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -74,11 +74,14 @@ class AndroidCamera extends CameraPlatform { @override Future> availableCameras() async { try { - final List cameraDescriptions = await _hostApi.getAvailableCameras(); - return cameraDescriptions.map((PlatformCameraDescription cameraDescription) { + final List cameraDescriptions = + await _hostApi.getAvailableCameras(); + return cameraDescriptions + .map((PlatformCameraDescription cameraDescription) { return CameraDescription( name: cameraDescription.name, - lensDirection: cameraLensDirectionFromPlatform(cameraDescription.lensDirection), + lensDirection: cameraLensDirectionFromPlatform( + cameraDescription.lensDirection), sensorOrientation: cameraDescription.sensorOrientation); }).toList(); } on PlatformException catch (e) { diff --git a/packages/camera/camera_android/lib/src/messages.g.dart b/packages/camera/camera_android/lib/src/messages.g.dart index 8b8bcca6755..9ee641509c4 100644 --- a/packages/camera/camera_android/lib/src/messages.g.dart +++ b/packages/camera/camera_android/lib/src/messages.g.dart @@ -55,7 +55,6 @@ class PlatformCameraDescription { } } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -63,10 +62,10 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformCameraLensDirection) { + } else if (value is PlatformCameraLensDirection) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is PlatformCameraDescription) { + } else if (value is PlatformCameraDescription) { buffer.putUint8(130); writeValue(buffer, value.encode()); } else { @@ -77,10 +76,10 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : PlatformCameraLensDirection.values[value]; - case 130: + case 130: return PlatformCameraDescription.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -92,9 +91,11 @@ class CameraApi { /// Constructor for [CameraApi]. 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. - CameraApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + CameraApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -102,8 +103,10 @@ class CameraApi { final String pigeonVar_messageChannelSuffix; Future> getAvailableCameras() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.camera_android.CameraApi.getAvailableCameras$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.camera_android.CameraApi.getAvailableCameras$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -124,7 +127,8 @@ class CameraApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as List?)!.cast(); + return (pigeonVar_replyList[0] as List?)! + .cast(); } } } diff --git a/packages/camera/camera_android/lib/src/utils.dart b/packages/camera/camera_android/lib/src/utils.dart index c65d41da9b8..d9a411689f4 100644 --- a/packages/camera/camera_android/lib/src/utils.dart +++ b/packages/camera/camera_android/lib/src/utils.dart @@ -20,7 +20,8 @@ CameraLensDirection parseCameraLensDirection(String string) { throw ArgumentError('Unknown CameraLensDirection value'); } -CameraLensDirection cameraLensDirectionFromPlatform(PlatformCameraLensDirection direction) { +CameraLensDirection cameraLensDirectionFromPlatform( + PlatformCameraLensDirection direction) { return switch (direction) { PlatformCameraLensDirection.front => CameraLensDirection.front, PlatformCameraLensDirection.back => CameraLensDirection.back, diff --git a/packages/camera/camera_android/pigeons/messages.dart b/packages/camera/camera_android/pigeons/messages.dart index 8e64b3089d0..7703a61db9c 100644 --- a/packages/camera/camera_android/pigeons/messages.dart +++ b/packages/camera/camera_android/pigeons/messages.dart @@ -6,7 +6,6 @@ import 'package:pigeon/pigeon.dart'; javaOut: 'android/src/main/java/io/flutter/plugins/camera/Messages.java', copyrightHeader: 'pigeons/copyright.txt', )) - enum PlatformCameraLensDirection { front, back, @@ -14,7 +13,10 @@ enum PlatformCameraLensDirection { } class PlatformCameraDescription { - PlatformCameraDescription({required this.name, required this.lensDirection, required this.sensorOrientation}); + PlatformCameraDescription( + {required this.name, + required this.lensDirection, + required this.sensorOrientation}); final String name; final PlatformCameraLensDirection lensDirection; final int sensorOrientation; From aaef440f7e37cb63982c252670fad3e2f71cec32 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Tue, 1 Oct 2024 13:29:43 -0400 Subject: [PATCH 05/13] Convert tests --- .../lib/src/android_camera.dart | 4 +- packages/camera/camera_android/pubspec.yaml | 2 + .../test/android_camera_test.dart | 59 +++++++++---------- .../test/android_camera_test.mocks.dart | 52 ++++++++++++++++ 4 files changed, 85 insertions(+), 32 deletions(-) create mode 100644 packages/camera/camera_android/test/android_camera_test.mocks.dart diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 3345d7665fa..45f672fb878 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -25,9 +25,11 @@ class AndroidCamera extends CameraPlatform { CameraPlatform.instance = AndroidCamera(); } + AndroidCamera([CameraApi? hostApi]) : _hostApi = hostApi ?? CameraApi(); + final Map _channels = {}; - final CameraApi _hostApi = CameraApi(); + final CameraApi _hostApi; /// The name of the channel that device events from the platform side are /// sent on. diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 9a5efb1769e..286d6c3ff0f 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -19,10 +19,12 @@ flutter: dartPluginClass: AndroidCamera dependencies: + build_runner: ^2.4.13 camera_platform_interface: ^2.6.0 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 + mockito: ^5.4.4 pigeon: ^22.4.1 stream_transform: ^2.0.0 diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index 04e2e256837..34dd3bdc13d 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -7,16 +7,21 @@ import 'dart:math'; import 'package:async/async.dart'; import 'package:camera_android/src/android_camera.dart'; +import 'package:camera_android/src/messages.g.dart'; import 'package:camera_android/src/utils.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'android_camera_test.mocks.dart'; import 'method_channel_mock.dart'; const String _channelName = 'plugins.flutter.io/camera_android'; +@GenerateMocks([CameraApi]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -490,6 +495,7 @@ void main() { group('Function Tests', () { late AndroidCamera camera; late int cameraId; + late MockCameraApi mockCameraApi; setUp(() async { MethodChannelMock( @@ -499,7 +505,8 @@ void main() { 'initialize': null }, ); - camera = AndroidCamera(); + mockCameraApi = MockCameraApi(); + camera = AndroidCamera(mockCameraApi); cameraId = await camera.createCamera( const CameraDescription( name: 'Test', @@ -526,40 +533,33 @@ void main() { test('Should fetch CameraDescription instances for available cameras', () async { // Arrange - final List returnData = [ - { - 'name': 'Test 1', - 'lensFacing': 'front', - 'sensorOrientation': 1 - }, - { - 'name': 'Test 2', - 'lensFacing': 'back', - 'sensorOrientation': 2 - } + final List returnData = + [ + PlatformCameraDescription( + name: 'Test 1', + lensDirection: PlatformCameraLensDirection.front, + sensorOrientation: 1), + PlatformCameraDescription( + name: 'Test 2', + lensDirection: PlatformCameraLensDirection.back, + sensorOrientation: 2), ]; - final MethodChannelMock channel = MethodChannelMock( - channelName: _channelName, - methods: {'availableCameras': returnData}, - ); + when(mockCameraApi.getAvailableCameras()) + .thenAnswer((_) async => returnData); // Act final List cameras = await camera.availableCameras(); // Assert - expect(channel.log, [ - isMethodCall('availableCameras', arguments: null), - ]); expect(cameras.length, returnData.length); for (int i = 0; i < returnData.length; i++) { - final Map typedData = - (returnData[i] as Map).cast(); + final PlatformCameraDescription platformCameraDescription = + returnData[i]; final CameraDescription cameraDescription = CameraDescription( - name: typedData['name']! as String, - lensDirection: - parseCameraLensDirection(typedData['lensFacing']! as String), - sensorOrientation: typedData['sensorOrientation']! as int, - ); + name: platformCameraDescription.name, + lensDirection: cameraLensDirectionFromPlatform( + platformCameraDescription.lensDirection), + sensorOrientation: platformCameraDescription.sensorOrientation); expect(cameras[i], cameraDescription); } }); @@ -568,12 +568,9 @@ void main() { 'Should throw CameraException when availableCameras throws a PlatformException', () { // Arrange - MethodChannelMock(channelName: _channelName, methods: { - 'availableCameras': PlatformException( + when(mockCameraApi.getAvailableCameras()).thenThrow(PlatformException( code: 'TESTING_ERROR_CODE', - message: 'Mock error message used during testing.', - ) - }); + message: 'Mock error message used during testing.')); // Act expect( diff --git a/packages/camera/camera_android/test/android_camera_test.mocks.dart b/packages/camera/camera_android/test/android_camera_test.mocks.dart new file mode 100644 index 00000000000..c833a265e36 --- /dev/null +++ b/packages/camera/camera_android/test/android_camera_test.mocks.dart @@ -0,0 +1,52 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in camera_android/test/android_camera_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i4; + +import 'package:camera_android/src/messages.g.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i3; + +// 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 [CameraApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCameraApi extends _i1.Mock implements _i2.CameraApi { + MockCameraApi() { + _i1.throwOnMissingStub(this); + } + + @override + String get pigeonVar_messageChannelSuffix => (super.noSuchMethod( + Invocation.getter(#pigeonVar_messageChannelSuffix), + returnValue: _i3.dummyValue( + this, + Invocation.getter(#pigeonVar_messageChannelSuffix), + ), + ) as String); + + @override + _i4.Future> getAvailableCameras() => + (super.noSuchMethod( + Invocation.method( + #getAvailableCameras, + [], + ), + returnValue: _i4.Future>.value( + <_i2.PlatformCameraDescription>[]), + ) as _i4.Future>); +} From 1061eb3077046e006ff790a213a1d922459e72f0 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Tue, 1 Oct 2024 13:42:28 -0400 Subject: [PATCH 06/13] Cleanup for PR --- .../main/java/io/flutter/plugins/camera/CameraUtils.java | 7 +++++++ packages/camera/camera_android/lib/src/android_camera.dart | 6 ++++-- packages/camera/camera_android/lib/src/utils.dart | 1 + packages/camera/camera_android/pigeons/messages.dart | 5 +++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java index 9ac4430d703..c9eedaa3b79 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java @@ -83,6 +83,13 @@ static PlatformChannel.DeviceOrientation deserializeDeviceOrientation(String ori } } + /** + * Converts a raw integer to a PlatformCameraLensDirection enum. + * + * @param lensDirection One of CameraMetadata.LENS_FACING_FRONT, LENS_FACING_BACK, or + * LENS_FACING_EXTERNAL. + * @return One of Messages.PlatformCameraLensDirection.FRONT, BACK, or EXTERNAL. + */ static Messages.PlatformCameraLensDirection lensDirectionFromInteger(int lensDirection) { switch (lensDirection) { case CameraMetadata.LENS_FACING_FRONT: diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 45f672fb878..17ae590080e 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -20,13 +20,15 @@ const MethodChannel _channel = /// The Android implementation of [CameraPlatform] that uses method channels. class AndroidCamera extends CameraPlatform { + /// Creates a new [CameraPlatform] instance. + AndroidCamera([@visibleForTesting CameraApi? hostApi]) + : _hostApi = hostApi ?? CameraApi(); + /// Registers this class as the default instance of [CameraPlatform]. static void registerWith() { CameraPlatform.instance = AndroidCamera(); } - AndroidCamera([CameraApi? hostApi]) : _hostApi = hostApi ?? CameraApi(); - final Map _channels = {}; final CameraApi _hostApi; diff --git a/packages/camera/camera_android/lib/src/utils.dart b/packages/camera/camera_android/lib/src/utils.dart index d9a411689f4..c7e11a1e2f1 100644 --- a/packages/camera/camera_android/lib/src/utils.dart +++ b/packages/camera/camera_android/lib/src/utils.dart @@ -20,6 +20,7 @@ CameraLensDirection parseCameraLensDirection(String string) { throw ArgumentError('Unknown CameraLensDirection value'); } +/// Converts a [PlatformCameraLensDirection] to [CameraLensDirection]. CameraLensDirection cameraLensDirectionFromPlatform( PlatformCameraLensDirection direction) { return switch (direction) { diff --git a/packages/camera/camera_android/pigeons/messages.dart b/packages/camera/camera_android/pigeons/messages.dart index 7703a61db9c..7f6cee7ce5e 100644 --- a/packages/camera/camera_android/pigeons/messages.dart +++ b/packages/camera/camera_android/pigeons/messages.dart @@ -1,3 +1,4 @@ +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:pigeon/pigeon.dart'; @ConfigurePigeon(PigeonOptions( @@ -6,12 +7,15 @@ import 'package:pigeon/pigeon.dart'; javaOut: 'android/src/main/java/io/flutter/plugins/camera/Messages.java', copyrightHeader: 'pigeons/copyright.txt', )) + +/// Pigeon equivalent of [CameraLensDirection]. enum PlatformCameraLensDirection { front, back, external, } +/// Pigeon equivalent of [CameraDescription]. class PlatformCameraDescription { PlatformCameraDescription( {required this.name, @@ -24,5 +28,6 @@ class PlatformCameraDescription { @HostApi() abstract class CameraApi { + /// Returns the list of available cameras. List getAvailableCameras(); } From 62450cb41f12712d277a306124a7247e60f950db Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Tue, 1 Oct 2024 13:43:13 -0400 Subject: [PATCH 07/13] Version bump --- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 9f5c4aebe19..b8ff9761889 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.9+12 + +* Converts `getAvailableCameras` to Pigeon. + ## 0.10.9+11 * Updates annotations lib to 1.8.2. diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 286d6c3ff0f..b18b5ebe181 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.9+11 +version: 0.10.9+12 environment: sdk: ^3.4.0 From 44bd3458d218b8e0f6012a3b190c724b275f8f06 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Tue, 1 Oct 2024 13:51:21 -0400 Subject: [PATCH 08/13] Appease analyze --- packages/camera/camera_android/pigeons/messages.dart | 3 +++ packages/camera/camera_android/pubspec.yaml | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android/pigeons/messages.dart b/packages/camera/camera_android/pigeons/messages.dart index 7f6cee7ce5e..e66f62c0232 100644 --- a/packages/camera/camera_android/pigeons/messages.dart +++ b/packages/camera/camera_android/pigeons/messages.dart @@ -1,3 +1,6 @@ +// 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_platform_interface/camera_platform_interface.dart'; import 'package:pigeon/pigeon.dart'; diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index b18b5ebe181..0378c25ce00 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -19,19 +19,19 @@ flutter: dartPluginClass: AndroidCamera dependencies: - build_runner: ^2.4.13 camera_platform_interface: ^2.6.0 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 - mockito: ^5.4.4 - pigeon: ^22.4.1 stream_transform: ^2.0.0 dev_dependencies: + build_runner: ^2.4.11 async: ^2.5.0 flutter_test: sdk: flutter + mockito: ^5.4.4 + pigeon: ^22.4.1 topics: - camera From da0a0a2c9bcadf9f7d4bef50f67f6cf9acf37ee4 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Tue, 1 Oct 2024 15:37:18 -0400 Subject: [PATCH 09/13] Reorder devs --- packages/camera/camera_android/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 0378c25ce00..d0f48a5ff5a 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -26,8 +26,8 @@ dependencies: stream_transform: ^2.0.0 dev_dependencies: - build_runner: ^2.4.11 async: ^2.5.0 + build_runner: ^2.4.11 flutter_test: sdk: flutter mockito: ^5.4.4 From b7f814af70671a8a5bad5251cdb14164c469654b Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Fri, 4 Oct 2024 08:46:36 -0400 Subject: [PATCH 10/13] Dart PR Feedback --- .../camera/camera_android/lib/src/utils.dart | 13 ------------- .../camera/camera_android/test/utils_test.dart | 16 ++++------------ 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/packages/camera/camera_android/lib/src/utils.dart b/packages/camera/camera_android/lib/src/utils.dart index c7e11a1e2f1..9af1d97d6ca 100644 --- a/packages/camera/camera_android/lib/src/utils.dart +++ b/packages/camera/camera_android/lib/src/utils.dart @@ -7,19 +7,6 @@ import 'package:flutter/services.dart'; import 'messages.g.dart'; -/// Parses a string into a corresponding CameraLensDirection. -CameraLensDirection parseCameraLensDirection(String string) { - switch (string) { - case 'front': - return CameraLensDirection.front; - case 'back': - return CameraLensDirection.back; - case 'external': - return CameraLensDirection.external; - } - throw ArgumentError('Unknown CameraLensDirection value'); -} - /// Converts a [PlatformCameraLensDirection] to [CameraLensDirection]. CameraLensDirection cameraLensDirectionFromPlatform( PlatformCameraLensDirection direction) { diff --git a/packages/camera/camera_android/test/utils_test.dart b/packages/camera/camera_android/test/utils_test.dart index 6f426bc90f6..81032101f18 100644 --- a/packages/camera/camera_android/test/utils_test.dart +++ b/packages/camera/camera_android/test/utils_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:camera_android/src/messages.g.dart'; import 'package:camera_android/src/utils.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/services.dart'; @@ -13,28 +14,19 @@ void main() { 'Should return CameraLensDirection when valid value is supplied when parsing camera lens direction', () { expect( - parseCameraLensDirection('back'), + cameraLensDirectionFromPlatform(PlatformCameraLensDirection.back), CameraLensDirection.back, ); expect( - parseCameraLensDirection('front'), + cameraLensDirectionFromPlatform(PlatformCameraLensDirection.front), CameraLensDirection.front, ); expect( - parseCameraLensDirection('external'), + cameraLensDirectionFromPlatform(PlatformCameraLensDirection.external), CameraLensDirection.external, ); }); - test( - 'Should throw ArgumentException when invalid value is supplied when parsing camera lens direction', - () { - expect( - () => parseCameraLensDirection('test'), - throwsA(isArgumentError), - ); - }); - test('serializeDeviceOrientation() should serialize correctly', () { expect(serializeDeviceOrientation(DeviceOrientation.portraitUp), 'portraitUp'); From 54e3a3e9009d561c74473f636f2a6da0c3747b7c Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Fri, 4 Oct 2024 08:52:58 -0400 Subject: [PATCH 11/13] Move CameraApi to MethodCallHandlerImpl --- .../flutter/plugins/camera/CameraPlugin.java | 21 +------------------ .../plugins/camera/MethodCallHandlerImpl.java | 18 +++++++++++++++- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java index 85ed614720e..aff225e2c35 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java @@ -5,7 +5,6 @@ package io.flutter.plugins.camera; import android.app.Activity; -import android.hardware.camera2.CameraAccessException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.flutter.embedding.engine.plugins.FlutterPlugin; @@ -14,8 +13,6 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.camera.CameraPermissions.PermissionsRegistry; import io.flutter.view.TextureRegistry; -import java.util.Collections; -import java.util.List; /** * Platform implementation of the camera_plugin. @@ -23,12 +20,11 @@ *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. * See {@code io.flutter.plugins.camera.MainActivity} for an example. */ -public final class CameraPlugin implements FlutterPlugin, ActivityAware, Messages.CameraApi { +public final class CameraPlugin implements FlutterPlugin, ActivityAware { private static final String TAG = "CameraPlugin"; private @Nullable FlutterPluginBinding flutterPluginBinding; private @Nullable MethodCallHandlerImpl methodCallHandler; - private @Nullable Activity activity; /** * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. @@ -80,23 +76,8 @@ private void maybeStartListening( BinaryMessenger messenger, PermissionsRegistry permissionsRegistry, TextureRegistry textureRegistry) { - this.activity = activity; methodCallHandler = new MethodCallHandlerImpl( activity, messenger, new CameraPermissions(), permissionsRegistry, textureRegistry); - Messages.CameraApi.setUp(messenger, this); - } - - @NonNull - @Override - public List getAvailableCameras() { - if (activity == null) { - return Collections.emptyList(); - } - try { - return CameraUtils.getAvailableCameras(activity); - } catch (CameraAccessException e) { - throw new RuntimeException(e); - } } } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index 0a8ae7b9730..ba80112a734 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -25,11 +25,13 @@ import io.flutter.plugins.camera.features.flash.FlashMode; import io.flutter.plugins.camera.features.resolution.ResolutionPreset; import io.flutter.view.TextureRegistry; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; -final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { +final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler, Messages.CameraApi { private final Activity activity; private final BinaryMessenger messenger; private final CameraPermissions cameraPermissions; @@ -55,6 +57,7 @@ final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { imageStreamChannel = new EventChannel(messenger, "plugins.flutter.io/camera_android/imageStream"); methodChannel.setMethodCallHandler(this); + Messages.CameraApi.setUp(messenger, this); } @Override @@ -423,4 +426,17 @@ private void handleException(Exception exception, Result result) { // CameraAccessException can not be cast to a RuntimeException. throw (RuntimeException) exception; } + + @NonNull + @Override + public List getAvailableCameras() { + if (activity == null) { + return Collections.emptyList(); + } + try { + return CameraUtils.getAvailableCameras(activity); + } catch (CameraAccessException e) { + throw new RuntimeException(e); + } + } } From 368d89fc027f99daf9da9957df4b3f02b8ee0247 Mon Sep 17 00:00:00 2001 From: Yaakov Schectman Date: Fri, 4 Oct 2024 11:08:58 -0400 Subject: [PATCH 12/13] Name parameter --- packages/camera/camera_android/lib/src/android_camera.dart | 2 +- packages/camera/camera_android/test/android_camera_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 17ae590080e..b5566090a07 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -21,7 +21,7 @@ const MethodChannel _channel = /// The Android implementation of [CameraPlatform] that uses method channels. class AndroidCamera extends CameraPlatform { /// Creates a new [CameraPlatform] instance. - AndroidCamera([@visibleForTesting CameraApi? hostApi]) + AndroidCamera({@visibleForTesting CameraApi? hostApi}) : _hostApi = hostApi ?? CameraApi(); /// Registers this class as the default instance of [CameraPlatform]. diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index 34dd3bdc13d..17b8904f09b 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -506,7 +506,7 @@ void main() { }, ); mockCameraApi = MockCameraApi(); - camera = AndroidCamera(mockCameraApi); + camera = AndroidCamera(hostApi: mockCameraApi); cameraId = await camera.createCamera( const CameraDescription( name: 'Test', From 78835d6b4c6a09327e1cb4abcc0c6f1bb4661a6b Mon Sep 17 00:00:00 2001 From: yaakovschectman <109111084+yaakovschectman@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:39:15 -0400 Subject: [PATCH 13/13] Bump version --- packages/camera/camera_android/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 752e69c8364..5d3e369ad47 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.9+12 +version: 0.10.9+13 environment: sdk: ^3.5.0