2015-01-31 00:44:42 -08:00
|
|
|
// 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.
|
|
|
|
|
|
2016-04-08 09:39:14 -07:00
|
|
|
package io.flutter.view;
|
2015-01-31 00:44:42 -08:00
|
|
|
|
2016-05-02 14:24:40 -07:00
|
|
|
import android.app.Activity;
|
2016-05-27 17:11:34 -07:00
|
|
|
import android.content.BroadcastReceiver;
|
2015-01-31 00:44:42 -08:00
|
|
|
import android.content.Context;
|
2016-05-27 17:11:34 -07:00
|
|
|
import android.content.Intent;
|
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
|
import android.content.pm.ApplicationInfo;
|
2016-03-14 16:37:58 -07:00
|
|
|
import android.content.res.Configuration;
|
2016-09-16 14:00:49 -07:00
|
|
|
import android.graphics.Bitmap;
|
2016-01-27 13:20:05 -08:00
|
|
|
import android.graphics.Rect;
|
2017-05-31 17:27:47 -07:00
|
|
|
import android.graphics.Canvas;
|
|
|
|
|
import android.graphics.Paint;
|
|
|
|
|
import android.graphics.Matrix;
|
2017-11-02 02:57:29 -07:00
|
|
|
import android.graphics.SurfaceTexture;
|
2015-07-15 11:07:53 -07:00
|
|
|
import android.os.Build;
|
2017-10-18 16:13:43 -07:00
|
|
|
import android.text.format.DateFormat;
|
2016-03-11 14:43:35 -08:00
|
|
|
import android.util.AttributeSet;
|
2016-01-21 11:22:09 -08:00
|
|
|
import android.util.Log;
|
2016-09-16 14:46:36 -07:00
|
|
|
import android.util.TypedValue;
|
2015-11-10 16:16:49 -08:00
|
|
|
import android.view.KeyEvent;
|
2015-02-18 16:57:32 -08:00
|
|
|
import android.view.MotionEvent;
|
2015-01-31 00:44:42 -08:00
|
|
|
import android.view.Surface;
|
|
|
|
|
import android.view.SurfaceHolder;
|
|
|
|
|
import android.view.SurfaceView;
|
2016-01-27 13:20:05 -08:00
|
|
|
import android.view.WindowInsets;
|
2017-09-25 11:25:47 -07:00
|
|
|
import android.view.WindowManager;
|
2016-01-27 13:20:05 -08:00
|
|
|
import android.view.accessibility.AccessibilityManager;
|
|
|
|
|
import android.view.accessibility.AccessibilityNodeProvider;
|
2015-03-09 16:19:56 -07:00
|
|
|
import android.view.inputmethod.EditorInfo;
|
|
|
|
|
import android.view.inputmethod.InputConnection;
|
2017-08-14 15:44:52 -07:00
|
|
|
import io.flutter.app.FlutterActivity;
|
2017-04-18 14:30:31 +02:00
|
|
|
import io.flutter.plugin.common.*;
|
|
|
|
|
import io.flutter.plugin.common.MethodChannel;
|
2017-03-17 09:04:59 +01:00
|
|
|
import io.flutter.plugin.editing.TextInputPlugin;
|
|
|
|
|
import io.flutter.plugin.platform.PlatformPlugin;
|
2017-09-01 11:11:25 -07:00
|
|
|
import io.flutter.view.VsyncWaiter;
|
2015-01-31 00:44:42 -08:00
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
import org.json.JSONException;
|
|
|
|
|
import org.json.JSONObject;
|
2015-01-31 00:44:42 -08:00
|
|
|
|
2016-11-01 17:11:56 -07:00
|
|
|
import java.net.URI;
|
2016-10-03 21:02:46 -07:00
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
import java.nio.ByteOrder;
|
2015-11-09 15:04:09 -08:00
|
|
|
import java.util.ArrayList;
|
2017-03-17 09:04:59 +01:00
|
|
|
import java.util.Arrays;
|
2016-01-27 13:20:05 -08:00
|
|
|
import java.util.HashMap;
|
2015-11-09 15:04:09 -08:00
|
|
|
import java.util.List;
|
2016-03-14 16:37:58 -07:00
|
|
|
import java.util.Locale;
|
2016-01-27 13:20:05 -08:00
|
|
|
import java.util.Map;
|
2017-04-19 21:53:46 +02:00
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
2017-11-02 02:57:29 -07:00
|
|
|
import java.util.concurrent.atomic.AtomicLong;
|
2015-11-09 15:04:09 -08:00
|
|
|
|
2015-01-31 00:44:42 -08:00
|
|
|
/**
|
2016-04-08 09:39:14 -07:00
|
|
|
* An Android view containing a Flutter app.
|
2015-01-31 00:44:42 -08:00
|
|
|
*/
|
2016-04-08 09:39:14 -07:00
|
|
|
public class FlutterView extends SurfaceView
|
2017-11-02 02:57:29 -07:00
|
|
|
implements BinaryMessenger, TextureRegistry, AccessibilityManager.AccessibilityStateChangeListener {
|
2017-03-17 09:04:59 +01:00
|
|
|
|
2017-06-07 12:28:41 -07:00
|
|
|
/**
|
|
|
|
|
* Interface for those objects that maintain and expose a reference to a
|
|
|
|
|
* {@code FlutterView} (such as a full-screen Flutter activity).
|
2017-06-09 07:35:50 -07:00
|
|
|
*
|
|
|
|
|
* <p>This indirection is provided to support applications that use an
|
|
|
|
|
* activity other than {@link io.flutter.app.FlutterActivity} (e.g. Android
|
|
|
|
|
* v4 support library's {@code FragmentActivity}). It allows Flutter plugins
|
|
|
|
|
* to deal in this interface and not require that the activity be a subclass
|
|
|
|
|
* of {@code FlutterActivity}.</p>
|
2017-06-07 12:28:41 -07:00
|
|
|
*/
|
|
|
|
|
public interface Provider {
|
|
|
|
|
/**
|
|
|
|
|
* Returns a reference to the Flutter view maintained by this object.
|
|
|
|
|
* This may be {@code null}.
|
|
|
|
|
*/
|
|
|
|
|
FlutterView getFlutterView();
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-08 09:39:14 -07:00
|
|
|
private static final String TAG = "FlutterView";
|
2015-02-27 16:46:19 -08:00
|
|
|
|
2016-05-27 17:11:34 -07:00
|
|
|
private static final String ACTION_DISCOVER = "io.flutter.view.DISCOVER";
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
static final class ViewportMetrics {
|
2016-10-27 13:12:55 -07:00
|
|
|
float devicePixelRatio = 1.0f;
|
|
|
|
|
int physicalWidth = 0;
|
|
|
|
|
int physicalHeight = 0;
|
|
|
|
|
int physicalPaddingTop = 0;
|
|
|
|
|
int physicalPaddingRight = 0;
|
|
|
|
|
int physicalPaddingBottom = 0;
|
|
|
|
|
int physicalPaddingLeft = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
private final TextInputPlugin mTextInputPlugin;
|
2015-01-31 00:44:42 -08:00
|
|
|
private final SurfaceHolder.Callback mSurfaceCallback;
|
2016-01-21 11:22:09 -08:00
|
|
|
private final ViewportMetrics mMetrics;
|
2016-01-27 13:20:05 -08:00
|
|
|
private final AccessibilityManager mAccessibilityManager;
|
2017-04-18 14:30:31 +02:00
|
|
|
private final MethodChannel mFlutterLocalizationChannel;
|
|
|
|
|
private final MethodChannel mFlutterNavigationChannel;
|
|
|
|
|
private final BasicMessageChannel<Object> mFlutterKeyEventChannel;
|
|
|
|
|
private final BasicMessageChannel<String> mFlutterLifecycleChannel;
|
|
|
|
|
private final BasicMessageChannel<Object> mFlutterSystemChannel;
|
2017-10-18 16:13:43 -07:00
|
|
|
private final BasicMessageChannel<Object> mFlutterSettingsChannel;
|
2017-03-17 09:04:59 +01:00
|
|
|
private final BroadcastReceiver mDiscoveryReceiver;
|
|
|
|
|
private final List<ActivityLifecycleListener> mActivityLifecycleListeners;
|
2017-08-14 15:44:52 -07:00
|
|
|
private final List<FirstFrameListener> mFirstFrameListeners;
|
2017-11-02 02:57:29 -07:00
|
|
|
private final AtomicLong nextTextureId = new AtomicLong(0L);
|
2017-11-09 12:10:00 -08:00
|
|
|
private FlutterNativeView mNativeView;
|
2017-05-31 17:27:47 -07:00
|
|
|
private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not
|
|
|
|
|
|
2016-04-08 09:39:14 -07:00
|
|
|
public FlutterView(Context context) {
|
2016-03-11 14:43:35 -08:00
|
|
|
this(context, null);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-08 09:39:14 -07:00
|
|
|
public FlutterView(Context context, AttributeSet attrs) {
|
2017-11-13 13:56:48 -08:00
|
|
|
this(context, attrs, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
|
2016-03-11 14:43:35 -08:00
|
|
|
super(context, attrs);
|
2015-01-31 00:44:42 -08:00
|
|
|
|
2017-05-31 17:27:47 -07:00
|
|
|
mIsSoftwareRenderingEnabled = nativeGetIsSoftwareRenderingEnabled();
|
|
|
|
|
|
2016-01-21 11:22:09 -08:00
|
|
|
mMetrics = new ViewportMetrics();
|
|
|
|
|
mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
|
2015-01-31 00:44:42 -08:00
|
|
|
setFocusable(true);
|
|
|
|
|
setFocusableInTouchMode(true);
|
|
|
|
|
|
2017-11-13 13:56:48 -08:00
|
|
|
if (nativeView == null) {
|
|
|
|
|
mNativeView = new FlutterNativeView(this);
|
|
|
|
|
} else {
|
|
|
|
|
mNativeView = nativeView;
|
|
|
|
|
mNativeView.setFlutterView(this);
|
|
|
|
|
}
|
2015-01-31 00:44:42 -08:00
|
|
|
|
2016-09-16 14:46:36 -07:00
|
|
|
int color = 0xFF000000;
|
|
|
|
|
TypedValue typedValue = new TypedValue();
|
|
|
|
|
context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true);
|
2017-03-17 09:04:59 +01:00
|
|
|
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT
|
|
|
|
|
&& typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
|
|
|
|
color = typedValue.data;
|
|
|
|
|
}
|
2016-09-16 14:46:36 -07:00
|
|
|
// TODO(abarth): Consider letting the developer override this color.
|
|
|
|
|
final int backgroundColor = color;
|
2015-01-31 00:44:42 -08:00
|
|
|
|
2016-09-16 14:46:36 -07:00
|
|
|
mSurfaceCallback = new SurfaceHolder.Callback() {
|
2015-01-31 00:44:42 -08:00
|
|
|
@Override
|
|
|
|
|
public void surfaceCreated(SurfaceHolder holder) {
|
2017-08-17 10:24:12 -07:00
|
|
|
assertAttached();
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeSurfaceCreated(mNativeView.get(), holder.getSurface(), backgroundColor);
|
2015-01-31 00:44:42 -08:00
|
|
|
}
|
|
|
|
|
|
2016-09-16 14:46:36 -07:00
|
|
|
@Override
|
|
|
|
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
2017-08-17 10:24:12 -07:00
|
|
|
assertAttached();
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeSurfaceChanged(mNativeView.get(), width, height);
|
2016-09-16 14:46:36 -07:00
|
|
|
}
|
|
|
|
|
|
2015-01-31 00:44:42 -08:00
|
|
|
@Override
|
|
|
|
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
2017-08-17 10:24:12 -07:00
|
|
|
assertAttached();
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeSurfaceDestroyed(mNativeView.get());
|
2015-01-31 00:44:42 -08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
getHolder().addCallback(mSurfaceCallback);
|
2015-02-27 16:46:19 -08:00
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
mAccessibilityManager = (AccessibilityManager) getContext()
|
|
|
|
|
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
2016-03-14 16:37:58 -07:00
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
mActivityLifecycleListeners = new ArrayList<>();
|
2017-08-14 15:44:52 -07:00
|
|
|
mFirstFrameListeners = new ArrayList<>();
|
2017-03-17 09:04:59 +01:00
|
|
|
|
|
|
|
|
// Configure the platform plugins and flutter channels.
|
2017-04-18 14:30:31 +02:00
|
|
|
mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization",
|
2017-03-17 09:04:59 +01:00
|
|
|
JSONMethodCodec.INSTANCE);
|
2017-04-18 14:30:31 +02:00
|
|
|
mFlutterNavigationChannel = new MethodChannel(this, "flutter/navigation",
|
2017-03-17 09:04:59 +01:00
|
|
|
JSONMethodCodec.INSTANCE);
|
2017-04-18 14:30:31 +02:00
|
|
|
mFlutterKeyEventChannel = new BasicMessageChannel<>(this, "flutter/keyevent",
|
2017-03-17 09:04:59 +01:00
|
|
|
JSONMessageCodec.INSTANCE);
|
2017-04-18 14:30:31 +02:00
|
|
|
mFlutterLifecycleChannel = new BasicMessageChannel<>(this, "flutter/lifecycle",
|
2017-03-17 09:04:59 +01:00
|
|
|
StringCodec.INSTANCE);
|
2017-04-18 14:30:31 +02:00
|
|
|
mFlutterSystemChannel = new BasicMessageChannel<>(this, "flutter/system",
|
2017-03-17 09:04:59 +01:00
|
|
|
JSONMessageCodec.INSTANCE);
|
2017-10-18 16:13:43 -07:00
|
|
|
mFlutterSettingsChannel = new BasicMessageChannel<>(this, "flutter/settings",
|
|
|
|
|
JSONMessageCodec.INSTANCE);
|
2017-10-17 13:44:09 -07:00
|
|
|
|
|
|
|
|
// TODO(plugins): Change PlatformPlugin to accept a Context. Disable the
|
|
|
|
|
// operations that require an Activity when a Context is passed.
|
|
|
|
|
if (getContext() instanceof Activity) {
|
|
|
|
|
PlatformPlugin platformPlugin = new PlatformPlugin((Activity) getContext());
|
|
|
|
|
MethodChannel flutterPlatformChannel = new MethodChannel(this,
|
|
|
|
|
"flutter/platform", JSONMethodCodec.INSTANCE);
|
|
|
|
|
flutterPlatformChannel.setMethodCallHandler(platformPlugin);
|
|
|
|
|
addActivityLifecycleListener(platformPlugin);
|
|
|
|
|
}
|
2017-06-14 10:24:41 -07:00
|
|
|
mTextInputPlugin = new TextInputPlugin(this);
|
2016-03-16 16:41:58 -07:00
|
|
|
|
2016-03-14 16:37:58 -07:00
|
|
|
setLocale(getResources().getConfiguration().locale);
|
2017-10-18 16:13:43 -07:00
|
|
|
setUserSettings();
|
2016-05-27 17:11:34 -07:00
|
|
|
|
|
|
|
|
if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
2017-03-17 09:04:59 +01:00
|
|
|
mDiscoveryReceiver = new DiscoveryReceiver();
|
|
|
|
|
context.registerReceiver(mDiscoveryReceiver, new IntentFilter(ACTION_DISCOVER));
|
|
|
|
|
} else {
|
|
|
|
|
mDiscoveryReceiver = null;
|
2016-05-27 17:11:34 -07:00
|
|
|
}
|
2015-11-10 16:16:49 -08:00
|
|
|
}
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
private void encodeKeyEvent(KeyEvent event, Map<String, Object> message) {
|
2016-10-17 16:55:42 -07:00
|
|
|
message.put("flags", event.getFlags());
|
2016-10-18 00:57:23 -07:00
|
|
|
message.put("codePoint", event.getUnicodeChar());
|
|
|
|
|
message.put("keyCode", event.getKeyCode());
|
2016-10-17 16:55:42 -07:00
|
|
|
message.put("scanCode", event.getScanCode());
|
|
|
|
|
message.put("metaState", event.getMetaState());
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-10 16:16:49 -08:00
|
|
|
@Override
|
|
|
|
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
2017-03-17 09:04:59 +01:00
|
|
|
if (!isAttached()) {
|
2016-12-12 16:30:26 -08:00
|
|
|
return super.onKeyUp(keyCode, event);
|
2016-10-17 16:55:42 -07:00
|
|
|
}
|
2017-03-17 09:04:59 +01:00
|
|
|
|
|
|
|
|
Map<String, Object> message = new HashMap<>();
|
|
|
|
|
message.put("type", "keyup");
|
|
|
|
|
message.put("keymap", "android");
|
|
|
|
|
encodeKeyEvent(event, message);
|
|
|
|
|
mFlutterKeyEventChannel.send(message);
|
2016-10-19 16:46:55 -07:00
|
|
|
return super.onKeyUp(keyCode, event);
|
2015-11-10 16:16:49 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
2017-03-17 09:04:59 +01:00
|
|
|
if (!isAttached()) {
|
2016-12-12 16:30:26 -08:00
|
|
|
return super.onKeyDown(keyCode, event);
|
2016-10-17 16:55:42 -07:00
|
|
|
}
|
2017-03-17 09:04:59 +01:00
|
|
|
|
|
|
|
|
Map<String, Object> message = new HashMap<>();
|
|
|
|
|
message.put("type", "keydown");
|
|
|
|
|
message.put("keymap", "android");
|
|
|
|
|
encodeKeyEvent(event, message);
|
|
|
|
|
mFlutterKeyEventChannel.send(message);
|
2016-10-19 16:46:55 -07:00
|
|
|
return super.onKeyDown(keyCode, event);
|
2015-01-31 00:44:42 -08:00
|
|
|
}
|
|
|
|
|
|
2017-11-13 13:56:48 -08:00
|
|
|
public FlutterNativeView getFlutterNativeView() {
|
|
|
|
|
return mNativeView;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 10:12:32 -07:00
|
|
|
public void addActivityLifecycleListener(ActivityLifecycleListener listener) {
|
|
|
|
|
mActivityLifecycleListeners.add(listener);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-14 16:37:58 -07:00
|
|
|
public void onPause() {
|
2017-03-17 09:04:59 +01:00
|
|
|
mFlutterLifecycleChannel.send("AppLifecycleState.paused");
|
2016-03-14 16:37:58 -07:00
|
|
|
}
|
|
|
|
|
|
2016-05-02 14:24:40 -07:00
|
|
|
public void onPostResume() {
|
2017-03-17 09:04:59 +01:00
|
|
|
for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
|
2016-07-12 10:12:32 -07:00
|
|
|
listener.onPostResume();
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
|
|
|
|
mFlutterLifecycleChannel.send("AppLifecycleState.resumed");
|
2016-03-14 16:37:58 -07:00
|
|
|
}
|
|
|
|
|
|
2017-05-24 16:34:34 -07:00
|
|
|
public void onStop() {
|
|
|
|
|
mFlutterLifecycleChannel.send("AppLifecycleState.suspending");
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-14 15:42:30 -07:00
|
|
|
public void onMemoryPressure() {
|
2017-03-17 09:04:59 +01:00
|
|
|
Map<String, Object> message = new HashMap<>(1);
|
|
|
|
|
message.put("type", "memoryPressure");
|
|
|
|
|
mFlutterSystemChannel.send(message);
|
2017-03-14 15:42:30 -07:00
|
|
|
}
|
|
|
|
|
|
2017-08-14 15:44:52 -07:00
|
|
|
/**
|
|
|
|
|
* Provide a listener that will be called once when the FlutterView renders its first frame
|
|
|
|
|
* to the underlaying SurfaceView.
|
|
|
|
|
*/
|
|
|
|
|
public void addFirstFrameListener(FirstFrameListener listener) {
|
|
|
|
|
mFirstFrameListeners.add(listener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove an existing first frame listener.
|
|
|
|
|
*/
|
|
|
|
|
public void removeFirstFrameListener(FirstFrameListener listener) {
|
|
|
|
|
mFirstFrameListeners.remove(listener);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-06 10:59:41 +02:00
|
|
|
public void setInitialRoute(String route) {
|
|
|
|
|
mFlutterNavigationChannel.invokeMethod("setInitialRoute", route);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-14 16:37:58 -07:00
|
|
|
public void pushRoute(String route) {
|
2017-03-17 09:04:59 +01:00
|
|
|
mFlutterNavigationChannel.invokeMethod("pushRoute", route);
|
2016-03-14 16:37:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void popRoute() {
|
2017-03-17 09:04:59 +01:00
|
|
|
mFlutterNavigationChannel.invokeMethod("popRoute", null);
|
2016-03-14 16:37:58 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-18 16:13:43 -07:00
|
|
|
private void setUserSettings() {
|
2017-09-29 13:19:06 -07:00
|
|
|
Map<String, Object> message = new HashMap<>();
|
2017-10-18 16:13:43 -07:00
|
|
|
message.put("textScaleFactor", getResources().getConfiguration().fontScale);
|
|
|
|
|
message.put("alwaysUse24HourFormat", DateFormat.is24HourFormat(getContext()));
|
|
|
|
|
mFlutterSettingsChannel.send(message);
|
2017-09-29 13:19:06 -07:00
|
|
|
}
|
2017-10-17 13:44:09 -07:00
|
|
|
|
2016-03-14 16:37:58 -07:00
|
|
|
private void setLocale(Locale locale) {
|
2017-03-17 09:04:59 +01:00
|
|
|
mFlutterLocalizationChannel.invokeMethod("setLocale",
|
|
|
|
|
Arrays.asList(locale.getLanguage(), locale.getCountry()));
|
2016-03-14 16:37:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onConfigurationChanged(Configuration newConfig) {
|
|
|
|
|
super.onConfigurationChanged(newConfig);
|
|
|
|
|
setLocale(newConfig.locale);
|
2017-10-18 16:13:43 -07:00
|
|
|
setUserSettings();
|
2016-03-14 16:37:58 -07:00
|
|
|
}
|
|
|
|
|
|
2016-01-27 13:20:05 -08:00
|
|
|
float getDevicePixelRatio() {
|
|
|
|
|
return mMetrics.devicePixelRatio;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-13 13:56:48 -08:00
|
|
|
public FlutterNativeView detach() {
|
|
|
|
|
if (!isAttached())
|
|
|
|
|
return null;
|
|
|
|
|
if (mDiscoveryReceiver != null) {
|
|
|
|
|
getContext().unregisterReceiver(mDiscoveryReceiver);
|
|
|
|
|
}
|
|
|
|
|
getHolder().removeCallback(mSurfaceCallback);
|
|
|
|
|
mNativeView.detach();
|
|
|
|
|
|
|
|
|
|
FlutterNativeView view = mNativeView;
|
|
|
|
|
mNativeView = null;
|
|
|
|
|
return view;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-11 14:43:35 -08:00
|
|
|
public void destroy() {
|
2017-08-17 10:24:12 -07:00
|
|
|
if (!isAttached())
|
|
|
|
|
return;
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
if (mDiscoveryReceiver != null) {
|
|
|
|
|
getContext().unregisterReceiver(mDiscoveryReceiver);
|
2016-05-27 17:11:34 -07:00
|
|
|
}
|
|
|
|
|
|
2015-01-31 00:44:42 -08:00
|
|
|
getHolder().removeCallback(mSurfaceCallback);
|
2017-11-13 13:16:48 -08:00
|
|
|
|
|
|
|
|
mNativeView.destroy();
|
|
|
|
|
mNativeView = null;
|
2015-01-31 00:44:42 -08:00
|
|
|
}
|
|
|
|
|
|
2015-03-09 16:19:56 -07:00
|
|
|
@Override
|
|
|
|
|
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
2017-03-17 09:04:59 +01:00
|
|
|
try {
|
|
|
|
|
return mTextInputPlugin.createInputConnection(this, outAttrs);
|
|
|
|
|
} catch (JSONException e) {
|
|
|
|
|
Log.e(TAG, "Failed to create input connection", e);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2015-03-09 16:19:56 -07:00
|
|
|
}
|
|
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
// Must match the PointerChange enum in pointer.dart.
|
2016-10-14 15:46:46 -07:00
|
|
|
private static final int kPointerChangeCancel = 0;
|
|
|
|
|
private static final int kPointerChangeAdd = 1;
|
|
|
|
|
private static final int kPointerChangeRemove = 2;
|
2016-11-16 10:13:28 -08:00
|
|
|
private static final int kPointerChangeHover = 3;
|
|
|
|
|
private static final int kPointerChangeDown = 4;
|
|
|
|
|
private static final int kPointerChangeMove = 5;
|
|
|
|
|
private static final int kPointerChangeUp = 6;
|
2016-10-03 21:02:46 -07:00
|
|
|
|
|
|
|
|
// 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) {
|
2015-02-26 12:23:18 -08:00
|
|
|
// Primary pointer:
|
2015-06-10 16:19:49 -07:00
|
|
|
if (maskedAction == MotionEvent.ACTION_DOWN) {
|
2016-10-03 21:02:46 -07:00
|
|
|
return kPointerChangeDown;
|
2015-06-10 16:19:49 -07:00
|
|
|
}
|
|
|
|
|
if (maskedAction == MotionEvent.ACTION_UP) {
|
2016-10-03 21:02:46 -07:00
|
|
|
return kPointerChangeUp;
|
2015-06-10 16:19:49 -07:00
|
|
|
}
|
2015-02-26 12:23:18 -08:00
|
|
|
// Secondary pointer:
|
2015-06-10 16:19:49 -07:00
|
|
|
if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
|
2016-10-03 21:02:46 -07:00
|
|
|
return kPointerChangeDown;
|
2015-06-10 16:19:49 -07:00
|
|
|
}
|
|
|
|
|
if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
|
2016-10-03 21:02:46 -07:00
|
|
|
return kPointerChangeUp;
|
2015-06-10 16:19:49 -07:00
|
|
|
}
|
2015-02-26 12:23:18 -08:00
|
|
|
// All pointers:
|
2015-06-10 16:19:49 -07:00
|
|
|
if (maskedAction == MotionEvent.ACTION_MOVE) {
|
2016-10-03 21:02:46 -07:00
|
|
|
return kPointerChangeMove;
|
2015-06-10 16:19:49 -07:00
|
|
|
}
|
|
|
|
|
if (maskedAction == MotionEvent.ACTION_CANCEL) {
|
2016-10-03 21:02:46 -07:00
|
|
|
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;
|
2015-06-10 16:19:49 -07:00
|
|
|
}
|
2015-02-18 16:57:32 -08:00
|
|
|
}
|
|
|
|
|
|
2015-11-09 15:04:09 -08:00
|
|
|
private void addPointerForIndex(MotionEvent event, int pointerIndex,
|
2017-03-17 09:04:59 +01:00
|
|
|
ByteBuffer packet) {
|
2016-10-03 21:02:46 -07:00
|
|
|
int pointerChange = getPointerChangeForAction(event.getActionMasked());
|
|
|
|
|
if (pointerChange == -1) {
|
2015-11-09 15:04:09 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
int pointerKind = event.getToolType(pointerIndex);
|
|
|
|
|
if (pointerKind == -1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-11-09 15:04:09 -08:00
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds.
|
2015-11-09 15:04:09 -08:00
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
packet.putLong(timeStamp); // time_stamp
|
|
|
|
|
packet.putLong(pointerChange); // change
|
|
|
|
|
packet.putLong(pointerKind); // kind
|
2016-11-15 20:18:22 -08:00
|
|
|
packet.putLong(event.getPointerId(pointerIndex)); // device
|
2016-10-03 21:02:46 -07:00
|
|
|
packet.putDouble(event.getX(pointerIndex)); // physical_x
|
|
|
|
|
packet.putDouble(event.getY(pointerIndex)); // physical_y
|
|
|
|
|
|
|
|
|
|
if (pointerKind == kPointerDeviceKindMouse) {
|
2017-03-17 09:04:59 +01:00
|
|
|
packet.putLong(event.getButtonState() & 0x1F); // buttons
|
2016-10-03 21:02:46 -07:00
|
|
|
} else if (pointerKind == kPointerDeviceKindStylus) {
|
2017-03-17 09:04:59 +01:00
|
|
|
packet.putLong((event.getButtonState() >> 4) & 0xF); // buttons
|
2016-10-03 21:02:46 -07:00
|
|
|
} else {
|
2017-03-17 09:04:59 +01:00
|
|
|
packet.putLong(0); // buttons
|
2016-10-03 21:02:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
packet.putLong(0); // obscured
|
2015-02-18 16:57:32 -08:00
|
|
|
|
2015-03-02 12:38:29 -08:00
|
|
|
// TODO(eseidel): Could get the calibrated range if necessary:
|
|
|
|
|
// event.getDevice().getMotionRange(MotionEvent.AXIS_PRESSURE)
|
2017-03-17 09:04:59 +01:00
|
|
|
packet.putDouble(event.getPressure(pointerIndex)); // pressure
|
2016-10-03 21:02:46 -07:00
|
|
|
packet.putDouble(0.0); // pressure_min
|
|
|
|
|
packet.putDouble(1.0); // pressure_max
|
2015-03-02 12:38:29 -08:00
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
if (pointerKind == kPointerDeviceKindStylus) {
|
2017-03-17 09:04:59 +01:00
|
|
|
packet
|
|
|
|
|
.putDouble(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex)); // distance
|
|
|
|
|
packet.putDouble(0.0); // distance_max
|
2016-10-03 21:02:46 -07:00
|
|
|
} else {
|
2017-03-17 09:04:59 +01:00
|
|
|
packet.putDouble(0.0); // distance
|
|
|
|
|
packet.putDouble(0.0); // distance_max
|
2016-10-03 21:02:46 -07:00
|
|
|
}
|
2015-02-18 16:57:32 -08:00
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
packet.putDouble(event.getToolMajor(pointerIndex)); // radius_major
|
|
|
|
|
packet.putDouble(event.getToolMinor(pointerIndex)); // radius_minor
|
2015-11-09 15:04:09 -08:00
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
packet.putDouble(0.0); // radius_min
|
|
|
|
|
packet.putDouble(0.0); // radius_max
|
2015-11-09 15:04:09 -08:00
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
packet.putDouble(
|
|
|
|
|
event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation
|
2016-10-03 21:02:46 -07:00
|
|
|
|
|
|
|
|
if (pointerKind == kPointerDeviceKindStylus) {
|
2017-03-17 09:04:59 +01:00
|
|
|
packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt
|
2016-10-03 21:02:46 -07:00
|
|
|
} else {
|
2017-03-17 09:04:59 +01:00
|
|
|
packet.putDouble(0.0); // tilt
|
2016-10-03 21:02:46 -07:00
|
|
|
}
|
2015-02-26 12:23:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onTouchEvent(MotionEvent event) {
|
2017-03-17 09:04:59 +01:00
|
|
|
if (!isAttached()) {
|
2016-12-12 16:30:26 -08:00
|
|
|
return false;
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
2016-12-12 16:30:26 -08:00
|
|
|
|
2015-07-15 11:07:53 -07:00
|
|
|
// 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);
|
|
|
|
|
}
|
2015-02-27 16:46:19 -08:00
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
// These values must match the unpacking code in hooks.dart.
|
|
|
|
|
final int kPointerDataFieldCount = 19;
|
|
|
|
|
final int kBytePerField = 8;
|
|
|
|
|
|
|
|
|
|
int pointerCount = event.getPointerCount();
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
ByteBuffer packet = ByteBuffer
|
|
|
|
|
.allocateDirect(pointerCount * kPointerDataFieldCount * kBytePerField);
|
2016-10-03 21:02:46 -07:00
|
|
|
packet.order(ByteOrder.LITTLE_ENDIAN);
|
2015-11-09 15:04:09 -08:00
|
|
|
|
2015-02-26 12:23:18 -08:00
|
|
|
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
|
2017-03-17 09:04:59 +01:00
|
|
|
|| maskedAction == MotionEvent.ACTION_POINTER_UP
|
|
|
|
|
|| maskedAction == MotionEvent.ACTION_DOWN
|
|
|
|
|
|| maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
|
2016-10-03 21:02:46 -07:00
|
|
|
addPointerForIndex(event, event.getActionIndex(), packet);
|
2015-02-26 12:23:18 -08:00
|
|
|
} 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.
|
2016-10-03 21:02:46 -07:00
|
|
|
for (int p = 0; p < pointerCount; p++) {
|
|
|
|
|
addPointerForIndex(event, p, packet);
|
2015-02-26 12:23:18 -08:00
|
|
|
}
|
|
|
|
|
}
|
2015-11-09 15:04:09 -08:00
|
|
|
|
2016-10-03 21:02:46 -07:00
|
|
|
assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0;
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position());
|
2015-02-18 16:57:32 -08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-04 16:56:07 -08:00
|
|
|
@Override
|
|
|
|
|
public boolean onHoverEvent(MotionEvent event) {
|
2017-03-17 09:04:59 +01:00
|
|
|
if (!isAttached()) {
|
2016-12-12 16:30:26 -08:00
|
|
|
return false;
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
2016-12-12 16:30:26 -08:00
|
|
|
|
2016-02-04 16:56:07 -08:00
|
|
|
boolean handled = handleAccessibilityHoverEvent(event);
|
|
|
|
|
if (!handled) {
|
|
|
|
|
// TODO(ianh): Expose hover events to the platform,
|
|
|
|
|
// implementing ADD, REMOVE, etc.
|
|
|
|
|
}
|
|
|
|
|
return handled;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-21 11:22:09 -08:00
|
|
|
@Override
|
|
|
|
|
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
|
|
|
|
|
mMetrics.physicalWidth = width;
|
|
|
|
|
mMetrics.physicalHeight = height;
|
2016-10-27 13:12:55 -07:00
|
|
|
updateViewportMetrics();
|
2016-01-21 11:22:09 -08:00
|
|
|
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();
|
2016-10-27 13:12:55 -07:00
|
|
|
updateViewportMetrics();
|
2016-01-21 11:22:09 -08:00
|
|
|
return super.onApplyWindowInsets(insets);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-17 15:36:47 -08:00
|
|
|
@Override
|
|
|
|
|
@SuppressWarnings("deprecation")
|
|
|
|
|
protected boolean fitSystemWindows(Rect insets) {
|
|
|
|
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
|
|
|
|
mMetrics.physicalPaddingTop = insets.top;
|
|
|
|
|
mMetrics.physicalPaddingRight = insets.right;
|
|
|
|
|
mMetrics.physicalPaddingBottom = insets.bottom;
|
|
|
|
|
mMetrics.physicalPaddingLeft = insets.left;
|
|
|
|
|
updateViewportMetrics();
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return super.fitSystemWindows(insets);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-12 16:30:26 -08:00
|
|
|
private boolean isAttached() {
|
2017-11-09 12:10:00 -08:00
|
|
|
return mNativeView.isAttached();
|
2016-12-12 16:30:26 -08:00
|
|
|
}
|
|
|
|
|
|
2017-08-17 10:24:12 -07:00
|
|
|
void assertAttached() {
|
2017-11-09 12:10:00 -08:00
|
|
|
mNativeView.assertAttached();
|
2017-08-17 10:24:12 -07:00
|
|
|
}
|
|
|
|
|
|
2016-07-20 10:35:45 -07:00
|
|
|
private void preRun() {
|
2016-01-27 13:20:05 -08:00
|
|
|
resetAccessibilityTree();
|
2016-07-20 10:35:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void postRun() {
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-28 11:46:57 -07:00
|
|
|
public void runFromBundle(String bundlePath, String snapshotOverride) {
|
2017-10-20 10:17:21 -07:00
|
|
|
runFromBundle(bundlePath, snapshotOverride, "main", false);
|
2017-10-18 14:19:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void runFromBundle(String bundlePath, String snapshotOverride, String entrypoint) {
|
2017-10-20 10:17:21 -07:00
|
|
|
runFromBundle(bundlePath, snapshotOverride, entrypoint, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void runFromBundle(String bundlePath, String snapshotOverride, String entrypoint, boolean reuseRuntimeController) {
|
2017-08-17 10:24:12 -07:00
|
|
|
assertAttached();
|
2016-07-20 10:35:45 -07:00
|
|
|
preRun();
|
2017-11-09 12:10:00 -08:00
|
|
|
mNativeView.runFromBundle(bundlePath, snapshotOverride, entrypoint, reuseRuntimeController);
|
2016-07-20 10:35:45 -07:00
|
|
|
postRun();
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-09 12:10:00 -08:00
|
|
|
private void runFromSource(final String assetsDirectory, final String main, final String packages) {
|
2016-08-18 14:00:25 -07:00
|
|
|
Runnable runnable = new Runnable() {
|
|
|
|
|
public void run() {
|
2017-08-17 10:24:12 -07:00
|
|
|
assertAttached();
|
2016-08-18 14:00:25 -07:00
|
|
|
preRun();
|
2017-11-09 12:10:00 -08:00
|
|
|
mNativeView.runFromSource(assetsDirectory, main, packages);
|
2016-08-18 14:00:25 -07:00
|
|
|
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 " +
|
2017-03-17 09:04:59 +01:00
|
|
|
"RunFromSourceRunnable to finish", e);
|
2016-08-18 14:00:25 -07:00
|
|
|
}
|
2015-02-18 16:57:32 -08:00
|
|
|
}
|
|
|
|
|
|
2017-02-03 16:25:25 -08:00
|
|
|
/**
|
|
|
|
|
* Return the most recent frame as a bitmap.
|
2017-03-17 09:04:59 +01:00
|
|
|
*
|
2017-02-03 16:25:25 -08:00
|
|
|
* @return A bitmap.
|
|
|
|
|
*/
|
2016-09-16 14:00:49 -07:00
|
|
|
public Bitmap getBitmap() {
|
2017-08-17 10:24:12 -07:00
|
|
|
assertAttached();
|
2017-11-09 12:10:00 -08:00
|
|
|
return nativeGetBitmap(mNativeView.get());
|
2016-09-16 14:00:49 -07:00
|
|
|
}
|
|
|
|
|
|
2015-06-10 16:19:49 -07:00
|
|
|
private static native void nativeSurfaceCreated(long nativePlatformViewAndroid,
|
2017-03-17 09:04:59 +01:00
|
|
|
Surface surface,
|
|
|
|
|
int backgroundColor);
|
|
|
|
|
|
2016-10-11 11:14:07 -07:00
|
|
|
private static native void nativeSurfaceChanged(long nativePlatformViewAndroid,
|
2017-03-17 09:04:59 +01:00
|
|
|
int width,
|
|
|
|
|
int height);
|
|
|
|
|
|
2015-06-10 16:19:49 -07:00
|
|
|
private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid);
|
2016-10-28 11:46:57 -07:00
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid,
|
|
|
|
|
float devicePixelRatio,
|
|
|
|
|
int physicalWidth,
|
|
|
|
|
int physicalHeight,
|
|
|
|
|
int physicalPaddingTop,
|
|
|
|
|
int physicalPaddingRight,
|
|
|
|
|
int physicalPaddingBottom,
|
|
|
|
|
int physicalPaddingLeft);
|
2016-10-28 11:46:57 -07:00
|
|
|
|
2016-09-16 14:00:49 -07:00
|
|
|
private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid);
|
2016-01-27 13:20:05 -08:00
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid,
|
|
|
|
|
ByteBuffer buffer, int position);
|
|
|
|
|
|
|
|
|
|
private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id,
|
|
|
|
|
int action);
|
|
|
|
|
|
|
|
|
|
private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid,
|
|
|
|
|
boolean enabled);
|
2016-10-07 12:05:43 -07:00
|
|
|
|
2017-05-31 17:27:47 -07:00
|
|
|
private static native boolean nativeGetIsSoftwareRenderingEnabled();
|
|
|
|
|
|
2017-11-02 02:57:29 -07:00
|
|
|
private static native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, SurfaceTexture surfaceTexture);
|
|
|
|
|
|
|
|
|
|
private static native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId);
|
|
|
|
|
|
|
|
|
|
private static native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId);
|
|
|
|
|
|
2016-10-27 13:12:55 -07:00
|
|
|
private void updateViewportMetrics() {
|
2017-08-17 10:24:12 -07:00
|
|
|
if (!isAttached())
|
|
|
|
|
return;
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeSetViewportMetrics(mNativeView.get(),
|
2017-03-17 09:04:59 +01:00
|
|
|
mMetrics.devicePixelRatio,
|
|
|
|
|
mMetrics.physicalWidth,
|
|
|
|
|
mMetrics.physicalHeight,
|
|
|
|
|
mMetrics.physicalPaddingTop,
|
|
|
|
|
mMetrics.physicalPaddingRight,
|
|
|
|
|
mMetrics.physicalPaddingBottom,
|
|
|
|
|
mMetrics.physicalPaddingLeft);
|
2017-09-01 11:11:25 -07:00
|
|
|
|
2017-09-25 11:25:47 -07:00
|
|
|
WindowManager wm = (WindowManager) getContext()
|
|
|
|
|
.getSystemService(Context.WINDOW_SERVICE);
|
|
|
|
|
float fps = wm.getDefaultDisplay().getRefreshRate();
|
2017-09-01 11:11:25 -07:00
|
|
|
VsyncWaiter.refreshPeriodNanos = (long)(1000000000.0 / fps);
|
2016-10-27 13:12:55 -07:00
|
|
|
}
|
|
|
|
|
|
2017-06-22 11:19:32 -07:00
|
|
|
// Called by native to update the semantics/accessibility tree.
|
2017-11-09 12:10:00 -08:00
|
|
|
public void updateSemantics(ByteBuffer buffer, String[] strings) {
|
2017-06-22 11:19:32 -07:00
|
|
|
try {
|
|
|
|
|
if (mAccessibilityNodeProvider != null) {
|
|
|
|
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
|
mAccessibilityNodeProvider.updateSemantics(buffer, strings);
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
Log.e(TAG, "Uncaught exception while updating semantics", ex);
|
2016-10-11 13:27:11 -07:00
|
|
|
}
|
2016-10-11 10:52:48 -07:00
|
|
|
}
|
|
|
|
|
|
2017-08-14 15:44:52 -07:00
|
|
|
// Called by native to notify first Flutter frame rendered.
|
2017-11-09 12:10:00 -08:00
|
|
|
public void onFirstFrame() {
|
2017-08-14 15:44:52 -07:00
|
|
|
for (FirstFrameListener listener : mFirstFrameListeners) {
|
|
|
|
|
listener.onFirstFrame();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-27 13:20:05 -08:00
|
|
|
// ACCESSIBILITY
|
|
|
|
|
|
2016-02-05 10:04:15 -08:00
|
|
|
private boolean mAccessibilityEnabled = false;
|
2016-02-04 16:56:07 -08:00
|
|
|
private boolean mTouchExplorationEnabled = false;
|
2016-10-19 14:59:22 -07:00
|
|
|
private TouchExplorationListener mTouchExplorationListener;
|
2016-02-04 16:56:07 -08:00
|
|
|
|
2016-10-11 10:52:48 -07:00
|
|
|
protected void dispatchSemanticsAction(int id, int action) {
|
2017-08-17 10:24:12 -07:00
|
|
|
if (!isAttached())
|
|
|
|
|
return;
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeDispatchSemanticsAction(mNativeView.get(), id, action);
|
2016-10-11 10:52:48 -07:00
|
|
|
}
|
|
|
|
|
|
2016-01-27 13:20:05 -08:00
|
|
|
@Override
|
|
|
|
|
protected void onAttachedToWindow() {
|
|
|
|
|
super.onAttachedToWindow();
|
2016-02-05 10:04:15 -08:00
|
|
|
mAccessibilityEnabled = mAccessibilityManager.isEnabled();
|
2016-02-04 16:56:07 -08:00
|
|
|
mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
|
2017-03-17 09:04:59 +01:00
|
|
|
if (mAccessibilityEnabled || mTouchExplorationEnabled) {
|
|
|
|
|
ensureAccessibilityEnabled();
|
|
|
|
|
}
|
2016-02-08 10:11:24 -08:00
|
|
|
resetWillNotDraw();
|
2016-01-27 13:20:05 -08:00
|
|
|
mAccessibilityManager.addAccessibilityStateChangeListener(this);
|
2016-10-19 14:59:22 -07:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
2017-03-17 09:04:59 +01:00
|
|
|
if (mTouchExplorationListener == null) {
|
2016-10-19 14:59:22 -07:00
|
|
|
mTouchExplorationListener = new TouchExplorationListener();
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
2016-10-19 14:59:22 -07:00
|
|
|
mAccessibilityManager.addTouchExplorationStateChangeListener(mTouchExplorationListener);
|
|
|
|
|
}
|
2016-01-27 13:20:05 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-08 10:11:24 -08:00
|
|
|
@Override
|
|
|
|
|
protected void onDetachedFromWindow() {
|
|
|
|
|
super.onDetachedFromWindow();
|
|
|
|
|
mAccessibilityManager.removeAccessibilityStateChangeListener(this);
|
2017-03-17 09:04:59 +01:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
|
|
|
mAccessibilityManager
|
|
|
|
|
.removeTouchExplorationStateChangeListener(mTouchExplorationListener);
|
|
|
|
|
}
|
2016-02-08 10:11:24 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void resetWillNotDraw() {
|
2017-05-31 17:27:47 -07:00
|
|
|
if (!mIsSoftwareRenderingEnabled) {
|
|
|
|
|
setWillNotDraw(!(mAccessibilityEnabled || mTouchExplorationEnabled));
|
|
|
|
|
} else {
|
|
|
|
|
setWillNotDraw(false);
|
|
|
|
|
}
|
2016-02-08 10:11:24 -08:00
|
|
|
}
|
|
|
|
|
|
2016-01-27 13:20:05 -08:00
|
|
|
@Override
|
|
|
|
|
public void onAccessibilityStateChanged(boolean enabled) {
|
2016-02-05 10:04:15 -08:00
|
|
|
if (enabled) {
|
2016-01-27 13:20:05 -08:00
|
|
|
ensureAccessibilityEnabled();
|
2016-02-05 10:04:15 -08:00
|
|
|
} else {
|
|
|
|
|
mAccessibilityEnabled = false;
|
2017-05-16 10:31:53 -07:00
|
|
|
if (mAccessibilityNodeProvider != null) {
|
|
|
|
|
mAccessibilityNodeProvider.setAccessibilityEnabled(false);
|
|
|
|
|
}
|
2016-02-05 10:04:15 -08:00
|
|
|
}
|
2016-02-08 10:11:24 -08:00
|
|
|
resetWillNotDraw();
|
2016-01-27 13:20:05 -08:00
|
|
|
}
|
|
|
|
|
|
2016-10-19 14:59:22 -07:00
|
|
|
class TouchExplorationListener
|
2017-03-17 09:04:59 +01:00
|
|
|
implements AccessibilityManager.TouchExplorationStateChangeListener {
|
|
|
|
|
|
2016-10-19 14:59:22 -07:00
|
|
|
@Override
|
|
|
|
|
public void onTouchExplorationStateChanged(boolean enabled) {
|
|
|
|
|
if (enabled) {
|
|
|
|
|
mTouchExplorationEnabled = true;
|
|
|
|
|
ensureAccessibilityEnabled();
|
|
|
|
|
} else {
|
|
|
|
|
mTouchExplorationEnabled = false;
|
|
|
|
|
if (mAccessibilityNodeProvider != null) {
|
|
|
|
|
mAccessibilityNodeProvider.handleTouchExplorationExit();
|
|
|
|
|
}
|
2016-02-04 16:56:07 -08:00
|
|
|
}
|
2016-10-19 14:59:22 -07:00
|
|
|
resetWillNotDraw();
|
2016-02-04 16:56:07 -08:00
|
|
|
}
|
2016-01-27 13:20:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
|
|
|
|
|
ensureAccessibilityEnabled();
|
|
|
|
|
return mAccessibilityNodeProvider;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-08 09:39:14 -07:00
|
|
|
private AccessibilityBridge mAccessibilityNodeProvider;
|
2016-02-04 09:57:33 -08:00
|
|
|
|
|
|
|
|
void ensureAccessibilityEnabled() {
|
2017-08-17 10:24:12 -07:00
|
|
|
if (!isAttached())
|
|
|
|
|
return;
|
2017-05-16 10:31:53 -07:00
|
|
|
mAccessibilityEnabled = true;
|
2016-02-04 09:57:33 -08:00
|
|
|
if (mAccessibilityNodeProvider == null) {
|
2016-10-11 10:52:48 -07:00
|
|
|
mAccessibilityNodeProvider = new AccessibilityBridge(this);
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeSetSemanticsEnabled(mNativeView.get(), true);
|
2016-02-04 09:57:33 -08:00
|
|
|
}
|
2017-05-16 10:31:53 -07:00
|
|
|
mAccessibilityNodeProvider.setAccessibilityEnabled(true);
|
2016-02-04 09:57:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void resetAccessibilityTree() {
|
|
|
|
|
if (mAccessibilityNodeProvider != null) {
|
2016-10-11 10:52:48 -07:00
|
|
|
mAccessibilityNodeProvider.reset();
|
2016-02-04 09:57:33 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-04 16:56:07 -08:00
|
|
|
private boolean handleAccessibilityHoverEvent(MotionEvent event) {
|
2017-03-17 09:04:59 +01:00
|
|
|
if (!mTouchExplorationEnabled) {
|
2016-02-04 16:56:07 -08:00
|
|
|
return false;
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
2016-02-08 10:11:24 -08:00
|
|
|
if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER ||
|
2017-03-17 09:04:59 +01:00
|
|
|
event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
|
2016-02-08 10:11:24 -08:00
|
|
|
mAccessibilityNodeProvider.handleTouchExploration(event.getX(), event.getY());
|
|
|
|
|
} else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
|
2016-02-04 16:56:07 -08:00
|
|
|
mAccessibilityNodeProvider.handleTouchExplorationExit();
|
|
|
|
|
} else {
|
2016-02-08 10:11:24 -08:00
|
|
|
Log.d("flutter", "unexpected accessibility hover event: " + event);
|
|
|
|
|
return false;
|
2016-02-04 16:56:07 -08:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-01-27 13:20:05 -08:00
|
|
|
|
2017-04-18 14:30:31 +02:00
|
|
|
@Override
|
|
|
|
|
public void send(String channel, ByteBuffer message) {
|
2017-11-09 12:10:00 -08:00
|
|
|
mNativeView.send(channel, message);
|
2017-04-18 14:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void send(String channel, ByteBuffer message, BinaryReply callback) {
|
2017-11-09 12:10:00 -08:00
|
|
|
mNativeView.send(channel, message, callback);
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-18 14:30:31 +02:00
|
|
|
@Override
|
|
|
|
|
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
|
2017-11-09 12:10:00 -08:00
|
|
|
mNativeView.setMessageHandler(channel, handler);
|
2016-03-16 16:41:58 -07:00
|
|
|
}
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
/**
|
|
|
|
|
* Broadcast receiver used to discover active Flutter instances.
|
2017-05-16 10:31:53 -07:00
|
|
|
*
|
|
|
|
|
* This is used by the `flutter` tool to find the observatory ports
|
|
|
|
|
* for all the active Flutter views. We dump the data to the logs
|
|
|
|
|
* and the tool scrapes the log lines for the data.
|
2017-03-17 09:04:59 +01:00
|
|
|
*/
|
2016-05-27 17:11:34 -07:00
|
|
|
private class DiscoveryReceiver extends BroadcastReceiver {
|
2017-03-17 09:04:59 +01:00
|
|
|
|
2016-05-27 17:11:34 -07:00
|
|
|
@Override
|
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
2017-11-09 12:10:00 -08:00
|
|
|
URI observatoryUri = URI.create(FlutterNativeView.getObservatoryUri());
|
2016-05-27 17:11:34 -07:00
|
|
|
JSONObject discover = new JSONObject();
|
|
|
|
|
try {
|
|
|
|
|
discover.put("id", getContext().getPackageName());
|
2016-11-01 17:11:56 -07:00
|
|
|
discover.put("observatoryPort", observatoryUri.getPort());
|
2017-05-16 10:31:53 -07:00
|
|
|
Log.i(TAG, "DISCOVER: " + discover); // The tool looks for this data. See android_device.dart.
|
2017-03-17 09:04:59 +01:00
|
|
|
} catch (JSONException e) {
|
|
|
|
|
}
|
2016-05-27 17:11:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
2017-08-14 15:44:52 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Listener will be called on the Android UI thread once when Flutter renders the first frame.
|
|
|
|
|
*/
|
|
|
|
|
public interface FirstFrameListener {
|
|
|
|
|
void onFirstFrame();
|
|
|
|
|
}
|
2017-11-02 02:57:29 -07:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public TextureRegistry.SurfaceTextureEntry createSurfaceTexture() {
|
|
|
|
|
final SurfaceTexture surfaceTexture = new SurfaceTexture(0);
|
|
|
|
|
surfaceTexture.detachFromGLContext();
|
|
|
|
|
final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(
|
|
|
|
|
nextTextureId.getAndIncrement(), surfaceTexture);
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture);
|
2017-11-02 02:57:29 -07:00
|
|
|
return entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry {
|
|
|
|
|
private final long id;
|
|
|
|
|
private final SurfaceTexture surfaceTexture;
|
|
|
|
|
private boolean released;
|
|
|
|
|
|
|
|
|
|
SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture) {
|
|
|
|
|
this.id = id;
|
|
|
|
|
this.surfaceTexture = surfaceTexture;
|
|
|
|
|
this.surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onFrameAvailable(SurfaceTexture texture) {
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id);
|
2017-11-02 02:57:29 -07:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public SurfaceTexture surfaceTexture() {
|
|
|
|
|
return surfaceTexture;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public long id() {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void release() {
|
|
|
|
|
if (released) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
released = true;
|
2017-11-09 12:10:00 -08:00
|
|
|
nativeUnregisterTexture(mNativeView.get(), id);
|
2017-11-02 02:57:29 -07:00
|
|
|
surfaceTexture.release();
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-01-31 00:44:42 -08:00
|
|
|
}
|