blob: b0d5ad9ba19e268f308f9cc358ca5b1108329dfb [file] [log] [blame]
// Copyright 2013 The Flutter 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.embedding.engine;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.os.Looper;
import android.view.Surface;
import android.view.SurfaceHolder;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener;
import io.flutter.embedding.engine.dart.PlatformMessageHandler;
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
import io.flutter.embedding.engine.renderer.RenderSurface;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.view.AccessibilityBridge;
import io.flutter.view.FlutterCallbackInformation;
import java.nio.ByteBuffer;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Interface between Flutter embedding's Java code and Flutter engine's C/C++ code.
*
* <p>Flutter's engine is built with C/C++. The Android Flutter embedding is responsible for
* coordinating Android OS events and app user interactions with the C/C++ engine. Such coordination
* requires messaging from an Android app in Java code to the C/C++ engine code. This communication
* requires a JNI (Java Native Interface) API to cross the Java/native boundary.
*
* <p>The entirety of Flutter's JNI API is codified in {@code FlutterJNI}. There are multiple
* reasons that all such calls are centralized in one class. First, JNI calls are inherently static
* and contain no Java implementation, therefore there is little reason to associate calls with
* different classes. Second, every JNI call must be registered in C/C++ code and this registration
* becomes more complicated with every additional Java class that contains JNI calls. Third, most
* Android developers are not familiar with native development or JNI intricacies, therefore it is
* in the interest of future maintenance to reduce the API surface that includes JNI declarations.
* Thus, all Flutter JNI calls are centralized in {@code FlutterJNI}.
*
* <p>Despite the fact that individual JNI calls are inherently static, there is state that exists
* within {@code FlutterJNI}. Most calls within {@code FlutterJNI} correspond to a specific
* "platform view", of which there may be many. Therefore, each {@code FlutterJNI} instance holds
* onto a "native platform view ID" after {@link #attachToNative(boolean)}, which is shared with the
* native C/C++ engine code. That ID is passed to every platform-view-specific native method. ID
* management is handled within {@code FlutterJNI} so that developers don't have to hold onto that
* ID.
*
* <p>To connect part of an Android app to Flutter's C/C++ engine, instantiate a {@code FlutterJNI}
* and then attach it to the native side:
*
* <pre>{@code
* // Instantiate FlutterJNI and attach to the native side.
* FlutterJNI flutterJNI = new FlutterJNI();
* flutterJNI.attachToNative();
*
* // Use FlutterJNI as desired. flutterJNI.dispatchPointerDataPacket(...);
*
* // Destroy the connection to the native side and cleanup.
* flutterJNI.detachFromNativeAndReleaseResources();
* }</pre>
*
* <p>To provide a visual, interactive surface for Flutter rendering and touch events, register a
* {@link RenderSurface} with {@link #setRenderSurface(RenderSurface)}
*
* <p>To receive callbacks for certain events that occur on the native side, register listeners:
*
* <ol>
* <li>{@link #addEngineLifecycleListener(EngineLifecycleListener)}
* <li>{@link #addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)}
* </ol>
*
* To facilitate platform messages between Java and Dart running in Flutter, register a handler:
*
* <p>{@link #setPlatformMessageHandler(PlatformMessageHandler)}
*
* <p>To invoke a native method that is not associated with a platform view, invoke it statically:
*
* <p>{@code bool enabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled(); }
*/
@Keep
public class FlutterJNI {
private static final String TAG = "FlutterJNI";
@Nullable private static AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate;
// This should also be updated by FlutterView when it is attached to a Display.
// The initial value of 0.0 indicates unknown refresh rate.
private static float refreshRateFPS = 0.0f;
// This is set from native code via JNI.
@Nullable private static String observatoryUri;
// TODO(mattcarroll): add javadocs
public static native void nativeInit(
@NonNull Context context,
@NonNull String[] args,
@Nullable String bundlePath,
@NonNull String appStoragePath,
@NonNull String engineCachesPath,
long initTimeMillis);
/**
* Prefetch the default font manager provided by SkFontMgr::RefDefault() which is a process-wide
* singleton owned by Skia. Note that, the first call to SkFontMgr::RefDefault() will take
* noticeable time, but later calls will return a reference to the preexisting font manager.
*/
public static native void nativePrefetchDefaultFontManager();
// TODO(mattcarroll): add javadocs
@UiThread
public native boolean nativeGetIsSoftwareRenderingEnabled();
@Nullable
// TODO(mattcarroll): add javadocs
public static String getObservatoryUri() {
return observatoryUri;
}
public static void setRefreshRateFPS(float refreshRateFPS) {
FlutterJNI.refreshRateFPS = refreshRateFPS;
}
// TODO(mattcarroll): add javadocs
public static void setAsyncWaitForVsyncDelegate(@Nullable AsyncWaitForVsyncDelegate delegate) {
asyncWaitForVsyncDelegate = delegate;
}
// TODO(mattcarroll): add javadocs
// Called by native.
private static void asyncWaitForVsync(final long cookie) {
if (asyncWaitForVsyncDelegate != null) {
asyncWaitForVsyncDelegate.asyncWaitForVsync(cookie);
} else {
throw new IllegalStateException(
"An AsyncWaitForVsyncDelegate must be registered with FlutterJNI before asyncWaitForVsync() is invoked.");
}
}
// TODO(mattcarroll): add javadocs
public static native void nativeOnVsync(
long frameTimeNanos, long frameTargetTimeNanos, long cookie);
// TODO(mattcarroll): add javadocs
@NonNull
public static native FlutterCallbackInformation nativeLookupCallbackInformation(long handle);
// ----- Start FlutterTextUtils Methods ----
public native boolean nativeFlutterTextUtilsIsEmoji(int codePoint);
public native boolean nativeFlutterTextUtilsIsEmojiModifier(int codePoint);
public native boolean nativeFlutterTextUtilsIsEmojiModifierBase(int codePoint);
public native boolean nativeFlutterTextUtilsIsVariationSelector(int codePoint);
public native boolean nativeFlutterTextUtilsIsRegionalIndicator(int codePoint);
// ----- End Engine FlutterTextUtils Methods ----
@Nullable private Long nativePlatformViewId;
@Nullable private AccessibilityDelegate accessibilityDelegate;
@Nullable private PlatformMessageHandler platformMessageHandler;
@NonNull
private final Set<EngineLifecycleListener> engineLifecycleListeners = new CopyOnWriteArraySet<>();
@NonNull
private final Set<FlutterUiDisplayListener> flutterUiDisplayListeners =
new CopyOnWriteArraySet<>();
@NonNull private final Looper mainLooper; // cached to avoid synchronization on repeat access.
public FlutterJNI() {
// We cache the main looper so that we can ensure calls are made on the main thread
// without consistently paying the synchronization cost of getMainLooper().
mainLooper = Looper.getMainLooper();
}
// ------ Start Native Attach/Detach Support ----
/**
* Returns true if this instance of {@code FlutterJNI} is connected to Flutter's native engine via
* a Java Native Interface (JNI).
*/
public boolean isAttached() {
return nativePlatformViewId != null;
}
/**
* Attaches this {@code FlutterJNI} instance to Flutter's native engine, which allows for
* communication between Android code and Flutter's platform agnostic engine.
*
* <p>This method must not be invoked if {@code FlutterJNI} is already attached to native.
*/
@UiThread
public void attachToNative(boolean isBackgroundView) {
ensureRunningOnMainThread();
ensureNotAttachedToNative();
nativePlatformViewId = nativeAttach(this, isBackgroundView);
}
private native long nativeAttach(@NonNull FlutterJNI flutterJNI, boolean isBackgroundView);
/**
* Detaches this {@code FlutterJNI} instance from Flutter's native engine, which precludes any
* further communication between Android code and Flutter's platform agnostic engine.
*
* <p>This method must not be invoked if {@code FlutterJNI} is not already attached to native.
*
* <p>Invoking this method will result in the release of all native-side resources that were setup
* during {@link #attachToNative(boolean)}, or accumulated thereafter.
*
* <p>It is permissable to re-attach this instance to native after detaching it from native.
*/
@UiThread
public void detachFromNativeAndReleaseResources() {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeDestroy(nativePlatformViewId);
nativePlatformViewId = null;
}
private native void nativeDestroy(long nativePlatformViewId);
private void ensureNotAttachedToNative() {
if (nativePlatformViewId != null) {
throw new RuntimeException(
"Cannot execute operation because FlutterJNI is attached to native.");
}
}
private void ensureAttachedToNative() {
if (nativePlatformViewId == null) {
throw new RuntimeException(
"Cannot execute operation because FlutterJNI is not attached to native.");
}
}
// ------ End Native Attach/Detach Support ----
// ----- Start Render Surface Support -----
/**
* Adds a {@link FlutterUiDisplayListener}, which receives a callback when Flutter's engine
* notifies {@code FlutterJNI} that Flutter is painting pixels to the {@link Surface} that was
* provided to Flutter.
*/
@UiThread
public void addIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener) {
ensureRunningOnMainThread();
flutterUiDisplayListeners.add(listener);
}
/**
* Removes a {@link FlutterUiDisplayListener} that was added with {@link
* #addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)}.
*/
@UiThread
public void removeIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener) {
ensureRunningOnMainThread();
flutterUiDisplayListeners.remove(listener);
}
// Called by native to notify first Flutter frame rendered.
@SuppressWarnings("unused")
@VisibleForTesting
@UiThread
void onFirstFrame() {
ensureRunningOnMainThread();
for (FlutterUiDisplayListener listener : flutterUiDisplayListeners) {
listener.onFlutterUiDisplayed();
}
}
// TODO(mattcarroll): get native to call this when rendering stops.
@VisibleForTesting
@UiThread
void onRenderingStopped() {
ensureRunningOnMainThread();
for (FlutterUiDisplayListener listener : flutterUiDisplayListeners) {
listener.onFlutterUiNoLongerDisplayed();
}
}
/**
* Call this method when a {@link Surface} has been created onto which you would like Flutter to
* paint.
*
* <p>See {@link android.view.SurfaceHolder.Callback#surfaceCreated(SurfaceHolder)} for an example
* of where this call might originate.
*/
@UiThread
public void onSurfaceCreated(@NonNull Surface surface) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeSurfaceCreated(nativePlatformViewId, surface);
}
private native void nativeSurfaceCreated(long nativePlatformViewId, @NonNull Surface surface);
/**
* Call this method when the {@link Surface} changes that was previously registered with {@link
* #onSurfaceCreated(Surface)}.
*
* <p>See {@link android.view.SurfaceHolder.Callback#surfaceChanged(SurfaceHolder, int, int, int)}
* for an example of where this call might originate.
*/
@UiThread
public void onSurfaceChanged(int width, int height) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeSurfaceChanged(nativePlatformViewId, width, height);
}
private native void nativeSurfaceChanged(long nativePlatformViewId, int width, int height);
/**
* Call this method when the {@link Surface} is destroyed that was previously registered with
* {@link #onSurfaceCreated(Surface)}.
*
* <p>See {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)} for an
* example of where this call might originate.
*/
@UiThread
public void onSurfaceDestroyed() {
ensureRunningOnMainThread();
ensureAttachedToNative();
onRenderingStopped();
nativeSurfaceDestroyed(nativePlatformViewId);
}
private native void nativeSurfaceDestroyed(long nativePlatformViewId);
/**
* Call this method to notify Flutter of the current device viewport metrics that are applies to
* the Flutter UI that is being rendered.
*
* <p>This method should be invoked with initial values upon attaching to native. Then, it should
* be invoked any time those metrics change while {@code FlutterJNI} is attached to native.
*/
@UiThread
public void setViewportMetrics(
float devicePixelRatio,
int physicalWidth,
int physicalHeight,
int physicalPaddingTop,
int physicalPaddingRight,
int physicalPaddingBottom,
int physicalPaddingLeft,
int physicalViewInsetTop,
int physicalViewInsetRight,
int physicalViewInsetBottom,
int physicalViewInsetLeft,
int systemGestureInsetTop,
int systemGestureInsetRight,
int systemGestureInsetBottom,
int systemGestureInsetLeft) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeSetViewportMetrics(
nativePlatformViewId,
devicePixelRatio,
physicalWidth,
physicalHeight,
physicalPaddingTop,
physicalPaddingRight,
physicalPaddingBottom,
physicalPaddingLeft,
physicalViewInsetTop,
physicalViewInsetRight,
physicalViewInsetBottom,
physicalViewInsetLeft,
systemGestureInsetTop,
systemGestureInsetRight,
systemGestureInsetBottom,
systemGestureInsetLeft);
}
private native void nativeSetViewportMetrics(
long nativePlatformViewId,
float devicePixelRatio,
int physicalWidth,
int physicalHeight,
int physicalPaddingTop,
int physicalPaddingRight,
int physicalPaddingBottom,
int physicalPaddingLeft,
int physicalViewInsetTop,
int physicalViewInsetRight,
int physicalViewInsetBottom,
int physicalViewInsetLeft,
int systemGestureInsetTop,
int systemGestureInsetRight,
int systemGestureInsetBottom,
int systemGestureInsetLeft);
// ----- End Render Surface Support -----
// ------ Start Touch Interaction Support ---
/** Sends a packet of pointer data to Flutter's engine. */
@UiThread
public void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeDispatchPointerDataPacket(nativePlatformViewId, buffer, position);
}
private native void nativeDispatchPointerDataPacket(
long nativePlatformViewId, @NonNull ByteBuffer buffer, int position);
// ------ End Touch Interaction Support ---
// ------ Start Accessibility Support -----
/**
* Sets the {@link AccessibilityDelegate} for the attached Flutter context.
*
* <p>The {@link AccessibilityDelegate} is responsible for maintaining an Android-side cache of
* Flutter's semantics tree and custom accessibility actions. This cache should be hooked up to
* Android's accessibility system.
*
* <p>See {@link AccessibilityBridge} for an example of an {@link AccessibilityDelegate} and the
* surrounding responsibilities.
*/
@UiThread
public void setAccessibilityDelegate(@Nullable AccessibilityDelegate accessibilityDelegate) {
ensureRunningOnMainThread();
this.accessibilityDelegate = accessibilityDelegate;
}
/**
* Invoked by native to send semantics tree updates from Flutter to Android.
*
* <p>The {@code buffer} and {@code strings} form a communication protocol that is implemented
* here:
* https://github.com/flutter/engine/blob/master/shell/platform/android/platform_view_android.cc#L207
*/
@SuppressWarnings("unused")
@UiThread
private void updateSemantics(@NonNull ByteBuffer buffer, @NonNull String[] strings) {
ensureRunningOnMainThread();
if (accessibilityDelegate != null) {
accessibilityDelegate.updateSemantics(buffer, strings);
}
// TODO(mattcarroll): log dropped messages when in debug mode
// (https://github.com/flutter/flutter/issues/25391)
}
/**
* Invoked by native to send new custom accessibility events from Flutter to Android.
*
* <p>The {@code buffer} and {@code strings} form a communication protocol that is implemented
* here:
* https://github.com/flutter/engine/blob/master/shell/platform/android/platform_view_android.cc#L207
*
* <p>// TODO(cbracken): expand these docs to include more actionable information.
*/
@SuppressWarnings("unused")
@UiThread
private void updateCustomAccessibilityActions(
@NonNull ByteBuffer buffer, @NonNull String[] strings) {
ensureRunningOnMainThread();
if (accessibilityDelegate != null) {
accessibilityDelegate.updateCustomAccessibilityActions(buffer, strings);
}
// TODO(mattcarroll): log dropped messages when in debug mode
// (https://github.com/flutter/flutter/issues/25391)
}
/** Sends a semantics action to Flutter's engine, without any additional arguments. */
public void dispatchSemanticsAction(int id, @NonNull AccessibilityBridge.Action action) {
dispatchSemanticsAction(id, action, null);
}
/** Sends a semantics action to Flutter's engine, with additional arguments. */
public void dispatchSemanticsAction(
int id, @NonNull AccessibilityBridge.Action action, @Nullable Object args) {
ensureAttachedToNative();
ByteBuffer encodedArgs = null;
int position = 0;
if (args != null) {
encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args);
position = encodedArgs.position();
}
dispatchSemanticsAction(id, action.value, encodedArgs, position);
}
/**
* Sends a semantics action to Flutter's engine, given arguments that are already encoded for the
* engine.
*
* <p>To send a semantics action that has not already been encoded, see {@link
* #dispatchSemanticsAction(int, AccessibilityBridge.Action)} and {@link
* #dispatchSemanticsAction(int, AccessibilityBridge.Action, Object)}.
*/
@UiThread
public void dispatchSemanticsAction(
int id, int action, @Nullable ByteBuffer args, int argsPosition) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeDispatchSemanticsAction(nativePlatformViewId, id, action, args, argsPosition);
}
private native void nativeDispatchSemanticsAction(
long nativePlatformViewId, int id, int action, @Nullable ByteBuffer args, int argsPosition);
/**
* Instructs Flutter to enable/disable its semantics tree, which is used by Flutter to support
* accessibility and related behaviors.
*/
@UiThread
public void setSemanticsEnabled(boolean enabled) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeSetSemanticsEnabled(nativePlatformViewId, enabled);
}
private native void nativeSetSemanticsEnabled(long nativePlatformViewId, boolean enabled);
// TODO(mattcarroll): figure out what flags are supported and add javadoc about when/why/where to
// use this.
@UiThread
public void setAccessibilityFeatures(int flags) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeSetAccessibilityFeatures(nativePlatformViewId, flags);
}
private native void nativeSetAccessibilityFeatures(long nativePlatformViewId, int flags);
// ------ End Accessibility Support ----
// ------ Start Texture Registration Support -----
/**
* Gives control of a {@link SurfaceTexture} to Flutter so that Flutter can display that texture
* within Flutter's UI.
*/
@UiThread
public void registerTexture(long textureId, @NonNull SurfaceTexture surfaceTexture) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeRegisterTexture(nativePlatformViewId, textureId, surfaceTexture);
}
private native void nativeRegisterTexture(
long nativePlatformViewId, long textureId, @NonNull SurfaceTexture surfaceTexture);
/**
* Call this method to inform Flutter that a texture previously registered with {@link
* #registerTexture(long, SurfaceTexture)} has a new frame available.
*
* <p>Invoking this method instructs Flutter to update its presentation of the given texture so
* that the new frame is displayed.
*/
@UiThread
public void markTextureFrameAvailable(long textureId) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeMarkTextureFrameAvailable(nativePlatformViewId, textureId);
}
private native void nativeMarkTextureFrameAvailable(long nativePlatformViewId, long textureId);
/**
* Unregisters a texture that was registered with {@link #registerTexture(long, SurfaceTexture)}.
*/
@UiThread
public void unregisterTexture(long textureId) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeUnregisterTexture(nativePlatformViewId, textureId);
}
private native void nativeUnregisterTexture(long nativePlatformViewId, long textureId);
// ------ Start Texture Registration Support -----
// ------ Start Dart Execution Support -------
/**
* Executes a Dart entrypoint.
*
* <p>This can only be done once per JNI attachment because a Dart isolate can only be entered
* once.
*/
@UiThread
public void runBundleAndSnapshotFromLibrary(
@NonNull String bundlePath,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager assetManager) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeRunBundleAndSnapshotFromLibrary(
nativePlatformViewId,
bundlePath,
entrypointFunctionName,
pathToEntrypointFunction,
assetManager);
}
private native void nativeRunBundleAndSnapshotFromLibrary(
long nativePlatformViewId,
@NonNull String bundlePath,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager manager);
// ------ End Dart Execution Support -------
// --------- Start Platform Message Support ------
/**
* Sets the handler for all platform messages that come from the attached platform view to Java.
*
* <p>Communication between a specific Flutter context (Dart) and the host platform (Java) is
* accomplished by passing messages. Messages can be sent from Java to Dart with the corresponding
* {@code FlutterJNI} methods:
*
* <ul>
* <li>{@link #dispatchPlatformMessage(String, ByteBuffer, int, int)}
* <li>{@link #dispatchEmptyPlatformMessage(String, int)}
* </ul>
*
* <p>{@code FlutterJNI} is also the recipient of all platform messages sent from its attached
* Flutter context. {@code FlutterJNI} does not know what to do with these messages, so a handler
* is exposed to allow these messages to be processed in whatever manner is desired:
*
* <p>{@code setPlatformMessageHandler(PlatformMessageHandler)}
*
* <p>If a message is received but no {@link PlatformMessageHandler} is registered, that message
* will be dropped (ignored). Therefore, when using {@code FlutterJNI} to integrate a Flutter
* context in an app, a {@link PlatformMessageHandler} must be registered for 2-way Java/Dart
* communication to operate correctly. Moreover, the handler must be implemented such that
* fundamental platform messages are handled as expected. See {@link FlutterNativeView} for an
* example implementation.
*/
@UiThread
public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) {
ensureRunningOnMainThread();
this.platformMessageHandler = platformMessageHandler;
}
// Called by native.
// TODO(mattcarroll): determine if message is nonull or nullable
@SuppressWarnings("unused")
private void handlePlatformMessage(
@NonNull final String channel, byte[] message, final int replyId) {
if (platformMessageHandler != null) {
platformMessageHandler.handleMessageFromDart(channel, message, replyId);
}
// TODO(mattcarroll): log dropped messages when in debug mode
// (https://github.com/flutter/flutter/issues/25391)
}
// Called by native to respond to a platform message that we sent.
// TODO(mattcarroll): determine if reply is nonull or nullable
@SuppressWarnings("unused")
private void handlePlatformMessageResponse(int replyId, byte[] reply) {
if (platformMessageHandler != null) {
platformMessageHandler.handlePlatformMessageResponse(replyId, reply);
}
// TODO(mattcarroll): log dropped messages when in debug mode
// (https://github.com/flutter/flutter/issues/25391)
}
/**
* Sends an empty reply (identified by {@code responseId}) from Android to Flutter over the given
* {@code channel}.
*/
@UiThread
public void dispatchEmptyPlatformMessage(@NonNull String channel, int responseId) {
ensureRunningOnMainThread();
if (isAttached()) {
nativeDispatchEmptyPlatformMessage(nativePlatformViewId, channel, responseId);
} else {
Log.w(
TAG,
"Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: "
+ channel
+ ". Response ID: "
+ responseId);
}
}
// Send an empty platform message to Dart.
private native void nativeDispatchEmptyPlatformMessage(
long nativePlatformViewId, @NonNull String channel, int responseId);
/** Sends a reply {@code message} from Android to Flutter over the given {@code channel}. */
@UiThread
public void dispatchPlatformMessage(
@NonNull String channel, @Nullable ByteBuffer message, int position, int responseId) {
ensureRunningOnMainThread();
if (isAttached()) {
nativeDispatchPlatformMessage(nativePlatformViewId, channel, message, position, responseId);
} else {
Log.w(
TAG,
"Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: "
+ channel
+ ". Response ID: "
+ responseId);
}
}
// Send a data-carrying platform message to Dart.
private native void nativeDispatchPlatformMessage(
long nativePlatformViewId,
@NonNull String channel,
@Nullable ByteBuffer message,
int position,
int responseId);
// TODO(mattcarroll): differentiate between channel responses and platform responses.
@UiThread
public void invokePlatformMessageEmptyResponseCallback(int responseId) {
ensureRunningOnMainThread();
if (isAttached()) {
nativeInvokePlatformMessageEmptyResponseCallback(nativePlatformViewId, responseId);
} else {
Log.w(
TAG,
"Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: "
+ responseId);
}
}
// Send an empty response to a platform message received from Dart.
private native void nativeInvokePlatformMessageEmptyResponseCallback(
long nativePlatformViewId, int responseId);
// TODO(mattcarroll): differentiate between channel responses and platform responses.
@UiThread
public void invokePlatformMessageResponseCallback(
int responseId, @Nullable ByteBuffer message, int position) {
ensureRunningOnMainThread();
if (isAttached()) {
nativeInvokePlatformMessageResponseCallback(
nativePlatformViewId, responseId, message, position);
} else {
Log.w(
TAG,
"Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: "
+ responseId);
}
}
// Send a data-carrying response to a platform message received from Dart.
private native void nativeInvokePlatformMessageResponseCallback(
long nativePlatformViewId, int responseId, @Nullable ByteBuffer message, int position);
// ------- End Platform Message Support ----
// ----- Start Engine Lifecycle Support ----
/**
* Adds the given {@code engineLifecycleListener} to be notified of Flutter engine lifecycle
* events, e.g., {@link EngineLifecycleListener#onPreEngineRestart()}.
*/
@UiThread
public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) {
ensureRunningOnMainThread();
engineLifecycleListeners.add(engineLifecycleListener);
}
/**
* Removes the given {@code engineLifecycleListener}, which was previously added using {@link
* #addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)}.
*/
@UiThread
public void removeEngineLifecycleListener(
@NonNull EngineLifecycleListener engineLifecycleListener) {
ensureRunningOnMainThread();
engineLifecycleListeners.remove(engineLifecycleListener);
}
// Called by native.
@SuppressWarnings("unused")
private void onPreEngineRestart() {
for (EngineLifecycleListener listener : engineLifecycleListeners) {
listener.onPreEngineRestart();
}
}
// ----- End Engine Lifecycle Support ----
// TODO(mattcarroll): determine if this is nonull or nullable
@UiThread
public Bitmap getBitmap() {
ensureRunningOnMainThread();
ensureAttachedToNative();
return nativeGetBitmap(nativePlatformViewId);
}
// TODO(mattcarroll): determine if this is nonull or nullable
private native Bitmap nativeGetBitmap(long nativePlatformViewId);
private void ensureRunningOnMainThread() {
if (Looper.myLooper() != mainLooper) {
throw new RuntimeException(
"Methods marked with @UiThread must be executed on the main thread. Current thread: "
+ Thread.currentThread().getName());
}
}
/**
* Delegate responsible for creating and updating Android-side caches of Flutter's semantics tree
* and custom accessibility actions.
*
* <p>{@link AccessibilityBridge} is an example of an {@code AccessibilityDelegate}.
*/
public interface AccessibilityDelegate {
/**
* Sends new custom accessibility actions from Flutter to Android.
*
* <p>Implementers are expected to maintain an Android-side cache of custom accessibility
* actions. This method provides new actions to add to that cache.
*/
void updateCustomAccessibilityActions(@NonNull ByteBuffer buffer, @NonNull String[] strings);
/**
* Sends new {@code SemanticsNode} information from Flutter to Android.
*
* <p>Implementers are expected to maintain an Android-side cache of Flutter's semantics tree.
* This method provides updates from Flutter for the Android-side semantics tree cache.
*/
void updateSemantics(@NonNull ByteBuffer buffer, @NonNull String[] strings);
}
public interface AsyncWaitForVsyncDelegate {
void asyncWaitForVsync(final long cookie);
}
}