FlutterView.java 27.6 KB
Newer Older
1 2 3 4
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
package io.flutter.view;
6

7
import android.app.Activity;
8
import android.content.BroadcastReceiver;
9
import android.content.Context;
10 11 12
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
13
import android.content.res.Configuration;
H
Hixie 已提交
14
import android.opengl.Matrix;
15
import android.graphics.Bitmap;
H
Hixie 已提交
16
import android.graphics.Rect;
A
Adam Barth 已提交
17
import android.os.Build;
18
import android.util.AttributeSet;
19
import android.util.Log;
20
import android.util.TypedValue;
21
import android.view.KeyEvent;
22
import android.view.MotionEvent;
23 24 25 26
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
H
Hixie 已提交
27 28 29 30
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
31 32
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
33 34
import org.json.JSONException;
import org.json.JSONObject;
35 36

import org.chromium.base.JNINamespace;
37
import org.chromium.mojo.bindings.Interface.Binding;
38 39
import org.chromium.mojo.bindings.InterfaceRequest;
import org.chromium.mojo.system.Core;
40
import org.chromium.mojo.system.MessagePipeHandle;
H
Hixie 已提交
41
import org.chromium.mojo.system.MojoException;
42
import org.chromium.mojo.system.Pair;
H
Hixie 已提交
43
import org.chromium.mojo.system.impl.CoreImpl;
A
Adam Barth 已提交
44
import org.chromium.mojom.editing.Keyboard;
45
import org.chromium.mojom.flutter.platform.ApplicationMessages;
A
Adam Barth 已提交
46
import org.chromium.mojom.mojo.ServiceProvider;
47 48 49 50
import org.chromium.mojom.pointer.Pointer;
import org.chromium.mojom.pointer.PointerKind;
import org.chromium.mojom.pointer.PointerPacket;
import org.chromium.mojom.pointer.PointerType;
51
import org.chromium.mojom.raw_keyboard.RawKeyboardService;
H
Hixie 已提交
52
import org.chromium.mojom.semantics.SemanticsServer;
53
import org.chromium.mojom.sky.AppLifecycleState;
A
Adam Barth 已提交
54
import org.chromium.mojom.sky.ServicesData;
55
import org.chromium.mojom.sky.SkyEngine;
A
Adam Barth 已提交
56
import org.chromium.mojom.sky.ViewportMetrics;
57

58
import java.util.ArrayList;
H
Hixie 已提交
59
import java.util.HashMap;
60
import java.util.List;
61
import java.util.Locale;
H
Hixie 已提交
62
import java.util.Map;
63

64
import org.domokit.common.ActivityLifecycleListener;
65
import org.domokit.activity.ActivityImpl;
A
Adam Barth 已提交
66 67
import org.domokit.editing.KeyboardImpl;
import org.domokit.editing.KeyboardViewState;
68 69 70
import org.domokit.raw_keyboard.RawKeyboardServiceImpl;
import org.domokit.raw_keyboard.RawKeyboardServiceState;

71
/**
72
 * An Android view containing a Flutter app.
73
 */
