736 lines
28 KiB
Dart
736 lines
28 KiB
Dart
// 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 'dart:ui';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
|
|
import 'message_codec.dart';
|
|
import 'system_channels.dart';
|
|
|
|
/// The [PlatformViewsRegistry] responsible for generating unique identifiers for platform views.
|
|
final PlatformViewsRegistry platformViewsRegistry = PlatformViewsRegistry._instance();
|
|
|
|
/// A registry responsible for generating unique identifier for platform views.
|
|
///
|
|
/// A Flutter application has a single [PlatformViewsRegistry] which can be accesses
|
|
/// through the [platformViewsRegistry] getter.
|
|
class PlatformViewsRegistry {
|
|
PlatformViewsRegistry._instance();
|
|
|
|
// Always non-negative. The id value -1 is used in the accessibility bridge
|
|
// to indicate the absence of a platform view.
|
|
int _nextPlatformViewId = 0;
|
|
|
|
/// Allocates a unique identifier for a platform view.
|
|
///
|
|
/// A platform view identifier can refer to a platform view that was never created,
|
|
/// a platform view that was disposed, or a platform view that is alive.
|
|
///
|
|
/// Typically a platform view identifier is passed to a [PlatformView] widget
|
|
/// which creates the platform view and manages its lifecycle.
|
|
int getNextPlatformViewId() => _nextPlatformViewId++;
|
|
}
|
|
|
|
/// Callback signature for when a platform view was created.
|
|
///
|
|
/// `id` is the platform view's unique identifier.
|
|
typedef PlatformViewCreatedCallback = void Function(int id);
|
|
|
|
/// Provides access to the platform views service.
|
|
///
|
|
/// This service allows creating and controlling platform-specific views.
|
|
class PlatformViewsService {
|
|
PlatformViewsService._() {
|
|
SystemChannels.platform_views.setMethodCallHandler(_onMethodCall);
|
|
}
|
|
|
|
static PlatformViewsService _serviceInstance;
|
|
|
|
static PlatformViewsService get _instance {
|
|
_serviceInstance ??= PlatformViewsService._();
|
|
return _serviceInstance;
|
|
}
|
|
|
|
Future<void> _onMethodCall(MethodCall call) {
|
|
switch(call.method) {
|
|
case 'viewFocused':
|
|
final int id = call.arguments as int;
|
|
if (_focusCallbacks.containsKey(id)) {
|
|
_focusCallbacks[id]();
|
|
}
|
|
break;
|
|
default:
|
|
throw UnimplementedError("${call.method} was invoked but isn't implemented by PlatformViewsService");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Maps platform view IDs to focus callbacks.
|
|
///
|
|
/// The callbacks are invoked when the platform view asks to be focused.
|
|
final Map<int, VoidCallback> _focusCallbacks = <int, VoidCallback>{};
|
|
|
|
|
|
/// Creates a controller for a new Android view.
|
|
///
|
|
/// `id` is an unused unique identifier generated with [platformViewsRegistry].
|
|
///
|
|
/// `viewType` is the identifier of the Android view type to be created, a
|
|
/// factory for this view type must have been registered on the platform side.
|
|
/// Platform view factories are typically registered by plugin code.
|
|
/// Plugins can register a platform view factory with
|
|
/// [PlatformViewRegistry#registerViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewRegistry.html#registerViewFactory-java.lang.String-io.flutter.plugin.platform.PlatformViewFactory-).
|
|
///
|
|
/// `creationParams` will be passed as the args argument of [PlatformViewFactory#create](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#create-android.content.Context-int-java.lang.Object-)
|
|
///
|
|
/// `creationParamsCodec` is the codec used to encode `creationParams` before sending it to the
|
|
/// platform side. It should match the codec passed to the constructor of [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#PlatformViewFactory-io.flutter.plugin.common.MessageCodec-).
|
|
/// This is typically one of: [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec].
|
|
///
|
|
/// `onFocus` is a callback that will be invoked when the Android View asks to get the
|
|
/// input focus.
|
|
///
|
|
/// The Android view will only be created after [AndroidViewController.setSize] is called for the
|
|
/// first time.
|
|
///
|
|
/// The `id, `viewType, and `layoutDirection` parameters must not be null.
|
|
/// If `creationParams` is non null then `cretaionParamsCodec` must not be null.
|
|
static AndroidViewController initAndroidView({
|
|
@required int id,
|
|
@required String viewType,
|
|
@required TextDirection layoutDirection,
|
|
dynamic creationParams,
|
|
MessageCodec<dynamic> creationParamsCodec,
|
|
VoidCallback onFocus,
|
|
}) {
|
|
assert(id != null);
|
|
assert(viewType != null);
|
|
assert(layoutDirection != null);
|
|
assert(creationParams == null || creationParamsCodec != null);
|
|
final AndroidViewController controller = AndroidViewController._(
|
|
id,
|
|
viewType,
|
|
creationParams,
|
|
creationParamsCodec,
|
|
layoutDirection,
|
|
);
|
|
_instance._focusCallbacks[id] = onFocus ?? () {};
|
|
return controller;
|
|
}
|
|
|
|
// TODO(amirh): reference the iOS plugin API for registering a UIView factory once it lands.
|
|
/// This is work in progress, not yet ready to be used, and requires a custom engine build. Creates a controller for a new iOS UIView.
|
|
///
|
|
/// `id` is an unused unique identifier generated with [platformViewsRegistry].
|
|
///
|
|
/// `viewType` is the identifier of the iOS view type to be created, a
|
|
/// factory for this view type must have been registered on the platform side.
|
|
/// Platform view factories are typically registered by plugin code.
|
|
///
|
|
/// The `id, `viewType, and `layoutDirection` parameters must not be null.
|
|
/// If `creationParams` is non null then `cretaionParamsCodec` must not be null.
|
|
static Future<UiKitViewController> initUiKitView({
|
|
@required int id,
|
|
@required String viewType,
|
|
@required TextDirection layoutDirection,
|
|
dynamic creationParams,
|
|
MessageCodec<dynamic> creationParamsCodec,
|
|
}) async {
|
|
assert(id != null);
|
|
assert(viewType != null);
|
|
assert(layoutDirection != null);
|
|
assert(creationParams == null || creationParamsCodec != null);
|
|
|
|
// TODO(amirh): pass layoutDirection once the system channel supports it.
|
|
final Map<String, dynamic> args = <String, dynamic>{
|
|
'id': id,
|
|
'viewType': viewType,
|
|
};
|
|
if (creationParams != null) {
|
|
final ByteData paramsByteData = creationParamsCodec.encodeMessage(creationParams);
|
|
args['params'] = Uint8List.view(
|
|
paramsByteData.buffer,
|
|
0,
|
|
paramsByteData.lengthInBytes,
|
|
);
|
|
}
|
|
await SystemChannels.platform_views.invokeMethod<void>('create', args);
|
|
return UiKitViewController._(id, layoutDirection);
|
|
}
|
|
}
|
|
|
|
/// Properties of an Android pointer.
|
|
///
|
|
/// A Dart version of Android's [MotionEvent.PointerProperties](https://developer.android.com/reference/android/view/MotionEvent.PointerProperties).
|
|
class AndroidPointerProperties {
|
|
/// Creates an AndroidPointerProperties.
|
|
///
|
|
/// All parameters must not be null.
|
|
const AndroidPointerProperties({
|
|
@required this.id,
|
|
@required this.toolType,
|
|
}) : assert(id != null),
|
|
assert(toolType != null);
|
|
|
|
/// See Android's [MotionEvent.PointerProperties#id](https://developer.android.com/reference/android/view/MotionEvent.PointerProperties.html#id).
|
|
final int id;
|
|
|
|
/// The type of tool used to make contact such as a finger or stylus, if known.
|
|
/// See Android's [MotionEvent.PointerProperties#toolType](https://developer.android.com/reference/android/view/MotionEvent.PointerProperties.html#toolType).
|
|
final int toolType;
|
|
|
|
/// Value for `toolType` when the tool type is unknown.
|
|
static const int kToolTypeUnknown = 0;
|
|
|
|
/// Value for `toolType` when the tool type is a finger.
|
|
static const int kToolTypeFinger = 1;
|
|
|
|
/// Value for `toolType` when the tool type is a stylus.
|
|
static const int kToolTypeStylus = 2;
|
|
|
|
/// Value for `toolType` when the tool type is a mouse.
|
|
static const int kToolTypeMouse = 3;
|
|
|
|
/// Value for `toolType` when the tool type is an eraser.
|
|
static const int kToolTypeEraser = 4;
|
|
|
|
List<int> _asList() => <int>[id, toolType];
|
|
|
|
@override
|
|
String toString() {
|
|
return 'AndroidPointerProperties(id: $id, toolType: $toolType)';
|
|
}
|
|
}
|
|
|
|
/// Position information for an Android pointer.
|
|
///
|
|
/// A Dart version of Android's [MotionEvent.PointerCoords](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords).
|
|
class AndroidPointerCoords {
|
|
/// Creates an AndroidPointerCoords.
|
|
///
|
|
/// All parameters must not be null.
|
|
const AndroidPointerCoords({
|
|
@required this.orientation,
|
|
@required this.pressure,
|
|
@required this.size,
|
|
@required this.toolMajor,
|
|
@required this.toolMinor,
|
|
@required this.touchMajor,
|
|
@required this.touchMinor,
|
|
@required this.x,
|
|
@required this.y,
|
|
}) : assert(orientation != null),
|
|
assert(pressure != null),
|
|
assert(size != null),
|
|
assert(toolMajor != null),
|
|
assert(toolMinor != null),
|
|
assert(touchMajor != null),
|
|
assert(touchMinor != null),
|
|
assert(x != null),
|
|
assert(y != null);
|
|
|
|
/// The orientation of the touch area and tool area in radians clockwise from vertical.
|
|
///
|
|
/// See Android's [MotionEvent.PointerCoords#orientation](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#orientation).
|
|
final double orientation;
|
|
|
|
/// A normalized value that describes the pressure applied to the device by a finger or other tool.
|
|
///
|
|
/// See Android's [MotionEvent.PointerCoords#pressure](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#pressure).
|
|
final double pressure;
|
|
|
|
/// A normalized value that describes the approximate size of the pointer touch area in relation to the maximum detectable size of the device.
|
|
///
|
|
/// See Android's [MotionEvent.PointerCoords#size](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#size).
|
|
final double size;
|
|
|
|
/// See Android's [MotionEvent.PointerCoords#toolMajor](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#toolMajor).
|
|
final double toolMajor;
|
|
|
|
/// See Android's [MotionEvent.PointerCoords#toolMinor](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#toolMinor).
|
|
final double toolMinor;
|
|
|
|
/// See Android's [MotionEvent.PointerCoords#touchMajor](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#touchMajor).
|
|
final double touchMajor;
|
|
|
|
/// See Android's [MotionEvent.PointerCoords#touchMinor](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#touchMinor).
|
|
final double touchMinor;
|
|
|
|
/// The X component of the pointer movement.
|
|
///
|
|
/// See Android's [MotionEvent.PointerCoords#x](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#x).
|
|
final double x;
|
|
|
|
/// The Y component of the pointer movement.
|
|
///
|
|
/// See Android's [MotionEvent.PointerCoords#y](https://developer.android.com/reference/android/view/MotionEvent.PointerCoords.html#y).
|
|
final double y;
|
|
|
|
List<double> _asList() {
|
|
return <double>[
|
|
orientation,
|
|
pressure,
|
|
size,
|
|
toolMajor,
|
|
toolMinor,
|
|
touchMajor,
|
|
touchMinor,
|
|
x,
|
|
y,
|
|
];
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'AndroidPointerCoords(orientation: $orientation, pressure: $pressure, size: $size, toolMajor: $toolMajor, toolMinor: $toolMinor, touchMajor: $touchMajor, touchMinor: $touchMinor, x: $x, y: $y)';
|
|
}
|
|
}
|
|
|
|
/// A Dart version of Android's [MotionEvent](https://developer.android.com/reference/android/view/MotionEvent).
|
|
class AndroidMotionEvent {
|
|
/// Creates an AndroidMotionEvent.
|
|
///
|
|
/// All parameters must not be null.
|
|
AndroidMotionEvent({
|
|
@required this.downTime,
|
|
@required this.eventTime,
|
|
@required this.action,
|
|
@required this.pointerCount,
|
|
@required this.pointerProperties,
|
|
@required this.pointerCoords,
|
|
@required this.metaState,
|
|
@required this.buttonState,
|
|
@required this.xPrecision,
|
|
@required this.yPrecision,
|
|
@required this.deviceId,
|
|
@required this.edgeFlags,
|
|
@required this.source,
|
|
@required this.flags,
|
|
}) : assert(downTime != null),
|
|
assert(eventTime != null),
|
|
assert(action != null),
|
|
assert(pointerCount != null),
|
|
assert(pointerProperties != null),
|
|
assert(pointerCoords != null),
|
|
assert(metaState != null),
|
|
assert(buttonState != null),
|
|
assert(xPrecision != null),
|
|
assert(yPrecision != null),
|
|
assert(deviceId != null),
|
|
assert(edgeFlags != null),
|
|
assert(source != null),
|
|
assert(flags != null),
|
|
assert(pointerProperties.length == pointerCount),
|
|
assert(pointerCoords.length == pointerCount);
|
|
|
|
/// The time (in ms) when the user originally pressed down to start a stream of position events,
|
|
/// relative to an arbitrary timeline.
|
|
///
|
|
/// See Android's [MotionEvent#getDownTime](https://developer.android.com/reference/android/view/MotionEvent.html#getDownTime()).
|
|
final int downTime;
|
|
|
|
/// The time this event occurred, relative to an arbitrary timeline.
|
|
///
|
|
/// See Android's [MotionEvent#getEventTime](https://developer.android.com/reference/android/view/MotionEvent.html#getEventTime()).
|
|
final int eventTime;
|
|
|
|
/// A value representing the kind of action being performed.
|
|
///
|
|
/// See Android's [MotionEvent#getAction](https://developer.android.com/reference/android/view/MotionEvent.html#getAction()).
|
|
final int action;
|
|
|
|
/// The number of pointers that are part of this event.
|
|
/// This must be equivalent to the length of `pointerProperties` and `pointerCoords`.
|
|
///
|
|
/// See Android's [MotionEvent#getPointerCount](https://developer.android.com/reference/android/view/MotionEvent.html#getPointerCount()).
|
|
final int pointerCount;
|
|
|
|
/// List of [AndroidPointerProperties] for each pointer that is part of this event.
|
|
final List<AndroidPointerProperties> pointerProperties;
|
|
|
|
/// List of [AndroidPointerCoords] for each pointer that is part of this event.
|
|
final List<AndroidPointerCoords> pointerCoords;
|
|
|
|
/// The state of any meta / modifier keys that were in effect when the event was generated.
|
|
///
|
|
/// See Android's [MotionEvent#getMetaState](https://developer.android.com/reference/android/view/MotionEvent.html#getMetaState()).
|
|
final int metaState;
|
|
|
|
/// The state of all buttons that are pressed such as a mouse or stylus button.
|
|
///
|
|
/// See Android's [MotionEvent#getButtonState](https://developer.android.com/reference/android/view/MotionEvent.html#getButtonState()).
|
|
final int buttonState;
|
|
|
|
/// The precision of the X coordinates being reported, in physical pixels.
|
|
///
|
|
/// See Android's [MotionEvent#getXPrecision](https://developer.android.com/reference/android/view/MotionEvent.html#getXPrecision()).
|
|
final double xPrecision;
|
|
|
|
/// The precision of the Y coordinates being reported, in physical pixels.
|
|
///
|
|
/// See Android's [MotionEvent#getYPrecision](https://developer.android.com/reference/android/view/MotionEvent.html#getYPrecision()).
|
|
final double yPrecision;
|
|
|
|
/// See Android's [MotionEvent#getDeviceId](https://developer.android.com/reference/android/view/MotionEvent.html#getDeviceId()).
|
|
final int deviceId;
|
|
|
|
/// A bit field indicating which edges, if any, were touched by this MotionEvent.
|
|
///
|
|
/// See Android's [MotionEvent#getEdgeFlags](https://developer.android.com/reference/android/view/MotionEvent.html#getEdgeFlags()).
|
|
final int edgeFlags;
|
|
|
|
/// The source of this event (e.g a touchpad or stylus).
|
|
///
|
|
/// See Android's [MotionEvent#getSource](https://developer.android.com/reference/android/view/MotionEvent.html#getSource()).
|
|
final int source;
|
|
|
|
/// See Android's [MotionEvent#getFlags](https://developer.android.com/reference/android/view/MotionEvent.html#getFlags()).
|
|
final int flags;
|
|
|
|
List<dynamic> _asList(int viewId) {
|
|
return <dynamic>[
|
|
viewId,
|
|
downTime,
|
|
eventTime,
|
|
action,
|
|
pointerCount,
|
|
pointerProperties.map<List<int>>((AndroidPointerProperties p) => p._asList()).toList(),
|
|
pointerCoords.map<List<double>>((AndroidPointerCoords p) => p._asList()).toList(),
|
|
metaState,
|
|
buttonState,
|
|
xPrecision,
|
|
yPrecision,
|
|
deviceId,
|
|
edgeFlags,
|
|
source,
|
|
flags,
|
|
];
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'AndroidPointerEvent(downTime: $downTime, eventTime: $eventTime, action: $action, pointerCount: $pointerCount, pointerProperties: $pointerProperties, pointerCoords: $pointerCoords, metaState: $metaState, buttonState: $buttonState, xPrecision: $xPrecision, yPrecision: $yPrecision, deviceId: $deviceId, edgeFlags: $edgeFlags, source: $source, flags: $flags)';
|
|
}
|
|
}
|
|
|
|
enum _AndroidViewState {
|
|
waitingForSize,
|
|
creating,
|
|
created,
|
|
disposed,
|
|
}
|
|
|
|
/// Controls an Android view.
|
|
///
|
|
/// Typically created with [PlatformViewsService.initAndroidView].
|
|
class AndroidViewController {
|
|
AndroidViewController._(
|
|
this.id,
|
|
String viewType,
|
|
dynamic creationParams,
|
|
MessageCodec<dynamic> creationParamsCodec,
|
|
TextDirection layoutDirection,
|
|
) : assert(id != null),
|
|
assert(viewType != null),
|
|
assert(layoutDirection != null),
|
|
assert(creationParams == null || creationParamsCodec != null),
|
|
_viewType = viewType,
|
|
_creationParams = creationParams,
|
|
_creationParamsCodec = creationParamsCodec,
|
|
_layoutDirection = layoutDirection,
|
|
_state = _AndroidViewState.waitingForSize;
|
|
|
|
/// Action code for when a primary pointer touched the screen.
|
|
///
|
|
/// Android's [MotionEvent.ACTION_DOWN](https://developer.android.com/reference/android/view/MotionEvent#ACTION_DOWN)
|
|
static const int kActionDown = 0;
|
|
|
|
/// Action code for when a primary pointer stopped touching the screen.
|
|
///
|
|
/// Android's [MotionEvent.ACTION_UP](https://developer.android.com/reference/android/view/MotionEvent#ACTION_UP)
|
|
static const int kActionUp = 1;
|
|
|
|
/// Action code for when the event only includes information about pointer movement.
|
|
///
|
|
/// Android's [MotionEvent.ACTION_MOVE](https://developer.android.com/reference/android/view/MotionEvent#ACTION_MOVE)
|
|
static const int kActionMove = 2;
|
|
|
|
/// Action code for when a motion event has been canceled.
|
|
///
|
|
/// Android's [MotionEvent.ACTION_CANCEL](https://developer.android.com/reference/android/view/MotionEvent#ACTION_CANCEL)
|
|
static const int kActionCancel = 3;
|
|
|
|
/// Action code for when a secondary pointer touched the screen.
|
|
///
|
|
/// Android's [MotionEvent.ACTION_POINTER_DOWN](https://developer.android.com/reference/android/view/MotionEvent#ACTION_POINTER_DOWN)
|
|
static const int kActionPointerDown = 5;
|
|
|
|
/// Action code for when a secondary pointer stopped touching the screen.
|
|
///
|
|
/// Android's [MotionEvent.ACTION_POINTER_UP](https://developer.android.com/reference/android/view/MotionEvent#ACTION_POINTER_UP)
|
|
static const int kActionPointerUp = 6;
|
|
|
|
/// Android's [View.LAYOUT_DIRECTION_LTR](https://developer.android.com/reference/android/view/View.html#LAYOUT_DIRECTION_LTR) value.
|
|
static const int kAndroidLayoutDirectionLtr = 0;
|
|
|
|
/// Android's [View.LAYOUT_DIRECTION_RTL](https://developer.android.com/reference/android/view/View.html#LAYOUT_DIRECTION_RTL) value.
|
|
static const int kAndroidLayoutDirectionRtl = 1;
|
|
|
|
/// The unique identifier of the Android view controlled by this controller.
|
|
final int id;
|
|
|
|
final String _viewType;
|
|
|
|
/// The texture entry id into which the Android view is rendered.
|
|
int _textureId;
|
|
|
|
/// Returns the texture entry id that the Android view is rendering into.
|
|
///
|
|
/// Returns null if the Android view has not been successfully created, or if it has been
|
|
/// disposed.
|
|
int get textureId => _textureId;
|
|
|
|
TextDirection _layoutDirection;
|
|
|
|
_AndroidViewState _state;
|
|
|
|
final dynamic _creationParams;
|
|
|
|
final MessageCodec<dynamic> _creationParamsCodec;
|
|
|
|
final List<PlatformViewCreatedCallback> _platformViewCreatedCallbacks = <PlatformViewCreatedCallback>[];
|
|
|
|
/// Whether the platform view has already been created.
|
|
bool get isCreated => _state == _AndroidViewState.created;
|
|
|
|
/// Adds a callback that will get invoke after the platform view has been
|
|
/// created.
|
|
void addOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
|
|
assert(listener != null);
|
|
assert(_state != _AndroidViewState.disposed);
|
|
_platformViewCreatedCallbacks.add(listener);
|
|
}
|
|
|
|
/// Removes a callback added with [addOnPlatformViewCreatedListener].
|
|
void removeOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
|
|
assert(_state != _AndroidViewState.disposed);
|
|
_platformViewCreatedCallbacks.remove(listener);
|
|
}
|
|
|
|
/// Disposes the Android view.
|
|
///
|
|
/// The [AndroidViewController] object is unusable after calling this.
|
|
/// The identifier of the platform view cannot be reused after the view is
|
|
/// disposed.
|
|
Future<void> dispose() async {
|
|
if (_state == _AndroidViewState.creating || _state == _AndroidViewState.created)
|
|
await SystemChannels.platform_views.invokeMethod<void>('dispose', id);
|
|
_platformViewCreatedCallbacks.clear();
|
|
_state = _AndroidViewState.disposed;
|
|
}
|
|
|
|
/// Sizes the Android View.
|
|
///
|
|
/// `size` is the view's new size in logical pixel, it must not be null and must
|
|
/// be bigger than zero.
|
|
///
|
|
/// The first time a size is set triggers the creation of the Android view.
|
|
Future<void> setSize(Size size) async {
|
|
assert(_state != _AndroidViewState.disposed, 'trying to size a disposed Android View. View id: $id');
|
|
|
|
assert(size != null);
|
|
assert(!size.isEmpty);
|
|
|
|
if (_state == _AndroidViewState.waitingForSize)
|
|
return _create(size);
|
|
|
|
await SystemChannels.platform_views.invokeMethod<void>('resize', <String, dynamic>{
|
|
'id': id,
|
|
'width': size.width,
|
|
'height': size.height,
|
|
});
|
|
}
|
|
|
|
/// Sets the layout direction for the Android view.
|
|
Future<void> setLayoutDirection(TextDirection layoutDirection) async {
|
|
assert(_state != _AndroidViewState.disposed,'trying to set a layout direction for a disposed UIView. View id: $id');
|
|
|
|
if (layoutDirection == _layoutDirection)
|
|
return;
|
|
|
|
assert(layoutDirection != null);
|
|
_layoutDirection = layoutDirection;
|
|
|
|
// If the view was not yet created we just update _layoutDirection and return, as the new
|
|
// direction will be used in _create.
|
|
if (_state == _AndroidViewState.waitingForSize)
|
|
return;
|
|
|
|
await SystemChannels.platform_views.invokeMethod<void>('setDirection', <String, dynamic>{
|
|
'id': id,
|
|
'direction': _getAndroidDirection(layoutDirection),
|
|
});
|
|
}
|
|
|
|
/// Clears the focus from the Android View if it is focused.
|
|
Future<void> clearFocus() {
|
|
if (_state != _AndroidViewState.created) {
|
|
return null;
|
|
}
|
|
return SystemChannels.platform_views.invokeMethod<void>('clearFocus', id);
|
|
}
|
|
|
|
static int _getAndroidDirection(TextDirection direction) {
|
|
assert(direction != null);
|
|
switch (direction) {
|
|
case TextDirection.ltr:
|
|
return kAndroidLayoutDirectionLtr;
|
|
case TextDirection.rtl:
|
|
return kAndroidLayoutDirectionRtl;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Sends an Android [MotionEvent](https://developer.android.com/reference/android/view/MotionEvent)
|
|
/// to the view.
|
|
///
|
|
/// The Android MotionEvent object is created with [MotionEvent.obtain](https://developer.android.com/reference/android/view/MotionEvent.html#obtain(long,%20long,%20int,%20float,%20float,%20float,%20float,%20int,%20float,%20float,%20int,%20int)).
|
|
/// See documentation of [MotionEvent.obtain](https://developer.android.com/reference/android/view/MotionEvent.html#obtain(long,%20long,%20int,%20float,%20float,%20float,%20float,%20int,%20float,%20float,%20int,%20int))
|
|
/// for description of the parameters.
|
|
Future<void> sendMotionEvent(AndroidMotionEvent event) async {
|
|
await SystemChannels.platform_views.invokeMethod<dynamic>(
|
|
'touch',
|
|
event._asList(id),
|
|
);
|
|
}
|
|
|
|
/// Creates a masked Android MotionEvent action value for an indexed pointer.
|
|
static int pointerAction(int pointerId, int action) {
|
|
return ((pointerId << 8) & 0xff00) | (action & 0xff);
|
|
}
|
|
|
|
Future<void> _create(Size size) async {
|
|
final Map<String, dynamic> args = <String, dynamic>{
|
|
'id': id,
|
|
'viewType': _viewType,
|
|
'width': size.width,
|
|
'height': size.height,
|
|
'direction': _getAndroidDirection(_layoutDirection),
|
|
};
|
|
if (_creationParams != null) {
|
|
final ByteData paramsByteData = _creationParamsCodec.encodeMessage(_creationParams);
|
|
args['params'] = Uint8List.view(
|
|
paramsByteData.buffer,
|
|
0,
|
|
paramsByteData.lengthInBytes,
|
|
);
|
|
}
|
|
_textureId = await SystemChannels.platform_views.invokeMethod('create', args);
|
|
_state = _AndroidViewState.created;
|
|
for (final PlatformViewCreatedCallback callback in _platformViewCreatedCallbacks) {
|
|
callback(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Controls an iOS UIView.
|
|
///
|
|
/// Typically created with [PlatformViewsService.initUiKitView].
|
|
class UiKitViewController {
|
|
UiKitViewController._(
|
|
this.id,
|
|
TextDirection layoutDirection,
|
|
) : assert(id != null),
|
|
assert(layoutDirection != null),
|
|
_layoutDirection = layoutDirection;
|
|
|
|
|
|
/// The unique identifier of the iOS view controlled by this controller.
|
|
///
|
|
/// This identifier is typically generated by
|
|
/// [PlatformViewsRegistry.getNextPlatformViewId].
|
|
final int id;
|
|
|
|
bool _debugDisposed = false;
|
|
|
|
TextDirection _layoutDirection;
|
|
|
|
/// Sets the layout direction for the iOS UIView.
|
|
Future<void> setLayoutDirection(TextDirection layoutDirection) async {
|
|
assert(!_debugDisposed, 'trying to set a layout direction for a disposed iOS UIView. View id: $id');
|
|
|
|
if (layoutDirection == _layoutDirection)
|
|
return;
|
|
|
|
assert(layoutDirection != null);
|
|
_layoutDirection = layoutDirection;
|
|
|
|
// TODO(amirh): invoke the iOS platform views channel direction method once available.
|
|
}
|
|
|
|
/// Accept an active gesture.
|
|
///
|
|
/// When a touch sequence is happening on the embedded UIView all touch events are delayed.
|
|
/// Calling this method releases the delayed events to the embedded UIView and makes it consume
|
|
/// any following touch events for the pointers involved in the active gesture.
|
|
Future<void> acceptGesture() {
|
|
final Map<String, dynamic> args = <String, dynamic>{
|
|
'id': id,
|
|
};
|
|
return SystemChannels.platform_views.invokeMethod('acceptGesture', args);
|
|
}
|
|
|
|
/// Rejects an active gesture.
|
|
///
|
|
/// When a touch sequence is happening on the embedded UIView all touch events are delayed.
|
|
/// Calling this method drops the buffered touch events and prevents any future touch events for
|
|
/// the pointers that are part of the active touch sequence from arriving to the embedded view.
|
|
Future<void> rejectGesture() {
|
|
final Map<String, dynamic> args = <String, dynamic>{
|
|
'id': id,
|
|
};
|
|
return SystemChannels.platform_views.invokeMethod('rejectGesture', args);
|
|
}
|
|
|
|
/// Disposes the view.
|
|
///
|
|
/// The [UiKitViewController] object is unusable after calling this.
|
|
/// The `id` of the platform view cannot be reused after the view is
|
|
/// disposed.
|
|
Future<void> dispose() async {
|
|
_debugDisposed = true;
|
|
await SystemChannels.platform_views.invokeMethod<void>('dispose', id);
|
|
}
|
|
}
|
|
|
|
/// An interface for a controlling a single platform view.
|
|
///
|
|
/// Used by [PlatformViewSurface] to interface with the platform view it embeds.
|
|
abstract class PlatformViewController {
|
|
|
|
/// The viewId associated with this controller.
|
|
///
|
|
/// The viewId should always be unique and non-negative. And it must not be null.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [PlatformViewRegistry], which is a helper for managing platform view ids.
|
|
int get viewId;
|
|
|
|
/// Dispatches the `event` to the platform view.
|
|
void dispatchPointerEvent(PointerEvent event);
|
|
|
|
/// Disposes the platform view.
|
|
///
|
|
/// The [PlatformViewController] is unusable after calling dispose.
|
|
void dispose();
|
|
|
|
/// Clears the view's focus on the platform side.
|
|
void clearFocus();
|
|
}
|