FlutterView.java 29.7 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
import org.json.JSONArray;
34 35
import org.json.JSONException;
import org.json.JSONObject;
36

37
import org.chromium.base.CalledByNative;
38 39
import org.chromium.base.JNINamespace;

40 41
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
42
import java.nio.charset.StandardCharsets;
43
import java.util.ArrayList;
H
Hixie 已提交
44
import java.util.HashMap;
45
import java.util.List;
46
import java.util.Locale;
H
Hixie 已提交
47
import java.util.Map;
48

A
Adam Barth 已提交
49
import io.flutter.plugin.common.ActivityLifecycleListener;
50
import io.flutter.plugin.editing.TextInputPlugin;
A
Adam Barth 已提交
51 52
import io.flutter.plugin.platform.PlatformPlugin;

53
/**
54
 * An Android view containing a Flutter app.
55
 */
56
@JNINamespace("shell")
57
public class FlutterView extends SurfaceView
58
  implements AccessibilityManager.AccessibilityStateChangeListener {
59
    private static final String TAG = "FlutterView";
60

61 62
    private static final String ACTION_DISCOVER = "io.flutter.view.DISCOVER";

63 64 65 66 67 68 69 70 71 72
    class ViewportMetrics {
        float devicePixelRatio = 1.0f;
        int physicalWidth = 0;
        int physicalHeight = 0;
        int physicalPaddingTop = 0;
        int physicalPaddingRight = 0;
        int physicalPaddingBottom = 0;
        int physicalPaddingLeft = 0;
    }

A
Adam Barth 已提交
73
    private long mNativePlatformView;
74 75
    private TextInputPlugin mTextInputPlugin;

76 77
    private HashMap<String, OnMessageListener> mOnMessageListeners;
    private HashMap<String, OnMessageListenerAsync> mAsyncOnMessageListeners;
78
    private final SurfaceHolder.Callback mSurfaceCallback;
79
    private final ViewportMetrics mMetrics;
H
Hixie 已提交
80
    private final AccessibilityManager mAccessibilityManager;
81
    private BroadcastReceiver discoveryReceiver;
82
    private List<ActivityLifecycleListener> mActivityLifecycleListeners;
83

84
    public FlutterView(Context context) {
85 86 87
        this(context, null);
    }

88
    public FlutterView(Context context, AttributeSet attrs) {
89
        super(context, attrs);
90

91 92
        mMetrics = new ViewportMetrics();
        mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
93 94 95
        setFocusable(true);
        setFocusableInTouchMode(true);

96
        attach();
A
Adam Barth 已提交
97
        assert mNativePlatformView != 0;
98

99 100 101 102 103 104 105 106
        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;

107 108
        mSurfaceCallback = new SurfaceHolder.Callback() {
            @Override
109 110
            public void surfaceCreated(SurfaceHolder holder) {
                assert mNativePlatformView != 0;
111
                nativeSurfaceCreated(mNativePlatformView, holder.getSurface(), backgroundColor);
112 113 114
            }

            @Override
115
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
A
Adam Barth 已提交
116
                assert mNativePlatformView != 0;
117
                nativeSurfaceChanged(mNativePlatformView, width, height);
118 119 120 121
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
A
Adam Barth 已提交
122 123
                assert mNativePlatformView != 0;
                nativeSurfaceDestroyed(mNativePlatformView);
124 125 126
            }
        };
        getHolder().addCallback(mSurfaceCallback);
127

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

130 131
        mOnMessageListeners = new HashMap<String, OnMessageListener>();
        mAsyncOnMessageListeners = new HashMap<String, OnMessageListenerAsync>();
132
        mActivityLifecycleListeners = new ArrayList<ActivityLifecycleListener>();
133

134
        setLocale(getResources().getConfiguration().locale);
135

136 137 138 139
        // Configure the platform plugin.
        PlatformPlugin platformPlugin = new PlatformPlugin((Activity)getContext());
        addOnMessageListener("flutter/platform", platformPlugin);
        addActivityLifecycleListener(platformPlugin);
140 141
        mTextInputPlugin = new TextInputPlugin((Activity)getContext());
        addOnMessageListener("flutter/textinput", mTextInputPlugin);
A
Adam Barth 已提交
142

143 144 145 146
        if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
            discoveryReceiver = new DiscoveryReceiver();
            context.registerReceiver(discoveryReceiver, new IntentFilter(ACTION_DISCOVER));
        }