74
@JNINamespace("shell")
75
public class FlutterView extends SurfaceView
H
Hixie 已提交
76 77
  implements AccessibilityManager.AccessibilityStateChangeListener,
             AccessibilityManager.TouchExplorationStateChangeListener {
78
    private static final String TAG = "FlutterView";
79

80 81
    private static final String ACTION_DISCOVER = "io.flutter.view.DISCOVER";

A
Adam Barth 已提交
82
    private long mNativePlatformView;
83
    private SkyEngine.Proxy mSkyEngine;
84
    private ServiceProviderImpl mPlatformServiceProvider;
85
    private Binding mPlatformServiceProviderBinding;
86
    private ServiceProviderImpl mViewServiceProvider;
87
    private Binding mViewServiceProviderBinding;
H
Hixie 已提交
88
    private ServiceProvider.Proxy mDartServiceProvider;
89 90 91
    private ApplicationMessages.Proxy mFlutterAppMessages;
    private HashMap<String, OnMessageListener> mOnMessageListeners;
    private HashMap<String, OnMessageListenerAsync> mAsyncOnMessageListeners;
92
    private final SurfaceHolder.Callback mSurfaceCallback;
93
    private final ViewportMetrics mMetrics;
A
Adam Barth 已提交
94
    private final KeyboardViewState mKeyboardState;
95
    private final RawKeyboardServiceState mRawKeyboardState;
H
Hixie 已提交
96
    private final AccessibilityManager mAccessibilityManager;
97
    private BroadcastReceiver discoveryReceiver;
98
    private List<ActivityLifecycleListener> mActivityLifecycleListeners;
99

100
    public FlutterView(Context context) {
101 102 103
        this(context, null);
    }

104
    public FlutterView(Context context, AttributeSet attrs) {
105
        super(context, attrs);
106

107 108 109 110
        // TODO(abarth): Remove this static and instead make everything that
        // depends on it into a view-associated service.
        ActivityImpl.setCurrentActivity((Activity) context);

111 112
        mMetrics = new ViewportMetrics();
        mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
113 114 115
        setFocusable(true);
        setFocusableInTouchMode(true);

116
        attach();
A
Adam Barth 已提交
117
        assert mNativePlatformView != 0;
118

119 120 121 122 123 124 125 126
        int color = 0xFF000000;
        TypedValue typedValue = new TypedValue();
        context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true);
        if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT)
          color = typedValue.data;
        // TODO(abarth): Consider letting the developer override this color.
        final int backgroundColor = color;

127 128
        mSurfaceCallback = new SurfaceHolder.Callback() {
            @Override
129 130 131
            public void surfaceCreated(SurfaceHolder holder) {
                assert mNativePlatformView != 0;
                nativeSurfaceCreated(mNativePlatformView, holder.getSurface());
132 133 134
            }

            @Override
135
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
A
Adam Barth 已提交
136
                assert mNativePlatformView != 0;
137
                nativeSurfaceChanged(mNativePlatformView, backgroundColor);
138 139 140 141
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
A
Adam Barth 已提交
142 143
                assert mNativePlatformView != 0;
                nativeSurfaceDestroyed(mNativePlatformView);
144 145 146
            }
        };
        getHolder().addCallback(mSurfaceCallback);
147

A
Adam Barth 已提交
148
        mKeyboardState = new KeyboardViewState(this);
149
        mRawKeyboardState = new RawKeyboardServiceState();
H
Hixie 已提交
150

151 152
        Core core = CoreImpl.getInstance();

153
        mPlatformServiceProvider = new ServiceProviderImpl(core, this, ServiceRegistry.SHARED);
154 155 156

        ServiceRegistry localRegistry = new ServiceRegistry();
        configureLocalServices(localRegistry);
157
        mViewServiceProvider = new ServiceProviderImpl(core, this, localRegistry);
158

H
Hixie 已提交
159
        mAccessibilityManager = (AccessibilityManager)getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
160

161 162
        mOnMessageListeners = new HashMap<String, OnMessageListener>();
        mAsyncOnMessageListeners = new HashMap<String, OnMessageListenerAsync>();
163
        mActivityLifecycleListeners = new ArrayList<ActivityLifecycleListener>();
164

165
        setLocale(getResources().getConfiguration().locale);
166 167 168 169 170

        if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
            discoveryReceiver = new DiscoveryReceiver();
            context.registerReceiver(discoveryReceiver, new IntentFilter(ACTION_DISCOVER));
        }
171 172 173 174 175 176 177 178 179 180 181 182 183 184
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (mRawKeyboardState.onKey(this, keyCode, event))
            return true;
        return super.onKeyUp(keyCode, event);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (mRawKeyboardState.onKey(this, keyCode, event))
            return true;
        return super.onKeyDown(keyCode, event);
185 186
    }

187
    SkyEngine getEngine() {
188 189 190
        return mSkyEngine;
    }

191 192 193 194
    public void addActivityLifecycleListener(ActivityLifecycleListener listener) {
        mActivityLifecycleListeners.add(listener);
    }

195 196 197 198
    public void onPause() {
        mSkyEngine.onAppLifecycleStateChanged(AppLifecycleState.PAUSED);
    }

