blob: 97c981eb31785d5148b996e336f9c71b5c5a360a [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
const MethodChannel _channel =
MethodChannel('plugins.flutter.io/image_picker_android');
/// An Android implementation of [ImagePickerPlatform].
class ImagePickerAndroid extends ImagePickerPlatform {
/// The MethodChannel that is being used by this implementation of the plugin.
@visibleForTesting
MethodChannel get channel => _channel;
/// Registers this class as the default platform implementation.
static void registerWith() {
ImagePickerPlatform.instance = ImagePickerAndroid();
}
@override
Future<PickedFile?> pickImage({
required ImageSource source,
double? maxWidth,
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) async {
final String? path = await _getImagePath(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
preferredCameraDevice: preferredCameraDevice,
);
return path != null ? PickedFile(path) : null;
}
@override
Future<List<PickedFile>?> pickMultiImage({
double? maxWidth,
double? maxHeight,
int? imageQuality,
}) async {
final List<dynamic>? paths = await _getMultiImagePath(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
);
if (paths == null) {
return null;
}
return paths.map((dynamic path) => PickedFile(path as String)).toList();
}
Future<List<dynamic>?> _getMultiImagePath({
double? maxWidth,
double? maxHeight,
int? imageQuality,
}) {
if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) {
throw ArgumentError.value(
imageQuality, 'imageQuality', 'must be between 0 and 100');
}
if (maxWidth != null && maxWidth < 0) {
throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative');
}
if (maxHeight != null && maxHeight < 0) {
throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative');
}
return _channel.invokeMethod<List<dynamic>?>(
'pickMultiImage',
<String, dynamic>{
'maxWidth': maxWidth,
'maxHeight': maxHeight,
'imageQuality': imageQuality,
'useAndroidPhotoPicker': useAndroidPhotoPicker,
},
);
}
Future<String?> _getImagePath({
required ImageSource source,
double? maxWidth,
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
bool requestFullMetadata = true,
}) {
if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) {
throw ArgumentError.value(
imageQuality, 'imageQuality', 'must be between 0 and 100');
}
if (maxWidth != null && maxWidth < 0) {
throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative');
}
if (maxHeight != null && maxHeight < 0) {
throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative');
}
return _channel.invokeMethod<String>(
'pickImage',
<String, dynamic>{
'source': source.index,
'maxWidth': maxWidth,
'maxHeight': maxHeight,
'imageQuality': imageQuality,
'cameraDevice': preferredCameraDevice.index,
'requestFullMetadata': requestFullMetadata,
'useAndroidPhotoPicker': useAndroidPhotoPicker,
},
);
}
@override
Future<PickedFile?> pickVideo({
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
}) async {
final String? path = await _getVideoPath(
source: source,
maxDuration: maxDuration,
preferredCameraDevice: preferredCameraDevice,
);
return path != null ? PickedFile(path) : null;
}
Future<String?> _getVideoPath({
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
}) {
return _channel.invokeMethod<String>(
'pickVideo',
<String, dynamic>{
'source': source.index,
'maxDuration': maxDuration?.inSeconds,
'cameraDevice': preferredCameraDevice.index,
'useAndroidPhotoPicker': useAndroidPhotoPicker,
},
);
}
@override
Future<XFile?> getImage({
required ImageSource source,
double? maxWidth,
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) async {
final String? path = await _getImagePath(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
preferredCameraDevice: preferredCameraDevice,
);
return path != null ? XFile(path) : null;
}
@override
Future<XFile?> getImageFromSource({
required ImageSource source,
ImagePickerOptions options = const ImagePickerOptions(),
}) async {
final String? path = await _getImagePath(
source: source,
maxHeight: options.maxHeight,
maxWidth: options.maxWidth,
imageQuality: options.imageQuality,
preferredCameraDevice: options.preferredCameraDevice,
requestFullMetadata: options.requestFullMetadata,
);
return path != null ? XFile(path) : null;
}
@override
Future<List<XFile>?> getMultiImage({
double? maxWidth,
double? maxHeight,
int? imageQuality,
}) async {
final List<dynamic>? paths = await _getMultiImagePath(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
);
if (paths == null) {
return null;
}
return paths.map((dynamic path) => XFile(path as String)).toList();
}
@override
Future<XFile?> getVideo({
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
}) async {
final String? path = await _getVideoPath(
source: source,
maxDuration: maxDuration,
preferredCameraDevice: preferredCameraDevice,
);
return path != null ? XFile(path) : null;
}
@override
Future<LostData> retrieveLostData() async {
final LostDataResponse result = await getLostData();
if (result.isEmpty) {
return LostData.empty();
}
return LostData(
file: result.file != null ? PickedFile(result.file!.path) : null,
exception: result.exception,
type: result.type,
);
}
@override
Future<LostDataResponse> getLostData() async {
List<XFile>? pickedFileList;
final Map<String, dynamic>? result =
await _channel.invokeMapMethod<String, dynamic>('retrieve');
if (result == null) {
return LostDataResponse.empty();
}
assert(result.containsKey('path') != result.containsKey('errorCode'));
final String? type = result['type'] as String?;
assert(type == kTypeImage || type == kTypeVideo);
RetrieveType? retrieveType;
if (type == kTypeImage) {
retrieveType = RetrieveType.image;
} else if (type == kTypeVideo) {
retrieveType = RetrieveType.video;
}
PlatformException? exception;
if (result.containsKey('errorCode')) {
exception = PlatformException(
code: result['errorCode']! as String,
message: result['errorMessage'] as String?);
}
final String? path = result['path'] as String?;
final List<String>? pathList =
(result['pathList'] as List<dynamic>?)?.cast<String>();
if (pathList != null) {
pickedFileList = <XFile>[];
for (final String path in pathList) {
pickedFileList.add(XFile(path));
}
}
return LostDataResponse(
file: path != null ? XFile(path) : null,
exception: exception,
type: retrieveType,
files: pickedFileList,
);
}
/// Set [ImagePickerAndroid] to use Android 13 Photo Picker.
///
/// Currently defaults to false, but the default is subject to change.
bool useAndroidPhotoPicker = false;
}