147 148
    }

149 150
    private void encodeKeyEvent(KeyEvent event, JSONObject message) throws JSONException {
        message.put("flags", event.getFlags());
151 152
        message.put("codePoint", event.getUnicodeChar());
        message.put("keyCode", event.getKeyCode());
153 154 155 156
        message.put("scanCode", event.getScanCode());
        message.put("metaState", event.getMetaState());
    }

157 158
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
159 160 161 162 163
        try {
            JSONObject message = new JSONObject();
            message.put("type", "keyup");
            message.put("keymap", "android");
            encodeKeyEvent(event, message);
164
            sendPlatformMessage("flutter/keyevent", message.toString(), null);
165 166 167
        } catch (JSONException e) {
            Log.e(TAG, "Failed to serialize key event", e);
        }
168
        return super.onKeyUp(keyCode, event);
169 170 171 172
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
173 174 175 176 177
        try {
            JSONObject message = new JSONObject();
            message.put("type", "keydown");
            message.put("keymap", "android");
            encodeKeyEvent(event, message);
178
            sendPlatformMessage("flutter/keyevent", message.toString(), null);
179 180 181
        } catch (JSONException e) {
            Log.e(TAG, "Failed to serialize key event", e);
        }
182
        return super.onKeyDown(keyCode, event);
183 184
    }

185 186 187 188
    public void addActivityLifecycleListener(ActivityLifecycleListener listener) {
        mActivityLifecycleListeners.add(listener);
    }

189
    public void onPause() {
190
        sendPlatformMessage("flutter/lifecycle", "AppLifecycleState.paused", null);
191 192
    }

193
    public void onPostResume() {
194 195 196
        for (ActivityLifecycleListener listener : mActivityLifecycleListeners)
            listener.onPostResume();

197
        sendPlatformMessage("flutter/lifecycle", "AppLifecycleState.resumed", null);
198 199 200
    }

    public void pushRoute(String route) {
201 202 203 204 205 206 207 208 209 210
        try {
            final JSONArray args = new JSONArray();
            args.put(0, route);
            final JSONObject message = new JSONObject();
            message.put("method", "pushRoute");
            message.put("args", args);
            sendPlatformMessage("flutter/navigation", message.toString(), null);
        } catch (JSONException e) {
            Log.e(TAG, "Unexpected JSONException pushing route", e);
        }
211 212 213
    }

    public void popRoute() {
214 215 216 217 218 219 220 221
        try {
            final JSONObject message = new JSONObject();
            message.put("method", "popRoute");
            message.put("args", new JSONArray());
            sendPlatformMessage("flutter/navigation", message.toString(), null);
        } catch (JSONException e) {
            Log.e(TAG, "Unexpected JSONException pushing route", e);
        }
222 223 224
    }

    private void setLocale(Locale locale) {
225 226 227 228 229 230 231 232 233 234 235
        try {
            final JSONArray args = new JSONArray();
            args.put(0, locale.getLanguage());
            args.put(1, locale.getCountry());
            final JSONObject message = new JSONObject();
            message.put("method", "setLocale");
            message.put("args", args);
            sendPlatformMessage("flutter/localization", message.toString(), null);
        } catch (JSONException e) {
            Log.e(TAG, "Unexpected JSONException pushing route", e);
        }
236 237 238 239 240 241 242 243
    }

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

H
Hixie 已提交
244 245 246 247
    float getDevicePixelRatio() {
        return mMetrics.devicePixelRatio;
    }

248
    public void destroy() {
249 250 251 252
        if (discoveryReceiver != null) {
            getContext().unregisterReceiver(discoveryReceiver);
        }

253
        getHolder().removeCallback(mSurfaceCallback);
A
Adam Barth 已提交
254 255
        nativeDetach(mNativePlatformView);
        mNativePlatformView = 0;
256 257
    }

258 259
    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
A
Adam Barth 已提交
260
        return mTextInputPlugin.createInputConnection(this, outAttrs);
261 262
    }

263
    // Must match the PointerChange enum in pointer.dart.