199
    public void onPostResume() {
200 201 202
        for (ActivityLifecycleListener listener : mActivityLifecycleListeners)
            listener.onPostResume();

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
        mSkyEngine.onAppLifecycleStateChanged(AppLifecycleState.RESUMED);
    }

    public void pushRoute(String route) {
        mSkyEngine.pushRoute(route);
    }

    public void popRoute() {
        mSkyEngine.popRoute();
    }

    private void setLocale(Locale locale) {
        mSkyEngine.onLocaleChanged(locale.getLanguage(), locale.getCountry());
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setLocale(newConfig.locale);
    }

H
Hixie 已提交
224 225 226 227
    float getDevicePixelRatio() {
        return mMetrics.devicePixelRatio;
    }

228
    public void destroy() {
229 230 231 232
        if (discoveryReceiver != null) {
            getContext().unregisterReceiver(discoveryReceiver);
        }

233 234 235 236 237 238 239 240 241 242
        if (mPlatformServiceProviderBinding != null) {
            mPlatformServiceProviderBinding.unbind().close();
            mPlatformServiceProvider.unbindServices();
        }

        if (mViewServiceProviderBinding != null) {
            mViewServiceProviderBinding.unbind().close();
            mViewServiceProvider.unbindServices();
        }

243
        getHolder().removeCallback(mSurfaceCallback);
A
Adam Barth 已提交
244 245
        nativeDetach(mNativePlatformView);
        mNativePlatformView = 0;
246 247 248 249

        mSkyEngine.close();
        mDartServiceProvider.close();
        mFlutterAppMessages.close();
250 251
    }

252 253
    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
E
Eric Seidel 已提交
254
        return mKeyboardState.createInputConnection(outAttrs);
255 256
    }

257
    private Integer getPointerTypeForAction(int maskedAction) {
258
        // Primary pointer:
259
        if (maskedAction == MotionEvent.ACTION_DOWN) {
260
            return PointerType.DOWN;
261 262
        }
        if (maskedAction == MotionEvent.ACTION_UP) {
263
            return PointerType.UP;
264
        }
265
        // Secondary pointer:
266
        if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
267
            return PointerType.DOWN;
268 269
        }
        if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
270
            return PointerType.UP;
271
        }
272
        // All pointers:
273
        if (maskedAction == MotionEvent.ACTION_MOVE) {
274
            return PointerType.MOVE;
275 276
        }
        if (maskedAction == MotionEvent.ACTION_CANCEL) {
277
            return PointerType.CANCEL;
278
        }
279
        return null;
280 281
    }

282 283 284 285 286 287 288 289 290
    private void addPointerForIndex(MotionEvent event, int pointerIndex,
                                    List<Pointer> result) {
        Integer pointerType = getPointerTypeForAction(event.getActionMasked());
        if (pointerType == null) {
            return;
        }

        Pointer pointer = new Pointer();

A
Adam Barth 已提交
291
        pointer.timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds.
292 293 294 295 296 297 298 299 300 301
        pointer.pointer = event.getPointerId(pointerIndex);
        pointer.type = pointerType;
        pointer.kind = PointerKind.TOUCH;
        pointer.x = event.getX(pointerIndex);
        pointer.y = event.getY(pointerIndex);

        pointer.buttons = 0;
        pointer.down = false;
        pointer.primary = false;
        pointer.obscured = false;
302

303 304
        // TODO(eseidel): Could get the calibrated range if necessary:
        // event.getDevice().getMotionRange(MotionEvent.AXIS_PRESSURE)
305 306 307
        pointer.pressure = event.getPressure(pointerIndex);
        pointer.pressureMin = 0.0f;
        pointer.pressureMax = 1.0f;
308

309 310 311
        pointer.distance = 0.0f;
        pointer.distanceMin = 0.0f;
        pointer.distanceMax = 0.0f;
312

313 314 315 316 317 318 319 320 321
        pointer.radiusMajor = 0.0f;
        pointer.radiusMinor = 0.0f;
        pointer.radiusMin = 0.0f;
        pointer.radiusMax = 0.0f;

        pointer.orientation = 0.0f;
        pointer.tilt = 0.0f;

        result.add(pointer);
322 323 324 325
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
A
Adam Barth 已提交
326 327 328 329 330 331 332 333
        // TODO(abarth): This version check might not be effective in some
        // versions of Android that statically compile code and will be upset
        // at the lack of |requestUnbufferedDispatch|. Instead, we should factor
        // version-dependent code into separate classes for each supported
        // version and dispatch dynamically.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            requestUnbufferedDispatch(event);
        }
