Flutter Engine
The Flutter Engine
PlatformViewsChannel.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package io.flutter.embedding.engine.systemchannels;
6
7import androidx.annotation.NonNull;
8import androidx.annotation.Nullable;
9import io.flutter.Log;
10import io.flutter.embedding.engine.dart.DartExecutor;
11import io.flutter.plugin.common.MethodCall;
12import io.flutter.plugin.common.MethodChannel;
13import io.flutter.plugin.common.StandardMethodCodec;
14import java.nio.ByteBuffer;
15import java.util.HashMap;
16import java.util.List;
17import java.util.Map;
18
19/**
20 * System channel that sends 2-way communication between Flutter and Android to facilitate embedding
21 * of Android Views within a Flutter application.
22 *
23 * <p>Register a {@link PlatformViewsHandler} to implement the Android side of this channel.
24 */
26 private static final String TAG = "PlatformViewsChannel";
27
28 private final MethodChannel channel;
29 private PlatformViewsHandler handler;
30
31 public void invokeViewFocused(int viewId) {
32 if (channel == null) {
33 return;
34 }
35 channel.invokeMethod("viewFocused", viewId);
36 }
37
38 private static String detailedExceptionString(Exception exception) {
39 return Log.getStackTraceString(exception);
40 }
41
42 private final MethodChannel.MethodCallHandler parsingHandler =
43 new MethodChannel.MethodCallHandler() {
44 @Override
45 public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
46 // If there is no handler to respond to this message then we don't need to
47 // parse it. Return.
48 if (handler == null) {
49 return;
50 }
51
52 Log.v(TAG, "Received '" + call.method + "' message.");
53 switch (call.method) {
54 case "create":
56 break;
57 case "dispose":
58 dispose(call, result);
59 break;
60 case "resize":
61 resize(call, result);
62 break;
63 case "offset":
65 break;
66 case "touch":
67 touch(call, result);
68 break;
69 case "setDirection":
70 setDirection(call, result);
71 break;
72 case "clearFocus":
73 clearFocus(call, result);
74 break;
75 case "synchronizeToNativeViewHierarchy":
76 synchronizeToNativeViewHierarchy(call, result);
77 break;
78 default:
79 result.notImplemented();
80 }
81 }
82
83 private void create(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
84 final Map<String, Object> createArgs = call.arguments();
85 // TODO(egarciad): Remove the "hybrid" case.
86 final boolean usesPlatformViewLayer =
87 createArgs.containsKey("hybrid") && (boolean) createArgs.get("hybrid");
88 final ByteBuffer additionalParams =
89 createArgs.containsKey("params")
90 ? ByteBuffer.wrap((byte[]) createArgs.get("params"))
91 : null;
92 try {
93 if (usesPlatformViewLayer) {
94 final PlatformViewCreationRequest request =
95 new PlatformViewCreationRequest(
96 (int) createArgs.get("id"),
97 (String) createArgs.get("viewType"),
98 0,
99 0,
100 0,
101 0,
102 (int) createArgs.get("direction"),
103 PlatformViewCreationRequest.RequestedDisplayMode.HYBRID_ONLY,
104 additionalParams);
105 handler.createForPlatformViewLayer(request);
106 result.success(null);
107 } else {
108 final boolean hybridFallback =
109 createArgs.containsKey("hybridFallback")
110 && (boolean) createArgs.get("hybridFallback");
111 final PlatformViewCreationRequest.RequestedDisplayMode displayMode =
112 hybridFallback
113 ? PlatformViewCreationRequest.RequestedDisplayMode
114 .TEXTURE_WITH_HYBRID_FALLBACK
115 : PlatformViewCreationRequest.RequestedDisplayMode
116 .TEXTURE_WITH_VIRTUAL_FALLBACK;
117 final PlatformViewCreationRequest request =
118 new PlatformViewCreationRequest(
119 (int) createArgs.get("id"),
120 (String) createArgs.get("viewType"),
121 createArgs.containsKey("top") ? (double) createArgs.get("top") : 0.0,
122 createArgs.containsKey("left") ? (double) createArgs.get("left") : 0.0,
123 (double) createArgs.get("width"),
124 (double) createArgs.get("height"),
125 (int) createArgs.get("direction"),
126 displayMode,
127 additionalParams);
128 long textureId = handler.createForTextureLayer(request);
129 if (textureId == PlatformViewsHandler.NON_TEXTURE_FALLBACK) {
130 if (!hybridFallback) {
131 throw new AssertionError(
132 "Platform view attempted to fall back to hybrid mode when not requested.");
133 }
134 // A fallback to hybrid mode is indicated with a null texture ID.
135 result.success(null);
136 } else {
137 result.success(textureId);
138 }
139 }
140 } catch (IllegalStateException exception) {
141 result.error("error", detailedExceptionString(exception), null);
142 }
143 }
144
145 private void dispose(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
146 Map<String, Object> disposeArgs = call.arguments();
147 int viewId = (int) disposeArgs.get("id");
148
149 try {
150 handler.dispose(viewId);
151 result.success(null);
152 } catch (IllegalStateException exception) {
153 result.error("error", detailedExceptionString(exception), null);
154 }
155 }
156
157 private void resize(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
158 Map<String, Object> resizeArgs = call.arguments();
159 PlatformViewResizeRequest resizeRequest =
160 new PlatformViewResizeRequest(
161 (int) resizeArgs.get("id"),
162 (double) resizeArgs.get("width"),
163 (double) resizeArgs.get("height"));
164 try {
165 handler.resize(
166 resizeRequest,
167 (PlatformViewBufferSize bufferSize) -> {
168 if (bufferSize == null) {
169 result.error("error", "Failed to resize the platform view", null);
170 } else {
171 final Map<String, Object> response = new HashMap<>();
172 response.put("width", (double) bufferSize.width);
173 response.put("height", (double) bufferSize.height);
174 result.success(response);
175 }
176 });
177 } catch (IllegalStateException exception) {
178 result.error("error", detailedExceptionString(exception), null);
179 }
180 }
181
182 private void offset(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
183 Map<String, Object> offsetArgs = call.arguments();
184 try {
185 handler.offset(
186 (int) offsetArgs.get("id"),
187 (double) offsetArgs.get("top"),
188 (double) offsetArgs.get("left"));
189 result.success(null);
190 } catch (IllegalStateException exception) {
191 result.error("error", detailedExceptionString(exception), null);
192 }
193 }
194
195 private void touch(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
196 List<Object> args = call.arguments();
197 PlatformViewTouch touch =
198 new PlatformViewTouch(
199 (int) args.get(0),
200 (Number) args.get(1),
201 (Number) args.get(2),
202 (int) args.get(3),
203 (int) args.get(4),
204 args.get(5),
205 args.get(6),
206 (int) args.get(7),
207 (int) args.get(8),
208 (float) (double) args.get(9),
209 (float) (double) args.get(10),
210 (int) args.get(11),
211 (int) args.get(12),
212 (int) args.get(13),
213 (int) args.get(14),
214 ((Number) args.get(15)).longValue());
215
216 try {
217 handler.onTouch(touch);
218 result.success(null);
219 } catch (IllegalStateException exception) {
220 result.error("error", detailedExceptionString(exception), null);
221 }
222 }
223
224 private void setDirection(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
225 Map<String, Object> setDirectionArgs = call.arguments();
226 int newDirectionViewId = (int) setDirectionArgs.get("id");
227 int direction = (int) setDirectionArgs.get("direction");
228
229 try {
230 handler.setDirection(newDirectionViewId, direction);
231 result.success(null);
232 } catch (IllegalStateException exception) {
233 result.error("error", detailedExceptionString(exception), null);
234 }
235 }
236
237 private void clearFocus(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
238 int viewId = call.arguments();
239 try {
240 handler.clearFocus(viewId);
241 result.success(null);
242 } catch (IllegalStateException exception) {
243 result.error("error", detailedExceptionString(exception), null);
244 }
245 }
246
247 private void synchronizeToNativeViewHierarchy(
248 @NonNull MethodCall call, @NonNull MethodChannel.Result result) {
249 boolean yes = call.arguments();
250 try {
252 result.success(null);
253 } catch (IllegalStateException exception) {
254 result.error("error", detailedExceptionString(exception), null);
255 }
256 }
257 };
258
259 /**
260 * Constructs a {@code PlatformViewsChannel} that connects Android to the Dart code running in
261 * {@code dartExecutor}.
262 *
263 * <p>The given {@code dartExecutor} is permitted to be idle or executing code.
264 *
265 * <p>See {@link DartExecutor}.
266 */
267 public PlatformViewsChannel(@NonNull DartExecutor dartExecutor) {
268 channel =
269 new MethodChannel(dartExecutor, "flutter/platform_views", StandardMethodCodec.INSTANCE);
270 channel.setMethodCallHandler(parsingHandler);
271 }
272
273 /**
274 * Sets the {@link PlatformViewsHandler} which receives all events and requests that are parsed
275 * from the underlying platform views channel.
276 */
277 public void setPlatformViewsHandler(@Nullable PlatformViewsHandler handler) {
278 this.handler = handler;
279 }
280
281 /**
282 * Handler that receives platform view messages sent from Flutter to Android through a given
283 * {@link PlatformViewsChannel}.
284 *
285 * <p>To register a {@code PlatformViewsHandler} with a {@link PlatformViewsChannel}, see {@link
286 * PlatformViewsChannel#setPlatformViewsHandler(PlatformViewsHandler)}.
287 */
288 public interface PlatformViewsHandler {
289 /*
290 * The ID returned by {@code createForTextureLayer} to indicate that the requested texture mode
291 * was not available and the view creation fell back to {@code PlatformViewLayer} mode.
292 *
293 * This can only be returned if the {@link PlatformViewCreationRequest} sets
294 * {@code TEXTURE_WITH_HYBRID_FALLBACK} as the requested display mode.
295 */
296 static final long NON_TEXTURE_FALLBACK = -2;
297
298 /**
299 * The Flutter application would like to display a new Android {@code View}, i.e., platform
300 * view.
301 *
302 * <p>The Android View is added to the view hierarchy. This view is rendered in the Flutter
303 * framework by a PlatformViewLayer.
304 *
305 * @param request The metadata sent from the framework.
306 */
308
309 /**
310 * The Flutter application would like to display a new Android {@code View}, i.e., platform
311 * view.
312 *
313 * <p>The Android View is added to the view hierarchy. This view is rendered in the Flutter
314 * framework by a TextureLayer.
315 *
316 * @param request The metadata sent from the framework.
317 * @return The texture ID.
318 */
320
321 /** The Flutter application would like to dispose of an existing Android {@code View}. */
322 void dispose(int viewId);
323
324 /**
325 * The Flutter application would like to resize an existing Android {@code View}.
326 *
327 * @param request The request to resize the platform view.
328 * @param onComplete Once the resize is completed, this is the handler to notify the size of the
329 * platform view buffer.
330 */
331 void resize(
332 @NonNull PlatformViewResizeRequest request, @NonNull PlatformViewBufferResized onComplete);
333
334 /**
335 * The Flutter application would like to change the offset of an existing Android {@code View}.
336 */
337 void offset(int viewId, double top, double left);
338
339 /**
340 * The user touched a platform view within Flutter.
341 *
342 * <p>Touch data is reported in {@code touch}.
343 */
344 void onTouch(@NonNull PlatformViewTouch touch);
345
346 /**
347 * The Flutter application would like to change the layout direction of an existing Android
348 * {@code View}, i.e., platform view.
349 */
350 // TODO(mattcarroll): Introduce an annotation for @TextureId
351 void setDirection(int viewId, int direction);
352
353 /** Clears the focus from the platform view with a give id if it is currently focused. */
354 void clearFocus(int viewId);
355
356 /**
357 * Whether the render surface of {@code FlutterView} should be converted to a {@code
358 * FlutterImageView} when a {@code PlatformView} is added.
359 *
360 * <p>This is done to syncronize the rendering of the PlatformView and the FlutterView. Defaults
361 * to true.
362 */
364 }
365
366 /** Request sent from Flutter to create a new platform view. */
367 public static class PlatformViewCreationRequest {
368 /** Platform view display modes that can be requested at creation time. */
370 /** Use Texture Layer if possible, falling back to Virtual Display if not. */
372 /** Use Texture Layer if possible, falling back to Hybrid Composition if not. */
374 /** Use Hybrid Composition in all cases. */
376 }
377
378 /** The ID of the platform view as seen by the Flutter side. */
379 public final int viewId;
380
381 /** The type of Android {@code View} to create for this platform view. */
382 @NonNull public final String viewType;
383
384 /** The density independent width to display the platform view. */
385 public final double logicalWidth;
386
387 /** The density independent height to display the platform view. */
388 public final double logicalHeight;
389
390 /** The density independent top position to display the platform view. */
391 public final double logicalTop;
392
393 /** The density independent left position to display the platform view. */
394 public final double logicalLeft;
395
396 /**
397 * The layout direction of the new platform view.
398 *
399 * <p>See {@link android.view.View#LAYOUT_DIRECTION_LTR} and {@link
400 * android.view.View#LAYOUT_DIRECTION_RTL}
401 */
402 public final int direction;
403
405
406 /** Custom parameters that are unique to the desired platform view. */
407 @Nullable public final ByteBuffer params;
408
409 /** Creates a request to construct a platform view. */
411 int viewId,
412 @NonNull String viewType,
413 double logicalTop,
414 double logicalLeft,
415 double logicalWidth,
416 double logicalHeight,
417 int direction,
418 @Nullable ByteBuffer params) {
419 this(
420 viewId,
421 viewType,
426 direction,
428 params);
429 }
430
431 /** Creates a request to construct a platform view with the given display mode. */
433 int viewId,
434 @NonNull String viewType,
435 double logicalTop,
436 double logicalLeft,
437 double logicalWidth,
438 double logicalHeight,
439 int direction,
441 @Nullable ByteBuffer params) {
442 this.viewId = viewId;
443 this.viewType = viewType;
444 this.logicalTop = logicalTop;
445 this.logicalLeft = logicalLeft;
446 this.logicalWidth = logicalWidth;
447 this.logicalHeight = logicalHeight;
448 this.direction = direction;
449 this.displayMode = displayMode;
450 this.params = params;
451 }
452 }
453
454 /** Request sent from Flutter to resize a platform view. */
455 public static class PlatformViewResizeRequest {
456 /** The ID of the platform view as seen by the Flutter side. */
457 public final int viewId;
458
459 /** The new density independent width to display the platform view. */
460 public final double newLogicalWidth;
461
462 /** The new density independent height to display the platform view. */
463 public final double newLogicalHeight;
464
466 this.viewId = viewId;
467 this.newLogicalWidth = newLogicalWidth;
468 this.newLogicalHeight = newLogicalHeight;
469 }
470 }
471
472 /** The platform view buffer size. */
473 public static class PlatformViewBufferSize {
474 /** The width of the screen buffer. */
475 public final int width;
476
477 /** The height of the screen buffer. */
478 public final int height;
479
481 this.width = width;
482 this.height = height;
483 }
484 }
485
486 /** Allows to notify when a platform view buffer has been resized. */
487 public interface PlatformViewBufferResized {
488 void run(@Nullable PlatformViewBufferSize bufferSize);
489 }
490
491 /** The state of a touch event in Flutter within a platform view. */
492 public static class PlatformViewTouch {
493 /** The ID of the platform view as seen by the Flutter side. */
494 public final int viewId;
495
496 /** The amount of time that the touch has been pressed. */
497 @NonNull public final Number downTime;
498 /** TODO(mattcarroll): javadoc */
499 @NonNull public final Number eventTime;
500
501 public final int action;
502 /** The number of pointers (e.g, fingers) involved in the touch event. */
503 public final int pointerCount;
504 /**
505 * Properties for each pointer, encoded in a raw format. Expected to be formatted as a
506 * List[List[Integer]], where each inner list has two items: - An id, at index 0, corresponding
507 * to {@link android.view.MotionEvent.PointerProperties#id} - A tool type, at index 1,
508 * corresponding to {@link android.view.MotionEvent.PointerProperties#toolType}.
509 */
510 @NonNull public final Object rawPointerPropertiesList;
511 /** Coordinates for each pointer, encoded in a raw format. */
512 @NonNull public final Object rawPointerCoords;
513 /** TODO(mattcarroll): javadoc */
514 public final int metaState;
515 /** TODO(mattcarroll): javadoc */
516 public final int buttonState;
517 /** Coordinate precision along the x-axis. */
518 public final float xPrecision;
519 /** Coordinate precision along the y-axis. */
520 public final float yPrecision;
521 /** TODO(mattcarroll): javadoc */
522 public final int deviceId;
523 /** TODO(mattcarroll): javadoc */
524 public final int edgeFlags;
525 /** TODO(mattcarroll): javadoc */
526 public final int source;
527 /** TODO(mattcarroll): javadoc */
528 public final int flags;
529 /** TODO(iskakaushik): javadoc */
530 public final long motionEventId;
531
533 int viewId,
534 @NonNull Number downTime,
535 @NonNull Number eventTime,
536 int action,
537 int pointerCount,
538 @NonNull Object rawPointerPropertiesList,
539 @NonNull Object rawPointerCoords,
540 int metaState,
541 int buttonState,
542 float xPrecision,
543 float yPrecision,
544 int deviceId,
545 int edgeFlags,
546 int source,
547 int flags,
548 long motionEventId) {
549 this.viewId = viewId;
550 this.downTime = downTime;
551 this.eventTime = eventTime;
552 this.action = action;
553 this.pointerCount = pointerCount;
554 this.rawPointerPropertiesList = rawPointerPropertiesList;
555 this.rawPointerCoords = rawPointerCoords;
556 this.metaState = metaState;
557 this.buttonState = buttonState;
558 this.xPrecision = xPrecision;
559 this.yPrecision = yPrecision;
560 this.deviceId = deviceId;
561 this.edgeFlags = edgeFlags;
562 this.source = source;
563 this.flags = flags;
564 this.motionEventId = motionEventId;
565 }
566 }
567}
static String getStackTraceString(@Nullable Throwable tr)
Definition: Log.java:101
static void v(@NonNull String tag, @NonNull String message)
Definition: Log.java:40
PlatformViewCreationRequest(int viewId, @NonNull String viewType, double logicalTop, double logicalLeft, double logicalWidth, double logicalHeight, int direction, RequestedDisplayMode displayMode, @Nullable ByteBuffer params)
PlatformViewCreationRequest(int viewId, @NonNull String viewType, double logicalTop, double logicalLeft, double logicalWidth, double logicalHeight, int direction, @Nullable ByteBuffer params)
PlatformViewResizeRequest(int viewId, double newLogicalWidth, double newLogicalHeight)
PlatformViewTouch(int viewId, @NonNull Number downTime, @NonNull Number eventTime, int action, int pointerCount, @NonNull Object rawPointerPropertiesList, @NonNull Object rawPointerCoords, int metaState, int buttonState, float xPrecision, float yPrecision, int deviceId, int edgeFlags, int source, int flags, long motionEventId)
void setPlatformViewsHandler(@Nullable PlatformViewsHandler handler)
void setMethodCallHandler(final @Nullable MethodCallHandler handler)
void invokeMethod(@NonNull String method, @Nullable Object arguments)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static ::testing::Matcher< GBytes * > MethodCall(const std::string &name, ::testing::Matcher< FlValue * > args)
GAsyncResult * result
void resize( @NonNull PlatformViewResizeRequest request, @NonNull PlatformViewBufferResized onComplete)
void createForPlatformViewLayer(@NonNull PlatformViewCreationRequest request)
long createForTextureLayer(@NonNull PlatformViewCreationRequest request)
def call(args)
Definition: dom.py:159
SeparatedVector2 offset
int_closure create