264 265 266 267 268 269
    private static final int kPointerChangeCancel = 0;
    private static final int kPointerChangeAdd = 1;
    private static final int kPointerChangeRemove = 2;
    private static final int kPointerChangeDown = 3;
    private static final int kPointerChangeMove = 4;
    private static final int kPointerChangeUp = 5;
270 271 272 273 274 275 276 277

    // Must match the PointerDeviceKind enum in pointer.dart.
    private static final int kPointerDeviceKindTouch = 0;
    private static final int kPointerDeviceKindMouse = 1;
    private static final int kPointerDeviceKindStylus = 2;
    private static final int kPointerDeviceKindInvertedStylus = 3;

    private int getPointerChangeForAction(int maskedAction) {
278
        // Primary pointer:
279
        if (maskedAction == MotionEvent.ACTION_DOWN) {
280
            return kPointerChangeDown;
281 282
        }
        if (maskedAction == MotionEvent.ACTION_UP) {
283
            return kPointerChangeUp;
284
        }
285
        // Secondary pointer:
286
        if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
287
            return kPointerChangeDown;
288 289
        }
        if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
290
            return kPointerChangeUp;
291
        }
292
        // All pointers:
293
        if (maskedAction == MotionEvent.ACTION_MOVE) {
294
            return kPointerChangeMove;
295 296
        }
        if (maskedAction == MotionEvent.ACTION_CANCEL) {
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
            return kPointerChangeCancel;
        }
        return -1;
    }

    private int getPointerDeviceTypeForToolType(int toolType) {
        switch (toolType) {
            case MotionEvent.TOOL_TYPE_FINGER:
                return kPointerDeviceKindTouch;
            case MotionEvent.TOOL_TYPE_STYLUS:
                return kPointerDeviceKindStylus;
            case MotionEvent.TOOL_TYPE_MOUSE:
                return kPointerDeviceKindMouse;
            default:
                // MotionEvent.TOOL_TYPE_UNKNOWN will reach here.
                return -1;
313
        }
314 315
    }

316
    private void addPointerForIndex(MotionEvent event, int pointerIndex,
317 318 319
                                    ByteBuffer packet) {
        int pointerChange = getPointerChangeForAction(event.getActionMasked());
        if (pointerChange == -1) {
320 321 322
            return;
        }

323 324 325 326
        int pointerKind = event.getToolType(pointerIndex);
        if (pointerKind == -1) {
            return;
        }
327

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
        long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds.

        packet.putLong(timeStamp); // time_stamp
        packet.putLong(event.getPointerId(pointerIndex)); // pointer
        packet.putLong(pointerChange); // change
        packet.putLong(pointerKind); // kind
        packet.putDouble(event.getX(pointerIndex)); // physical_x
        packet.putDouble(event.getY(pointerIndex)); // physical_y

        if (pointerKind == kPointerDeviceKindMouse) {
          packet.putLong(event.getButtonState() & 0x1F); // buttons
        } else if (pointerKind == kPointerDeviceKindStylus) {
          packet.putLong((event.getButtonState() >> 4) & 0xF); // buttons
        } else {
          packet.putLong(0); // buttons
        }
344

345
        packet.putLong(0); // obscured
346

347 348
        // TODO(eseidel): Could get the calibrated range if necessary:
        // event.getDevice().getMotionRange(MotionEvent.AXIS_PRESSURE)
349 350 351
        packet.putDouble(event.getPressure(pointerIndex)); // presure
        packet.putDouble(0.0); // pressure_min
        packet.putDouble(1.0); // pressure_max
352

353 354 355 356 357 358 359 360 361 362
        if (pointerKind == kPointerDeviceKindStylus) {
          packet.putDouble(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex)); // distance
          packet.putDouble(0.0); // distance_max
        } else {
          packet.putDouble(0.0); // distance
          packet.putDouble(0.0); // distance_max
        }

        packet.putDouble(event.getToolMajor(pointerIndex)); // radius_major
        packet.putDouble(event.getToolMinor(pointerIndex)); // radius_minor
363

364 365
        packet.putDouble(0.0); // radius_min
        packet.putDouble(0.0); // radius_max
366

367
        packet.putDouble(event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation
368

369 370 371 372 373
        if (pointerKind == kPointerDeviceKindStylus) {
          packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt
        } else {
          packet.putDouble(0.0); // tilt
        }
374 375 376 377
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
A
Adam Barth 已提交
378 379 380 381 382 383 384 385
        // 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);
        }
