Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Next Next commit
Switch to internal copy of method channel implementation
  • Loading branch information
stuartmorgan-g committed Apr 12, 2022
commit 8961c9bd909bf3cd60acd3df7a83832380cac17f
280 changes: 280 additions & 0 deletions packages/image_picker/image_picker_ios/lib/image_picker_ios.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
// 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');

/// An implementation of [ImagePickerPlatform] for iOS.
class ImagePickerIOS extends ImagePickerPlatform {
/// The MethodChannel that is being used by this implementation of the plugin.
@visibleForTesting
MethodChannel get channel => _channel;

@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,
},
);
}

Future<String?> _getImagePath({
required ImageSource source,
double? maxWidth,
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) {
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
},
);
}

@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({
Copy link
Member

Choose a reason for hiding this comment

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

nit: naming is a bit confusing since this says "getVideoPath" but is calling "pickVideo"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's some weird legacy naming in this plugin from the way things were named when the platform interface was created, and then when the new cross-file version were implemented. (This is just a copy from the shared method channel.)

I'll give it a better name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Standardized the helpers as _<host API name>AsPath. Still not great, but consistent, and it makes it more clear what the distinction is between the helper (returns paths) and the public variants (return XFile or PickedFile).

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

@override
Future<LostData> retrieveLostData() async {
final Map<String, dynamic>? result =
await _channel.invokeMapMethod<String, dynamic>('retrieve');

if (result == null) {
return LostData.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?;

return LostData(
file: path != null ? PickedFile(path) : null,
exception: exception,
type: retrieveType,
);
}

@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<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<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,
);
}
}
4 changes: 3 additions & 1 deletion packages/image_picker/image_picker_ios/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ version: 0.8.4+11

environment:
sdk: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0"
flutter: ">=2.8.0"

flutter:
plugin:
implements: image_picker
platforms:
ios:
dartPluginClass: ImagePickerIOS
pluginClass: FLTImagePickerPlugin

dependencies:
Expand All @@ -24,3 +25,4 @@ dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.0.0
pigeon: ^2.0.3
Loading