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