386

387 388 389 390 391 392 393 394
        // These values must match the unpacking code in hooks.dart.
        final int kPointerDataFieldCount = 19;
        final int kBytePerField = 8;

        int pointerCount = event.getPointerCount();

        ByteBuffer packet = ByteBuffer.allocateDirect(pointerCount * kPointerDataFieldCount * kBytePerField);
        packet.order(ByteOrder.LITTLE_ENDIAN);
395

396 397 398 399 400 401 402
        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) {
403
            addPointerForIndex(event, event.getActionIndex(), packet);
404 405 406 407
        } 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.
408 409
            for (int p = 0; p < pointerCount; p++) {
                addPointerForIndex(event, p, packet);
410 411
            }
        }
412

413 414
        assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0;
        nativeDispatchPointerDataPacket(mNativePlatformView, packet, packet.position());
415 416 417
        return true;
    }

418 419 420 421 422 423 424 425 426 427
    @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;
    }

428 429 430 431
    @Override
    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
        mMetrics.physicalWidth = width;
        mMetrics.physicalHeight = height;
432
        updateViewportMetrics();
433 434 435 436 437 438 439 440 441
        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();
442
        updateViewportMetrics();
443 444 445
        return super.onApplyWindowInsets(insets);
    }

446
    private void attach() {
A
Adam Barth 已提交
447
        mNativePlatformView = nativeAttach(this);
448
    }
449

450
    private void preRun() {
H
Hixie 已提交
451
        resetAccessibilityTree();
452 453 454 455 456
    }

    private void postRun() {
    }

A
Adam Barth 已提交
457
    public void runFromBundle(String bundlePath, String snapshotOverride) {
458
        preRun();
A
Adam Barth 已提交
459
        nativeRunBundleAndSnapshot(mNativePlatformView, bundlePath, snapshotOverride);
460 461 462
        postRun();
    }

A
Adam Barth 已提交
463 464 465
    private void runFromSource(final String assetsDirectory,
                               final String main,
                               final String packages) {
466 467 468
        Runnable runnable = new Runnable() {
            public void run() {
                preRun();
A
Adam Barth 已提交
469
                nativeRunBundleAndSource(mNativePlatformView, assetsDirectory, main, packages);
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
                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);
        }
487 488
    }

489 490 491 492 493
    /** Return the most recent frame as a bitmap. */
    public Bitmap getBitmap() {
        return nativeGetBitmap(mNativePlatformView);
    }

A
Adam Barth 已提交
494
    private static native long nativeAttach(FlutterView view);
495
    private static native int nativeGetObservatoryPort();
496 497
    private static native void nativeDetach(long nativePlatformViewAndroid);
    private static native void nativeSurfaceCreated(long nativePlatformViewAndroid,
498 499 500 501 502
                                                    Surface surface,
                                                    int backgroundColor);
    private static native void nativeSurfaceChanged(long nativePlatformViewAndroid,
                                                    int width,
                                                    int height);
503
    private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid);
A
Adam Barth 已提交
504 505 506 507 508 509 510 511 512

    private static native void nativeRunBundleAndSnapshot(long nativePlatformViewAndroid,
                                                          String bundlePath,
                                                          String snapshotOverride);
    private static native void nativeRunBundleAndSource(long nativePlatformViewAndroid,
                                                        String bundlePath,
                                                        String main,
                                                        String packages);

513 514 515 516 517 518 519 520
    private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid, 
                                                        float devicePixelRatio,
                                                        int physicalWidth,
                                                        int physicalHeight,
                                                        int physicalPaddingTop,
                                                        int physicalPaddingRight,
                                                        int physicalPaddingBottom,
                                                        int physicalPaddingLeft);
521
    private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid);
H
Hixie 已提交
522

523
    // Send a platform message to Dart.
524
    private static native void nativeDispatchPlatformMessage(long nativePlatformViewAndroid, String channel, String message, int responseId);
525
    private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, ByteBuffer buffer, int position);
526 527
    private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id, int action);
    private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled);
528

529 530 531
    // Send a response to a platform message received from Dart.
    private static native void nativeInvokePlatformMessageResponseCallback(long nativePlatformViewAndroid, int responseId, String message);

