2018-11-07 12:24:35 -08:00
|
|
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
2017-11-09 12:10:00 -08:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package io.flutter.view;
|
|
|
|
|
|
2017-11-30 11:05:55 -08:00
|
|
|
import android.app.Activity;
|
|
|
|
|
import android.content.Context;
|
2017-11-09 12:10:00 -08:00
|
|
|
import android.util.Log;
|
2017-11-30 11:05:55 -08:00
|
|
|
import io.flutter.app.FlutterPluginRegistry;
|
2018-12-21 01:07:04 -05:00
|
|
|
import io.flutter.embedding.engine.FlutterJNI;
|
|
|
|
|
import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener;
|
|
|
|
|
import io.flutter.embedding.engine.renderer.FlutterRenderer.RenderSurface;
|
2017-11-09 12:10:00 -08:00
|
|
|
import io.flutter.plugin.common.*;
|
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
2018-12-21 01:07:04 -05:00
|
|
|
|
|
|
|
|
import io.flutter.embedding.engine.dart.PlatformMessageHandler;
|
2017-11-09 12:10:00 -08:00
|
|
|
|
2017-11-13 13:56:48 -08:00
|
|
|
public class FlutterNativeView implements BinaryMessenger {
|
2017-11-09 12:10:00 -08:00
|
|
|
private static final String TAG = "FlutterNativeView";
|
|
|
|
|
|
|
|
|
|
private final Map<String, BinaryMessageHandler> mMessageHandlers;
|
|
|
|
|
private int mNextReplyId = 1;
|
|
|
|
|
private final Map<Integer, BinaryReply> mPendingReplies = new HashMap<>();
|
|
|
|
|
|
2017-11-30 11:05:55 -08:00
|
|
|
private final FlutterPluginRegistry mPluginRegistry;
|
2017-11-09 12:10:00 -08:00
|
|
|
private FlutterView mFlutterView;
|
2018-12-21 01:07:04 -05:00
|
|
|
private FlutterJNI mFlutterJNI;
|
2018-03-05 14:09:45 +01:00
|
|
|
private final Context mContext;
|
2018-05-15 12:05:54 -07:00
|
|
|
private boolean applicationIsRunning;
|
2017-11-09 12:10:00 -08:00
|
|
|
|
2017-11-30 11:05:55 -08:00
|
|
|
public FlutterNativeView(Context context) {
|
2018-08-07 12:42:22 -07:00
|
|
|
this(context, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FlutterNativeView(Context context, boolean isBackgroundView) {
|
2018-03-05 14:09:45 +01:00
|
|
|
mContext = context;
|
2017-11-30 11:05:55 -08:00
|
|
|
mPluginRegistry = new FlutterPluginRegistry(this, context);
|
2018-12-21 01:07:04 -05:00
|
|
|
mFlutterJNI = new FlutterJNI();
|
|
|
|
|
mFlutterJNI.setRenderSurface(new RenderSurfaceImpl());
|
|
|
|
|
mFlutterJNI.setPlatformMessageHandler(new PlatformMessageHandlerImpl());
|
|
|
|
|
mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
|
2018-08-07 12:42:22 -07:00
|
|
|
attach(this, isBackgroundView);
|
2017-11-09 12:10:00 -08:00
|
|
|
assertAttached();
|
|
|
|
|
mMessageHandlers = new HashMap<>();
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-13 13:56:48 -08:00
|
|
|
public void detach() {
|
2017-11-30 11:05:55 -08:00
|
|
|
mPluginRegistry.detach();
|
2017-11-13 13:56:48 -08:00
|
|
|
mFlutterView = null;
|
2018-12-21 01:07:04 -05:00
|
|
|
mFlutterJNI.detachFromNativeButKeepNativeResources();
|
2017-11-13 13:56:48 -08:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 12:10:00 -08:00
|
|
|
public void destroy() {
|
2018-07-11 14:11:15 -07:00
|
|
|
mPluginRegistry.destroy();
|
2017-11-09 12:10:00 -08:00
|
|
|
mFlutterView = null;
|
2018-12-21 01:07:04 -05:00
|
|
|
mFlutterJNI.detachFromNativeAndReleaseResources();
|
2018-05-15 12:05:54 -07:00
|
|
|
applicationIsRunning = false;
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
2017-11-30 11:05:55 -08:00
|
|
|
public FlutterPluginRegistry getPluginRegistry() {
|
|
|
|
|
return mPluginRegistry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void attachViewAndActivity(FlutterView flutterView, Activity activity) {
|
2017-11-13 13:56:48 -08:00
|
|
|
mFlutterView = flutterView;
|
2017-11-30 11:05:55 -08:00
|
|
|
mPluginRegistry.attach(flutterView, activity);
|
2017-11-13 13:56:48 -08:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 12:10:00 -08:00
|
|
|
public boolean isAttached() {
|
2018-12-21 01:07:04 -05:00
|
|
|
return mFlutterJNI.isAttached();
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void assertAttached() {
|
2018-08-07 12:42:22 -07:00
|
|
|
if (!isAttached()) throw new AssertionError("Platform view is not attached");
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
2018-08-07 12:42:22 -07:00
|
|
|
public void runFromBundle(FlutterRunArguments args) {
|
2018-12-10 14:38:44 -08:00
|
|
|
boolean hasBundlePaths = args.bundlePaths != null && args.bundlePaths.length != 0;
|
|
|
|
|
if (args.bundlePath == null && !hasBundlePaths) {
|
2018-12-10 12:19:22 -08:00
|
|
|
throw new AssertionError("Either bundlePath or bundlePaths must be specified");
|
|
|
|
|
} else if ((args.bundlePath != null || args.defaultPath != null) &&
|
2018-12-10 14:38:44 -08:00
|
|
|
hasBundlePaths) {
|
2018-12-10 12:19:22 -08:00
|
|
|
throw new AssertionError("Can't specify both bundlePath and bundlePaths");
|
2018-08-07 12:42:22 -07:00
|
|
|
} else if (args.entrypoint == null) {
|
2018-12-10 12:19:22 -08:00
|
|
|
throw new AssertionError("An entrypoint must be specified");
|
|
|
|
|
}
|
2018-12-10 14:38:44 -08:00
|
|
|
if (hasBundlePaths) {
|
2018-12-10 12:19:22 -08:00
|
|
|
runFromBundleInternal(args.bundlePaths, args.entrypoint, args.libraryPath);
|
|
|
|
|
} else {
|
|
|
|
|
runFromBundleInternal(new String[] {args.bundlePath, args.defaultPath},
|
|
|
|
|
args.entrypoint, args.libraryPath);
|
2018-08-07 12:42:22 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @deprecated
|
2018-08-10 13:23:51 -07:00
|
|
|
* Please use runFromBundle with `FlutterRunArguments`.
|
|
|
|
|
* Parameter `reuseRuntimeController` has no effect.
|
2018-08-07 12:42:22 -07:00
|
|
|
*/
|
2018-08-08 13:02:41 -07:00
|
|
|
@Deprecated
|
2018-08-10 13:23:51 -07:00
|
|
|
public void runFromBundle(String bundlePath, String defaultPath, String entrypoint,
|
2018-08-07 12:42:22 -07:00
|
|
|
boolean reuseRuntimeController) {
|
2018-12-10 12:19:22 -08:00
|
|
|
runFromBundleInternal(new String[] {bundlePath, defaultPath}, entrypoint, null);
|
2018-08-07 12:42:22 -07:00
|
|
|
}
|
|
|
|
|
|
2018-12-10 12:19:22 -08:00
|
|
|
private void runFromBundleInternal(String[] bundlePaths, String entrypoint,
|
|
|
|
|
String libraryPath) {
|
2017-11-09 12:10:00 -08:00
|
|
|
assertAttached();
|
2018-05-15 12:05:54 -07:00
|
|
|
if (applicationIsRunning)
|
2018-08-07 12:42:22 -07:00
|
|
|
throw new AssertionError(
|
|
|
|
|
"This Flutter engine instance is already running an application");
|
2018-12-21 01:07:04 -05:00
|
|
|
mFlutterJNI.runBundleAndSnapshotFromLibrary(
|
|
|
|
|
bundlePaths,
|
|
|
|
|
entrypoint,
|
|
|
|
|
libraryPath,
|
|
|
|
|
mContext.getResources().getAssets()
|
|
|
|
|
);
|
2018-05-15 12:05:54 -07:00
|
|
|
|
|
|
|
|
applicationIsRunning = true;
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
2018-05-15 12:05:54 -07:00
|
|
|
public boolean isApplicationRunning() {
|
2018-08-07 12:42:22 -07:00
|
|
|
return applicationIsRunning;
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static String getObservatoryUri() {
|
2018-12-21 01:07:04 -05:00
|
|
|
return FlutterJNI.nativeGetObservatoryUri();
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void send(String channel, ByteBuffer message) {
|
2018-08-07 12:42:22 -07:00
|
|
|
send(channel, message, null);
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void send(String channel, ByteBuffer message, BinaryReply callback) {
|
|
|
|
|
if (!isAttached()) {
|
|
|
|
|
Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int replyId = 0;
|
|
|
|
|
if (callback != null) {
|
|
|
|
|
replyId = mNextReplyId++;
|
|
|
|
|
mPendingReplies.put(replyId, callback);
|
|
|
|
|
}
|
|
|
|
|
if (message == null) {
|
2018-12-21 01:07:04 -05:00
|
|
|
mFlutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
|
2017-11-09 12:10:00 -08:00
|
|
|
} else {
|
2018-12-21 01:07:04 -05:00
|
|
|
mFlutterJNI.dispatchPlatformMessage(
|
|
|
|
|
channel,
|
|
|
|
|
message,
|
|
|
|
|
message.position(),
|
|
|
|
|
replyId
|
|
|
|
|
);
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
|
|
|
|
|
if (handler == null) {
|
|
|
|
|
mMessageHandlers.remove(channel);
|
|
|
|
|
} else {
|
|
|
|
|
mMessageHandlers.put(channel, handler);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-21 01:07:04 -05:00
|
|
|
/*package*/ FlutterJNI getFlutterJNI() {
|
|
|
|
|
return mFlutterJNI;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-07 12:42:22 -07:00
|
|
|
private void attach(FlutterNativeView view, boolean isBackgroundView) {
|
2018-12-21 01:07:04 -05:00
|
|
|
mFlutterJNI.attachToNative(isBackgroundView);
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
2018-12-21 01:07:04 -05:00
|
|
|
private final class PlatformMessageHandlerImpl implements PlatformMessageHandler {
|
|
|
|
|
// Called by native to send us a platform message.
|
2019-02-06 17:07:30 -08:00
|
|
|
public void handleMessageFromDart(final String channel, byte[] message, final int replyId) {
|
2018-12-21 01:07:04 -05:00
|
|
|
assertAttached();
|
|
|
|
|
BinaryMessageHandler handler = mMessageHandlers.get(channel);
|
|
|
|
|
if (handler != null) {
|
|
|
|
|
try {
|
|
|
|
|
final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
|
|
|
|
|
handler.onMessage(buffer, new BinaryReply() {
|
|
|
|
|
private final AtomicBoolean done = new AtomicBoolean(false);
|
|
|
|
|
@Override
|
|
|
|
|
public void reply(ByteBuffer reply) {
|
|
|
|
|
if (!isAttached()) {
|
2019-02-06 17:07:30 -08:00
|
|
|
Log.d(TAG, "handleMessageFromDart replying ot a detached view, channel=" + channel);
|
2018-12-21 01:07:04 -05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (done.getAndSet(true)) {
|
|
|
|
|
throw new IllegalStateException("Reply already submitted");
|
|
|
|
|
}
|
|
|
|
|
if (reply == null) {
|
|
|
|
|
mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
|
|
|
|
|
} else {
|
|
|
|
|
mFlutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());
|
|
|
|
|
}
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
2018-12-21 01:07:04 -05:00
|
|
|
});
|
|
|
|
|
} catch (Exception exception) {
|
|
|
|
|
Log.e(TAG, "Uncaught exception in binary message listener", exception);
|
|
|
|
|
mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
|
|
|
|
|
}
|
|
|
|
|
return;
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
2018-12-21 01:07:04 -05:00
|
|
|
mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
|
2018-12-21 01:07:04 -05:00
|
|
|
// Called by native to respond to a platform message that we sent.
|
|
|
|
|
public void handlePlatformMessageResponse(int replyId, byte[] reply) {
|
|
|
|
|
BinaryReply callback = mPendingReplies.remove(replyId);
|
|
|
|
|
if (callback != null) {
|
|
|
|
|
try {
|
|
|
|
|
callback.reply(reply == null ? null : ByteBuffer.wrap(reply));
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
Log.e(TAG, "Uncaught exception in binary message reply handler", ex);
|
|
|
|
|
}
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-21 01:07:04 -05:00
|
|
|
private final class RenderSurfaceImpl implements RenderSurface {
|
|
|
|
|
// Called by native to update the semantics/accessibility tree.
|
|
|
|
|
public void updateSemantics(ByteBuffer buffer, String[] strings) {
|
|
|
|
|
if (mFlutterView == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mFlutterView.updateSemantics(buffer, strings);
|
|
|
|
|
}
|
2017-11-09 12:10:00 -08:00
|
|
|
|
2018-12-21 01:07:04 -05:00
|
|
|
// Called by native to update the custom accessibility actions.
|
|
|
|
|
public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) {
|
|
|
|
|
if (mFlutterView == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mFlutterView.updateCustomAccessibilityActions(buffer, strings);
|
|
|
|
|
}
|
2018-07-11 10:27:50 -07:00
|
|
|
|
2018-12-21 01:07:04 -05:00
|
|
|
// Called by native to notify first Flutter frame rendered.
|
|
|
|
|
public void onFirstFrameRendered() {
|
|
|
|
|
if (mFlutterView == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mFlutterView.onFirstFrame();
|
2018-11-14 11:13:39 -08:00
|
|
|
}
|
2018-08-03 08:54:12 -07:00
|
|
|
}
|
|
|
|
|
|
2018-12-21 01:07:04 -05:00
|
|
|
private final class EngineLifecycleListenerImpl implements EngineLifecycleListener {
|
|
|
|
|
// Called by native to notify when the engine is restarted (cold reload).
|
|
|
|
|
@SuppressWarnings("unused")
|
|
|
|
|
public void onPreEngineRestart() {
|
|
|
|
|
if (mFlutterView != null) {
|
|
|
|
|
mFlutterView.resetAccessibilityTree();
|
|
|
|
|
}
|
|
|
|
|
if (mPluginRegistry == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mPluginRegistry.onPreEngineRestart();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-11-09 12:10:00 -08:00
|
|
|
}
|