334

335 336
        ArrayList<Pointer> pointers = new ArrayList<Pointer>();

A
Adam Barth 已提交
337 338
        // TODO(abarth): Rather than unpacking these events here, we should
        // probably send them in one packet to the engine.
339 340 341 342 343 344 345
        int maskedAction = event.getActionMasked();
        // ACTION_UP, ACTION_POINTER_UP, ACTION_DOWN, and ACTION_POINTER_DOWN
        // only apply to a single pointer, other events apply to all pointers.
        if (maskedAction == MotionEvent.ACTION_UP
                || maskedAction == MotionEvent.ACTION_POINTER_UP
                || maskedAction == MotionEvent.ACTION_DOWN
                || maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
346
            addPointerForIndex(event, event.getActionIndex(), pointers);
347 348 349 350 351
        } else {
            // ACTION_MOVE may not actually mean all pointers have moved
            // but it's the responsibility of a later part of the system to
            // ignore 0-deltas if desired.
            for (int p = 0; p < event.getPointerCount(); p++) {
352
                addPointerForIndex(event, p, pointers);
353 354
            }
        }
355 356 357 358 359

        PointerPacket packet = new PointerPacket();
        packet.pointers = pointers.toArray(new Pointer[0]);
        mSkyEngine.onPointerPacket(packet);

360 361 362
        return true;
    }

363 364 365 366 367 368 369 370 371 372
    @Override
    public boolean onHoverEvent(MotionEvent event) {
        boolean handled = handleAccessibilityHoverEvent(event);
        if (!handled) {
            // TODO(ianh): Expose hover events to the platform,
            // implementing ADD, REMOVE, etc.
        }
        return handled;
    }

373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
    @Override
    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
        mMetrics.physicalWidth = width;
        mMetrics.physicalHeight = height;
        mSkyEngine.onViewportMetricsChanged(mMetrics);
        super.onSizeChanged(width, height, oldWidth, oldHeight);
    }

    @Override
    public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
        mMetrics.physicalPaddingTop = insets.getSystemWindowInsetTop();
        mMetrics.physicalPaddingRight = insets.getSystemWindowInsetRight();
        mMetrics.physicalPaddingBottom = insets.getSystemWindowInsetBottom();
        mMetrics.physicalPaddingLeft = insets.getSystemWindowInsetLeft();
        mSkyEngine.onViewportMetricsChanged(mMetrics);
        return super.onApplyWindowInsets(insets);
    }

391
    private void configureLocalServices(ServiceRegistry registry) {
A
Adam Barth 已提交
392
        registry.register(Keyboard.MANAGER.getName(), new ServiceFactory() {
393
            @Override
394 395
            public Binding connectToService(FlutterView view, Core core, MessagePipeHandle pipe) {
                return Keyboard.MANAGER.bind(new KeyboardImpl(view.getContext(), mKeyboardState), pipe);
396 397 398 399 400
            }
        });

        registry.register(RawKeyboardService.MANAGER.getName(), new ServiceFactory() {
            @Override
401
            public Binding connectToService(FlutterView view, Core core, MessagePipeHandle pipe) {
402
                return RawKeyboardService.MANAGER.bind(new RawKeyboardServiceImpl(mRawKeyboardState), pipe);
403 404
            }
        });
405 406 407

        registry.register(ApplicationMessages.MANAGER.getName(), new ServiceFactory() {
            @Override
408
            public Binding connectToService(FlutterView view, Core core, MessagePipeHandle pipe) {
409
                return ApplicationMessages.MANAGER.bind(new ApplicationMessagesImpl(), pipe);
410 411
            }
        });
412 413
    }