532 533 534 535 536 537 538 539 540 541 542
    private void updateViewportMetrics() {
        nativeSetViewportMetrics(mNativePlatformView,
                                 mMetrics.devicePixelRatio,
                                 mMetrics.physicalWidth,
                                 mMetrics.physicalHeight,
                                 mMetrics.physicalPaddingTop,
                                 mMetrics.physicalPaddingRight,
                                 mMetrics.physicalPaddingBottom,
                                 mMetrics.physicalPaddingLeft);
    }

543
    // Called by native to send us a platform message.
544
    @CalledByNative
545 546
    private void handlePlatformMessage(String channel, String message, final int responseId) {
        OnMessageListener listener = mOnMessageListeners.get(channel);
547 548 549 550 551
        if (listener != null) {
            nativeInvokePlatformMessageResponseCallback(mNativePlatformView, responseId, listener.onMessage(this, message));
            return;
        }

552
        OnMessageListenerAsync asyncListener = mAsyncOnMessageListeners.get(channel);
553 554
        if (asyncListener != null) {
            asyncListener.onMessage(this, message, new MessageResponse() {
555 556 557 558
                @Override
                public void send(String response) {
                    nativeInvokePlatformMessageResponseCallback(mNativePlatformView, responseId, response);
                }
559 560 561 562 563 564
            });
            return;
        }

        nativeInvokePlatformMessageResponseCallback(mNativePlatformView, responseId, null);
    }
H
Hixie 已提交
565

566 567 568 569 570 571 572 573 574 575 576
    private int mNextResponseId = 1;
    private final Map<Integer, MessageReplyCallback> mPendingResponses = new HashMap<Integer, MessageReplyCallback>();

    // Called by native to respond to a platform message that we sent.
    @CalledByNative
    private void handlePlatformMessageResponse(int responseId, String response) {
        MessageReplyCallback callback = mPendingResponses.remove(responseId);
        if (callback != null)
            callback.onReply(response);
    }

577 578
    @CalledByNative
    private void updateSemantics(ByteBuffer buffer, String[] strings) {
A
Adam Barth 已提交
579 580
        if (mAccessibilityNodeProvider != null) {
            buffer.order(ByteOrder.LITTLE_ENDIAN);
581
            mAccessibilityNodeProvider.updateSemantics(buffer, strings);
A
Adam Barth 已提交
582
        }
583 584
    }

H
Hixie 已提交
585 586
    // ACCESSIBILITY

H
Hixie 已提交
587
    private boolean mAccessibilityEnabled = false;
588
    private boolean mTouchExplorationEnabled = false;
589
    private TouchExplorationListener mTouchExplorationListener;
590

591 592 593 594
    protected void dispatchSemanticsAction(int id, int action) {
        nativeDispatchSemanticsAction(mNativePlatformView, id, action);
    }

H
Hixie 已提交
595 596 597
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
H
Hixie 已提交
598
        mAccessibilityEnabled = mAccessibilityManager.isEnabled();
599
        mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
H
Hixie 已提交
600
        if (mAccessibilityEnabled || mTouchExplorationEnabled)
H
Hixie 已提交
601
          ensureAccessibilityEnabled();
H
Hixie 已提交
602
        resetWillNotDraw();
H
Hixie 已提交
603
        mAccessibilityManager.addAccessibilityStateChangeListener(this);
604 605 606 607 608
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            if (mTouchExplorationListener == null)
                mTouchExplorationListener = new TouchExplorationListener();
            mAccessibilityManager.addTouchExplorationStateChangeListener(mTouchExplorationListener);
        }
H
Hixie 已提交
609 610
    }

H
Hixie 已提交
611 612 613 614
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mAccessibilityManager.removeAccessibilityStateChangeListener(this);
615 616
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
            mAccessibilityManager.removeTouchExplorationStateChangeListener(mTouchExplorationListener);
H
Hixie 已提交
617 618 619 620 621 622
    }

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

H
Hixie 已提交
623 624
    @Override
    public void onAccessibilityStateChanged(boolean enabled) {
H
Hixie 已提交
625 626
        if (enabled) {
            mAccessibilityEnabled = true;
H
Hixie 已提交
627
            ensureAccessibilityEnabled();
H
Hixie 已提交
628 629 630 631 632 633
        } else {
            mAccessibilityEnabled = false;
        }
        if (mAccessibilityNodeProvider != null) {
            mAccessibilityNodeProvider.setAccessibilityEnabled(mAccessibilityEnabled);
        }
H
Hixie 已提交
634
        resetWillNotDraw();
H
Hixie 已提交
635 636
    }

