From 47d1d3c966c2e827f93da787f30fccadd1af8483 Mon Sep 17 00:00:00 2001 From: Maurice Parrish Date: Mon, 22 Jul 2019 14:25:05 -0700 Subject: [PATCH 1/5] Camera dart api for Android --- .../lib/new/src/support_android/camera.dart | 71 +++++++++ .../new/src/support_android/camera_info.dart | 42 ++++++ .../lib/new/support_android_camera.dart | 13 ++ .../support_android_camera_test.dart | 137 ++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 packages/camera/lib/new/src/support_android/camera.dart create mode 100644 packages/camera/lib/new/src/support_android/camera_info.dart create mode 100644 packages/camera/lib/new/support_android_camera.dart create mode 100644 packages/camera/test/support_android/support_android_camera_test.dart diff --git a/packages/camera/lib/new/src/support_android/camera.dart b/packages/camera/lib/new/src/support_android/camera.dart new file mode 100644 index 000000000000..5aa854e267dd --- /dev/null +++ b/packages/camera/lib/new/src/support_android/camera.dart @@ -0,0 +1,71 @@ +part of support_android_camera; + +class Camera with NativeMethodCallHandler { + Camera._(); + + bool _isClosed = false; + + static Future getNumberOfCameras() { + return CameraChannel.channel.invokeMethod( + 'Camera#getNumberOfCameras', + ); + } + + static Camera open(int cameraId) { + final Camera camera = Camera._(); + + CameraChannel.channel.invokeMethod( + 'Camera#open', + {'cameraId': cameraId, 'cameraHandle': camera.handle}, + ); + + return camera; + } + + static Future getCameraInfo(int cameraId) async { + final Map infoMap = + await CameraChannel.channel.invokeMapMethod( + 'Camera#getCameraInfo', + {'cameraId': cameraId}, + ); + + return CameraInfo._fromMap(infoMap); + } + + set previewTexture(NativeTexture texture) { + assert(!_isClosed); + + CameraChannel.channel.invokeMethod( + 'Camera#previewTexture', + {'handle': handle, 'nativeTexture': texture?.asMap()}, + ); + } + + Future startPreview() { + assert(!_isClosed); + + return CameraChannel.channel.invokeMethod( + 'Camera#startPreview', + {'handle': handle}, + ); + } + + Future stopPreview() { + if (_isClosed) return Future.value(); + + return CameraChannel.channel.invokeMethod( + 'Camera#stopPreview', + {'handle': handle}, + ); + } + + Future release() { + if (_isClosed) return Future.value(); + + _isClosed = true; + return CameraChannel.channel.invokeMethod( + 'Camera#release', + {'handle': handle}, + ); + } +} diff --git a/packages/camera/lib/new/src/support_android/camera_info.dart b/packages/camera/lib/new/src/support_android/camera_info.dart new file mode 100644 index 000000000000..41756c84c94e --- /dev/null +++ b/packages/camera/lib/new/src/support_android/camera_info.dart @@ -0,0 +1,42 @@ +part of support_android_camera; + +enum Facing { back, front } + +class CameraInfo implements CameraDescription { + const CameraInfo._({ + @required this.id, + @required this.facing, + @required this.orientation, + }) : assert(id != null), + assert(facing != null), + assert(orientation != null); + + factory CameraInfo._fromMap(Map map) { + return CameraInfo._( + id: map['id'], + orientation: map['orientation'], + facing: Facing.values.firstWhere( + (Facing facing) => facing.toString() == map['facing'], + ), + ); + } + + final int id; + final Facing facing; + final int orientation; + + @override + String get name => id.toString(); + + @override + LensDirection get direction { + switch (facing) { + case Facing.front: + return LensDirection.front; + case Facing.back: + return LensDirection.back; + } + + return null; + } +} diff --git a/packages/camera/lib/new/support_android_camera.dart b/packages/camera/lib/new/support_android_camera.dart new file mode 100644 index 000000000000..bb714ce1e6fa --- /dev/null +++ b/packages/camera/lib/new/support_android_camera.dart @@ -0,0 +1,13 @@ +library support_android_camera; + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; + +import 'src/common/camera_channel.dart'; +import 'src/common/camera_interface.dart'; +import 'src/common/camera_mixins.dart'; +import 'src/common/native_texture.dart'; + +part 'src/support_android/camera_info.dart'; +part 'src/support_android/camera.dart'; diff --git a/packages/camera/test/support_android/support_android_camera_test.dart b/packages/camera/test/support_android/support_android_camera_test.dart new file mode 100644 index 000000000000..7ccd9af7827c --- /dev/null +++ b/packages/camera/test/support_android/support_android_camera_test.dart @@ -0,0 +1,137 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:camera/new/src/camera_testing.dart'; + +import 'package:camera/new/support_android_camera.dart'; + +void main() { + group('Support Android Camera', () { + group('$Camera', () { + final List log = []; + setUpAll(() { + CameraTesting.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + switch (methodCall.method) { + case 'Camera#getNumberOfCameras': + return 3; + case 'Camera#open': + return null; + case 'Camera#getCameraInfo': + return { + 'id': 3, + 'orientation': 90, + 'facing': Facing.front.toString(), + }; + case 'Camera#startPreview': + return null; + case 'Camera#stopPreview': + return null; + case 'Camera#release': + return null; + } + + throw ArgumentError.value( + methodCall.method, + 'methodCall.method', + 'No method found for', + ); + }); + }); + + setUp(() { + log.clear(); + CameraTesting.nextHandle = 0; + }); + + test('getNumberOfCameras', () async { + final int result = await Camera.getNumberOfCameras(); + + expect(result, 3); + expect(log, [ + isMethodCall( + '$Camera#getNumberOfCameras', + arguments: null, + ) + ]); + }); + + test('open', () { + Camera.open(14); + + expect(log, [ + isMethodCall( + '$Camera#open', + arguments: { + 'cameraId': 14, + 'cameraHandle': 0, + }, + ) + ]); + }); + + test('getCameraInfo', () async { + final CameraInfo info = await Camera.getCameraInfo(14); + + expect(info.id, 3); + expect(info.orientation, 90); + expect(info.facing, Facing.front); + + expect(log, [ + isMethodCall( + '$Camera#getCameraInfo', + arguments: {'cameraId': 14}, + ) + ]); + }); + + test('startPreview', () { + final Camera camera = Camera.open(0); + + log.clear(); + camera.startPreview(); + + expect(log, [ + isMethodCall( + '$Camera#startPreview', + arguments: { + 'handle': 0, + }, + ) + ]); + }); + + test('stopPreview', () { + final Camera camera = Camera.open(0); + + log.clear(); + camera.stopPreview(); + + expect(log, [ + isMethodCall( + '$Camera#stopPreview', + arguments: { + 'handle': 0, + }, + ) + ]); + }); + + test('release', () { + final Camera camera = Camera.open(0); + + log.clear(); + camera.release(); + + expect(log, [ + isMethodCall( + '$Camera#release', + arguments: { + 'handle': 0, + }, + ) + ]); + }); + }); + }); +} From 6ab10c13e9e79a75e6ca0654960160dbca71d5a6 Mon Sep 17 00:00:00 2001 From: Maurice Parrish Date: Mon, 22 Jul 2019 14:48:34 -0700 Subject: [PATCH 2/5] documentation --- .../lib/new/src/support_android/camera.dart | 42 ++++++++++++++++++- .../new/src/support_android/camera_info.dart | 20 +++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/camera/lib/new/src/support_android/camera.dart b/packages/camera/lib/new/src/support_android/camera.dart index 5aa854e267dd..203c4500129e 100644 --- a/packages/camera/lib/new/src/support_android/camera.dart +++ b/packages/camera/lib/new/src/support_android/camera.dart @@ -1,16 +1,35 @@ part of support_android_camera; +/// The Camera class used to set image capture settings, start/stop preview, snap pictures, and retrieve frames for encoding for video. +/// +/// This class is a client for the Camera service, which manages the actual +/// camera hardware. +/// +/// This exposes the deprecated Android +/// [Camera](https://developer.android.com/reference/android/hardware/Camera) +/// API. This should only be used with Android sdk versions less than 21. class Camera with NativeMethodCallHandler { Camera._(); bool _isClosed = false; + /// Retrieves the number of physical cameras available on this device. static Future getNumberOfCameras() { return CameraChannel.channel.invokeMethod( 'Camera#getNumberOfCameras', ); } + /// Creates a new [Camera] object to access a particular hardware camera. + /// + /// If the same camera is opened by other applications, this will throw a + /// [PlatformException]. + /// + /// You must call [release] when you are done using the camera, otherwise it + /// will remain locked and be unavailable to other applications. + /// + /// Your application should only have one [Camera] object active at a time for + /// a particular hardware camera. static Camera open(int cameraId) { final Camera camera = Camera._(); @@ -22,6 +41,9 @@ class Camera with NativeMethodCallHandler { return camera; } + /// Retrieves information about a particular camera. + /// + /// If [getNumberOfCameras] returns N, the valid id is 0 to N-1. static Future getCameraInfo(int cameraId) async { final Map infoMap = await CameraChannel.channel.invokeMapMethod( @@ -32,6 +54,16 @@ class Camera with NativeMethodCallHandler { return CameraInfo._fromMap(infoMap); } + /// Sets the [NativeTexture] to be used for live preview. + /// + /// This method must be called before [startPreview]. + /// + /// The one exception is that if the preview native texture is not set (or + /// set to null) before [startPreview] is called, then this method may be + /// called once with a non-null parameter to set the preview texture. + /// (This allows camera setup and surface creation to happen in parallel, + /// saving time.) The preview native texture may not otherwise change while + /// preview is running. set previewTexture(NativeTexture texture) { assert(!_isClosed); @@ -41,6 +73,10 @@ class Camera with NativeMethodCallHandler { ); } + /// Starts capturing and drawing preview frames to the screen. + /// + /// Preview will not actually start until a surface is supplied with + /// [previewTexture]. Future startPreview() { assert(!_isClosed); @@ -50,8 +86,9 @@ class Camera with NativeMethodCallHandler { ); } + /// Stops capturing and drawing preview frames to the [previewTexture], and resets the camera for a future call to [startPreview]. Future stopPreview() { - if (_isClosed) return Future.value(); + assert(!_isClosed); return CameraChannel.channel.invokeMethod( 'Camera#stopPreview', @@ -59,6 +96,9 @@ class Camera with NativeMethodCallHandler { ); } + /// Disconnects and releases the Camera object resources. + /// + /// You must call this as soon as you're done with the Camera object. Future release() { if (_isClosed) return Future.value(); diff --git a/packages/camera/lib/new/src/support_android/camera_info.dart b/packages/camera/lib/new/src/support_android/camera_info.dart index 41756c84c94e..c15d7999c098 100644 --- a/packages/camera/lib/new/src/support_android/camera_info.dart +++ b/packages/camera/lib/new/src/support_android/camera_info.dart @@ -1,7 +1,11 @@ part of support_android_camera; +/// The direction that the camera faces. enum Facing { back, front } +/// Information about a camera. +/// +/// Retrieved from [Camera.getCameraInfo]. class CameraInfo implements CameraDescription { const CameraInfo._({ @required this.id, @@ -21,8 +25,24 @@ class CameraInfo implements CameraDescription { ); } + /// Identifier for a particular camera. final int id; + + /// The direction that the camera faces. final Facing facing; + + /// The orientation of the camera image. + /// + /// The value is the angle that the camera image needs to be rotated clockwise + /// so it shows correctly on the display in its natural orientation. + /// It should be 0, 90, 180, or 270. + /// + /// For example, suppose a device has a naturally tall screen. The back-facing + /// camera sensor is mounted in landscape. You are looking at the screen. If + /// the top side of the camera sensor is aligned with the right edge of the + /// screen in natural orientation, the value should be 90. If the top side of + /// a front-facing camera sensor is aligned with the right of the screen, the + /// value should be 270. final int orientation; @override From cef20b502309b8afaad66b03cc2a795258fc991f Mon Sep 17 00:00:00 2001 From: Maurice Parrish Date: Mon, 22 Jul 2019 17:22:35 -0700 Subject: [PATCH 3/5] Add liscensing and removed creation of a library --- packages/camera/lib/new/camera.dart | 1 + .../lib/new/src/support_android/camera_info.dart | 6 +++++- .../{camera.dart => support_android_camera.dart} | 16 +++++++++++++++- .../camera/lib/new/support_android_camera.dart | 13 ------------- .../support_android_camera_test.dart | 7 +++++-- 5 files changed, 26 insertions(+), 17 deletions(-) rename packages/camera/lib/new/src/support_android/{camera.dart => support_android_camera.dart} (88%) delete mode 100644 packages/camera/lib/new/support_android_camera.dart diff --git a/packages/camera/lib/new/camera.dart b/packages/camera/lib/new/camera.dart index ab135079d2dd..c2ae6310f395 100644 --- a/packages/camera/lib/new/camera.dart +++ b/packages/camera/lib/new/camera.dart @@ -6,3 +6,4 @@ export 'src/camera_controller.dart'; export 'src/camera_testing.dart'; export 'src/common/camera_interface.dart'; export 'src/common/native_texture.dart'; +export 'src/support_android/support_android_camera.dart'; diff --git a/packages/camera/lib/new/src/support_android/camera_info.dart b/packages/camera/lib/new/src/support_android/camera_info.dart index c15d7999c098..f327848ee22b 100644 --- a/packages/camera/lib/new/src/support_android/camera_info.dart +++ b/packages/camera/lib/new/src/support_android/camera_info.dart @@ -1,4 +1,8 @@ -part of support_android_camera; +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of 'support_android_camera.dart'; /// The direction that the camera faces. enum Facing { back, front } diff --git a/packages/camera/lib/new/src/support_android/camera.dart b/packages/camera/lib/new/src/support_android/support_android_camera.dart similarity index 88% rename from packages/camera/lib/new/src/support_android/camera.dart rename to packages/camera/lib/new/src/support_android/support_android_camera.dart index 203c4500129e..9b7e020c2f43 100644 --- a/packages/camera/lib/new/src/support_android/camera.dart +++ b/packages/camera/lib/new/src/support_android/support_android_camera.dart @@ -1,4 +1,18 @@ -part of support_android_camera; +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:camera/new/src/common/camera_mixins.dart'; +import 'package:flutter/foundation.dart'; + +import '../common/camera_channel.dart'; +import '../common/camera_interface.dart'; +import '../common/camera_mixins.dart'; +import '../common/native_texture.dart'; + +part 'camera_info.dart'; /// The Camera class used to set image capture settings, start/stop preview, snap pictures, and retrieve frames for encoding for video. /// diff --git a/packages/camera/lib/new/support_android_camera.dart b/packages/camera/lib/new/support_android_camera.dart deleted file mode 100644 index bb714ce1e6fa..000000000000 --- a/packages/camera/lib/new/support_android_camera.dart +++ /dev/null @@ -1,13 +0,0 @@ -library support_android_camera; - -import 'dart:async'; - -import 'package:flutter/foundation.dart'; - -import 'src/common/camera_channel.dart'; -import 'src/common/camera_interface.dart'; -import 'src/common/camera_mixins.dart'; -import 'src/common/native_texture.dart'; - -part 'src/support_android/camera_info.dart'; -part 'src/support_android/camera.dart'; diff --git a/packages/camera/test/support_android/support_android_camera_test.dart b/packages/camera/test/support_android/support_android_camera_test.dart index 7ccd9af7827c..cefc478852f7 100644 --- a/packages/camera/test/support_android/support_android_camera_test.dart +++ b/packages/camera/test/support_android/support_android_camera_test.dart @@ -1,9 +1,12 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:camera/new/src/support_android/support_android_camera.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:camera/new/src/camera_testing.dart'; -import 'package:camera/new/support_android_camera.dart'; - void main() { group('Support Android Camera', () { group('$Camera', () { From a27f7f39a5bbf928f0a64039d116e1866cba9aea Mon Sep 17 00:00:00 2001 From: Maurice Parrish Date: Mon, 22 Jul 2019 17:30:44 -0700 Subject: [PATCH 4/5] removed duplicate import --- .../lib/new/src/support_android/support_android_camera.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/lib/new/src/support_android/support_android_camera.dart b/packages/camera/lib/new/src/support_android/support_android_camera.dart index 9b7e020c2f43..eb8be4548ce8 100644 --- a/packages/camera/lib/new/src/support_android/support_android_camera.dart +++ b/packages/camera/lib/new/src/support_android/support_android_camera.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:camera/new/src/common/camera_mixins.dart'; import 'package:flutter/foundation.dart'; import '../common/camera_channel.dart'; From 94d174eaa37d1ada4d12309f9900222858c3aa5e Mon Sep 17 00:00:00 2001 From: Maurice Parrish Date: Tue, 23 Jul 2019 12:51:31 -0700 Subject: [PATCH 5/5] Make code idiomatic --- packages/camera/lib/new/camera.dart | 3 ++- .../{support_android_camera.dart => camera.dart} | 8 ++------ .../lib/new/src/support_android/camera_info.dart | 10 ++++++---- ...roid_camera_test.dart => support_android_test.dart} | 3 ++- 4 files changed, 12 insertions(+), 12 deletions(-) rename packages/camera/lib/new/src/support_android/{support_android_camera.dart => camera.dart} (96%) rename packages/camera/test/support_android/{support_android_camera_test.dart => support_android_test.dart} (96%) diff --git a/packages/camera/lib/new/camera.dart b/packages/camera/lib/new/camera.dart index c2ae6310f395..08b085f8e2c8 100644 --- a/packages/camera/lib/new/camera.dart +++ b/packages/camera/lib/new/camera.dart @@ -6,4 +6,5 @@ export 'src/camera_controller.dart'; export 'src/camera_testing.dart'; export 'src/common/camera_interface.dart'; export 'src/common/native_texture.dart'; -export 'src/support_android/support_android_camera.dart'; +export 'src/support_android/camera.dart'; +export 'src/support_android/camera_info.dart'; diff --git a/packages/camera/lib/new/src/support_android/support_android_camera.dart b/packages/camera/lib/new/src/support_android/camera.dart similarity index 96% rename from packages/camera/lib/new/src/support_android/support_android_camera.dart rename to packages/camera/lib/new/src/support_android/camera.dart index eb8be4548ce8..d78753d24355 100644 --- a/packages/camera/lib/new/src/support_android/support_android_camera.dart +++ b/packages/camera/lib/new/src/support_android/camera.dart @@ -4,14 +4,10 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; - import '../common/camera_channel.dart'; -import '../common/camera_interface.dart'; import '../common/camera_mixins.dart'; import '../common/native_texture.dart'; - -part 'camera_info.dart'; +import 'camera_info.dart'; /// The Camera class used to set image capture settings, start/stop preview, snap pictures, and retrieve frames for encoding for video. /// @@ -64,7 +60,7 @@ class Camera with NativeMethodCallHandler { {'cameraId': cameraId}, ); - return CameraInfo._fromMap(infoMap); + return CameraInfo.fromMap(infoMap); } /// Sets the [NativeTexture] to be used for live preview. diff --git a/packages/camera/lib/new/src/support_android/camera_info.dart b/packages/camera/lib/new/src/support_android/camera_info.dart index f327848ee22b..033fecfea6d9 100644 --- a/packages/camera/lib/new/src/support_android/camera_info.dart +++ b/packages/camera/lib/new/src/support_android/camera_info.dart @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of 'support_android_camera.dart'; +import 'package:flutter/foundation.dart'; + +import '../common/camera_interface.dart'; /// The direction that the camera faces. enum Facing { back, front } @@ -11,7 +13,7 @@ enum Facing { back, front } /// /// Retrieved from [Camera.getCameraInfo]. class CameraInfo implements CameraDescription { - const CameraInfo._({ + const CameraInfo({ @required this.id, @required this.facing, @required this.orientation, @@ -19,8 +21,8 @@ class CameraInfo implements CameraDescription { assert(facing != null), assert(orientation != null); - factory CameraInfo._fromMap(Map map) { - return CameraInfo._( + factory CameraInfo.fromMap(Map map) { + return CameraInfo( id: map['id'], orientation: map['orientation'], facing: Facing.values.firstWhere( diff --git a/packages/camera/test/support_android/support_android_camera_test.dart b/packages/camera/test/support_android/support_android_test.dart similarity index 96% rename from packages/camera/test/support_android/support_android_camera_test.dart rename to packages/camera/test/support_android/support_android_test.dart index cefc478852f7..114f56ab3348 100644 --- a/packages/camera/test/support_android/support_android_camera_test.dart +++ b/packages/camera/test/support_android/support_android_test.dart @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:camera/new/src/support_android/support_android_camera.dart'; +import 'package:camera/new/src/support_android/camera_info.dart'; +import 'package:camera/new/src/support_android/camera.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:camera/new/src/camera_testing.dart';