414 415
    private void attach() {
        Core core = CoreImpl.getInstance();
416
        Pair<SkyEngine.Proxy, InterfaceRequest<SkyEngine>> engine =
417
                SkyEngine.MANAGER.getInterfaceRequest(core);
418
        mSkyEngine = engine.first;
419 420
        mNativePlatformView =
            nativeAttach(engine.second.passHandle().releaseNativeHandle(), this);
421
    }
422

423
    private void preRun() {
424 425 426 427 428 429 430
        if (mPlatformServiceProviderBinding != null) {
            mPlatformServiceProviderBinding.unbind().close();
            mPlatformServiceProvider.unbindServices();
        }
        if (mViewServiceProviderBinding != null) {
            mViewServiceProviderBinding.unbind().close();
            mViewServiceProvider.unbindServices();
H
Hixie 已提交
431 432 433 434 435
        }
        if (mDartServiceProvider != null) {
            mDartServiceProvider.close();
        }

436
        Core core = CoreImpl.getInstance();
437

H
Hixie 已提交
438 439 440 441
        Pair<ServiceProvider.Proxy, InterfaceRequest<ServiceProvider>> dartServiceProvider =
                ServiceProvider.MANAGER.getInterfaceRequest(core);
        mDartServiceProvider = dartServiceProvider.first;

442 443 444 445 446 447 448 449 450 451
        Pair<ServiceProvider.Proxy, InterfaceRequest<ServiceProvider>> platformServiceProvider =
                ServiceProvider.MANAGER.getInterfaceRequest(core);
        mPlatformServiceProviderBinding = ServiceProvider.MANAGER.bind(
                mPlatformServiceProvider, platformServiceProvider.second);

        Pair<ServiceProvider.Proxy, InterfaceRequest<ServiceProvider>> viewServiceProvider =
                ServiceProvider.MANAGER.getInterfaceRequest(core);
        mViewServiceProviderBinding = ServiceProvider.MANAGER.bind(
                mViewServiceProvider, viewServiceProvider.second);

452
        ServicesData services = new ServicesData();
453
        services.incomingServices = platformServiceProvider.first;
A
Adam Barth 已提交
454
        services.outgoingServices = dartServiceProvider.second;
455
        services.viewServices = viewServiceProvider.first;
456
        mSkyEngine.setServices(services);
457

H
Hixie 已提交
458
        resetAccessibilityTree();
459 460 461 462 463 464 465 466 467 468 469 470 471 472
    }

    private void postRun() {
        Core core = CoreImpl.getInstance();
        // Connect to the ApplicationMessages service exported by the Flutter framework
        Pair<ApplicationMessages.Proxy, InterfaceRequest<ApplicationMessages>> appMessages =
                  ApplicationMessages.MANAGER.getInterfaceRequest(core);
        mDartServiceProvider.connectToService(ApplicationMessages.MANAGER.getName(),
                                              appMessages.second.passHandle());
        mFlutterAppMessages = appMessages.first;
    }

    public void runFromBundle(String bundlePath, String snapshotPath) {
        preRun();
H
Hixie 已提交
473

474 475
        if (FlutterMain.isRunningPrecompiledCode()) {
            mSkyEngine.runFromPrecompiledSnapshot(bundlePath);
476
        } else {
477 478 479 480 481 482
            String scriptUri = "file://" + bundlePath;
            if (snapshotPath != null) {
                mSkyEngine.runFromBundleAndSnapshot(scriptUri, bundlePath, snapshotPath);
            } else {
                mSkyEngine.runFromBundle(scriptUri, bundlePath);
            }
483
        }
484

485 486 487
        postRun();
    }

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
    public void runFromSource(final String main,
                              final String packages,
                              final String assetsDirectory) {
        Runnable runnable = new Runnable() {
            public void run() {
                preRun();
                mSkyEngine.runFromFile(main,
                                       packages,
                                       assetsDirectory);
                postRun();
                synchronized (this) {
                    notify();
                }
            }
        };

        try {
            synchronized (runnable) {
                // Post to the Android UI thread and wait for the response.
                post(runnable);
                runnable.wait();
            }
        } catch (InterruptedException e) {
            Log.e(TAG, "Thread got interrupted waiting for " +
                       "RunFromSourceRunnable to finish", e);
        }
514 515
    }

