• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
5 package io.flutter.embedding.engine.systemchannels;
6 
7 import android.support.annotation.NonNull;
8 import android.support.annotation.Nullable;
9 
10 import java.nio.ByteBuffer;
11 import java.util.List;
12 import java.util.Map;
13 
14 import io.flutter.Log;
15 import io.flutter.embedding.engine.dart.DartExecutor;
16 import io.flutter.plugin.common.MethodCall;
17 import io.flutter.plugin.common.MethodChannel;
18 import io.flutter.plugin.common.StandardMethodCodec;
19 
20 /**
21  * System channel that sends 2-way communication between Flutter and Android to
22  * facilitate embedding of Android Views within a Flutter application.
23  *
24  * Register a {@link PlatformViewsHandler} to implement the Android side of this channel.
25  */
26 public class PlatformViewsChannel {
27   private static final String TAG = "PlatformViewsChannel";
28 
29   private final MethodChannel channel;
30   private PlatformViewsHandler handler;
31 
invokeViewFocused(int viewId)32   public void invokeViewFocused(int viewId) {
33     if (channel == null) {
34       return;
35     }
36     channel.invokeMethod("viewFocused", viewId);
37   }
38 
39   private final MethodChannel.MethodCallHandler parsingHandler = new MethodChannel.MethodCallHandler() {
40     @Override
41     public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
42       // If there is no handler to respond to this message then we don't need to
43       // parse it. Return.
44       if (handler == null) {
45         return;
46       }
47 
48       Log.v(TAG, "Received '" + call.method + "' message.");
49       switch (call.method) {
50         case "create":
51           create(call, result);
52           break;
53         case "dispose":
54           dispose(call, result);
55           break;
56         case "resize":
57           resize(call, result);
58           break;
59         case "touch":
60           touch(call, result);
61           break;
62         case "setDirection":
63           setDirection(call, result);
64           break;
65         case "clearFocus":
66           clearFocus(call, result);
67           break;
68         default:
69           result.notImplemented();
70       }
71     }
72 
73     private void create(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
74       Map<String, Object> createArgs = call.arguments();
75       PlatformViewCreationRequest request = new PlatformViewCreationRequest(
76           (int) createArgs.get("id"),
77           (String) createArgs.get("viewType"),
78           (double) createArgs.get("width"),
79           (double) createArgs.get("height"),
80           (int) createArgs.get("direction"),
81           createArgs.containsKey("params")
82               ? ByteBuffer.wrap((byte[]) createArgs.get("params"))
83               : null
84       );
85 
86       try {
87         long textureId = handler.createPlatformView(request);
88         result.success(textureId);
89       } catch (IllegalStateException exception) {
90         result.error(
91             "error",
92             exception.getMessage(),
93             null
94         );
95       }
96     }
97 
98     private void dispose(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
99       int viewId = call.arguments();
100       try {
101         handler.disposePlatformView(viewId);
102         result.success(null);
103       } catch (IllegalStateException exception) {
104         result.error(
105             "error",
106             exception.getMessage(),
107             null
108         );
109       }
110     }
111 
112     private void resize(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
113       Map<String, Object> resizeArgs = call.arguments();
114       PlatformViewResizeRequest resizeRequest = new PlatformViewResizeRequest(
115           (int) resizeArgs.get("id"),
116           (double) resizeArgs.get("width"),
117           (double) resizeArgs.get("height")
118       );
119       try {
120         handler.resizePlatformView(
121             resizeRequest,
122             new Runnable() {
123               @Override
124               public void run() {
125                 result.success(null);
126               }
127             }
128         );
129       } catch (IllegalStateException exception) {
130         result.error(
131             "error",
132             exception.getMessage(),
133             null
134         );
135       }
136     }
137 
138     private void touch(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
139       List<Object> args = call.arguments();
140       PlatformViewTouch touch = new PlatformViewTouch(
141           (int) args.get(0),
142           (Number) args.get(1),
143           (Number) args.get(2),
144           (int) args.get(3),
145           (int) args.get(4),
146           args.get(5),
147           args.get(6),
148           (int) args.get(7),
149           (int) args.get(8),
150           (float) (double) args.get(9),
151           (float) (double) args.get(10),
152           (int) args.get(11),
153           (int) args.get(12),
154           (int) args.get(13),
155           (int) args.get(14)
156       );
157 
158       try {
159         handler.onTouch(touch);
160         result.success(null);
161       } catch (IllegalStateException exception) {
162         result.error(
163             "error",
164             exception.getMessage(),
165             null
166         );
167       }
168     }
169 
170     private void setDirection(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
171       Map<String, Object> setDirectionArgs = call.arguments();
172       int newDirectionViewId = (int) setDirectionArgs.get("id");
173       int direction = (int) setDirectionArgs.get("direction");
174 
175       try {
176         handler.setDirection(
177             newDirectionViewId,
178             direction
179         );
180         result.success(null);
181       } catch (IllegalStateException exception) {
182         result.error(
183             "error",
184             exception.getMessage(),
185             null
186         );
187       }
188     }
189 
190     private void clearFocus(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
191       int viewId = call.arguments();
192       try {
193         handler.clearFocus(viewId);
194         result.success(null);
195       } catch (IllegalStateException exception) {
196         result.error(
197                 "error",
198                 exception.getMessage(),
199                 null
200         );
201       }
202     }
203   };
204 
205   /**
206    * Constructs a {@code PlatformViewsChannel} that connects Android to the Dart code
207    * running in {@code dartExecutor}.
208    *
209    * The given {@code dartExecutor} is permitted to be idle or executing code.
210    *
211    * See {@link DartExecutor}.
212    */
PlatformViewsChannel(@onNull DartExecutor dartExecutor)213   public PlatformViewsChannel(@NonNull DartExecutor dartExecutor) {
214     channel = new MethodChannel(dartExecutor, "flutter/platform_views", StandardMethodCodec.INSTANCE);
215     channel.setMethodCallHandler(parsingHandler);
216   }
217 
218   /**
219    * Sets the {@link PlatformViewsHandler} which receives all events and requests
220    * that are parsed from the underlying platform views channel.
221    */
setPlatformViewsHandler(@ullable PlatformViewsHandler handler)222   public void setPlatformViewsHandler(@Nullable PlatformViewsHandler handler) {
223     this.handler = handler;
224   }
225 
226   /**
227    * Handler that receives platform view messages sent from Flutter to Android
228    * through a given {@link PlatformViewsChannel}.
229    *
230    * To register a {@code PlatformViewsHandler} with a {@link PlatformViewsChannel},
231    * see {@link PlatformViewsChannel#setPlatformViewsHandler(PlatformViewsHandler)}.
232    */
233   public interface PlatformViewsHandler {
234     /**
235      * The Flutter application would like to display a new Android {@code View}, i.e.,
236      * platform view.
237      *
238      * The handler should instantiate the desired Android {@code View}, create a new
239      * {@link io.flutter.view.FlutterView.SurfaceTextureRegistryEntry} within the
240      * given Flutter execution context, and then return the new texture's ID.
241      */
242     // TODO(mattcarroll): Introduce an annotation for @TextureId
createPlatformView(@onNull PlatformViewCreationRequest request)243     long createPlatformView(@NonNull PlatformViewCreationRequest request);
244 
245     /**
246      * The Flutter application could like dispose of an existing Android {@code View},
247      * i.e., platform view.
248      */
disposePlatformView(int viewId)249     void disposePlatformView(int viewId);
250 
251     /**
252      * The Flutter application would like to resize an existing Android {@code View},
253      * i.e., platform view.
254      */
resizePlatformView( @onNull PlatformViewResizeRequest request, @NonNull Runnable onComplete)255     void resizePlatformView(
256         @NonNull PlatformViewResizeRequest request,
257         @NonNull Runnable onComplete);
258 
259     /**
260      * The user touched a platform view within Flutter.
261      *
262      * Touch data is reported in {@code touch}.
263      */
onTouch(@onNull PlatformViewTouch touch)264     void onTouch(@NonNull PlatformViewTouch touch);
265 
266     /**
267      * The Flutter application would like to change the layout direction of
268      * an existing Android {@code View}, i.e., platform view.
269      */
270     // TODO(mattcarroll): Introduce an annotation for @TextureId
setDirection(int viewId, int direction)271     void setDirection(int viewId, int direction);
272 
273     /**
274      * Clears the focus from the platform view with a give id if it is currently focused.
275      */
clearFocus(int viewId)276     void clearFocus(int viewId);
277   }
278 
279   /**
280    * Request sent from Flutter to create a new platform view.
281    */
282   public static class PlatformViewCreationRequest {
283     /**
284      * The ID of the platform view as seen by the Flutter side.
285      */
286     public final int viewId;
287 
288     /**
289      * The type of Android {@code View} to create for this platform view.
290      */
291     @NonNull
292     public final String viewType;
293 
294     /**
295      * The density independent width to display the platform view.
296      */
297     public final double logicalWidth;
298 
299     /**
300      * The density independent height to display the platform view.
301      */
302     public final double logicalHeight;
303 
304     /**
305      * The layout direction of the new platform view.
306      *
307      * See {@link android.view.View.LAYOUT_DIRECTION_LTR} and
308      * {@link android.view.View.LAYOUT_DIRECTION_RTL}
309      */
310     public final int direction;
311 
312     /**
313      * Custom parameters that are unique to the desired platform view.
314      */
315     @Nullable
316     public final ByteBuffer params;
317 
PlatformViewCreationRequest( int viewId, @NonNull String viewType, double logicalWidth, double logicalHeight, int direction, @Nullable ByteBuffer params )318     public PlatformViewCreationRequest(
319         int viewId,
320         @NonNull String viewType,
321         double logicalWidth,
322         double logicalHeight,
323         int direction,
324         @Nullable ByteBuffer params
325     ) {
326       this.viewId = viewId;
327       this.viewType = viewType;
328       this.logicalWidth = logicalWidth;
329       this.logicalHeight = logicalHeight;
330       this.direction = direction;
331       this.params = params;
332     }
333   }
334 
335   /**
336    * Request sent from Flutter to resize a platform view.
337    */
338   public static class PlatformViewResizeRequest {
339     /**
340      * The ID of the platform view as seen by the Flutter side.
341      */
342     public final int viewId;
343 
344     /**
345      * The new density independent width to display the platform view.
346      */
347     public final double newLogicalWidth;
348 
349     /**
350      * The new density independent height to display the platform view.
351      */
352     public final double newLogicalHeight;
353 
PlatformViewResizeRequest( int viewId, double newLogicalWidth, double newLogicalHeight )354     public PlatformViewResizeRequest(
355         int viewId,
356         double newLogicalWidth,
357         double newLogicalHeight
358     ) {
359       this.viewId = viewId;
360       this.newLogicalWidth = newLogicalWidth;
361       this.newLogicalHeight = newLogicalHeight;
362     }
363   }
364 
365   /**
366    * The state of a touch event in Flutter within a platform view.
367    */
368   public static class PlatformViewTouch {
369     /**
370      * The ID of the platform view as seen by the Flutter side.
371      */
372     public final int viewId;
373 
374     /**
375      * The amount of time that the touch has been pressed.
376      */
377     @NonNull
378     public final Number downTime;
379     /**
380      * TODO(mattcarroll): javadoc
381      */
382     @NonNull
383     public final Number eventTime;
384     public final int action;
385     /**
386      * The number of pointers (e.g, fingers) involved in the touch event.
387      */
388     public final int pointerCount;
389     /**
390      * Properties for each pointer, encoded in a raw format.
391      */
392     @NonNull
393     public final Object rawPointerPropertiesList;
394     /**
395      * Coordinates for each pointer, encoded in a raw format.
396      */
397     @NonNull
398     public final Object rawPointerCoords;
399     /**
400      * TODO(mattcarroll): javadoc
401      */
402     public final int metaState;
403     /**
404      * TODO(mattcarroll): javadoc
405      */
406     public final int buttonState;
407     /**
408      * Coordinate precision along the x-axis.
409      */
410     public final float xPrecision;
411     /**
412      * Coordinate precision along the y-axis.
413      */
414     public final float yPrecision;
415     /**
416      * TODO(mattcarroll): javadoc
417      */
418     public final int deviceId;
419     /**
420      * TODO(mattcarroll): javadoc
421      */
422     public final int edgeFlags;
423     /**
424      * TODO(mattcarroll): javadoc
425      */
426     public final int source;
427     /**
428      * TODO(mattcarroll): javadoc
429      */
430     public final int flags;
431 
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 )432     PlatformViewTouch(
433         int viewId,
434         @NonNull Number downTime,
435         @NonNull Number eventTime,
436         int action,
437         int pointerCount,
438         @NonNull Object rawPointerPropertiesList,
439         @NonNull Object rawPointerCoords,
440         int metaState,
441         int buttonState,
442         float xPrecision,
443         float yPrecision,
444         int deviceId,
445         int edgeFlags,
446         int source,
447         int flags
448     ) {
449       this.viewId = viewId;
450       this.downTime = downTime;
451       this.eventTime = eventTime;
452       this.action = action;
453       this.pointerCount = pointerCount;
454       this.rawPointerPropertiesList = rawPointerPropertiesList;
455       this.rawPointerCoords = rawPointerCoords;
456       this.metaState = metaState;
457       this.buttonState = buttonState;
458       this.xPrecision = xPrecision;
459       this.yPrecision = yPrecision;
460       this.deviceId = deviceId;
461       this.edgeFlags = edgeFlags;
462       this.source = source;
463       this.flags = flags;
464     }
465   }
466 }
467