blob: b9908b5531ece19a29205be9641544eb98b05516 [file] [log] [blame]
// Copyright 2017 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.
package io.flutter.view;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import io.flutter.app.FlutterPluginRegistry;
import io.flutter.plugin.common.*;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.HashMap;
import java.util.Map;
import android.content.res.AssetManager;
public class FlutterNativeView implements BinaryMessenger {
private static final String TAG = "FlutterNativeView";
private final Map<String, BinaryMessageHandler> mMessageHandlers;
private int mNextReplyId = 1;
private final Map<Integer, BinaryReply> mPendingReplies = new HashMap<>();
private final FlutterPluginRegistry mPluginRegistry;
private long mNativePlatformView;
private FlutterView mFlutterView;
private final Context mContext;
private boolean applicationIsRunning;
public FlutterNativeView(Context context) {
mContext = context;
mPluginRegistry = new FlutterPluginRegistry(this, context);
attach(this);
assertAttached();
mMessageHandlers = new HashMap<>();
}
public void detach() {
mPluginRegistry.detach();
mFlutterView = null;
nativeDetach(mNativePlatformView);
}
public void destroy() {
mFlutterView = null;
nativeDestroy(mNativePlatformView);
mNativePlatformView = 0;
applicationIsRunning = false;
}
public FlutterPluginRegistry getPluginRegistry() {
return mPluginRegistry;
}
public void attachViewAndActivity(FlutterView flutterView, Activity activity) {
mFlutterView = flutterView;
mPluginRegistry.attach(flutterView, activity);
}
public boolean isAttached() {
return mNativePlatformView != 0;
}
public long get() {
return mNativePlatformView;
}
public void assertAttached() {
if (!isAttached())
throw new AssertionError("Platform view is not attached");
}
public void runFromBundle(String bundlePath, String snapshotOverride, String entrypoint, boolean reuseRuntimeController) {
assertAttached();
if (applicationIsRunning)
throw new AssertionError("This Flutter engine instance is already running an application");
nativeRunBundleAndSnapshot(mNativePlatformView, bundlePath, snapshotOverride, entrypoint, reuseRuntimeController, mContext.getResources().getAssets());
applicationIsRunning = true;
}
public void runFromSource(final String assetsDirectory, final String main, final String packages) {
assertAttached();
if (applicationIsRunning)
throw new AssertionError("This Flutter engine instance is already running an application");
nativeRunBundleAndSource(mNativePlatformView, assetsDirectory, main, packages);
applicationIsRunning = true;
}
public boolean isApplicationRunning() {
return applicationIsRunning;
}
public void setAssetBundlePathOnUI(final String assetsDirectory) {
assertAttached();
nativeSetAssetBundlePathOnUI(mNativePlatformView, assetsDirectory);
}
public static String getObservatoryUri() {
return nativeGetObservatoryUri();
}
@Override
public void send(String channel, ByteBuffer message) {
send(channel, message, null);
}
@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) {
nativeDispatchEmptyPlatformMessage(mNativePlatformView, channel, replyId);
} else {
nativeDispatchPlatformMessage(mNativePlatformView, channel, message,
message.position(), replyId);
}
}
@Override
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
if (handler == null) {
mMessageHandlers.remove(channel);
} else {
mMessageHandlers.put(channel, handler);
}
}
private void attach(FlutterNativeView view) {
mNativePlatformView = nativeAttach(view);
}
// Called by native to send us a platform message.
private void handlePlatformMessage(final String channel, byte[] message, final int replyId) {
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()) {
Log.d(TAG, "handlePlatformMessage replying to a detached view, channel=" + channel);
return;
}
if (done.getAndSet(true)) {
throw new IllegalStateException("Reply already submitted");
}
if (reply == null) {
nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView,
replyId);
} else {
nativeInvokePlatformMessageResponseCallback(mNativePlatformView,
replyId, reply, reply.position());
}
}
});
} catch (Exception ex) {
Log.e(TAG, "Uncaught exception in binary message listener", ex);
nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId);
}
return;
}
nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId);
}
// Called by native to respond to a platform message that we sent.
private 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);
}
}
}
// Called by native to update the semantics/accessibility tree.
private void updateSemantics(ByteBuffer buffer, String[] strings) {
if (mFlutterView == null)
return;
mFlutterView.updateSemantics(buffer, strings);
}
// Called by native to notify first Flutter frame rendered.
private void onFirstFrame() {
if (mFlutterView == null)
return;
mFlutterView.onFirstFrame();
}
private static native long nativeAttach(FlutterNativeView view);
private static native void nativeDestroy(long nativePlatformViewAndroid);
private static native void nativeDetach(long nativePlatformViewAndroid);
private static native void nativeRunBundleAndSnapshot(long nativePlatformViewAndroid,
String bundlePath,
String snapshotOverride,
String entrypoint,
boolean reuseRuntimeController,
AssetManager manager);
private static native void nativeRunBundleAndSource(long nativePlatformViewAndroid,
String bundlePath,
String main,
String packages);
private static native void nativeSetAssetBundlePathOnUI(long nativePlatformViewAndroid,
String bundlePath);
private static native String nativeGetObservatoryUri();
// Send an empty platform message to Dart.
private static native void nativeDispatchEmptyPlatformMessage(long nativePlatformViewAndroid,
String channel, int responseId);
// Send a data-carrying platform message to Dart.
private static native void nativeDispatchPlatformMessage(long nativePlatformViewAndroid,
String channel, ByteBuffer message, int position, int responseId);
// Send an empty response to a platform message received from Dart.
private static native void nativeInvokePlatformMessageEmptyResponseCallback(
long nativePlatformViewAndroid, int responseId);
// Send a data-carrying response to a platform message received from Dart.
private static native void nativeInvokePlatformMessageResponseCallback(
long nativePlatformViewAndroid, int responseId, ByteBuffer message, int position);
}