516 517 518 519 520
    /** Return the most recent frame as a bitmap. */
    public Bitmap getBitmap() {
        return nativeGetBitmap(mNativePlatformView);
    }

521 522
    private static native long nativeAttach(int skyEngineHandle,
                                            FlutterView view);
523
    private static native int nativeGetObservatoryPort();
524 525 526
    private static native void nativeDetach(long nativePlatformViewAndroid);
    private static native void nativeSurfaceCreated(long nativePlatformViewAndroid,
                                                    Surface surface);
527
    private static native void nativeSurfaceChanged(long nativePlatformViewAndroid, int backgroundColor);
528
    private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid);
529
    private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid);
H
Hixie 已提交
530 531 532 533


    // ACCESSIBILITY

H
Hixie 已提交
534
    private boolean mAccessibilityEnabled = false;
535 536
    private boolean mTouchExplorationEnabled = false;

H
Hixie 已提交
537 538 539
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
H
Hixie 已提交
540
        mAccessibilityEnabled = mAccessibilityManager.isEnabled();
541
        mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
H
Hixie 已提交
542
        if (mAccessibilityEnabled || mTouchExplorationEnabled)
H
Hixie 已提交
543
          ensureAccessibilityEnabled();
H
Hixie 已提交
544
        resetWillNotDraw();
H
Hixie 已提交
545 546 547 548
        mAccessibilityManager.addAccessibilityStateChangeListener(this);
        mAccessibilityManager.addTouchExplorationStateChangeListener(this);
    }

H
Hixie 已提交
549 550 551 552 553 554 555 556 557 558 559
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mAccessibilityManager.removeAccessibilityStateChangeListener(this);
        mAccessibilityManager.removeTouchExplorationStateChangeListener(this);
    }

    private void resetWillNotDraw() {
        setWillNotDraw(!(mAccessibilityEnabled || mTouchExplorationEnabled));
    }

H
Hixie 已提交
560 561
    @Override
    public void onAccessibilityStateChanged(boolean enabled) {
H
Hixie 已提交
562 563
        if (enabled) {
            mAccessibilityEnabled = true;
H
Hixie 已提交
564
            ensureAccessibilityEnabled();
H
Hixie 已提交
565 566 567 568 569 570
        } else {
            mAccessibilityEnabled = false;
        }
        if (mAccessibilityNodeProvider != null) {
            mAccessibilityNodeProvider.setAccessibilityEnabled(mAccessibilityEnabled);
        }
H
Hixie 已提交
571
        resetWillNotDraw();
H
Hixie 已提交
572 573 574 575
    }

    @Override
    public void onTouchExplorationStateChanged(boolean enabled) {
576 577
        if (enabled) {
            mTouchExplorationEnabled = true;
H
Hixie 已提交
578
            ensureAccessibilityEnabled();
579 580 581 582 583 584
        } else {
            mTouchExplorationEnabled = false;
            if (mAccessibilityNodeProvider != null) {
                mAccessibilityNodeProvider.handleTouchExplorationExit();
            }
        }
H
Hixie 已提交
585
        resetWillNotDraw();
H
Hixie 已提交
586 587
    }

H
Hixie 已提交
588 589 590 591 592 593
    @Override
    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
        ensureAccessibilityEnabled();
        return mAccessibilityNodeProvider;
    }

594
    private AccessibilityBridge mAccessibilityNodeProvider;
H
Hixie 已提交
595 596 597

    void ensureAccessibilityEnabled() {
        if (mAccessibilityNodeProvider == null) {
598
            mAccessibilityNodeProvider = new AccessibilityBridge(this, createSemanticsServer());
H
Hixie 已提交
599 600 601
        }
    }

H
Hixie 已提交
602 603 604 605 606 607 608 609 610 611 612 613
    private SemanticsServer.Proxy createSemanticsServer() {
        Core core = CoreImpl.getInstance();
        Pair<SemanticsServer.Proxy, InterfaceRequest<SemanticsServer>> server =
                  SemanticsServer.MANAGER.getInterfaceRequest(core);
        mDartServiceProvider.connectToService(SemanticsServer.MANAGER.getName(), server.second.passHandle());
        return server.first;
    }

    void resetAccessibilityTree() {
        if (mAccessibilityNodeProvider != null) {
            mAccessibilityNodeProvider.reset(createSemanticsServer());
        }
H
Hixie 已提交
614 615
    }

