Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
FlutterJNI.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package io.flutter.embedding.engine;
6
7import static io.flutter.Build.API_LEVELS;
8
9import android.content.Context;
10import android.content.res.AssetManager;
11import android.graphics.Bitmap;
12import android.graphics.ColorSpace;
13import android.graphics.ImageDecoder;
14import android.graphics.SurfaceTexture;
15import android.os.Build;
16import android.os.Looper;
17import android.util.DisplayMetrics;
18import android.util.Size;
19import android.util.TypedValue;
20import android.view.Surface;
21import android.view.SurfaceHolder;
22import androidx.annotation.Keep;
23import androidx.annotation.NonNull;
24import androidx.annotation.Nullable;
25import androidx.annotation.UiThread;
26import androidx.annotation.VisibleForTesting;
27import io.flutter.Log;
28import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener;
29import io.flutter.embedding.engine.dart.PlatformMessageHandler;
30import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager;
31import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStack;
32import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
33import io.flutter.embedding.engine.renderer.SurfaceTextureWrapper;
34import io.flutter.embedding.engine.systemchannels.SettingsChannel;
35import io.flutter.plugin.common.StandardMessageCodec;
36import io.flutter.plugin.localization.LocalizationPlugin;
37import io.flutter.plugin.platform.PlatformViewsController;
38import io.flutter.util.Preconditions;
39import io.flutter.view.AccessibilityBridge;
40import io.flutter.view.FlutterCallbackInformation;
41import io.flutter.view.TextureRegistry;
42import java.io.IOException;
43import java.lang.ref.WeakReference;
44import java.nio.ByteBuffer;
45import java.util.ArrayList;
46import java.util.List;
47import java.util.Locale;
48import java.util.Set;
49import java.util.concurrent.CopyOnWriteArraySet;
50import java.util.concurrent.locks.ReentrantReadWriteLock;
51
52/**
53 * Interface between Flutter embedding's Java code and Flutter engine's C/C++ code.
54 *
55 * <p>Flutter's engine is built with C/C++. The Android Flutter embedding is responsible for
56 * coordinating Android OS events and app user interactions with the C/C++ engine. Such coordination
57 * requires messaging from an Android app in Java code to the C/C++ engine code. This communication
58 * requires a JNI (Java Native Interface) API to cross the Java/native boundary.
59 *
60 * <p>The entirety of Flutter's JNI API is codified in {@code FlutterJNI}. There are multiple
61 * reasons that all such calls are centralized in one class. First, JNI calls are inherently static
62 * and contain no Java implementation, therefore there is little reason to associate calls with
63 * different classes. Second, every JNI call must be registered in C/C++ code and this registration
64 * becomes more complicated with every additional Java class that contains JNI calls. Third, most
65 * Android developers are not familiar with native development or JNI intricacies, therefore it is
66 * in the interest of future maintenance to reduce the API surface that includes JNI declarations.
67 * Thus, all Flutter JNI calls are centralized in {@code FlutterJNI}.
68 *
69 * <p>Despite the fact that individual JNI calls are inherently static, there is state that exists
70 * within {@code FlutterJNI}. Most calls within {@code FlutterJNI} correspond to a specific
71 * "platform view", of which there may be many. Therefore, each {@code FlutterJNI} instance holds
72 * onto a "native platform view ID" after {@link #attachToNative()}, which is shared with the native
73 * C/C++ engine code. That ID is passed to every platform-view-specific native method. ID management
74 * is handled within {@code FlutterJNI} so that developers don't have to hold onto that ID.
75 *
76 * <p>To connect part of an Android app to Flutter's C/C++ engine, instantiate a {@code FlutterJNI}
77 * and then attach it to the native side:
78 *
79 * <pre>{@code
80 * // Instantiate FlutterJNI and attach to the native side.
81 * FlutterJNI flutterJNI = new FlutterJNI();
82 * flutterJNI.attachToNative();
83 *
84 * // Use FlutterJNI as desired. flutterJNI.dispatchPointerDataPacket(...);
85 *
86 * // Destroy the connection to the native side and cleanup.
87 * flutterJNI.detachFromNativeAndReleaseResources();
88 * }</pre>
89 *
90 * <p>To receive callbacks for certain events that occur on the native side, register listeners:
91 *
92 * <ol>
93 * <li>{@link #addEngineLifecycleListener(FlutterEngine.EngineLifecycleListener)}
94 * <li>{@link #addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)}
95 * </ol>
96 *
97 * To facilitate platform messages between Java and Dart running in Flutter, register a handler:
98 *
99 * <p>{@link #setPlatformMessageHandler(PlatformMessageHandler)}
100 *
101 * <p>To invoke a native method that is not associated with a platform view, invoke it statically:
102 *
103 * <p>{@code bool enabled = FlutterJNI.getIsSoftwareRenderingEnabled(); }
104 */
105@Keep
106public class FlutterJNI {
107 private static final String TAG = "FlutterJNI";
108 // This serializes the invocation of platform message responses and the
109 // attachment and detachment of the shell holder. This ensures that we don't
110 // detach FlutterJNI on the platform thread while a background thread invokes
111 // a message response. Typically accessing the shell holder happens on the
112 // platform thread and doesn't require locking.
113 private ReentrantReadWriteLock shellHolderLock = new ReentrantReadWriteLock();
114
115 // Prefer using the FlutterJNI.Factory so it's easier to test.
116 public FlutterJNI() {
117 // We cache the main looper so that we can ensure calls are made on the main thread
118 // without consistently paying the synchronization cost of getMainLooper().
119 mainLooper = Looper.getMainLooper();
120 }
121
122 /**
123 * A factory for creating {@code FlutterJNI} instances. Useful for FlutterJNI injections during
124 * tests.
125 */
126 public static class Factory {
127 /** @return a {@link FlutterJNI} instance. */
129 return new FlutterJNI();
130 }
131 }
132
133 // BEGIN Methods related to loading for FlutterLoader.
134 /**
135 * Loads the libflutter.so C++ library.
136 *
137 * <p>This must be called before any other native methods, and can be overridden by tests to avoid
138 * loading native libraries.
139 *
140 * <p>This method should only be called once across all FlutterJNI instances.
141 */
142 public void loadLibrary() {
143 if (FlutterJNI.loadLibraryCalled) {
144 Log.w(TAG, "FlutterJNI.loadLibrary called more than once");
145 }
146
147 System.loadLibrary("flutter");
148 FlutterJNI.loadLibraryCalled = true;
149 }
150
151 private static boolean loadLibraryCalled = false;
152
153 private static native void nativePrefetchDefaultFontManager();
154
155 /**
156 * Prefetch the default font manager provided by txt::GetDefaultFontManager() which is a
157 * process-wide singleton owned by Skia. Note that, the first call to txt::GetDefaultFontManager()
158 * will take noticeable time, but later calls will return a reference to the preexisting font
159 * manager.
160 *
161 * <p>This method should only be called once across all FlutterJNI instances.
162 */
164 if (FlutterJNI.prefetchDefaultFontManagerCalled) {
165 Log.w(TAG, "FlutterJNI.prefetchDefaultFontManager called more than once");
166 }
167
168 FlutterJNI.nativePrefetchDefaultFontManager();
169 FlutterJNI.prefetchDefaultFontManagerCalled = true;
170 }
171
172 private static boolean prefetchDefaultFontManagerCalled = false;
173
174 private static native void nativeInit(
175 @NonNull Context context,
176 @NonNull String[] args,
177 @Nullable String bundlePath,
178 @NonNull String appStoragePath,
179 @NonNull String engineCachesPath,
180 long initTimeMillis);
181
182 /**
183 * Perform one time initialization of the Dart VM and Flutter engine.
184 *
185 * <p>This method must be called only once. Calling more than once will cause an exception.
186 *
187 * @param context The application context.
188 * @param args Arguments to the Dart VM/Flutter engine.
189 * @param bundlePath For JIT runtimes, the path to the Dart kernel file for the application.
190 * @param appStoragePath The path to the application data directory.
191 * @param engineCachesPath The path to the application cache directory.
192 * @param initTimeMillis The time, in milliseconds, taken for initialization.
193 */
194 public void init(
195 @NonNull Context context,
196 @NonNull String[] args,
197 @Nullable String bundlePath,
198 @NonNull String appStoragePath,
199 @NonNull String engineCachesPath,
200 long initTimeMillis) {
201 if (FlutterJNI.initCalled) {
202 Log.w(TAG, "FlutterJNI.init called more than once");
203 }
204
205 FlutterJNI.nativeInit(
206 context, args, bundlePath, appStoragePath, engineCachesPath, initTimeMillis);
207 FlutterJNI.initCalled = true;
208 }
209
210 private static boolean initCalled = false;
211 // END methods related to FlutterLoader
212
213 @Nullable private static AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate;
214
215 /**
216 * This value is updated by the VsyncWaiter when it is initialized.
217 *
218 * <p>On API 17+, it is updated whenever the default display refresh rate changes.
219 *
220 * <p>It is defaulted to 60.
221 */
222 private static float refreshRateFPS = 60.0f;
223
224 private static float displayWidth = -1.0f;
225 private static float displayHeight = -1.0f;
226 private static float displayDensity = -1.0f;
227
228 // This is set from native code via JNI.
229 @Nullable private static String vmServiceUri;
230
231 private native boolean nativeGetIsSoftwareRenderingEnabled();
232
233 /**
234 * Checks launch settings for whether software rendering is requested.
235 *
236 * <p>The value is the same per program.
237 */
238 @UiThread
240 return nativeGetIsSoftwareRenderingEnabled();
241 }
242
243 /**
244 * VM Service URI for the VM instance.
245 *
246 * <p>Its value is set by the native engine once {@link #init(Context, String[], String, String,
247 * String, long)} is run.
248 */
249 @Nullable
250 public static String getVMServiceUri() {
251 return vmServiceUri;
252 }
253
254 /**
255 * VM Service URI for the VM instance.
256 *
257 * <p>Its value is set by the native engine once {@link #init(Context, String[], String, String,
258 * String, long)} is run.
259 *
260 * @deprecated replaced by {@link #getVMServiceUri()}.
261 */
262 @Deprecated
263 @Nullable
264 public static String getObservatoryUri() {
265 return vmServiceUri;
266 }
267
268 /**
269 * Notifies the engine about the refresh rate of the display when the API level is below 30.
270 *
271 * <p>For API 30 and above, this value is ignored.
272 *
273 * <p>Calling this method multiple times will update the refresh rate for the next vsync period.
274 * However, callers should avoid calling {@link android.view.Display#getRefreshRate} frequently,
275 * since it is expensive on some vendor implementations.
276 *
277 * @param refreshRateFPS The refresh rate in nanoseconds.
278 */
279 public void setRefreshRateFPS(float refreshRateFPS) {
280 // This is ok because it only ever tracks the refresh rate of the main
281 // display. If we ever need to support the refresh rate of other displays
282 // on Android we will need to refactor this. Static lookup makes things a
283 // bit easier on the C++ side.
284 FlutterJNI.refreshRateFPS = refreshRateFPS;
286 }
287
288 public void updateDisplayMetrics(int displayId, float width, float height, float density) {
289 FlutterJNI.displayWidth = width;
290 FlutterJNI.displayHeight = height;
291 FlutterJNI.displayDensity = density;
292 if (!FlutterJNI.loadLibraryCalled) {
293 return;
294 }
295 nativeUpdateDisplayMetrics(nativeShellHolderId);
296 }
297
298 private native void nativeUpdateDisplayMetrics(long nativeShellHolderId);
299
300 public void updateRefreshRate() {
301 if (!FlutterJNI.loadLibraryCalled) {
302 return;
303 }
304 nativeUpdateRefreshRate(refreshRateFPS);
305 }
306
307 private native void nativeUpdateRefreshRate(float refreshRateFPS);
308
309 /**
310 * The Android vsync waiter implementation in C++ needs to know when a vsync signal arrives, which
311 * is obtained via Java API. The delegate set here is called on the C++ side when the engine is
312 * ready to wait for the next vsync signal. The delegate is expected to add a postFrameCallback to
313 * the {@link android.view.Choreographer}, and call {@link onVsync} to notify the engine.
314 *
315 * @param delegate The delegate that will call the engine back on the next vsync signal.
316 */
318 asyncWaitForVsyncDelegate = delegate;
319 }
320
321 // TODO(mattcarroll): add javadocs
322 // Called by native.
323 private static void asyncWaitForVsync(final long cookie) {
324 if (asyncWaitForVsyncDelegate != null) {
325 asyncWaitForVsyncDelegate.asyncWaitForVsync(cookie);
326 } else {
327 throw new IllegalStateException(
328 "An AsyncWaitForVsyncDelegate must be registered with FlutterJNI before asyncWaitForVsync() is invoked.");
329 }
330 }
331
332 private native void nativeOnVsync(long frameDelayNanos, long refreshPeriodNanos, long cookie);
333
334 /**
335 * Notifies the engine that the Choreographer has signaled a vsync.
336 *
337 * @param frameDelayNanos The time in nanoseconds when the frame started being rendered,
338 * subtracted from the {@link System#nanoTime} timebase.
339 * @param refreshPeriodNanos The display refresh period in nanoseconds.
340 * @param cookie An opaque handle to the C++ VSyncWaiter object.
341 */
342 public void onVsync(long frameDelayNanos, long refreshPeriodNanos, long cookie) {
343 nativeOnVsync(frameDelayNanos, refreshPeriodNanos, cookie);
344 }
345
346 @NonNull
347 @Deprecated
349
350 // ----- Start FlutterTextUtils Methods ----
351 private native boolean nativeFlutterTextUtilsIsEmoji(int codePoint);
352
353 public boolean isCodePointEmoji(int codePoint) {
354 return nativeFlutterTextUtilsIsEmoji(codePoint);
355 }
356
357 private native boolean nativeFlutterTextUtilsIsEmojiModifier(int codePoint);
358
359 public boolean isCodePointEmojiModifier(int codePoint) {
360 return nativeFlutterTextUtilsIsEmojiModifier(codePoint);
361 }
362
363 private native boolean nativeFlutterTextUtilsIsEmojiModifierBase(int codePoint);
364
365 public boolean isCodePointEmojiModifierBase(int codePoint) {
366 return nativeFlutterTextUtilsIsEmojiModifierBase(codePoint);
367 }
368
369 private native boolean nativeFlutterTextUtilsIsVariationSelector(int codePoint);
370
371 public boolean isCodePointVariantSelector(int codePoint) {
372 return nativeFlutterTextUtilsIsVariationSelector(codePoint);
373 }
374
375 private native boolean nativeFlutterTextUtilsIsRegionalIndicator(int codePoint);
376
377 public boolean isCodePointRegionalIndicator(int codePoint) {
378 return nativeFlutterTextUtilsIsRegionalIndicator(codePoint);
379 }
380
381 // ----- End Engine FlutterTextUtils Methods ----
382
383 // Below represents the stateful part of the FlutterJNI instances that aren't static per program.
384 // Conceptually, it represents a native shell instance.
385
386 @Nullable private Long nativeShellHolderId;
387 @Nullable private AccessibilityDelegate accessibilityDelegate;
388 @Nullable private PlatformMessageHandler platformMessageHandler;
389 @Nullable private LocalizationPlugin localizationPlugin;
390 @Nullable private PlatformViewsController platformViewsController;
391
392 @Nullable private DeferredComponentManager deferredComponentManager;
393
394 @NonNull
395 private final Set<EngineLifecycleListener> engineLifecycleListeners = new CopyOnWriteArraySet<>();
396
397 @NonNull
398 private final Set<FlutterUiDisplayListener> flutterUiDisplayListeners =
399 new CopyOnWriteArraySet<>();
400
401 @NonNull private final Looper mainLooper; // cached to avoid synchronization on repeat access.
402
403 // ------ Start Native Attach/Detach Support ----
404 /**
405 * Returns true if this instance of {@code FlutterJNI} is connected to Flutter's native engine via
406 * a Java Native Interface (JNI).
407 */
408 public boolean isAttached() {
409 return nativeShellHolderId != null;
410 }
411
412 /**
413 * Attaches this {@code FlutterJNI} instance to Flutter's native engine, which allows for
414 * communication between Android code and Flutter's platform agnostic engine.
415 *
416 * <p>This method must not be invoked if {@code FlutterJNI} is already attached to native.
417 */
418 @UiThread
419 public void attachToNative() {
420 ensureRunningOnMainThread();
421 ensureNotAttachedToNative();
422 shellHolderLock.writeLock().lock();
423 try {
424 nativeShellHolderId = performNativeAttach(this);
425 } finally {
426 shellHolderLock.writeLock().unlock();
427 }
428 }
429
430 @VisibleForTesting
431 public long performNativeAttach(@NonNull FlutterJNI flutterJNI) {
432 return nativeAttach(flutterJNI);
433 }
434
435 private native long nativeAttach(@NonNull FlutterJNI flutterJNI);
436
437 /**
438 * Spawns a new FlutterJNI instance from the current instance.
439 *
440 * <p>This creates another native shell from the current shell. This causes the 2 shells to re-use
441 * some of the shared resources, reducing the total memory consumption versus creating a new
442 * FlutterJNI by calling its standard constructor.
443 *
444 * <p>This can only be called once the current FlutterJNI instance is attached by calling {@link
445 * #attachToNative()}.
446 *
447 * <p>Static methods that should be only called once such as {@link #init(Context, String[],
448 * String, String, String, long)} shouldn't be called again on the spawned FlutterJNI instance.
449 */
450 @UiThread
451 @NonNull
453 @Nullable String entrypointFunctionName,
454 @Nullable String pathToEntrypointFunction,
455 @Nullable String initialRoute,
456 @Nullable List<String> entrypointArgs) {
457 ensureRunningOnMainThread();
458 ensureAttachedToNative();
459 FlutterJNI spawnedJNI =
460 nativeSpawn(
461 nativeShellHolderId,
462 entrypointFunctionName,
463 pathToEntrypointFunction,
464 initialRoute,
465 entrypointArgs);
466 Preconditions.checkState(
467 spawnedJNI.nativeShellHolderId != null && spawnedJNI.nativeShellHolderId != 0,
468 "Failed to spawn new JNI connected shell from existing shell.");
469
470 return spawnedJNI;
471 }
472
473 private native FlutterJNI nativeSpawn(
474 long nativeSpawningShellId,
475 @Nullable String entrypointFunctionName,
476 @Nullable String pathToEntrypointFunction,
477 @Nullable String initialRoute,
478 @Nullable List<String> entrypointArgs);
479
480 /**
481 * Detaches this {@code FlutterJNI} instance from Flutter's native engine, which precludes any
482 * further communication between Android code and Flutter's platform agnostic engine.
483 *
484 * <p>This method must not be invoked if {@code FlutterJNI} is not already attached to native.
485 *
486 * <p>Invoking this method will result in the release of all native-side resources that were set
487 * up during {@link #attachToNative()} or {@link #spawn(String, String, String, List)}, or
488 * accumulated thereafter.
489 *
490 * <p>It is permissible to re-attach this instance to native after detaching it from native.
491 */
492 @UiThread
494 ensureRunningOnMainThread();
495 ensureAttachedToNative();
496 shellHolderLock.writeLock().lock();
497 try {
498 nativeDestroy(nativeShellHolderId);
499 nativeShellHolderId = null;
500 } finally {
501 shellHolderLock.writeLock().unlock();
502 }
503 }
504
505 private native void nativeDestroy(long nativeShellHolderId);
506
507 private void ensureNotAttachedToNative() {
508 if (nativeShellHolderId != null) {
509 throw new RuntimeException(
510 "Cannot execute operation because FlutterJNI is attached to native.");
511 }
512 }
513
514 private void ensureAttachedToNative() {
515 if (nativeShellHolderId == null) {
516 throw new RuntimeException(
517 "Cannot execute operation because FlutterJNI is not attached to native.");
518 }
519 }
520 // ------ End Native Attach/Detach Support ----
521
522 // ----- Start Render Surface Support -----
523 /**
524 * Adds a {@link FlutterUiDisplayListener}, which receives a callback when Flutter's engine
525 * notifies {@code FlutterJNI} that Flutter is painting pixels to the {@link Surface} that was
526 * provided to Flutter.
527 */
528 @UiThread
529 public void addIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener) {
530 ensureRunningOnMainThread();
531 flutterUiDisplayListeners.add(listener);
532 }
533
534 /**
535 * Removes a {@link FlutterUiDisplayListener} that was added with {@link
536 * #addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)}.
537 */
538 @UiThread
539 public void removeIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener) {
540 ensureRunningOnMainThread();
541 flutterUiDisplayListeners.remove(listener);
542 }
543
544 public static native void nativeImageHeaderCallback(
545 long imageGeneratorPointer, int width, int height);
546
547 /**
548 * Called by native as a fallback method of image decoding. There are other ways to decode images
549 * on lower API levels, they involve copying the native data _and_ do not support any additional
550 * formats, whereas ImageDecoder supports HEIF images. Unlike most other methods called from
551 * native, this method is expected to be called on a worker thread, since it only uses thread safe
552 * methods and may take multiple frames to complete.
553 */
554 @SuppressWarnings("unused")
555 @VisibleForTesting
556 @Nullable
557 public static Bitmap decodeImage(@NonNull ByteBuffer buffer, long imageGeneratorAddress) {
558 if (Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
559 ImageDecoder.Source source = ImageDecoder.createSource(buffer);
560 try {
561 return ImageDecoder.decodeBitmap(
562 source,
563 (decoder, info, src) -> {
564 // i.e. ARGB_8888
565 decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
566 // TODO(bdero): Switch to ALLOCATOR_HARDWARE for devices that have
567 // `AndroidBitmap_getHardwareBuffer` (API 30+) available once Skia supports
568 // `SkImage::MakeFromAHardwareBuffer` via dynamic lookups:
569 // https://skia-review.googlesource.com/c/skia/+/428960
570 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
571
572 Size size = info.getSize();
573 nativeImageHeaderCallback(imageGeneratorAddress, size.getWidth(), size.getHeight());
574 });
575 } catch (IOException e) {
576 Log.e(TAG, "Failed to decode image", e);
577 return null;
578 }
579 }
580 return null;
581 }
582
583 // Called by native to notify first Flutter frame rendered.
584 @SuppressWarnings("unused")
585 @VisibleForTesting
586 @UiThread
587 public void onFirstFrame() {
588 ensureRunningOnMainThread();
589
590 for (FlutterUiDisplayListener listener : flutterUiDisplayListeners) {
591 listener.onFlutterUiDisplayed();
592 }
593 }
594
595 // TODO(mattcarroll): get native to call this when rendering stops.
596 @VisibleForTesting
597 @UiThread
599 ensureRunningOnMainThread();
600
601 for (FlutterUiDisplayListener listener : flutterUiDisplayListeners) {
602 listener.onFlutterUiNoLongerDisplayed();
603 }
604 }
605
606 /**
607 * Call this method when a {@link Surface} has been created onto which you would like Flutter to
608 * paint.
609 *
610 * <p>See {@link android.view.SurfaceHolder.Callback#surfaceCreated(SurfaceHolder)} for an example
611 * of where this call might originate.
612 */
613 @UiThread
614 public void onSurfaceCreated(@NonNull Surface surface) {
615 ensureRunningOnMainThread();
616 ensureAttachedToNative();
617 nativeSurfaceCreated(nativeShellHolderId, surface);
618 }
619
620 private native void nativeSurfaceCreated(long nativeShellHolderId, @NonNull Surface surface);
621
622 /**
623 * In hybrid composition, call this method when the {@link Surface} has changed.
624 *
625 * <p>In hybrid composition, the root surfaces changes from {@link
626 * android.view.SurfaceHolder#getSurface()} to {@link android.media.ImageReader#getSurface()} when
627 * a platform view is in the current frame.
628 */
629 @UiThread
630 public void onSurfaceWindowChanged(@NonNull Surface surface) {
631 ensureRunningOnMainThread();
632 ensureAttachedToNative();
633 nativeSurfaceWindowChanged(nativeShellHolderId, surface);
634 }
635
636 private native void nativeSurfaceWindowChanged(
637 long nativeShellHolderId, @NonNull Surface surface);
638
639 /**
640 * Call this method when the {@link Surface} changes that was previously registered with {@link
641 * #onSurfaceCreated(Surface)}.
642 *
643 * <p>See {@link android.view.SurfaceHolder.Callback#surfaceChanged(SurfaceHolder, int, int, int)}
644 * for an example of where this call might originate.
645 */
646 @UiThread
647 public void onSurfaceChanged(int width, int height) {
648 ensureRunningOnMainThread();
649 ensureAttachedToNative();
650 nativeSurfaceChanged(nativeShellHolderId, width, height);
651 }
652
653 private native void nativeSurfaceChanged(long nativeShellHolderId, int width, int height);
654
655 /**
656 * Call this method when the {@link Surface} is destroyed that was previously registered with
657 * {@link #onSurfaceCreated(Surface)}.
658 *
659 * <p>See {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)} for an
660 * example of where this call might originate.
661 */
662 @UiThread
663 public void onSurfaceDestroyed() {
664 ensureRunningOnMainThread();
665 ensureAttachedToNative();
667 nativeSurfaceDestroyed(nativeShellHolderId);
668 }
669
670 private native void nativeSurfaceDestroyed(long nativeShellHolderId);
671
672 /**
673 * Call this method to notify Flutter of the current device viewport metrics that are applies to
674 * the Flutter UI that is being rendered.
675 *
676 * <p>This method should be invoked with initial values upon attaching to native. Then, it should
677 * be invoked any time those metrics change while {@code FlutterJNI} is attached to native.
678 */
679 @UiThread
681 float devicePixelRatio,
682 int physicalWidth,
683 int physicalHeight,
684 int physicalPaddingTop,
685 int physicalPaddingRight,
686 int physicalPaddingBottom,
687 int physicalPaddingLeft,
688 int physicalViewInsetTop,
689 int physicalViewInsetRight,
690 int physicalViewInsetBottom,
691 int physicalViewInsetLeft,
692 int systemGestureInsetTop,
693 int systemGestureInsetRight,
694 int systemGestureInsetBottom,
695 int systemGestureInsetLeft,
696 int physicalTouchSlop,
697 int[] displayFeaturesBounds,
698 int[] displayFeaturesType,
699 int[] displayFeaturesState) {
700 ensureRunningOnMainThread();
701 ensureAttachedToNative();
702 nativeSetViewportMetrics(
703 nativeShellHolderId,
704 devicePixelRatio,
705 physicalWidth,
706 physicalHeight,
707 physicalPaddingTop,
708 physicalPaddingRight,
709 physicalPaddingBottom,
710 physicalPaddingLeft,
711 physicalViewInsetTop,
712 physicalViewInsetRight,
713 physicalViewInsetBottom,
714 physicalViewInsetLeft,
715 systemGestureInsetTop,
716 systemGestureInsetRight,
717 systemGestureInsetBottom,
718 systemGestureInsetLeft,
719 physicalTouchSlop,
720 displayFeaturesBounds,
721 displayFeaturesType,
722 displayFeaturesState);
723 }
724
725 private native void nativeSetViewportMetrics(
726 long nativeShellHolderId,
727 float devicePixelRatio,
728 int physicalWidth,
729 int physicalHeight,
730 int physicalPaddingTop,
731 int physicalPaddingRight,
732 int physicalPaddingBottom,
733 int physicalPaddingLeft,
734 int physicalViewInsetTop,
735 int physicalViewInsetRight,
736 int physicalViewInsetBottom,
737 int physicalViewInsetLeft,
738 int systemGestureInsetTop,
739 int systemGestureInsetRight,
740 int systemGestureInsetBottom,
741 int systemGestureInsetLeft,
742 int physicalTouchSlop,
743 int[] displayFeaturesBounds,
744 int[] displayFeaturesType,
745 int[] displayFeaturesState);
746
747 // ----- End Render Surface Support -----
748
749 // ------ Start Touch Interaction Support ---
750 /** Sends a packet of pointer data to Flutter's engine. */
751 @UiThread
752 public void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position) {
753 ensureRunningOnMainThread();
754 ensureAttachedToNative();
755 nativeDispatchPointerDataPacket(nativeShellHolderId, buffer, position);
756 }
757
758 private native void nativeDispatchPointerDataPacket(
759 long nativeShellHolderId, @NonNull ByteBuffer buffer, int position);
760 // ------ End Touch Interaction Support ---
761
762 @UiThread
763 public void setPlatformViewsController(@NonNull PlatformViewsController platformViewsController) {
764 ensureRunningOnMainThread();
765 this.platformViewsController = platformViewsController;
766 }
767
768 // ------ Start Accessibility Support -----
769 /**
770 * Sets the {@link AccessibilityDelegate} for the attached Flutter context.
771 *
772 * <p>The {@link AccessibilityDelegate} is responsible for maintaining an Android-side cache of
773 * Flutter's semantics tree and custom accessibility actions. This cache should be hooked up to
774 * Android's accessibility system.
775 *
776 * <p>See {@link AccessibilityBridge} for an example of an {@link AccessibilityDelegate} and the
777 * surrounding responsibilities.
778 */
779 @UiThread
780 public void setAccessibilityDelegate(@Nullable AccessibilityDelegate accessibilityDelegate) {
781 ensureRunningOnMainThread();
782 this.accessibilityDelegate = accessibilityDelegate;
783 }
784
785 /**
786 * Invoked by native to send semantics tree updates from Flutter to Android.
787 *
788 * <p>The {@code buffer} and {@code strings} form a communication protocol that is implemented
789 * here:
790 * https://github.com/flutter/engine/blob/main/shell/platform/android/platform_view_android.cc#L207
791 */
792 @SuppressWarnings("unused")
793 @UiThread
794 private void updateSemantics(
795 @NonNull ByteBuffer buffer,
796 @NonNull String[] strings,
797 @NonNull ByteBuffer[] stringAttributeArgs) {
798 ensureRunningOnMainThread();
799 if (accessibilityDelegate != null) {
800 accessibilityDelegate.updateSemantics(buffer, strings, stringAttributeArgs);
801 }
802 // TODO(mattcarroll): log dropped messages when in debug mode
803 // (https://github.com/flutter/flutter/issues/25391)
804 }
805
806 /**
807 * Invoked by native to send new custom accessibility events from Flutter to Android.
808 *
809 * <p>The {@code buffer} and {@code strings} form a communication protocol that is implemented
810 * here:
811 * https://github.com/flutter/engine/blob/main/shell/platform/android/platform_view_android.cc#L207
812 *
813 * <p>// TODO(cbracken): expand these docs to include more actionable information.
814 */
815 @SuppressWarnings("unused")
816 @UiThread
817 private void updateCustomAccessibilityActions(
818 @NonNull ByteBuffer buffer, @NonNull String[] strings) {
819 ensureRunningOnMainThread();
820 if (accessibilityDelegate != null) {
821 accessibilityDelegate.updateCustomAccessibilityActions(buffer, strings);
822 }
823 // TODO(mattcarroll): log dropped messages when in debug mode
824 // (https://github.com/flutter/flutter/issues/25391)
825 }
826
827 /** Sends a semantics action to Flutter's engine, without any additional arguments. */
828 public void dispatchSemanticsAction(int nodeId, @NonNull AccessibilityBridge.Action action) {
829 dispatchSemanticsAction(nodeId, action, null);
830 }
831
832 /** Sends a semantics action to Flutter's engine, with additional arguments. */
834 int nodeId, @NonNull AccessibilityBridge.Action action, @Nullable Object args) {
835 ensureAttachedToNative();
836
837 ByteBuffer encodedArgs = null;
838 int position = 0;
839 if (args != null) {
840 encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args);
841 position = encodedArgs.position();
842 }
843 dispatchSemanticsAction(nodeId, action.value, encodedArgs, position);
844 }
845
846 /**
847 * Sends a semantics action to Flutter's engine, given arguments that are already encoded for the
848 * engine.
849 *
850 * <p>To send a semantics action that has not already been encoded, see {@link
851 * #dispatchSemanticsAction(int, AccessibilityBridge.Action)} and {@link
852 * #dispatchSemanticsAction(int, AccessibilityBridge.Action, Object)}.
853 */
854 @UiThread
856 int nodeId, int action, @Nullable ByteBuffer args, int argsPosition) {
857 ensureRunningOnMainThread();
858 ensureAttachedToNative();
859 nativeDispatchSemanticsAction(nativeShellHolderId, nodeId, action, args, argsPosition);
860 }
861
862 private native void nativeDispatchSemanticsAction(
863 long nativeShellHolderId,
864 int nodeId,
865 int action,
866 @Nullable ByteBuffer args,
867 int argsPosition);
868
869 /**
870 * Instructs Flutter to enable/disable its semantics tree, which is used by Flutter to support
871 * accessibility and related behaviors.
872 */
873 @UiThread
874 public void setSemanticsEnabled(boolean enabled) {
875 ensureRunningOnMainThread();
876 ensureAttachedToNative();
877 nativeSetSemanticsEnabled(nativeShellHolderId, enabled);
878 }
879
880 private native void nativeSetSemanticsEnabled(long nativeShellHolderId, boolean enabled);
881
882 // TODO(mattcarroll): figure out what flags are supported and add javadoc about when/why/where to
883 // use this.
884 @UiThread
886 ensureRunningOnMainThread();
887 ensureAttachedToNative();
888 nativeSetAccessibilityFeatures(nativeShellHolderId, flags);
889 }
890
891 private native void nativeSetAccessibilityFeatures(long nativeShellHolderId, int flags);
892 // ------ End Accessibility Support ----
893
894 // ------ Start Texture Registration Support -----
895 /**
896 * Gives control of a {@link SurfaceTexture} to Flutter so that Flutter can display that texture
897 * within Flutter's UI.
898 */
899 @UiThread
900 public void registerTexture(long textureId, @NonNull SurfaceTextureWrapper textureWrapper) {
901 ensureRunningOnMainThread();
902 ensureAttachedToNative();
903 nativeRegisterTexture(
904 nativeShellHolderId, textureId, new WeakReference<SurfaceTextureWrapper>(textureWrapper));
905 }
906
907 private native void nativeRegisterTexture(
908 long nativeShellHolderId,
909 long textureId,
910 @NonNull WeakReference<SurfaceTextureWrapper> textureWrapper);
911
912 /**
913 * Registers a ImageTexture with the given id.
914 *
915 * <p>REQUIRED: Callers should eventually unregisterTexture with the same id.
916 */
917 @UiThread
919 long textureId, @NonNull TextureRegistry.ImageConsumer imageTexture) {
920 ensureRunningOnMainThread();
921 ensureAttachedToNative();
922 nativeRegisterImageTexture(
923 nativeShellHolderId,
924 textureId,
925 new WeakReference<TextureRegistry.ImageConsumer>(imageTexture));
926 }
927
928 private native void nativeRegisterImageTexture(
929 long nativeShellHolderId,
930 long textureId,
931 @NonNull WeakReference<TextureRegistry.ImageConsumer> imageTexture);
932
933 /**
934 * Call this method to inform Flutter that a texture previously registered with {@link
935 * #registerTexture(long, SurfaceTextureWrapper)} has a new frame available.
936 *
937 * <p>Invoking this method instructs Flutter to update its presentation of the given texture so
938 * that the new frame is displayed.
939 */
940 @UiThread
941 public void markTextureFrameAvailable(long textureId) {
942 ensureRunningOnMainThread();
943 ensureAttachedToNative();
944 nativeMarkTextureFrameAvailable(nativeShellHolderId, textureId);
945 }
946
947 private native void nativeMarkTextureFrameAvailable(long nativeShellHolderId, long textureId);
948
949 /** Schedule the engine to draw a frame but does not invalidate the layout tree. */
950 @UiThread
951 public void scheduleFrame() {
952 ensureRunningOnMainThread();
953 ensureAttachedToNative();
954 nativeScheduleFrame(nativeShellHolderId);
955 }
956
957 private native void nativeScheduleFrame(long nativeShellHolderId);
958
959 /**
960 * Unregisters a texture that was registered with {@link #registerTexture(long,
961 * SurfaceTextureWrapper)}.
962 */
963 @UiThread
964 public void unregisterTexture(long textureId) {
965 ensureRunningOnMainThread();
966 ensureAttachedToNative();
967 nativeUnregisterTexture(nativeShellHolderId, textureId);
968 }
969
970 private native void nativeUnregisterTexture(long nativeShellHolderId, long textureId);
971 // ------ Start Texture Registration Support -----
972
973 // ------ Start Dart Execution Support -------
974 /**
975 * Executes a Dart entrypoint.
976 *
977 * <p>This can only be done once per JNI attachment because a Dart isolate can only be entered
978 * once.
979 */
980 @UiThread
982 @NonNull String bundlePath,
983 @Nullable String entrypointFunctionName,
984 @Nullable String pathToEntrypointFunction,
985 @NonNull AssetManager assetManager,
986 @Nullable List<String> entrypointArgs) {
987 ensureRunningOnMainThread();
988 ensureAttachedToNative();
989 nativeRunBundleAndSnapshotFromLibrary(
990 nativeShellHolderId,
991 bundlePath,
992 entrypointFunctionName,
993 pathToEntrypointFunction,
994 assetManager,
995 entrypointArgs);
996 }
997
998 private native void nativeRunBundleAndSnapshotFromLibrary(
999 long nativeShellHolderId,
1000 @NonNull String bundlePath,
1001 @Nullable String entrypointFunctionName,
1002 @Nullable String pathToEntrypointFunction,
1003 @NonNull AssetManager manager,
1004 @Nullable List<String> entrypointArgs);
1005 // ------ End Dart Execution Support -------
1006
1007 // --------- Start Platform Message Support ------
1008 /**
1009 * Sets the handler for all platform messages that come from the attached platform view to Java.
1010 *
1011 * <p>Communication between a specific Flutter context (Dart) and the host platform (Java) is
1012 * accomplished by passing messages. Messages can be sent from Java to Dart with the corresponding
1013 * {@code FlutterJNI} methods:
1014 *
1015 * <ul>
1016 * <li>{@link #dispatchPlatformMessage(String, ByteBuffer, int, int)}
1017 * <li>{@link #dispatchEmptyPlatformMessage(String, int)}
1018 * </ul>
1019 *
1020 * <p>{@code FlutterJNI} is also the recipient of all platform messages sent from its attached
1021 * Flutter context. {@code FlutterJNI} does not know what to do with these messages, so a handler
1022 * is exposed to allow these messages to be processed in whatever manner is desired:
1023 *
1024 * <p>{@code setPlatformMessageHandler(PlatformMessageHandler)}
1025 *
1026 * <p>If a message is received but no {@link PlatformMessageHandler} is registered, that message
1027 * will be dropped (ignored). Therefore, when using {@code FlutterJNI} to integrate a Flutter
1028 * context in an app, a {@link PlatformMessageHandler} must be registered for 2-way Java/Dart
1029 * communication to operate correctly. Moreover, the handler must be implemented such that
1030 * fundamental platform messages are handled as expected. See {@link
1031 * io.flutter.view.FlutterNativeView} for an example implementation.
1032 */
1033 @UiThread
1034 public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) {
1035 ensureRunningOnMainThread();
1036 this.platformMessageHandler = platformMessageHandler;
1037 }
1038
1039 private native void nativeCleanupMessageData(long messageData);
1040
1041 /**
1042 * Destroys the resources provided sent to `handlePlatformMessage`.
1043 *
1044 * <p>This can be called on any thread.
1045 *
1046 * @param messageData the argument sent to handlePlatformMessage.
1047 */
1048 public void cleanupMessageData(long messageData) {
1049 // This doesn't rely on being attached like other methods.
1050 nativeCleanupMessageData(messageData);
1051 }
1052
1053 // Called by native on any thread.
1054 // TODO(mattcarroll): determine if message is nonull or nullable
1055 @SuppressWarnings("unused")
1056 @VisibleForTesting
1058 @NonNull final String channel,
1059 ByteBuffer message,
1060 final int replyId,
1061 final long messageData) {
1062 if (platformMessageHandler != null) {
1063 platformMessageHandler.handleMessageFromDart(channel, message, replyId, messageData);
1064 } else {
1065 nativeCleanupMessageData(messageData);
1066 }
1067 // TODO(mattcarroll): log dropped messages when in debug mode
1068 // (https://github.com/flutter/flutter/issues/25391)
1069 }
1070
1071 // Called by native to respond to a platform message that we sent.
1072 // TODO(mattcarroll): determine if reply is nonull or nullable
1073 @SuppressWarnings("unused")
1074 private void handlePlatformMessageResponse(int replyId, ByteBuffer reply) {
1075 if (platformMessageHandler != null) {
1076 platformMessageHandler.handlePlatformMessageResponse(replyId, reply);
1077 }
1078 // TODO(mattcarroll): log dropped messages when in debug mode
1079 // (https://github.com/flutter/flutter/issues/25391)
1080 }
1081
1082 /**
1083 * Sends an empty reply (identified by {@code responseId}) from Android to Flutter over the given
1084 * {@code channel}.
1085 */
1086 @UiThread
1087 public void dispatchEmptyPlatformMessage(@NonNull String channel, int responseId) {
1088 ensureRunningOnMainThread();
1089 if (isAttached()) {
1090 nativeDispatchEmptyPlatformMessage(nativeShellHolderId, channel, responseId);
1091 } else {
1092 Log.w(
1093 TAG,
1094 "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: "
1095 + channel
1096 + ". Response ID: "
1097 + responseId);
1098 }
1099 }
1100
1101 // Send an empty platform message to Dart.
1102 private native void nativeDispatchEmptyPlatformMessage(
1103 long nativeShellHolderId, @NonNull String channel, int responseId);
1104
1105 /** Sends a reply {@code message} from Android to Flutter over the given {@code channel}. */
1106 @UiThread
1108 @NonNull String channel, @Nullable ByteBuffer message, int position, int responseId) {
1109 ensureRunningOnMainThread();
1110 if (isAttached()) {
1111 nativeDispatchPlatformMessage(nativeShellHolderId, channel, message, position, responseId);
1112 } else {
1113 Log.w(
1114 TAG,
1115 "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: "
1116 + channel
1117 + ". Response ID: "
1118 + responseId);
1119 }
1120 }
1121
1122 // Send a data-carrying platform message to Dart.
1123 private native void nativeDispatchPlatformMessage(
1124 long nativeShellHolderId,
1125 @NonNull String channel,
1126 @Nullable ByteBuffer message,
1127 int position,
1128 int responseId);
1129
1130 // TODO(mattcarroll): differentiate between channel responses and platform responses.
1132 // Called on any thread.
1133 shellHolderLock.readLock().lock();
1134 try {
1135 if (isAttached()) {
1136 nativeInvokePlatformMessageEmptyResponseCallback(nativeShellHolderId, responseId);
1137 } else {
1138 Log.w(
1139 TAG,
1140 "Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: "
1141 + responseId);
1142 }
1143 } finally {
1144 shellHolderLock.readLock().unlock();
1145 }
1146 }
1147
1148 // Send an empty response to a platform message received from Dart.
1149 private native void nativeInvokePlatformMessageEmptyResponseCallback(
1150 long nativeShellHolderId, int responseId);
1151
1152 // TODO(mattcarroll): differentiate between channel responses and platform responses.
1154 int responseId, @NonNull ByteBuffer message, int position) {
1155 // Called on any thread.
1156 if (!message.isDirect()) {
1157 throw new IllegalArgumentException("Expected a direct ByteBuffer.");
1158 }
1159 shellHolderLock.readLock().lock();
1160 try {
1161 if (isAttached()) {
1162 nativeInvokePlatformMessageResponseCallback(
1163 nativeShellHolderId, responseId, message, position);
1164 } else {
1165 Log.w(
1166 TAG,
1167 "Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: "
1168 + responseId);
1169 }
1170 } finally {
1171 shellHolderLock.readLock().unlock();
1172 }
1173 }
1174
1175 // Send a data-carrying response to a platform message received from Dart.
1176 private native void nativeInvokePlatformMessageResponseCallback(
1177 long nativeShellHolderId, int responseId, @Nullable ByteBuffer message, int position);
1178 // ------- End Platform Message Support ----
1179
1180 // ----- Start Engine Lifecycle Support ----
1181 /**
1182 * Adds the given {@code engineLifecycleListener} to be notified of Flutter engine lifecycle
1183 * events, e.g., {@link EngineLifecycleListener#onPreEngineRestart()}.
1184 */
1185 @UiThread
1186 public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) {
1187 ensureRunningOnMainThread();
1188 engineLifecycleListeners.add(engineLifecycleListener);
1189 }
1190
1191 /**
1192 * Removes the given {@code engineLifecycleListener}, which was previously added using {@link
1193 * #addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)}.
1194 */
1195 @UiThread
1197 @NonNull EngineLifecycleListener engineLifecycleListener) {
1198 ensureRunningOnMainThread();
1199 engineLifecycleListeners.remove(engineLifecycleListener);
1200 }
1201
1202 // Called by native.
1203 @SuppressWarnings("unused")
1204 private void onPreEngineRestart() {
1205 for (EngineLifecycleListener listener : engineLifecycleListeners) {
1206 listener.onPreEngineRestart();
1207 }
1208 }
1209
1210 @SuppressWarnings("unused")
1211 @UiThread
1212 public void onDisplayOverlaySurface(int id, int x, int y, int width, int height) {
1213 ensureRunningOnMainThread();
1214 if (platformViewsController == null) {
1215 throw new RuntimeException(
1216 "platformViewsController must be set before attempting to position an overlay surface");
1217 }
1218 platformViewsController.onDisplayOverlaySurface(id, x, y, width, height);
1219 }
1220
1221 @SuppressWarnings("unused")
1222 @UiThread
1223 public void onBeginFrame() {
1224 ensureRunningOnMainThread();
1225 if (platformViewsController == null) {
1226 throw new RuntimeException(
1227 "platformViewsController must be set before attempting to begin the frame");
1228 }
1229 platformViewsController.onBeginFrame();
1230 }
1231
1232 @SuppressWarnings("unused")
1233 @UiThread
1234 public void onEndFrame() {
1235 ensureRunningOnMainThread();
1236 if (platformViewsController == null) {
1237 throw new RuntimeException(
1238 "platformViewsController must be set before attempting to end the frame");
1239 }
1240 platformViewsController.onEndFrame();
1241 }
1242
1243 @SuppressWarnings("unused")
1244 @UiThread
1246 ensureRunningOnMainThread();
1247 if (platformViewsController == null) {
1248 throw new RuntimeException(
1249 "platformViewsController must be set before attempting to position an overlay surface");
1250 }
1251 return platformViewsController.createOverlaySurface();
1252 }
1253
1254 @SuppressWarnings("unused")
1255 @UiThread
1257 ensureRunningOnMainThread();
1258 if (platformViewsController == null) {
1259 throw new RuntimeException(
1260 "platformViewsController must be set before attempting to destroy an overlay surface");
1261 }
1262 platformViewsController.destroyOverlaySurfaces();
1263 }
1264 // ----- End Engine Lifecycle Support ----
1265
1266 // ----- Start Localization Support ----
1267
1268 /** Sets the localization plugin that is used in various localization methods. */
1269 @UiThread
1270 public void setLocalizationPlugin(@Nullable LocalizationPlugin localizationPlugin) {
1271 ensureRunningOnMainThread();
1272 this.localizationPlugin = localizationPlugin;
1273 }
1274
1275 /** Invoked by native to obtain the results of Android's locale resolution algorithm. */
1276 @SuppressWarnings("unused")
1277 @VisibleForTesting
1278 public String[] computePlatformResolvedLocale(@NonNull String[] strings) {
1279 if (localizationPlugin == null) {
1280 return new String[0];
1281 }
1282 List<Locale> supportedLocales = new ArrayList<Locale>();
1283 final int localeDataLength = 3;
1284 for (int i = 0; i < strings.length; i += localeDataLength) {
1285 String languageCode = strings[i + 0];
1286 String countryCode = strings[i + 1];
1287 String scriptCode = strings[i + 2];
1288 // Convert to Locales via LocaleBuilder if available (API 21+) to include scriptCode.
1289 Locale.Builder localeBuilder = new Locale.Builder();
1290 if (!languageCode.isEmpty()) {
1291 localeBuilder.setLanguage(languageCode);
1292 }
1293 if (!countryCode.isEmpty()) {
1294 localeBuilder.setRegion(countryCode);
1295 }
1296 if (!scriptCode.isEmpty()) {
1297 localeBuilder.setScript(scriptCode);
1298 }
1299 supportedLocales.add(localeBuilder.build());
1300 }
1301
1302 Locale result = localizationPlugin.resolveNativeLocale(supportedLocales);
1303
1304 if (result == null) {
1305 return new String[0];
1306 }
1307 String[] output = new String[localeDataLength];
1308 output[0] = result.getLanguage();
1309 output[1] = result.getCountry();
1310 output[2] = result.getScript();
1311 return output;
1312 }
1313
1314 // ----- End Localization Support ----
1315 @Nullable
1316 public float getScaledFontSize(float fontSize, int configurationId) {
1317 final DisplayMetrics metrics = SettingsChannel.getPastDisplayMetrics(configurationId);
1318 if (metrics == null) {
1319 Log.e(
1320 TAG,
1321 "getScaledFontSize called with configurationId "
1322 + String.valueOf(configurationId)
1323 + ", which can't be found.");
1324 return -1f;
1325 }
1326 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, fontSize, metrics)
1327 / metrics.density;
1328 }
1329
1330 // ----- Start Deferred Components Support ----
1331
1332 /** Sets the deferred component manager that is used to download and install split features. */
1333 @UiThread
1335 @Nullable DeferredComponentManager deferredComponentManager) {
1336 ensureRunningOnMainThread();
1337 this.deferredComponentManager = deferredComponentManager;
1338 if (deferredComponentManager != null) {
1339 deferredComponentManager.setJNI(this);
1340 }
1341 }
1342
1343 /**
1344 * Called by dart to request that a Dart deferred library corresponding to loadingUnitId be
1345 * downloaded (if necessary) and loaded into the dart vm.
1346 *
1347 * <p>This method delegates the task to DeferredComponentManager, which handles the download and
1348 * loading of the dart library and any assets.
1349 *
1350 * @param loadingUnitId The loadingUnitId is assigned during compile time by gen_snapshot and is
1351 * automatically retrieved when loadLibrary() is called on a dart deferred library.
1352 */
1353 @SuppressWarnings("unused")
1354 @UiThread
1355 public void requestDartDeferredLibrary(int loadingUnitId) {
1356 if (deferredComponentManager != null) {
1357 deferredComponentManager.installDeferredComponent(loadingUnitId, null);
1358 } else {
1359 // TODO(garyq): Add link to setup/instructions guide wiki.
1360 Log.e(
1361 TAG,
1362 "No DeferredComponentManager found. Android setup must be completed before using split AOT deferred components.");
1363 }
1364 }
1365
1366 /**
1367 * Searches each of the provided paths for a valid Dart shared library .so file and resolves
1368 * symbols to load into the dart VM.
1369 *
1370 * <p>Successful loading of the dart library completes the future returned by loadLibrary() that
1371 * triggered the install/load process.
1372 *
1373 * @param loadingUnitId The loadingUnitId is assigned during compile time by gen_snapshot and is
1374 * automatically retrieved when loadLibrary() is called on a dart deferred library. This is
1375 * used to identify which Dart deferred library the resolved correspond to.
1376 * @param searchPaths An array of paths in which to look for valid dart shared libraries. This
1377 * supports paths within zipped apks as long as the apks are not compressed using the
1378 * `path/to/apk.apk!path/inside/apk/lib.so` format. Paths will be tried first to last and ends
1379 * when a library is successfully found. When the found library is invalid, no additional
1380 * paths will be attempted.
1381 */
1382 @UiThread
1383 public void loadDartDeferredLibrary(int loadingUnitId, @NonNull String[] searchPaths) {
1384 ensureRunningOnMainThread();
1385 ensureAttachedToNative();
1386 nativeLoadDartDeferredLibrary(nativeShellHolderId, loadingUnitId, searchPaths);
1387 }
1388
1389 private native void nativeLoadDartDeferredLibrary(
1390 long nativeShellHolderId, int loadingUnitId, @NonNull String[] searchPaths);
1391
1392 /**
1393 * Adds the specified AssetManager as an APKAssetResolver in the Flutter Engine's AssetManager.
1394 *
1395 * <p>This may be used to update the engine AssetManager when a new deferred component is
1396 * installed and a new Android AssetManager is created with access to new assets.
1397 *
1398 * @param assetManager An android AssetManager that is able to access the newly downloaded assets.
1399 * @param assetBundlePath The subdirectory that the flutter assets are stored in. The typical
1400 * value is `flutter_assets`.
1401 */
1402 @UiThread
1404 @NonNull AssetManager assetManager, @NonNull String assetBundlePath) {
1405 ensureRunningOnMainThread();
1406 ensureAttachedToNative();
1407 nativeUpdateJavaAssetManager(nativeShellHolderId, assetManager, assetBundlePath);
1408 }
1409
1410 private native void nativeUpdateJavaAssetManager(
1411 long nativeShellHolderId,
1412 @NonNull AssetManager assetManager,
1413 @NonNull String assetBundlePath);
1414
1415 /**
1416 * Indicates that a failure was encountered during the Android portion of downloading a dynamic
1417 * feature module and loading a dart deferred library, which is typically done by
1418 * DeferredComponentManager.
1419 *
1420 * <p>This will inform dart that the future returned by loadLibrary() should complete with an
1421 * error.
1422 *
1423 * @param loadingUnitId The loadingUnitId that corresponds to the dart deferred library that
1424 * failed to install.
1425 * @param error The error message to display.
1426 * @param isTransient When isTransient is false, new attempts to install will automatically result
1427 * in same error in Dart before the request is passed to Android.
1428 */
1429 @SuppressWarnings("unused")
1430 @UiThread
1432 int loadingUnitId, @NonNull String error, boolean isTransient) {
1433 ensureRunningOnMainThread();
1434 nativeDeferredComponentInstallFailure(loadingUnitId, error, isTransient);
1435 }
1436
1437 private native void nativeDeferredComponentInstallFailure(
1438 int loadingUnitId, @NonNull String error, boolean isTransient);
1439
1440 // ----- End Deferred Components Support ----
1441
1442 // @SuppressWarnings("unused")
1443 @UiThread
1445 int viewId,
1446 int x,
1447 int y,
1448 int width,
1449 int height,
1450 int viewWidth,
1451 int viewHeight,
1452 FlutterMutatorsStack mutatorsStack) {
1453 ensureRunningOnMainThread();
1454 if (platformViewsController == null) {
1455 throw new RuntimeException(
1456 "platformViewsController must be set before attempting to position a platform view");
1457 }
1458 platformViewsController.onDisplayPlatformView(
1459 viewId, x, y, width, height, viewWidth, viewHeight, mutatorsStack);
1460 }
1461
1462 // TODO(mattcarroll): determine if this is nonull or nullable
1463 @UiThread
1464 public Bitmap getBitmap() {
1465 ensureRunningOnMainThread();
1466 ensureAttachedToNative();
1467 return nativeGetBitmap(nativeShellHolderId);
1468 }
1469
1470 // TODO(mattcarroll): determine if this is nonull or nullable
1471 private native Bitmap nativeGetBitmap(long nativeShellHolderId);
1472
1473 /**
1474 * Notifies the Dart VM of a low memory event, or that the application is in a state such that now
1475 * is an appropriate time to free resources, such as going to the background.
1476 *
1477 * <p>This is distinct from sending a SystemChannel message about low memory, which only notifies
1478 * the running Flutter application.
1479 */
1480 @UiThread
1482 ensureRunningOnMainThread();
1483 ensureAttachedToNative();
1484 nativeNotifyLowMemoryWarning(nativeShellHolderId);
1485 }
1486
1487 private native void nativeNotifyLowMemoryWarning(long nativeShellHolderId);
1488
1489 private void ensureRunningOnMainThread() {
1490 if (Looper.myLooper() != mainLooper) {
1491 throw new RuntimeException(
1492 "Methods marked with @UiThread must be executed on the main thread. Current thread: "
1493 + Thread.currentThread().getName());
1494 }
1495 }
1496
1497 /**
1498 * Delegate responsible for creating and updating Android-side caches of Flutter's semantics tree
1499 * and custom accessibility actions.
1500 *
1501 * <p>{@link AccessibilityBridge} is an example of an {@code AccessibilityDelegate}.
1502 */
1503 public interface AccessibilityDelegate {
1504 /**
1505 * Sends new custom accessibility actions from Flutter to Android.
1506 *
1507 * <p>Implementers are expected to maintain an Android-side cache of custom accessibility
1508 * actions. This method provides new actions to add to that cache.
1509 */
1510 void updateCustomAccessibilityActions(@NonNull ByteBuffer buffer, @NonNull String[] strings);
1511
1512 /**
1513 * Sends new {@code SemanticsNode} information from Flutter to Android.
1514 *
1515 * <p>Implementers are expected to maintain an Android-side cache of Flutter's semantics tree.
1516 * This method provides updates from Flutter for the Android-side semantics tree cache.
1517 */
1519 @NonNull ByteBuffer buffer,
1520 @NonNull String[] strings,
1521 @NonNull ByteBuffer[] stringAttributeArgs);
1522 }
1523
1524 public interface AsyncWaitForVsyncDelegate {
1525 void asyncWaitForVsync(final long cookie);
1526 }
1527}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
void add(sk_sp< SkIDChangeListener > listener) SK_EXCLUDES(fMutex)
static void e(@NonNull String tag, @NonNull String message)
Definition Log.java:84
static void w(@NonNull String tag, @NonNull String message)
Definition Log.java:76
void setSemanticsEnabled(boolean enabled)
void updateJavaAssetManager( @NonNull AssetManager assetManager, @NonNull String assetBundlePath)
void addIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener)
void cleanupMessageData(long messageData)
void requestDartDeferredLibrary(int loadingUnitId)
void runBundleAndSnapshotFromLibrary( @NonNull String bundlePath, @Nullable String entrypointFunctionName, @Nullable String pathToEntrypointFunction, @NonNull AssetManager assetManager, @Nullable List< String > entrypointArgs)
void setDeferredComponentManager( @Nullable DeferredComponentManager deferredComponentManager)
void init( @NonNull Context context, @NonNull String[] args, @Nullable String bundlePath, @NonNull String appStoragePath, @NonNull String engineCachesPath, long initTimeMillis)
boolean isCodePointEmojiModifier(int codePoint)
long performNativeAttach(@NonNull FlutterJNI flutterJNI)
void dispatchEmptyPlatformMessage(@NonNull String channel, int responseId)
void setPlatformViewsController(@NonNull PlatformViewsController platformViewsController)
void dispatchSemanticsAction(int nodeId, @NonNull AccessibilityBridge.Action action, @Nullable Object args)
void invokePlatformMessageEmptyResponseCallback(int responseId)
void registerTexture(long textureId, @NonNull SurfaceTextureWrapper textureWrapper)
void setAccessibilityDelegate(@Nullable AccessibilityDelegate accessibilityDelegate)
void deferredComponentInstallFailure(int loadingUnitId, @NonNull String error, boolean isTransient)
void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position)
void registerImageTexture(long textureId, @NonNull TextureRegistry.ImageConsumer imageTexture)
void onSurfaceCreated(@NonNull Surface surface)
void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener)
void setLocalizationPlugin(@Nullable LocalizationPlugin localizationPlugin)
boolean isCodePointEmojiModifierBase(int codePoint)
boolean isCodePointRegionalIndicator(int codePoint)
float getScaledFontSize(float fontSize, int configurationId)
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, int physicalTouchSlop, int[] displayFeaturesBounds, int[] displayFeaturesType, int[] displayFeaturesState)
static native void nativeImageHeaderCallback(long imageGeneratorPointer, int width, int height)
void handlePlatformMessage( @NonNull final String channel, ByteBuffer message, final int replyId, final long messageData)
void onSurfaceChanged(int width, int height)
void dispatchSemanticsAction(int nodeId, @NonNull AccessibilityBridge.Action action)
void onSurfaceWindowChanged(@NonNull Surface surface)
FlutterOverlaySurface createOverlaySurface()
static native FlutterCallbackInformation nativeLookupCallbackInformation(long handle)
void dispatchSemanticsAction(int nodeId, int action, @Nullable ByteBuffer args, int argsPosition)
static Bitmap decodeImage(@NonNull ByteBuffer buffer, long imageGeneratorAddress)
boolean isCodePointEmoji(int codePoint)
void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler)
void setRefreshRateFPS(float refreshRateFPS)
void removeIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListener listener)
void setAsyncWaitForVsyncDelegate(@Nullable AsyncWaitForVsyncDelegate delegate)
void onVsync(long frameDelayNanos, long refreshPeriodNanos, long cookie)
void updateDisplayMetrics(int displayId, float width, float height, float density)
boolean isCodePointVariantSelector(int codePoint)
FlutterJNI spawn( @Nullable String entrypointFunctionName, @Nullable String pathToEntrypointFunction, @Nullable String initialRoute, @Nullable List< String > entrypointArgs)
void loadDartDeferredLibrary(int loadingUnitId, @NonNull String[] searchPaths)
void onDisplayOverlaySurface(int id, int x, int y, int width, int height)
void dispatchPlatformMessage( @NonNull String channel, @Nullable ByteBuffer message, int position, int responseId)
void markTextureFrameAvailable(long textureId)
void removeEngineLifecycleListener( @NonNull EngineLifecycleListener engineLifecycleListener)
void invokePlatformMessageResponseCallback(int responseId, @NonNull ByteBuffer message, int position)
String[] computePlatformResolvedLocale(@NonNull String[] strings)
void onDisplayPlatformView(int viewId, int x, int y, int width, int height, int viewWidth, int viewHeight, FlutterMutatorsStack mutatorsStack)
VkSurfaceKHR surface
Definition main.cc:49
SkBitmap source
Definition examples.cpp:28
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
void updateSemantics( @NonNull ByteBuffer buffer, @NonNull String[] strings, @NonNull ByteBuffer[] stringAttributeArgs)
void updateCustomAccessibilityActions(@NonNull ByteBuffer buffer, @NonNull String[] strings)
Win32Message message
double y
double x
#define TAG()
int32_t height
int32_t width