Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/camera/lib/new/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +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/camera.dart';
export 'src/support_android/camera_info.dart';
120 changes: 120 additions & 0 deletions packages/camera/lib/new/src/support_android/camera.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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 '../common/camera_channel.dart';
import '../common/camera_mixins.dart';
import '../common/native_texture.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.
///
/// 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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Camera._();

bool _isClosed = false;

/// Retrieves the number of physical cameras available on this device.
static Future<int> getNumberOfCameras() {
return CameraChannel.channel.invokeMethod<int>(
'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._();

CameraChannel.channel.invokeMethod<int>(
'Camera#open',
<String, dynamic>{'cameraId': cameraId, 'cameraHandle': camera.handle},
);

return camera;
}

/// Retrieves information about a particular camera.
///
/// If [getNumberOfCameras] returns N, the valid id is 0 to N-1.
static Future<CameraInfo> getCameraInfo(int cameraId) async {
final Map<String, dynamic> infoMap =
await CameraChannel.channel.invokeMapMethod<String, dynamic>(
'Camera#getCameraInfo',
<String, dynamic>{'cameraId': cameraId},
);

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);

CameraChannel.channel.invokeMethod<void>(
'Camera#previewTexture',
<String, dynamic>{'handle': handle, 'nativeTexture': texture?.asMap()},
);
}

/// Starts capturing and drawing preview frames to the screen.
///
/// Preview will not actually start until a surface is supplied with
/// [previewTexture].
Future<void> startPreview() {
assert(!_isClosed);

return CameraChannel.channel.invokeMethod<void>(
'Camera#startPreview',
<String, dynamic>{'handle': handle},
);
}

/// Stops capturing and drawing preview frames to the [previewTexture], and resets the camera for a future call to [startPreview].
Future<void> stopPreview() {
assert(!_isClosed);

return CameraChannel.channel.invokeMethod<void>(
'Camera#stopPreview',
<String, dynamic>{'handle': handle},
);
}

/// Disconnects and releases the Camera object resources.
///
/// You must call this as soon as you're done with the Camera object.
Future<void> release() {
if (_isClosed) return Future<void>.value();

_isClosed = true;
return CameraChannel.channel.invokeMethod<void>(
'Camera#release',
<String, dynamic>{'handle': handle},
);
}
}
68 changes: 68 additions & 0 deletions packages/camera/lib/new/src/support_android/camera_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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:flutter/foundation.dart';

import '../common/camera_interface.dart';

/// The direction that the camera faces.
enum Facing { back, front }

/// Information about a camera.
///
/// Retrieved from [Camera.getCameraInfo].
class CameraInfo implements CameraDescription {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const CameraInfo({
@required this.id,
@required this.facing,
@required this.orientation,
}) : assert(id != null),
assert(facing != null),
assert(orientation != null);

factory CameraInfo.fromMap(Map<String, dynamic> map) {
return CameraInfo(
id: map['id'],
orientation: map['orientation'],
facing: Facing.values.firstWhere(
(Facing facing) => facing.toString() == map['facing'],
),
);
}

/// 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
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;
}
}
141 changes: 141 additions & 0 deletions packages/camera/test/support_android/support_android_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// 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/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';

void main() {
group('Support Android Camera', () {
group('$Camera', () {
final List<MethodCall> log = <MethodCall>[];
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 <dynamic, dynamic>{
'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, <Matcher>[
isMethodCall(
'$Camera#getNumberOfCameras',
arguments: null,
)
]);
});

test('open', () {
Camera.open(14);

expect(log, <Matcher>[
isMethodCall(
'$Camera#open',
arguments: <String, dynamic>{
'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, <Matcher>[
isMethodCall(
'$Camera#getCameraInfo',
arguments: <String, dynamic>{'cameraId': 14},
)
]);
});

test('startPreview', () {
final Camera camera = Camera.open(0);

log.clear();
camera.startPreview();

expect(log, <Matcher>[
isMethodCall(
'$Camera#startPreview',
arguments: <String, dynamic>{
'handle': 0,
},
)
]);
});

test('stopPreview', () {
final Camera camera = Camera.open(0);

log.clear();
camera.stopPreview();

expect(log, <Matcher>[
isMethodCall(
'$Camera#stopPreview',
arguments: <String, dynamic>{
'handle': 0,
},
)
]);
});

test('release', () {
final Camera camera = Camera.open(0);

log.clear();
camera.release();

expect(log, <Matcher>[
isMethodCall(
'$Camera#release',
arguments: <String, dynamic>{
'handle': 0,
},
)
]);
});
});
});
}