616 617 618
    private boolean handleAccessibilityHoverEvent(MotionEvent event) {
        if (!mTouchExplorationEnabled)
            return false;
H
Hixie 已提交
619 620 621 622
        if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER ||
                   event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
            mAccessibilityNodeProvider.handleTouchExploration(event.getX(), event.getY());
        } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
623 624
            mAccessibilityNodeProvider.handleTouchExplorationExit();
        } else {
H
Hixie 已提交
625 626
            Log.d("flutter", "unexpected accessibility hover event: " + event);
            return false;
627 628 629
        }
        return true;
    }
H
Hixie 已提交
630

631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
    /**
     * Send a message to the Flutter application.  The Flutter Dart code can register a
     * host message handler that will receive these messages.
     */
    public void sendToFlutter(String messageName, String message,
                              final MessageReplyCallback callback) {
        mFlutterAppMessages.sendString(messageName, message,
            new ApplicationMessages.SendStringResponse() {
                @Override
                public void call(String reply) {
                    if (callback != null) {
                        callback.onReply(reply);
                    }
                }
            });
    }

    public void sendToFlutter(String messageName, String message) {
        sendToFlutter(messageName, message, null);
    }

    /** Callback invoked when the app replies to a message sent with sendToFlutter. */
    public interface MessageReplyCallback {
        void onReply(String reply);
    }

    /**
     * Register a callback to be invoked when the Flutter application sends a message
     * to its host.
     */
    public void addOnMessageListener(String messageName, OnMessageListener listener) {
        mOnMessageListeners.put(messageName, listener);
    }

    /**
     * Register a callback to be invoked when the Flutter application sends a message
     * to its host.  The reply to the message can be provided asynchronously.
     */
    public void addOnMessageListenerAsync(String messageName, OnMessageListenerAsync listener) {
        mAsyncOnMessageListeners.put(messageName, listener);
    }

    public interface OnMessageListener {
        /**
         * Called when a message is received from the Flutter app.
         * @return the reply to the message (can be null)
         */
678
        String onMessage(FlutterView view, String message);
679 680 681 682 683 684 685
    };

    public interface OnMessageListenerAsync {
        /**
         * Called when a message is received from the Flutter app.
         * @param response Used to send a reply back to the app.
         */
686
        void onMessage(FlutterView view, String message, MessageResponse response);
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
    }

    public interface MessageResponse {
        void send(String reply);
    }

    private class ApplicationMessagesImpl implements ApplicationMessages {
        @Override
        public void close() {}

        @Override
        public void onConnectionError(MojoException e) {}

        @Override
        public void sendString(String messageName, String message, SendStringResponse callback) {
            OnMessageListener listener = mOnMessageListeners.get(messageName);
            if (listener != null) {
704
                callback.call(listener.onMessage(FlutterView.this, message));
705 706 707 708 709
                return;
            }

            OnMessageListenerAsync asyncListener = mAsyncOnMessageListeners.get(messageName);
            if (asyncListener != null) {
710
                asyncListener.onMessage(FlutterView.this, message, new MessageResponseAdapter(callback));
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
                return;
            }

            callback.call(null);
        }
    }

    /**
     * This class wraps the raw Mojo callback object in an interface that is owned
     * by Flutter and can be safely given to host apps.
     */
    private static class MessageResponseAdapter implements MessageResponse {
        private ApplicationMessages.SendStringResponse callback;

        MessageResponseAdapter(ApplicationMessages.SendStringResponse callback) {
            this.callback = callback;
        }

        @Override
        public void send(String reply) {
            callback.call(reply);
        }
    }
734 735 736 737 738 739 740 741 742 743 744 745 746

    /** Broadcast receiver used to discover active Flutter instances. */
    private class DiscoveryReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            JSONObject discover = new JSONObject();
            try {
                discover.put("id", getContext().getPackageName());
                discover.put("observatoryPort", nativeGetObservatoryPort());
                Log.i(TAG, "DISCOVER: " + discover);
            } catch (JSONException e) {}
        }
    }
747
}