blob: f70bca420bc00689ff1c8230a4183f95df8a5275 [file] [log] [blame]
// Copyright 2014 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 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
/// Used in internal testing.
class FakePlatformViewController extends PlatformViewController {
FakePlatformViewController(int id) {
_id = id;
}
bool disposed = false;
bool focusCleared = false;
/// Events that are dispatched;
List<PointerEvent> dispatchedPointerEvents = <PointerEvent>[];
int _id;
@override
int get viewId => _id;
@override
void dispatchPointerEvent(PointerEvent event) {
dispatchedPointerEvents.add(event);
}
void clearTestingVariables() {
dispatchedPointerEvents.clear();
disposed = false;
focusCleared = false;
}
@override
void dispose() {
disposed = true;
}
@override
void clearFocus() {
focusCleared = true;
}
}
class FakeAndroidPlatformViewsController {
FakeAndroidPlatformViewsController() {
SystemChannels.platform_views.setMockMethodCallHandler(_onMethodCall);
}
Iterable<FakeAndroidPlatformView> get views => _views.values;
final Map<int, FakeAndroidPlatformView> _views = <int, FakeAndroidPlatformView>{};
final Map<int, List<FakeAndroidMotionEvent>> motionEvents = <int, List<FakeAndroidMotionEvent>>{};
final Set<String> _registeredViewTypes = <String>{};
int _textureCounter = 0;
Completer<void> resizeCompleter;
Completer<void> createCompleter;
int lastClearedFocusViewId;
void registerViewType(String viewType) {
_registeredViewTypes.add(viewType);
}
void invokeViewFocused(int viewId) {
final MethodCodec codec = SystemChannels.platform_views.codec;
final ByteData data = codec.encodeMethodCall(MethodCall('viewFocused', viewId));
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(SystemChannels.platform_views.name, data, (ByteData data) {});
}
Future<dynamic> _onMethodCall(MethodCall call) {
switch(call.method) {
case 'create':
return _create(call);
case 'dispose':
return _dispose(call);
case 'resize':
return _resize(call);
case 'touch':
return _touch(call);
case 'setDirection':
return _setDirection(call);
case 'clearFocus':
return _clearFocus(call);
}
return Future<dynamic>.sync(() => null);
}
Future<dynamic> _create(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments as Map<dynamic, dynamic>;
final int id = args['id'] as int;
final String viewType = args['viewType'] as String;
final double width = args['width'] as double;
final double height = args['height'] as double;
final int layoutDirection = args['direction'] as int;
final Uint8List creationParams = args['params'] as Uint8List;
if (_views.containsKey(id))
throw PlatformException(
code: 'error',
message: 'Trying to create an already created platform view, view id: $id',
);
if (!_registeredViewTypes.contains(viewType))
throw PlatformException(
code: 'error',
message: 'Trying to create a platform view of unregistered type: $viewType',
);
if (createCompleter != null) {
await createCompleter.future;
}
_views[id] = FakeAndroidPlatformView(id, viewType, Size(width, height), layoutDirection, creationParams);
final int textureId = _textureCounter++;
return Future<int>.sync(() => textureId);
}
Future<dynamic> _dispose(MethodCall call) {
final int id = call.arguments as int;
if (!_views.containsKey(id))
throw PlatformException(
code: 'error',
message: 'Trying to dispose a platform view with unknown id: $id',
);
_views.remove(id);
return Future<dynamic>.sync(() => null);
}
Future<dynamic> _resize(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments as Map<dynamic, dynamic>;
final int id = args['id'] as int;
final double width = args['width'] as double;
final double height = args['height'] as double;
if (!_views.containsKey(id))
throw PlatformException(
code: 'error',
message: 'Trying to resize a platform view with unknown id: $id',
);
if (resizeCompleter != null) {
await resizeCompleter.future;
}
_views[id] = _views[id].copyWith(size: Size(width, height));
return Future<dynamic>.sync(() => null);
}
Future<dynamic> _touch(MethodCall call) {
final List<dynamic> args = call.arguments as List<dynamic>;
final int id = args[0] as int;
final int action = args[3] as int;
final List<List<dynamic>> pointerProperties = (args[5] as List<dynamic>).cast<List<dynamic>>();
final List<List<dynamic>> pointerCoords = (args[6] as List<dynamic>).cast<List<dynamic>>();
final List<Offset> pointerOffsets = <Offset> [];
final List<int> pointerIds = <int> [];
for (int i = 0; i < pointerCoords.length; i++) {
pointerIds.add(pointerProperties[i][0] as int);
final double x = pointerCoords[i][7] as double;
final double y = pointerCoords[i][8] as double;
pointerOffsets.add(Offset(x, y));
}
if (!motionEvents.containsKey(id))
motionEvents[id] = <FakeAndroidMotionEvent> [];
motionEvents[id].add(FakeAndroidMotionEvent(action, pointerIds, pointerOffsets));
return Future<dynamic>.sync(() => null);
}
Future<dynamic> _setDirection(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments as Map<dynamic, dynamic>;
final int id = args['id'] as int;
final int layoutDirection = args['direction'] as int;
if (!_views.containsKey(id))
throw PlatformException(
code: 'error',
message: 'Trying to resize a platform view with unknown id: $id',
);
_views[id] = _views[id].copyWith(layoutDirection: layoutDirection);
return Future<dynamic>.sync(() => null);
}
Future<dynamic> _clearFocus(MethodCall call) {
final int id = call.arguments as int;
if (!_views.containsKey(id))
throw PlatformException(
code: 'error',
message: 'Trying to clear the focus on a platform view with unknown id: $id',
);
lastClearedFocusViewId = id;
return Future<dynamic>.sync(() => null);
}
}
class FakeIosPlatformViewsController {
FakeIosPlatformViewsController() {
SystemChannels.platform_views.setMockMethodCallHandler(_onMethodCall);
}
Iterable<FakeUiKitView> get views => _views.values;
final Map<int, FakeUiKitView> _views = <int, FakeUiKitView>{};
final Set<String> _registeredViewTypes = <String>{};
// When this completer is non null, the 'create' method channel call will be
// delayed until it completes.
Completer<void> creationDelay;
// Maps a view id to the number of gestures it accepted so far.
final Map<int, int> gesturesAccepted = <int, int>{};
// Maps a view id to the number of gestures it rejected so far.
final Map<int, int> gesturesRejected = <int, int>{};
void registerViewType(String viewType) {
_registeredViewTypes.add(viewType);
}
Future<dynamic> _onMethodCall(MethodCall call) {
switch(call.method) {
case 'create':
return _create(call);
case 'dispose':
return _dispose(call);
case 'acceptGesture':
return _acceptGesture(call);
case 'rejectGesture':
return _rejectGesture(call);
}
return Future<dynamic>.sync(() => null);
}
Future<dynamic> _create(MethodCall call) async {
if (creationDelay != null)
await creationDelay.future;
final Map<dynamic, dynamic> args = call.arguments as Map<dynamic, dynamic>;
final int id = args['id'] as int;
final String viewType = args['viewType'] as String;
final Uint8List creationParams = args['params'] as Uint8List;
if (_views.containsKey(id)) {
throw PlatformException(
code: 'error',
message: 'Trying to create an already created platform view, view id: $id',
);
}
if (!_registeredViewTypes.contains(viewType)) {
throw PlatformException(
code: 'error',
message: 'Trying to create a platform view of unregistered type: $viewType',
);
}
_views[id] = FakeUiKitView(id, viewType, creationParams);
gesturesAccepted[id] = 0;
gesturesRejected[id] = 0;
return Future<int>.sync(() => null);
}
Future<dynamic> _acceptGesture(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments as Map<dynamic, dynamic>;
final int id = args['id'] as int;
gesturesAccepted[id] += 1;
return Future<int>.sync(() => null);
}
Future<dynamic> _rejectGesture(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments as Map<dynamic, dynamic>;
final int id = args['id'] as int;
gesturesRejected[id] += 1;
return Future<int>.sync(() => null);
}
Future<dynamic> _dispose(MethodCall call) {
final int id = call.arguments as int;
if (!_views.containsKey(id)) {
throw PlatformException(
code: 'error',
message: 'Trying to dispose a platform view with unknown id: $id',
);
}
_views.remove(id);
return Future<dynamic>.sync(() => null);
}
}
class FakeHtmlPlatformViewsController {
FakeHtmlPlatformViewsController() {
SystemChannels.platform_views.setMockMethodCallHandler(_onMethodCall);
}
Iterable<FakeHtmlPlatformView> get views => _views.values;
final Map<int, FakeHtmlPlatformView> _views = <int, FakeHtmlPlatformView>{};
final Set<String> _registeredViewTypes = <String>{};
Completer<void> resizeCompleter;
Completer<void> createCompleter;
void registerViewType(String viewType) {
_registeredViewTypes.add(viewType);
}
Future<dynamic> _onMethodCall(MethodCall call) {
switch(call.method) {
case 'create':
return _create(call);
case 'dispose':
return _dispose(call);
}
return Future<dynamic>.sync(() => null);
}
Future<dynamic> _create(MethodCall call) async {
final Map<dynamic, dynamic> args = call.arguments as Map<dynamic, dynamic>;
final int id = args['id'] as int;
final String viewType = args['viewType'] as String;
if (_views.containsKey(id))
throw PlatformException(
code: 'error',
message: 'Trying to create an already created platform view, view id: $id',
);
if (!_registeredViewTypes.contains(viewType))
throw PlatformException(
code: 'error',
message: 'Trying to create a platform view of unregistered type: $viewType',
);
if (createCompleter != null) {
await createCompleter.future;
}
_views[id] = FakeHtmlPlatformView(id, viewType);
return Future<int>.sync(() => null);
}
Future<dynamic> _dispose(MethodCall call) {
final int id = call.arguments as int;
if (!_views.containsKey(id))
throw PlatformException(
code: 'error',
message: 'Trying to dispose a platform view with unknown id: $id',
);
_views.remove(id);
return Future<dynamic>.sync(() => null);
}
}
@immutable
class FakeAndroidPlatformView {
const FakeAndroidPlatformView(this.id, this.type, this.size, this.layoutDirection, [this.creationParams]);
final int id;
final String type;
final Uint8List creationParams;
final Size size;
final int layoutDirection;
FakeAndroidPlatformView copyWith({Size size, int layoutDirection}) => FakeAndroidPlatformView(
id,
type,
size ?? this.size,
layoutDirection ?? this.layoutDirection,
creationParams,
);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is FakeAndroidPlatformView
&& other.id == id
&& other.type == type
&& listEquals<int>(other.creationParams, creationParams)
&& other.size == size
&& other.layoutDirection == layoutDirection;
}
@override
int get hashCode => hashValues(id, type, hashList(creationParams), size, layoutDirection);
@override
String toString() {
return 'FakeAndroidPlatformView(id: $id, type: $type, size: $size, layoutDirection: $layoutDirection, creationParams: $creationParams)';
}
}
@immutable
class FakeAndroidMotionEvent {
const FakeAndroidMotionEvent(this.action, this.pointerIds, this.pointers);
final int action;
final List<Offset> pointers;
final List<int> pointerIds;
@override
bool operator ==(Object other) {
return other is FakeAndroidMotionEvent
&& listEquals<int>(other.pointerIds, pointerIds)
&& other.action == action
&& listEquals<Offset>(other.pointers, pointers);
}
@override
int get hashCode => hashValues(action, hashList(pointers), hashList(pointerIds));
@override
String toString() {
return 'FakeAndroidMotionEvent(action: $action, pointerIds: $pointerIds, pointers: $pointers)';
}
}
@immutable
class FakeUiKitView {
const FakeUiKitView(this.id, this.type, [this.creationParams]);
final int id;
final String type;
final Uint8List creationParams;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is FakeUiKitView
&& other.id == id
&& other.type == type
&& other.creationParams == creationParams;
}
@override
int get hashCode => hashValues(id, type);
@override
String toString() {
return 'FakeUiKitView(id: $id, type: $type, creationParams: $creationParams)';
}
}
@immutable
class FakeHtmlPlatformView {
const FakeHtmlPlatformView(this.id, this.type);
final int id;
final String type;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is FakeHtmlPlatformView
&& other.id == id
&& other.type == type;
}
@override
int get hashCode => hashValues(id, type);
@override
String toString() {
return 'FakeHtmlPlatformView(id: $id, type: $type)';
}
}