FlutterView.java 29.8 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;

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

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

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

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

64 65 66 67 68 69 70 71 72 73
    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 已提交
74
    private long mNativePlatformView;
75 76
    private TextInputPlugin mTextInputPlugin;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    public void pushRoute(String route) {
202 203 204 205 206 207 208 209 210 211
        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);
        }
212 213 214
    }

    public void popRoute() {
215 216 217 218 219 220 221 222
        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);
        }
223 224 225
    }

    private void setLocale(Locale locale) {
226 227 228 229 230 231 232 233 234 235 236
        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);
        }
237 238 239 240 241 242 243 244
    }

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

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

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

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

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

264
    // Must match the PointerChange enum in pointer.dart.
265 266 267 268 269 270
    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;
271 272 273 274 275 276 277 278

    // 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) {
279
        // Primary pointer:
280
        if (maskedAction == MotionEvent.ACTION_DOWN) {
281
            return kPointerChangeDown;
282 283
        }
        if (maskedAction == MotionEvent.ACTION_UP) {
284
            return kPointerChangeUp;
285
        }
286
        // Secondary pointer:
287
        if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
288
            return kPointerChangeDown;
289 290
        }
        if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
291
            return kPointerChangeUp;
292
        }
293
        // All pointers:
294
        if (maskedAction == MotionEvent.ACTION_MOVE) {
295
            return kPointerChangeMove;
296 297
        }
        if (maskedAction == MotionEvent.ACTION_CANCEL) {
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
            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;
314
        }
315 316
    }

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

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

329 330 331 332 333
        long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds.

        packet.putLong(timeStamp); // time_stamp
        packet.putLong(pointerChange); // change
        packet.putLong(pointerKind); // kind
334
        packet.putLong(event.getPointerId(pointerIndex)); // device
335 336 337 338 339 340 341 342 343 344
        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
        }
345

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

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

354 355 356 357 358 359 360 361 362 363
        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
364

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

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

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

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

388 389 390 391 392 393 394 395
        // 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);
396

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

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

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

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

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

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

    private void postRun() {
    }

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

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

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

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

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

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

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

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

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

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

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

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

567 568 569 570 571 572 573 574 575 576 577
    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);
    }

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

H
Hixie 已提交
586 587
    // ACCESSIBILITY

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

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

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

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

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

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

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

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

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

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

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

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

691 692 693 694 695
    /**
     * 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.
     */
696
    public void sendPlatformMessage(String channel, String message, MessageReplyCallback callback) {
697 698 699 700 701
        int responseId = 0;
        if (callback != null) {
            responseId = mNextResponseId++;
            mPendingResponses.put(responseId, callback);
        }
702
        nativeDispatchPlatformMessage(mNativePlatformView, channel, message, responseId);
703 704
    }

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

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

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

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

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

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

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

758 759 760 761
    /** Broadcast receiver used to discover active Flutter instances. */
    private class DiscoveryReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
J
Jason Simmons 已提交
762
            URI observatoryUri = URI.create(nativeGetObservatoryUri());
763 764 765
            JSONObject discover = new JSONObject();
            try {
                discover.put("id", getContext().getPackageName());
J
Jason Simmons 已提交
766
                discover.put("observatoryPort", observatoryUri.getPort());
767 768 769 770
                Log.i(TAG, "DISCOVER: " + discover);
            } catch (JSONException e) {}
        }
    }
771
}