637 638 639 640 641 642 643 644 645 646 647 648
    class TouchExplorationListener
      implements AccessibilityManager.TouchExplorationStateChangeListener {
        @Override
        public void onTouchExplorationStateChanged(boolean enabled) {
            if (enabled) {
                mTouchExplorationEnabled = true;
                ensureAccessibilityEnabled();
            } else {
                mTouchExplorationEnabled = false;
                if (mAccessibilityNodeProvider != null) {
                    mAccessibilityNodeProvider.handleTouchExplorationExit();
                }
649
            }
650
            resetWillNotDraw();
651
        }
H
Hixie 已提交
652 653
    }

H
Hixie 已提交
654 655 656 657 658 659
    @Override
    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
        ensureAccessibilityEnabled();
        return mAccessibilityNodeProvider;
    }

660
    private AccessibilityBridge mAccessibilityNodeProvider;
H
Hixie 已提交
661 662 663

    void ensureAccessibilityEnabled() {
        if (mAccessibilityNodeProvider == null) {
664 665
            mAccessibilityNodeProvider = new AccessibilityBridge(this);
            nativeSetSemanticsEnabled(mNativePlatformView, true);
H
Hixie 已提交
666 667 668
        }
    }

H
Hixie 已提交
669 670
    void resetAccessibilityTree() {
        if (mAccessibilityNodeProvider != null) {
671
            mAccessibilityNodeProvider.reset();
H
Hixie 已提交
672
        }
H
Hixie 已提交
673 674
    }

675 676 677
    private boolean handleAccessibilityHoverEvent(MotionEvent event) {
        if (!mTouchExplorationEnabled)
            return false;
H
Hixie 已提交
678 679 680 681
        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) {
682 683
            mAccessibilityNodeProvider.handleTouchExplorationExit();
        } else {
H
Hixie 已提交
684 685
            Log.d("flutter", "unexpected accessibility hover event: " + event);
            return false;
686 687 688
        }
        return true;
    }
H
Hixie 已提交
689

690 691 692 693 694
    /**
     * Send a message to the Flutter application. The Flutter application can
     * register a platform message handler that will receive these messages with
     * the PlatformMessages object.
     */
695
    public void sendPlatformMessage(String channel, String message, MessageReplyCallback callback) {
696 697 698 699 700
        int responseId = 0;
        if (callback != null) {
            responseId = mNextResponseId++;
            mPendingResponses.put(responseId, callback);
        }
701
        nativeDispatchPlatformMessage(mNativePlatformView, channel, message, responseId);
702 703
    }

704 705 706 707
    /**
     * Send a message to the Flutter application.  The Flutter Dart code can register a
     * host message handler that will receive these messages.
     */
708 709
    public void sendToFlutter(String channel, String message, MessageReplyCallback callback) {
        sendPlatformMessage(channel, message, callback);
710 711
    }

712 713
    public void sendToFlutter(String channel, String message) {
        sendToFlutter(channel, message, null);
714 715 716 717 718 719 720 721 722 723 724
    }

    /** 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.
     */
725 726
    public void addOnMessageListener(String channel, OnMessageListener listener) {
        mOnMessageListeners.put(channel, listener);
727 728 729 730 731 732
    }

    /**
     * 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.
     */
733 734
    public void addOnMessageListenerAsync(String channel, OnMessageListenerAsync listener) {
        mAsyncOnMessageListeners.put(channel, listener);
735 736 737 738 739 740 741
    }

    public interface OnMessageListener {
        /**
         * Called when a message is received from the Flutter app.
         * @return the reply to the message (can be null)
         */
742
        String onMessage(FlutterView view, String message);
743 744 745 746 747 748 749
    };

    public interface OnMessageListenerAsync {
        /**
         * Called when a message is received from the Flutter app.
         * @param response Used to send a reply back to the app.
         */
750
        void onMessage(FlutterView view, String message, MessageResponse response);
751 752 753 754 755 756
    }

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

757 758 759 760 761 762 763 764 765 766 767 768
    /** 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) {}
        }
    }
769
}