Delete v1 android engine embedding (flutter/engine#52022)

Fixes https://github.com/flutter/flutter/issues/143531

Other failures from the initial attempt are fixed in https://github.com/flutter/flutter/pull/146523/
This commit is contained in:
Gray Mackall 2024-10-23 14:12:52 -07:00 committed by GitHub
parent 3e89cd79b5
commit c5d163fccb
23 changed files with 8 additions and 3584 deletions

View File

@ -44211,13 +44211,6 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/Build.java + ../../..
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/BuildConfig.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/FlutterInjector.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/Log.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivity.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityEvents.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/app/FlutterApplication.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/ExclusiveAppComponent.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java + ../../../flutter/LICENSE
@ -44274,8 +44267,6 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plug
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceAware.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java + ../../../flutter/LICENSE
@ -44349,10 +44340,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/ViewUtils.java +
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/FlutterMain.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArguments.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/FlutterView.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/TextureRegistry.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/view/VsyncWaiter.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/jni/jni_mock.h + ../../../flutter/LICENSE
@ -47088,13 +47076,6 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/Build.java
FILE: ../../../flutter/shell/platform/android/io/flutter/BuildConfig.java
FILE: ../../../flutter/shell/platform/android/io/flutter/FlutterInjector.java
FILE: ../../../flutter/shell/platform/android/io/flutter/Log.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivity.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityEvents.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterApplication.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/ExclusiveAppComponent.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
@ -47154,8 +47135,6 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugin
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceAware.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java
@ -47237,10 +47216,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/util/ViewUtils.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterMain.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArguments.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterView.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/TextureRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/view/VsyncWaiter.java
FILE: ../../../flutter/shell/platform/android/jni/jni_mock.h

View File

@ -3,28 +3,13 @@
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.flutter.app" android:versionCode="1" android:versionName="0.0.1">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.flutter.embedding" android:versionCode="1" android:versionName="0.0.1">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="35" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />
<application android:label="Flutter Shell" android:name="FlutterApplication" android:debuggable="true">
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
android:hardwareAccelerated="true"
android:launchMode="standard"
android:name="FlutterActivity"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<!-- Required for io.flutter.plugin.text.ProcessTextPlugin to query activities that can process text. -->
<queries>
<intent>

View File

@ -207,13 +207,6 @@ android_java_sources = [
"io/flutter/Build.java",
"io/flutter/FlutterInjector.java",
"io/flutter/Log.java",
"io/flutter/app/FlutterActivity.java",
"io/flutter/app/FlutterActivityDelegate.java",
"io/flutter/app/FlutterActivityEvents.java",
"io/flutter/app/FlutterApplication.java",
"io/flutter/app/FlutterFragmentActivity.java",
"io/flutter/app/FlutterPlayStoreSplitApplication.java",
"io/flutter/app/FlutterPluginRegistry.java",
"io/flutter/embedding/android/AndroidTouchProcessor.java",
"io/flutter/embedding/android/ExclusiveAppComponent.java",
"io/flutter/embedding/android/FlutterActivity.java",
@ -273,8 +266,6 @@ android_java_sources = [
"io/flutter/embedding/engine/plugins/service/ServiceAware.java",
"io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java",
"io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java",
"io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java",
"io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java",
"io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java",
"io/flutter/embedding/engine/renderer/FlutterRenderer.java",
"io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java",
@ -356,10 +347,7 @@ android_java_sources = [
"io/flutter/view/AccessibilityBridge.java",
"io/flutter/view/AccessibilityViewEmbedder.java",
"io/flutter/view/FlutterCallbackInformation.java",
"io/flutter/view/FlutterMain.java",
"io/flutter/view/FlutterNativeView.java",
"io/flutter/view/FlutterRunArguments.java",
"io/flutter/view/FlutterView.java",
"io/flutter/view/TextureRegistry.java",
"io/flutter/view/VsyncWaiter.java",
]

View File

@ -1,181 +0,0 @@
// 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.app;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.annotation.NonNull;
import io.flutter.app.FlutterActivityDelegate.ViewFactory;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterView;
/**
* Deprecated base class for activities that use Flutter.
*
* @deprecated {@link io.flutter.embedding.android.FlutterActivity} is the new API that now replaces
* this class. See https://flutter.dev/go/android-project-migration for more migration details.
*/
@Deprecated
public class FlutterActivity extends Activity
implements FlutterView.Provider, PluginRegistry, ViewFactory {
private static final String TAG = "FlutterActivity";
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
// These aliases ensure that the methods we forward to the delegate adhere
// to relevant interfaces versus just existing in FlutterActivityDelegate.
private final FlutterActivityEvents eventDelegate = delegate;
private final FlutterView.Provider viewProvider = delegate;
private final PluginRegistry pluginRegistry = delegate;
/**
* Returns the Flutter view used by this activity; will be null before {@link #onCreate(Bundle)}
* is called.
*/
@Override
public FlutterView getFlutterView() {
return viewProvider.getFlutterView();
}
/**
* Hook for subclasses to customize the creation of the {@code FlutterView}.
*
* <p>The default implementation returns {@code null}, which will cause the activity to use a
* newly instantiated full-screen view.
*/
@Override
public FlutterView createFlutterView(Context context) {
return null;
}
/**
* Hook for subclasses to customize the creation of the {@code FlutterNativeView}.
*
* <p>The default implementation returns {@code null}, which will cause the activity to use a
* newly instantiated native view object.
*/
@Override
public FlutterNativeView createFlutterNativeView() {
return null;
}
@Override
public boolean retainFlutterNativeView() {
return false;
}
@Override
public final boolean hasPlugin(String key) {
return pluginRegistry.hasPlugin(key);
}
@Override
public final <T> T valuePublishedByPlugin(String pluginKey) {
return pluginRegistry.valuePublishedByPlugin(pluginKey);
}
@Override
public final Registrar registrarFor(String pluginKey) {
return pluginRegistry.registrarFor(pluginKey);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
eventDelegate.onCreate(savedInstanceState);
}
@Override
protected void onStart() {
super.onStart();
eventDelegate.onStart();
}
@Override
protected void onResume() {
super.onResume();
eventDelegate.onResume();
}
@Override
protected void onDestroy() {
eventDelegate.onDestroy();
super.onDestroy();
}
@Override
public void onBackPressed() {
if (!eventDelegate.onBackPressed()) {
super.onBackPressed();
}
}
@Override
protected void onStop() {
eventDelegate.onStop();
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
eventDelegate.onPause();
}
@Override
protected void onPostResume() {
super.onPostResume();
eventDelegate.onPostResume();
}
// @Override - added in API level 23
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
eventDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!eventDelegate.onActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
protected void onNewIntent(Intent intent) {
eventDelegate.onNewIntent(intent);
}
@Override
public void onUserLeaveHint() {
eventDelegate.onUserLeaveHint();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
eventDelegate.onWindowFocusChanged(hasFocus);
}
@Override
public void onTrimMemory(int level) {
eventDelegate.onTrimMemory(level);
}
@Override
public void onLowMemory() {
eventDelegate.onLowMemory();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
eventDelegate.onConfigurationChanged(newConfig);
}
}

View File

@ -1,495 +0,0 @@
// 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.app;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources.NotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import io.flutter.Log;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.util.Preconditions;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterRunArguments;
import io.flutter.view.FlutterView;
import java.util.ArrayList;
/**
* Deprecated class that performs the actual work of tying Android {@link android.app.Activity}
* instances to Flutter.
*
* <p>This exists as a dedicated class (as opposed to being integrated directly into {@link
* FlutterActivity}) to facilitate applications that don't wish to subclass {@code FlutterActivity}.
* The most obvious example of when this may come in handy is if an application wishes to subclass
* the Android v4 support library's {@code FragmentActivity}.
*
* <p><b>Usage:</b>
*
* <p>To wire this class up to your activity, simply forward the events defined in {@link
* FlutterActivityEvents} from your activity to an instance of this class. Optionally, you can make
* your activity implement {@link PluginRegistry} and/or {@link
* io.flutter.view.FlutterView.Provider} and forward those methods to this class as well.
*
* @deprecated {@link io.flutter.embedding.android.FlutterActivity} is the new API that now replaces
* this class and {@link io.flutter.app.FlutterActivity}. See
* https://flutter.dev/go/android-project-migration for more migration details.
*/
@Deprecated
public final class FlutterActivityDelegate
implements FlutterActivityEvents, FlutterView.Provider, PluginRegistry {
private static final String SPLASH_SCREEN_META_DATA_KEY =
"io.flutter.app.android.SplashScreenUntilFirstFrame";
private static final String TAG = "FlutterActivityDelegate";
private static final LayoutParams matchParent =
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
/**
* Specifies the mechanism by which Flutter views are created during the operation of a {@code
* FlutterActivityDelegate}.
*
* <p>A delegate's view factory will be consulted during {@link #onCreate(Bundle)}. If it returns
* {@code null}, then the delegate will fall back to instantiating a new full-screen {@code
* FlutterView}.
*
* <p>A delegate's native view factory will be consulted during {@link #onCreate(Bundle)}. If it
* returns {@code null}, then the delegate will fall back to instantiating a new {@code
* FlutterNativeView}. This is useful for applications to override to reuse the FlutterNativeView
* held e.g. by a pre-existing background service.
*/
public interface ViewFactory {
FlutterView createFlutterView(Context context);
FlutterNativeView createFlutterNativeView();
/**
* Hook for subclasses to indicate that the {@code FlutterNativeView} returned by {@link
* #createFlutterNativeView()} should not be destroyed when this activity is destroyed.
*
* @return Whether the FlutterNativeView is retained.
*/
boolean retainFlutterNativeView();
}
private final Activity activity;
private final ViewFactory viewFactory;
private FlutterView flutterView;
private View launchView;
public FlutterActivityDelegate(Activity activity, ViewFactory viewFactory) {
this.activity = Preconditions.checkNotNull(activity);
this.viewFactory = Preconditions.checkNotNull(viewFactory);
}
@Override
public FlutterView getFlutterView() {
return flutterView;
}
// The implementation of PluginRegistry forwards to flutterView.
@Override
public boolean hasPlugin(String key) {
return flutterView.getPluginRegistry().hasPlugin(key);
}
@Override
@SuppressWarnings("unchecked")
public <T> T valuePublishedByPlugin(String pluginKey) {
return (T) flutterView.getPluginRegistry().valuePublishedByPlugin(pluginKey);
}
@Override
public Registrar registrarFor(String pluginKey) {
return flutterView.getPluginRegistry().registrarFor(pluginKey);
}
@Override
public boolean onRequestPermissionsResult(
int requestCode, String[] permissions, int[] grantResults) {
return flutterView
.getPluginRegistry()
.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
return flutterView.getPluginRegistry().onActivityResult(requestCode, resultCode, data);
}
@Override
public void onCreate(Bundle savedInstanceState) {
Window window = activity.getWindow();
window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(0x40000000);
window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
String[] args = getArgsFromIntent(activity.getIntent());
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);
flutterView = viewFactory.createFlutterView(activity);
if (flutterView == null) {
FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
flutterView = new FlutterView(activity, null, nativeView);
flutterView.setLayoutParams(matchParent);
activity.setContentView(flutterView);
launchView = createLaunchView();
if (launchView != null) {
addLaunchView();
}
}
if (loadIntent(activity.getIntent())) {
return;
}
String appBundlePath = FlutterMain.findAppBundlePath();
if (appBundlePath != null) {
runBundle(appBundlePath);
}
}
@Override
public void onNewIntent(Intent intent) {
// Only attempt to reload the Flutter Dart code during development. Use
// the debuggable flag as an indicator that we are in development mode.
if (!isDebuggable() || !loadIntent(intent)) {
flutterView.getPluginRegistry().onNewIntent(intent);
}
}
private boolean isDebuggable() {
return (activity.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
@Override
public void onPause() {
Application app = (Application) activity.getApplicationContext();
if (app instanceof FlutterApplication) {
FlutterApplication flutterApp = (FlutterApplication) app;
if (activity.equals(flutterApp.getCurrentActivity())) {
flutterApp.setCurrentActivity(null);
}
}
if (flutterView != null) {
flutterView.onPause();
}
}
@Override
public void onStart() {
if (flutterView != null) {
flutterView.onStart();
}
}
@Override
public void onResume() {
Application app = (Application) activity.getApplicationContext();
if (app instanceof FlutterApplication) {
FlutterApplication flutterApp = (FlutterApplication) app;
flutterApp.setCurrentActivity(activity);
}
}
@Override
public void onStop() {
flutterView.onStop();
}
@Override
public void onPostResume() {
if (flutterView != null) {
flutterView.onPostResume();
}
}
@Override
public void onDestroy() {
Application app = (Application) activity.getApplicationContext();
if (app instanceof FlutterApplication) {
FlutterApplication flutterApp = (FlutterApplication) app;
if (activity.equals(flutterApp.getCurrentActivity())) {
flutterApp.setCurrentActivity(null);
}
}
if (flutterView != null) {
final boolean detach =
flutterView.getPluginRegistry().onViewDestroy(flutterView.getFlutterNativeView());
if (detach || viewFactory.retainFlutterNativeView()) {
// Detach, but do not destroy the FlutterView if a plugin
// expressed interest in its FlutterNativeView.
flutterView.detach();
} else {
flutterView.destroy();
}
}
}
@Override
public boolean onBackPressed() {
if (flutterView != null) {
flutterView.popRoute();
return true;
}
return false;
}
@Override
public void onUserLeaveHint() {
flutterView.getPluginRegistry().onUserLeaveHint();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
flutterView.getPluginRegistry().onWindowFocusChanged(hasFocus);
}
@Override
public void onTrimMemory(int level) {
// Use a trim level delivered while the application is running so the
// framework has a chance to react to the notification.
if (level == TRIM_MEMORY_RUNNING_LOW) {
flutterView.onMemoryPressure();
}
}
@Override
public void onLowMemory() {
flutterView.onMemoryPressure();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {}
private static String[] getArgsFromIntent(Intent intent) {
// Before adding more entries to this list, consider that arbitrary
// Android applications can generate intents with extra data and that
// there are many security-sensitive args in the binary.
ArrayList<String> args = new ArrayList<>();
if (intent.getBooleanExtra("trace-startup", false)) {
args.add("--trace-startup");
}
if (intent.getBooleanExtra("start-paused", false)) {
args.add("--start-paused");
}
if (intent.getBooleanExtra("disable-service-auth-codes", false)) {
args.add("--disable-service-auth-codes");
}
if (intent.getBooleanExtra("use-test-fonts", false)) {
args.add("--use-test-fonts");
}
if (intent.getBooleanExtra("enable-dart-profiling", false)) {
args.add("--enable-dart-profiling");
}
if (intent.getBooleanExtra("enable-software-rendering", false)) {
args.add("--enable-software-rendering");
}
if (intent.getBooleanExtra("skia-deterministic-rendering", false)) {
args.add("--skia-deterministic-rendering");
}
if (intent.getBooleanExtra("trace-skia", false)) {
args.add("--trace-skia");
}
if (intent.getBooleanExtra("trace-systrace", false)) {
args.add("--trace-systrace");
}
if (intent.hasExtra("trace-to-file")) {
args.add("--trace-to-file=" + intent.getStringExtra("trace-to-file"));
}
if (intent.getBooleanExtra("dump-skp-on-shader-compilation", false)) {
args.add("--dump-skp-on-shader-compilation");
}
if (intent.getBooleanExtra("cache-sksl", false)) {
args.add("--cache-sksl");
}
if (intent.getBooleanExtra("purge-persistent-cache", false)) {
args.add("--purge-persistent-cache");
}
if (intent.getBooleanExtra("verbose-logging", false)) {
args.add("--verbose-logging");
}
int vmServicePort = intent.getIntExtra("vm-service-port", 0);
if (vmServicePort > 0) {
args.add("--vm-service-port=" + Integer.toString(vmServicePort));
} else {
// TODO(bkonyi): remove once flutter_tools no longer uses this option.
// See https://github.com/dart-lang/sdk/issues/50233
vmServicePort = intent.getIntExtra("observatory-port", 0);
if (vmServicePort > 0) {
args.add("--vm-service-port=" + Integer.toString(vmServicePort));
}
}
if (intent.getBooleanExtra("endless-trace-buffer", false)) {
args.add("--endless-trace-buffer");
}
// NOTE: all flags provided with this argument are subject to filtering
// based on a list of allowed flags in shell/common/switches.cc. If any
// flag provided is not allowed, the process will immediately terminate.
if (intent.hasExtra("dart-flags")) {
args.add("--dart-flags=" + intent.getStringExtra("dart-flags"));
}
if (!args.isEmpty()) {
String[] argsArray = new String[args.size()];
return args.toArray(argsArray);
}
return null;
}
private boolean loadIntent(Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_RUN.equals(action)) {
String route = intent.getStringExtra("route");
String appBundlePath = intent.getDataString();
if (appBundlePath == null) {
// Fall back to the installation path if no bundle path was specified.
appBundlePath = FlutterMain.findAppBundlePath();
}
if (route != null) {
flutterView.setInitialRoute(route);
}
runBundle(appBundlePath);
return true;
}
return false;
}
private void runBundle(String appBundlePath) {
if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
FlutterRunArguments args = new FlutterRunArguments();
args.bundlePath = appBundlePath;
args.entrypoint = "main";
flutterView.runFromBundle(args);
}
}
/**
* Creates a {@link View} containing the same {@link Drawable} as the one set as the {@code
* windowBackground} of the parent activity for use as a launch splash view.
*
* <p>Returns null if no {@code windowBackground} is set for the activity.
*/
private View createLaunchView() {
if (!showSplashScreenUntilFirstFrame()) {
return null;
}
final Drawable launchScreenDrawable = getLaunchScreenDrawableFromActivityTheme();
if (launchScreenDrawable == null) {
return null;
}
final View view = new View(activity);
view.setLayoutParams(matchParent);
view.setBackground(launchScreenDrawable);
return view;
}
/**
* Extracts a {@link Drawable} from the parent activity's {@code windowBackground}.
*
* <p>{@code android:windowBackground} is specifically reused instead of a other attributes
* because the Android framework can display it fast enough when launching the app as opposed to
* anything defined in the Activity subclass.
*
* <p>Returns null if no {@code windowBackground} is set for the activity.
*/
@SuppressWarnings("deprecation")
private Drawable getLaunchScreenDrawableFromActivityTheme() {
TypedValue typedValue = new TypedValue();
if (!activity.getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true)) {
return null;
}
if (typedValue.resourceId == 0) {
return null;
}
try {
return activity.getResources().getDrawable(typedValue.resourceId);
} catch (NotFoundException e) {
Log.e(TAG, "Referenced launch screen windowBackground resource does not exist");
return null;
}
}
/**
* Let the user specify whether the activity's {@code windowBackground} is a launch screen and
* should be shown until the first frame via a <meta-data> tag in the activity.
*/
private Boolean showSplashScreenUntilFirstFrame() {
try {
ActivityInfo activityInfo =
activity
.getPackageManager()
.getActivityInfo(activity.getComponentName(), PackageManager.GET_META_DATA);
Bundle metadata = activityInfo.metaData;
return metadata != null && metadata.getBoolean(SPLASH_SCREEN_META_DATA_KEY);
} catch (NameNotFoundException e) {
return false;
}
}
/**
* Show and then automatically animate out the launch view.
*
* <p>If a launch screen is defined in the user application's AndroidManifest.xml as the
* activity's {@code windowBackground}, display it on top of the {@link FlutterView} and remove
* the activity's {@code windowBackground}.
*
* <p>Fade it out and remove it when the {@link FlutterView} renders its first frame.
*/
private void addLaunchView() {
if (launchView == null) {
return;
}
activity.addContentView(launchView, matchParent);
flutterView.addFirstFrameListener(
new FlutterView.FirstFrameListener() {
@Override
public void onFirstFrame() {
FlutterActivityDelegate.this
.launchView
.animate()
.alpha(0f)
// Use Android's default animation duration.
.setListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Views added to an Activity's addContentView is always added to its
// root FrameLayout.
((ViewGroup) FlutterActivityDelegate.this.launchView.getParent())
.removeView(FlutterActivityDelegate.this.launchView);
FlutterActivityDelegate.this.launchView = null;
}
});
FlutterActivityDelegate.this.flutterView.removeFirstFrameListener(this);
}
});
// Resets the activity theme from the one containing the launch screen in the window
// background to a blank one since the launch screen is now in a view in front of the
// FlutterView.
//
// We can make this configurable if users want it.
activity.setTheme(android.R.style.Theme_Black_NoTitleBar);
}
}

View File

@ -1,73 +0,0 @@
// 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.app;
import android.content.ComponentCallbacks2;
import android.content.Intent;
import android.os.Bundle;
import io.flutter.plugin.common.PluginRegistry.ActivityResultListener;
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener;
/**
* A collection of Android {@code Activity} methods that are relevant to the core operation of
* Flutter applications.
*
* <p>Application authors that use an activity other than {@link FlutterActivity} should forward all
* events herein from their activity to an instance of {@link FlutterActivityDelegate} in order to
* wire the activity up to the Flutter framework. This forwarding is already provided in {@code
* FlutterActivity}.
*/
public interface FlutterActivityEvents
extends ComponentCallbacks2, ActivityResultListener, RequestPermissionsResultListener {
/**
* @param savedInstanceState If the activity is being re-initialized after previously being shut
* down then this Bundle contains the data it most recently supplied in {@code
* onSaveInstanceState(Bundle)}.
* @see android.app.Activity#onCreate(android.os.Bundle)
*/
void onCreate(Bundle savedInstanceState);
/**
* @param intent The new intent that was started for the activity.
* @see android.app.Activity#onNewIntent(Intent)
*/
void onNewIntent(Intent intent);
/** @see android.app.Activity#onPause() */
void onPause();
/** @see android.app.Activity#onStart() */
void onStart();
/** @see android.app.Activity#onResume() */
void onResume();
/** @see android.app.Activity#onPostResume() */
void onPostResume();
/** @see android.app.Activity#onDestroy() */
void onDestroy();
/** @see android.app.Activity#onStop() */
void onStop();
/**
* Invoked when the activity has detected the user's press of the back key.
*
* @return {@code true} if the listener handled the event; {@code false} to let the activity
* continue with its default back button handling.
* @see android.app.Activity#onBackPressed()
*/
boolean onBackPressed();
/** @see android.app.Activity#onUserLeaveHint() */
void onUserLeaveHint();
/**
* @param hasFocus True if the current activity window has focus.
* @see android.app.Activity#onWindowFocusChanged(boolean)
*/
void onWindowFocusChanged(boolean hasFocus);
}

View File

@ -1,36 +0,0 @@
// 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.app;
import android.app.Activity;
import android.app.Application;
import androidx.annotation.CallSuper;
import io.flutter.FlutterInjector;
/**
* Flutter implementation of {@link android.app.Application}, managing application-level global
* initializations.
*
* <p>Using this {@link android.app.Application} is not required when using APIs in the package
* {@code io.flutter.embedding.android} since they self-initialize on first use.
*/
public class FlutterApplication extends Application {
@Override
@CallSuper
public void onCreate() {
super.onCreate();
FlutterInjector.instance().flutterLoader().startInitialization(this);
}
private Activity mCurrentActivity = null;
public Activity getCurrentActivity() {
return mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity) {
this.mCurrentActivity = mCurrentActivity;
}
}

View File

@ -1,181 +0,0 @@
// 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.app;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
import io.flutter.app.FlutterActivityDelegate.ViewFactory;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterView;
/**
* Deprecated class for activities that use Flutter who also require the use of the Android v4
* Support library's {@link FragmentActivity}.
*
* <p>Applications that don't have this need will likely want to use {@link FlutterActivity}
* instead.
*
* <p><strong>Important!</strong> Flutter does not bundle the necessary Android v4 Support library
* classes for this class to work at runtime. It is the responsibility of the app developer using
* this class to ensure that they link against the v4 support library .jar file when creating their
* app to ensure that {@link FragmentActivity} is available at runtime.
*
* @see <a target="_new"
* href="https://developer.android.com/training/testing/set-up-project">https://developer.android.com/training/testing/set-up-project</a>
* @deprecated this class is replaced by {@link
* io.flutter.embedding.android.FlutterFragmentActivity}.
*/
@Deprecated
public class FlutterFragmentActivity extends FragmentActivity
implements FlutterView.Provider, PluginRegistry, ViewFactory {
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
// These aliases ensure that the methods we forward to the delegate adhere
// to relevant interfaces versus just existing in FlutterActivityDelegate.
private final FlutterActivityEvents eventDelegate = delegate;
private final FlutterView.Provider viewProvider = delegate;
private final PluginRegistry pluginRegistry = delegate;
/**
* Returns the Flutter view used by this activity; will be null before {@link #onCreate(Bundle)}
* is called.
*/
@Override
public FlutterView getFlutterView() {
return viewProvider.getFlutterView();
}
/**
* Hook for subclasses to customize the creation of the {@code FlutterView}.
*
* <p>The default implementation returns {@code null}, which will cause the activity to use a
* newly instantiated full-screen view.
*/
@Override
public FlutterView createFlutterView(Context context) {
return null;
}
@Override
public FlutterNativeView createFlutterNativeView() {
return null;
}
@Override
public boolean retainFlutterNativeView() {
return false;
}
@Override
public final boolean hasPlugin(String key) {
return pluginRegistry.hasPlugin(key);
}
@Override
public final <T> T valuePublishedByPlugin(String pluginKey) {
return pluginRegistry.valuePublishedByPlugin(pluginKey);
}
@Override
public final Registrar registrarFor(String pluginKey) {
return pluginRegistry.registrarFor(pluginKey);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
eventDelegate.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
eventDelegate.onDestroy();
super.onDestroy();
}
@Override
public void onBackPressed() {
if (!eventDelegate.onBackPressed()) {
super.onBackPressed();
}
}
@Override
protected void onStart() {
super.onStart();
eventDelegate.onStart();
}
@Override
protected void onStop() {
eventDelegate.onStop();
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
eventDelegate.onPause();
}
@Override
protected void onPostResume() {
super.onPostResume();
eventDelegate.onPostResume();
}
@Override
public void onRequestPermissionsResult(
int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
eventDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!eventDelegate.onActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
eventDelegate.onNewIntent(intent);
}
@Override
@SuppressWarnings("MissingSuperCall")
public void onUserLeaveHint() {
eventDelegate.onUserLeaveHint();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
eventDelegate.onWindowFocusChanged(hasFocus);
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
eventDelegate.onTrimMemory(level);
}
@Override
public void onLowMemory() {
eventDelegate.onLowMemory();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
eventDelegate.onConfigurationChanged(newConfig);
}
}

View File

@ -1,53 +0,0 @@
// 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.app;
import androidx.annotation.CallSuper;
import com.google.android.play.core.splitcompat.SplitCompatApplication;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager;
/**
* Flutter's extension of {@link SplitCompatApplication} that injects a {@link
* PlayStoreDeferredComponentManager} with {@link FlutterInjector} to enable Split AOT Flutter apps.
*
* <p>To use this class, either have your custom application class extend
* FlutterPlayStoreSplitApplication or use it directly in the app's AndroidManifest.xml by adding
* the following line:
*
* <pre>{@code
* <manifest
* ...
* <application
* android:name="io.flutter.app.FlutterPlayStoreSplitApplication"
* ...>
* </application>
* </manifest>
* }</pre>
*
* This class is meant to be used with the Google Play store. Custom non-play store applications do
* not need to extend SplitCompatApplication and should inject a custom {@link
* io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager} implementation like so:
*
* <pre>{@code
* FlutterInjector.setInstance(
* new FlutterInjector.Builder().setDeferredComponentManager(yourCustomManager).build());
* }</pre>
*/
public class FlutterPlayStoreSplitApplication extends SplitCompatApplication {
@Override
@CallSuper
public void onCreate() {
super.onCreate();
// Create and inject a PlayStoreDeferredComponentManager, which is the default manager for
// interacting with the Google Play Store.
PlayStoreDeferredComponentManager deferredComponentManager =
new PlayStoreDeferredComponentManager(this, null);
FlutterInjector.setInstance(
new FlutterInjector.Builder()
.setDeferredComponentManager(deferredComponentManager)
.build());
}
}

View File

@ -1,259 +0,0 @@
// 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.app;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.plugin.platform.PlatformViewsController;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterView;
import io.flutter.view.TextureRegistry;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** @deprecated See https://flutter.dev/go/android-project-migration for migration instructions. */
@Deprecated
public class FlutterPluginRegistry
implements PluginRegistry,
PluginRegistry.RequestPermissionsResultListener,
PluginRegistry.ActivityResultListener,
PluginRegistry.NewIntentListener,
PluginRegistry.WindowFocusChangedListener,
PluginRegistry.UserLeaveHintListener,
PluginRegistry.ViewDestroyListener {
private static final String TAG = "FlutterPluginRegistry";
private Activity mActivity;
private Context mAppContext;
private FlutterNativeView mNativeView;
private FlutterView mFlutterView;
private final PlatformViewsController mPlatformViewsController;
private final Map<String, Object> mPluginMap = new LinkedHashMap<>(0);
private final List<RequestPermissionsResultListener> mRequestPermissionsResultListeners =
new ArrayList<>(0);
private final List<ActivityResultListener> mActivityResultListeners = new ArrayList<>(0);
private final List<NewIntentListener> mNewIntentListeners = new ArrayList<>(0);
private final List<UserLeaveHintListener> mUserLeaveHintListeners = new ArrayList<>(0);
private final List<WindowFocusChangedListener> mWindowFocusChangedListeners = new ArrayList<>(0);
private final List<ViewDestroyListener> mViewDestroyListeners = new ArrayList<>(0);
public FlutterPluginRegistry(FlutterNativeView nativeView, Context context) {
mNativeView = nativeView;
mAppContext = context;
mPlatformViewsController = new PlatformViewsController();
}
public FlutterPluginRegistry(FlutterEngine engine, Context context) {
// TODO(mattcarroll): implement use of engine instead of nativeView.
mAppContext = context;
mPlatformViewsController = new PlatformViewsController();
}
@Override
public boolean hasPlugin(String key) {
return mPluginMap.containsKey(key);
}
@Override
@SuppressWarnings("unchecked")
public <T> T valuePublishedByPlugin(String pluginKey) {
return (T) mPluginMap.get(pluginKey);
}
@Override
public Registrar registrarFor(String pluginKey) {
if (mPluginMap.containsKey(pluginKey)) {
throw new IllegalStateException("Plugin key " + pluginKey + " is already in use");
}
mPluginMap.put(pluginKey, null);
return new FlutterRegistrar(pluginKey);
}
public void attach(FlutterView flutterView, Activity activity) {
mFlutterView = flutterView;
mActivity = activity;
mPlatformViewsController.attach(activity, flutterView, flutterView.getDartExecutor());
}
public void detach() {
mPlatformViewsController.detach();
mPlatformViewsController.onDetachedFromJNI();
mFlutterView = null;
mActivity = null;
}
public void onPreEngineRestart() {
mPlatformViewsController.onPreEngineRestart();
}
public PlatformViewsController getPlatformViewsController() {
return mPlatformViewsController;
}
private class FlutterRegistrar implements Registrar {
private final String pluginKey;
FlutterRegistrar(String pluginKey) {
this.pluginKey = pluginKey;
}
@Override
public Activity activity() {
return mActivity;
}
@Override
public Context context() {
return mAppContext;
}
@Override
public Context activeContext() {
return (mActivity != null) ? mActivity : mAppContext;
}
@Override
public BinaryMessenger messenger() {
return mNativeView;
}
@Override
public TextureRegistry textures() {
return mFlutterView;
}
@Override
public PlatformViewRegistry platformViewRegistry() {
return mPlatformViewsController.getRegistry();
}
@Override
public FlutterView view() {
return mFlutterView;
}
@Override
public String lookupKeyForAsset(String asset) {
return FlutterMain.getLookupKeyForAsset(asset);
}
@Override
public String lookupKeyForAsset(String asset, String packageName) {
return FlutterMain.getLookupKeyForAsset(asset, packageName);
}
@Override
public Registrar publish(Object value) {
mPluginMap.put(pluginKey, value);
return this;
}
@Override
public Registrar addRequestPermissionsResultListener(
RequestPermissionsResultListener listener) {
mRequestPermissionsResultListeners.add(listener);
return this;
}
@Override
public Registrar addActivityResultListener(ActivityResultListener listener) {
mActivityResultListeners.add(listener);
return this;
}
@Override
public Registrar addNewIntentListener(NewIntentListener listener) {
mNewIntentListeners.add(listener);
return this;
}
@Override
public Registrar addUserLeaveHintListener(UserLeaveHintListener listener) {
mUserLeaveHintListeners.add(listener);
return this;
}
@Override
public Registrar addWindowFocusChangedListener(WindowFocusChangedListener listener) {
mWindowFocusChangedListeners.add(listener);
return this;
}
@Override
public Registrar addViewDestroyListener(ViewDestroyListener listener) {
mViewDestroyListeners.add(listener);
return this;
}
}
@Override
public boolean onRequestPermissionsResult(
int requestCode, String[] permissions, int[] grantResults) {
for (RequestPermissionsResultListener listener : mRequestPermissionsResultListeners) {
if (listener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
return true;
}
}
return false;
}
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
for (ActivityResultListener listener : mActivityResultListeners) {
if (listener.onActivityResult(requestCode, resultCode, data)) {
return true;
}
}
return false;
}
@Override
public boolean onNewIntent(Intent intent) {
for (NewIntentListener listener : mNewIntentListeners) {
if (listener.onNewIntent(intent)) {
return true;
}
}
return false;
}
@Override
public void onUserLeaveHint() {
for (UserLeaveHintListener listener : mUserLeaveHintListeners) {
listener.onUserLeaveHint();
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
for (WindowFocusChangedListener listener : mWindowFocusChangedListeners) {
listener.onWindowFocusChanged(hasFocus);
}
}
@Override
public boolean onViewDestroy(FlutterNativeView view) {
boolean handled = false;
for (ViewDestroyListener listener : mViewDestroyListeners) {
if (listener.onViewDestroy(view)) {
handled = true;
}
}
return handled;
}
public void destroy() {
mPlatformViewsController.onDetachedFromJNI();
}
}

View File

@ -1039,8 +1039,7 @@ public class FlutterJNI {
* 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
* io.flutter.view.FlutterNativeView} for an example implementation.
* fundamental platform messages are handled as expected.
*/
@UiThread
public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) {

View File

@ -1,153 +0,0 @@
// 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.plugins.shim;
import androidx.annotation.NonNull;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.PluginRegistry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A {@link PluginRegistry} that is shimmed to let old plugins use the new Android embedding and
* plugin API behind the scenes.
*
* <p>The following is an example usage of {@code ShimPluginRegistry} within a {@code
* FlutterActivity}:
*
* <pre>
* // Create the FlutterEngine that will back the Flutter UI.
* FlutterEngineGroup group = new FlutterEngineGroup(context);
* FlutterEngine flutterEngine = group.createAndRunDefaultEngine(context);
*
* // Create a ShimPluginRegistry and wrap the FlutterEngine with the shim.
* ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine, platformViewsController);
*
* // Use the GeneratedPluginRegistrant to add every plugin that's in the pubspec.
* GeneratedPluginRegistrant.registerWith(shimPluginRegistry);
* </pre>
*/
public class ShimPluginRegistry implements PluginRegistry {
private static final String TAG = "ShimPluginRegistry";
private final FlutterEngine flutterEngine;
private final Map<String, Object> pluginMap = new HashMap<>();
private final ShimRegistrarAggregate shimRegistrarAggregate;
public ShimPluginRegistry(@NonNull FlutterEngine flutterEngine) {
this.flutterEngine = flutterEngine;
this.shimRegistrarAggregate = new ShimRegistrarAggregate();
this.flutterEngine.getPlugins().add(shimRegistrarAggregate);
}
@Override
@NonNull
public Registrar registrarFor(@NonNull String pluginKey) {
Log.v(TAG, "Creating plugin Registrar for '" + pluginKey + "'");
if (pluginMap.containsKey(pluginKey)) {
throw new IllegalStateException("Plugin key " + pluginKey + " is already in use");
}
pluginMap.put(pluginKey, null);
ShimRegistrar registrar = new ShimRegistrar(pluginKey, pluginMap);
shimRegistrarAggregate.addPlugin(registrar);
return registrar;
}
@Override
public boolean hasPlugin(@NonNull String pluginKey) {
return pluginMap.containsKey(pluginKey);
}
@Override
@SuppressWarnings("unchecked")
public <T> T valuePublishedByPlugin(@NonNull String pluginKey) {
return (T) pluginMap.get(pluginKey);
}
/**
* Aggregates all {@link ShimRegistrar}s within one single {@link FlutterPlugin}.
*
* <p>The reason we need this aggregate is because the new embedding uniquely identifies plugins
* by their plugin class, but the plugin shim system represents every plugin with a {@link
* ShimRegistrar}. Therefore, every plugin we would register after the first plugin, would
* overwrite the previous plugin, because they're all {@link ShimRegistrar} instances.
*
* <p>{@code ShimRegistrarAggregate} multiplexes {@link FlutterPlugin} and {@link ActivityAware}
* calls so that we can register just one {@code ShimRegistrarAggregate} with a {@link
* FlutterEngine}, while forwarding the relevant plugin resources to any number of {@link
* ShimRegistrar}s within this {@code ShimRegistrarAggregate}.
*/
private static class ShimRegistrarAggregate implements FlutterPlugin, ActivityAware {
private final Set<ShimRegistrar> shimRegistrars = new HashSet<>();
private FlutterPluginBinding flutterPluginBinding;
private ActivityPluginBinding activityPluginBinding;
public void addPlugin(@NonNull ShimRegistrar shimRegistrar) {
shimRegistrars.add(shimRegistrar);
if (flutterPluginBinding != null) {
shimRegistrar.onAttachedToEngine(flutterPluginBinding);
}
if (activityPluginBinding != null) {
shimRegistrar.onAttachedToActivity(activityPluginBinding);
}
}
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
flutterPluginBinding = binding;
for (ShimRegistrar shimRegistrar : shimRegistrars) {
shimRegistrar.onAttachedToEngine(binding);
}
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
for (ShimRegistrar shimRegistrar : shimRegistrars) {
shimRegistrar.onDetachedFromEngine(binding);
}
flutterPluginBinding = null;
activityPluginBinding = null;
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
activityPluginBinding = binding;
for (ShimRegistrar shimRegistrar : shimRegistrars) {
shimRegistrar.onAttachedToActivity(binding);
}
}
@Override
public void onDetachedFromActivityForConfigChanges() {
for (ShimRegistrar shimRegistrar : shimRegistrars) {
shimRegistrar.onDetachedFromActivity();
}
activityPluginBinding = null;
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
activityPluginBinding = binding;
for (ShimRegistrar shimRegistrar : shimRegistrars) {
shimRegistrar.onReattachedToActivityForConfigChanges(binding);
}
}
@Override
public void onDetachedFromActivity() {
for (ShimRegistrar shimRegistrar : shimRegistrars) {
shimRegistrar.onDetachedFromActivity();
}
activityPluginBinding = null;
}
}
}

View File

@ -1,234 +0,0 @@
// 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.plugins.shim;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import io.flutter.FlutterInjector;
import io.flutter.Log;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.view.FlutterView;
import io.flutter.view.TextureRegistry;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A {@link PluginRegistry.Registrar} that is shimmed let old plugins use the new Android embedding
* and plugin API behind the scenes.
*
* <p>Instances of {@code ShimRegistrar}s are vended internally by a {@link ShimPluginRegistry}.
*/
class ShimRegistrar implements PluginRegistry.Registrar, FlutterPlugin, ActivityAware {
private static final String TAG = "ShimRegistrar";
private final Map<String, Object> globalRegistrarMap;
private final String pluginId;
private final Set<PluginRegistry.ViewDestroyListener> viewDestroyListeners = new HashSet<>();
private final Set<PluginRegistry.RequestPermissionsResultListener>
requestPermissionsResultListeners = new HashSet<>();
private final Set<PluginRegistry.ActivityResultListener> activityResultListeners =
new HashSet<>();
private final Set<PluginRegistry.NewIntentListener> newIntentListeners = new HashSet<>();
private final Set<PluginRegistry.UserLeaveHintListener> userLeaveHintListeners = new HashSet<>();
private final Set<PluginRegistry.WindowFocusChangedListener> WindowFocusChangedListeners =
new HashSet<>();
private FlutterPlugin.FlutterPluginBinding pluginBinding;
private ActivityPluginBinding activityPluginBinding;
public ShimRegistrar(@NonNull String pluginId, @NonNull Map<String, Object> globalRegistrarMap) {
this.pluginId = pluginId;
this.globalRegistrarMap = globalRegistrarMap;
}
@Override
public Activity activity() {
return activityPluginBinding != null ? activityPluginBinding.getActivity() : null;
}
@Override
public Context context() {
return pluginBinding != null ? pluginBinding.getApplicationContext() : null;
}
@Override
public Context activeContext() {
return activityPluginBinding == null ? context() : activity();
}
@Override
public BinaryMessenger messenger() {
return pluginBinding != null ? pluginBinding.getBinaryMessenger() : null;
}
@Override
public TextureRegistry textures() {
return pluginBinding != null ? pluginBinding.getTextureRegistry() : null;
}
@Override
public PlatformViewRegistry platformViewRegistry() {
return pluginBinding != null ? pluginBinding.getPlatformViewRegistry() : null;
}
@Override
public FlutterView view() {
throw new UnsupportedOperationException(
"The new embedding does not support the old FlutterView.");
}
@Override
public String lookupKeyForAsset(String asset) {
return FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(asset);
}
@Override
public String lookupKeyForAsset(String asset, String packageName) {
return FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(asset, packageName);
}
@Override
public PluginRegistry.Registrar publish(Object value) {
globalRegistrarMap.put(pluginId, value);
return this;
}
@Override
public PluginRegistry.Registrar addRequestPermissionsResultListener(
PluginRegistry.RequestPermissionsResultListener listener) {
requestPermissionsResultListeners.add(listener);
if (activityPluginBinding != null) {
activityPluginBinding.addRequestPermissionsResultListener(listener);
}
return this;
}
@Override
public PluginRegistry.Registrar addActivityResultListener(
PluginRegistry.ActivityResultListener listener) {
activityResultListeners.add(listener);
if (activityPluginBinding != null) {
activityPluginBinding.addActivityResultListener(listener);
}
return this;
}
@Override
public PluginRegistry.Registrar addNewIntentListener(PluginRegistry.NewIntentListener listener) {
newIntentListeners.add(listener);
if (activityPluginBinding != null) {
activityPluginBinding.addOnNewIntentListener(listener);
}
return this;
}
@Override
public PluginRegistry.Registrar addUserLeaveHintListener(
PluginRegistry.UserLeaveHintListener listener) {
userLeaveHintListeners.add(listener);
if (activityPluginBinding != null) {
activityPluginBinding.addOnUserLeaveHintListener(listener);
}
return this;
}
@Override
public PluginRegistry.Registrar addWindowFocusChangedListener(
PluginRegistry.WindowFocusChangedListener listener) {
WindowFocusChangedListeners.add(listener);
if (activityPluginBinding != null) {
activityPluginBinding.addOnWindowFocusChangedListener(listener);
}
return this;
}
@Override
@NonNull
public PluginRegistry.Registrar addViewDestroyListener(
@NonNull PluginRegistry.ViewDestroyListener listener) {
viewDestroyListeners.add(listener);
return this;
}
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
Log.v(TAG, "Attached to FlutterEngine.");
pluginBinding = binding;
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
Log.v(TAG, "Detached from FlutterEngine.");
for (PluginRegistry.ViewDestroyListener listener : viewDestroyListeners) {
// The following invocation might produce unexpected behavior in old plugins because
// we have no FlutterNativeView to pass to onViewDestroy(). This is a limitation of this shim.
listener.onViewDestroy(null);
}
pluginBinding = null;
activityPluginBinding = null;
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
Log.v(TAG, "Attached to an Activity.");
activityPluginBinding = binding;
addExistingListenersToActivityPluginBinding();
}
@Override
public void onDetachedFromActivityForConfigChanges() {
Log.v(TAG, "Detached from an Activity for config changes.");
activityPluginBinding = null;
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
Log.v(TAG, "Reconnected to an Activity after config changes.");
activityPluginBinding = binding;
addExistingListenersToActivityPluginBinding();
}
@Override
public void onDetachedFromActivity() {
Log.v(TAG, "Detached from an Activity.");
activityPluginBinding = null;
}
private void addExistingListenersToActivityPluginBinding() {
for (PluginRegistry.RequestPermissionsResultListener listener :
requestPermissionsResultListeners) {
activityPluginBinding.addRequestPermissionsResultListener(listener);
}
for (PluginRegistry.ActivityResultListener listener : activityResultListeners) {
activityPluginBinding.addActivityResultListener(listener);
}
for (PluginRegistry.NewIntentListener listener : newIntentListeners) {
activityPluginBinding.addOnNewIntentListener(listener);
}
for (PluginRegistry.UserLeaveHintListener listener : userLeaveHintListeners) {
activityPluginBinding.addOnUserLeaveHintListener(listener);
}
for (PluginRegistry.WindowFocusChangedListener listener : WindowFocusChangedListeners) {
activityPluginBinding.addOnWindowFocusChangedListener(listener);
}
}
}

View File

@ -5,353 +5,13 @@
package io.flutter.plugin.common;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterView;
import io.flutter.view.TextureRegistry;
/**
* Container class for Android API listeners used by {@link ActivityPluginBinding}.
*
* <p>This class also contains deprecated v1 embedding APIs used for plugin registration.
*
* <p>In v1 Android applications, an auto-generated and auto-updated plugin registrant class
* (GeneratedPluginRegistrant) makes use of a {@link PluginRegistry} to register contributions from
* each plugin mentioned in the application's pubspec file. The generated registrant class is, again
* by default, called from the application's main {@link android.app.Activity}, which defaults to an
* instance of {@link io.flutter.app.FlutterActivity}, itself a {@link PluginRegistry}.
*/
/** Container class for Android API listeners used by {@link ActivityPluginBinding}. */
public interface PluginRegistry {
/**
* Returns a {@link Registrar} for receiving the registrations pertaining to the specified plugin.
*
* @param pluginKey a unique String identifying the plugin; typically the fully qualified name of
* the plugin's main class.
* @return A {@link Registrar} for receiving the registrations pertianing to the specified plugin.
* @deprecated See https://flutter.dev/go/android-project-migration for migration details.
*/
@Deprecated
@NonNull
Registrar registrarFor(@NonNull String pluginKey);
/**
* Returns whether the specified plugin is known to this registry.
*
* @param pluginKey a unique String identifying the plugin; typically the fully qualified name of
* the plugin's main class.
* @return true if this registry has handed out a registrar for the specified plugin.
* @deprecated See https://flutter.dev/go/android-project-migration for migration details.
*/
@Deprecated
boolean hasPlugin(@NonNull String pluginKey);
/**
* Returns the value published by the specified plugin, if any.
*
* <p>Plugins may publish a single value, such as an instance of the plugin's main class, for
* situations where external control or interaction is needed. Clients are expected to know the
* value's type.
*
* @param <T> The type of the value.
* @param pluginKey a unique String identifying the plugin; typically the fully qualified name of
* the plugin's main class.
* @return the published value, possibly null.
* @deprecated See https://flutter.dev/go/android-project-migration for migration details.
*/
@Deprecated
@Nullable
<T> T valuePublishedByPlugin(@NonNull String pluginKey);
/**
* Receiver of registrations from a single plugin.
*
* @deprecated This registrar is for Flutter's v1 embedding. For instructions on migrating a
* plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*/
@Deprecated
interface Registrar {
/**
* Returns the {@link android.app.Activity} that forms the plugin's operating context.
*
* <p>Plugin authors should not assume the type returned by this method is any specific subclass
* of {@code Activity} (such as {@link io.flutter.app.FlutterActivity} or {@link
* io.flutter.app.FlutterFragmentActivity}), as applications are free to use any activity
* subclass.
*
* <p>When there is no foreground activity in the application, this will return null. If a
* {@link Context} is needed, use context() to get the application's context.
*
* <p>This registrar is for Flutter's v1 embedding. To access an {@code Activity} from a plugin
* using the v2 embedding, see {@link ActivityPluginBinding#getActivity()}. To obtain an
* instance of an {@link ActivityPluginBinding} in a Flutter plugin, implement the {@link
* ActivityAware} interface. A binding is provided in {@link
* ActivityAware#onAttachedToActivity(ActivityPluginBinding)} and {@link
* ActivityAware#onReattachedToActivityForConfigChanges(ActivityPluginBinding)}.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*/
@Nullable
Activity activity();
/**
* Returns the {@link android.app.Application}'s {@link Context}.
*
* <p>This registrar is for Flutter's v1 embedding. To access a {@code Context} from a plugin
* using the v2 embedding, see {@link
* FlutterPlugin.FlutterPluginBinding#getApplicationContext()}
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*/
@NonNull
Context context();
/**
* Returns the active {@link Context}.
*
* <p>This registrar is for Flutter's v1 embedding. In the v2 embedding, there is no concept of
* an "active context". Either use the application {@code Context} or an attached {@code
* Activity}. See {@link #context()} and {@link #activity()} for more details.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*
* @return the current {@link #activity() Activity}, if not null, otherwise the {@link
* #context() Application}.
*/
@NonNull
Context activeContext();
/**
* Returns a {@link BinaryMessenger} which the plugin can use for creating channels for
* communicating with the Dart side.
*
* <p>This registrar is for Flutter's v1 embedding. To access a {@code BinaryMessenger} from a
* plugin using the v2 embedding, see {@link
* FlutterPlugin.FlutterPluginBinding#getBinaryMessenger()}
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*/
@NonNull
BinaryMessenger messenger();
/**
* Returns a {@link TextureRegistry} which the plugin can use for managing backend textures.
*
* <p>This registrar is for Flutter's v1 embedding. To access a {@code TextureRegistry} from a
* plugin using the v2 embedding, see {@link
* FlutterPlugin.FlutterPluginBinding#getTextureRegistry()}
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*/
@NonNull
TextureRegistry textures();
/**
* Returns the application's {@link PlatformViewRegistry}.
*
* <p>Plugins can use the platform registry to register their view factories.
*
* <p>This registrar is for Flutter's v1 embedding. To access a {@code PlatformViewRegistry}
* from a plugin using the v2 embedding, see {@link
* FlutterPlugin.FlutterPluginBinding#getPlatformViewRegistry()}
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*/
@NonNull
PlatformViewRegistry platformViewRegistry();
/**
* Returns the {@link FlutterView} that's instantiated by this plugin's {@link #activity()
* activity}.
*
* <p>This registrar is for Flutter's v1 embedding. The {@link FlutterView} referenced by this
* method does not exist in the v2 embedding. Additionally, no {@code View} is exposed to any
* plugins in the v2 embedding. Platform views can access their containing {@code View} using
* the platform views APIs. If you have a use-case that absolutely requires a plugin to access
* an Android {@code View}, please file a ticket on GitHub.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*/
@NonNull
FlutterView view();
/**
* Returns the file name for the given asset. The returned file name can be used to access the
* asset in the APK through the {@link android.content.res.AssetManager} API.
*
* <p>TODO(mattcarroll): point this method towards new lookup method.
*
* @param asset the name of the asset. The name can be hierarchical
* @return the filename to be used with {@link android.content.res.AssetManager}
*/
@NonNull
String lookupKeyForAsset(@NonNull String asset);
/**
* Returns the file name for the given asset which originates from the specified packageName.
* The returned file name can be used to access the asset in the APK through the {@link
* android.content.res.AssetManager} API.
*
* <p>TODO(mattcarroll): point this method towards new lookup method.
*
* @param asset the name of the asset. The name can be hierarchical
* @param packageName the name of the package from which the asset originates
* @return the file name to be used with {@link android.content.res.AssetManager}
*/
@NonNull
String lookupKeyForAsset(@NonNull String asset, @NonNull String packageName);
/**
* Publishes a value associated with the plugin being registered.
*
* <p>The published value is available to interested clients via {@link
* PluginRegistry#valuePublishedByPlugin(String)}.
*
* <p>Publication should be done only when client code needs to interact with the plugin in a
* way that cannot be accomplished by the plugin registering callbacks with client APIs.
*
* <p>Overwrites any previously published value.
*
* <p>This registrar is for Flutter's v1 embedding. The concept of publishing values from
* plugins is not supported in the v2 embedding.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*
* @param value the value, possibly null.
* @return this {@link Registrar}.
*/
@NonNull
Registrar publish(@Nullable Object value);
/**
* Adds a callback allowing the plugin to take part in handling incoming calls to {@code
* Activity#onRequestPermissionsResult(int, String[], int[])} or {@code
* androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int,
* String[], int[])}.
*
* <p>This registrar is for Flutter's v1 embedding. To listen for permission results in the v2
* embedding, use {@link
* ActivityPluginBinding#addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener)}.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*
* @param listener a {@link RequestPermissionsResultListener} callback.
* @return this {@link Registrar}.
*/
@NonNull
Registrar addRequestPermissionsResultListener(
@NonNull RequestPermissionsResultListener listener);
/**
* Adds a callback allowing the plugin to take part in handling incoming calls to {@link
* Activity#onActivityResult(int, int, Intent)}.
*
* <p>This registrar is for Flutter's v1 embedding. To listen for {@code Activity} results in
* the v2 embedding, use {@link
* ActivityPluginBinding#addActivityResultListener(PluginRegistry.ActivityResultListener)}.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*
* @param listener an {@link ActivityResultListener} callback.
* @return this {@link Registrar}.
*/
@NonNull
Registrar addActivityResultListener(@NonNull ActivityResultListener listener);
/**
* Adds a callback allowing the plugin to take part in handling incoming calls to {@link
* Activity#onNewIntent(Intent)}.
*
* <p>This registrar is for Flutter's v1 embedding. To listen for new {@code Intent}s in the v2
* embedding, use {@link
* ActivityPluginBinding#addOnNewIntentListener(PluginRegistry.NewIntentListener)}.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*
* @param listener a {@link NewIntentListener} callback.
* @return this {@link Registrar}.
*/
@NonNull
Registrar addNewIntentListener(@NonNull NewIntentListener listener);
/**
* Adds a callback allowing the plugin to take part in handling incoming calls to {@link
* Activity#onUserLeaveHint()}.
*
* <p>This registrar is for Flutter's v1 embedding. To listen for leave hints in the v2
* embedding, use {@link
* ActivityPluginBinding#addOnUserLeaveHintListener(PluginRegistry.UserLeaveHintListener)}.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*
* @param listener a {@link UserLeaveHintListener} callback.
* @return this {@link Registrar}.
*/
@NonNull
Registrar addUserLeaveHintListener(@NonNull UserLeaveHintListener listener);
/**
* Adds a callback allowing the plugin to take part in handling incoming calls to {@link
* Activity#onWindowFocusChanged(boolean)}.
*
* <p>This registrar is for Flutter's v1 embedding. To listen for leave hints in the v2
* embedding, use {@link
* ActivityPluginBinding#addOnWindowFocusChangedListener(PluginRegistry.WindowFocusChangedListener)}.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*
* @param listener a {@link WindowFocusChangedListener} callback.
* @return this {@link Registrar}.
*/
@NonNull
Registrar addWindowFocusChangedListener(@NonNull WindowFocusChangedListener listener);
/**
* Adds a callback allowing the plugin to take part in handling incoming calls to {@link
* Activity#onDestroy()}.
*
* <p>This registrar is for Flutter's v1 embedding. The concept of {@code View} destruction does
* not exist in the v2 embedding. However, plugins in the v2 embedding can respond to {@link
* ActivityAware#onDetachedFromActivityForConfigChanges()} and {@link
* ActivityAware#onDetachedFromActivity()}, which indicate the loss of a visual context for the
* running Flutter experience. Developers should implement {@link ActivityAware} for their
* {@link FlutterPlugin} if such callbacks are needed. Also, plugins can respond to {@link
* FlutterPlugin#onDetachedFromEngine(FlutterPlugin.FlutterPluginBinding)}, which indicates that
* the given plugin has been completely disconnected from the associated Flutter experience and
* should clean up any resources.
*
* <p>For instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*
* @param listener a {@link ViewDestroyListener} callback.
* @return this {@link Registrar}.
*/
// TODO(amirh): Add a line in the javadoc above that points to a Platform Views website guide
// when one is available (but not a website API doc)
@NonNull
Registrar addViewDestroyListener(@NonNull ViewDestroyListener listener);
}
/**
* Delegate interface for handling result of permissions requests on behalf of the main {@link
* Activity}.
@ -412,29 +72,4 @@ public interface PluginRegistry {
interface WindowFocusChangedListener {
void onWindowFocusChanged(boolean hasFocus);
}
/**
* Delegate interface for handling an {@link android.app.Activity}'s onDestroy method being
* called. A plugin that implements this interface can adopt the {@link FlutterNativeView} by
* retaining a reference and returning true.
*
* @deprecated See https://flutter.dev/go/android-project-migration for migration details.
*/
@Deprecated
interface ViewDestroyListener {
boolean onViewDestroy(@NonNull FlutterNativeView view);
}
/**
* Callback interface for registering plugins with a plugin registry.
*
* <p>For example, an Application may use this callback interface to provide a background service
* with a callback for calling its GeneratedPluginRegistrant.registerWith method.
*
* @deprecated See https://flutter.dev/go/android-project-migration for migration details.
*/
@Deprecated
interface PluginRegistrantCallback {
void registerWith(@NonNull PluginRegistry registry);
}
}

View File

@ -18,7 +18,7 @@ import java.util.ArrayList;
/// The current editing state (text, selection range, composing range) the text input plugin holds.
///
/// As the name implies, this class also notifies its listeners when the editing state changes. When
/// there're ongoing batch edits, change notifications will be deferred until all batch edits end
/// there are ongoing batch edits, change notifications will be deferred until all batch edits end
/// (i.e. when the outermost batch edit ends). Listeners added during a batch edit will always be
/// notified when all batch edits end, even if there's no real change.
///

View File

@ -44,9 +44,8 @@ import java.util.List;
/**
* Manages platform views.
*
* <p>Each {@link io.flutter.embedding.engine.FlutterEngine} or {@link
* io.flutter.app.FlutterPluginRegistry} has a single platform views controller. A platform views
* controller can be attached to at most one Flutter view.
* <p>Each {@link io.flutter.embedding.engine.FlutterEngine} has a single platform views controller.
* A platform views controller can be attached to at most one Flutter view.
*/
public class PlatformViewsController implements PlatformViewsAccessibilityDelegate {
private static final String TAG = "PlatformViewsController";

View File

@ -1,132 +0,0 @@
// 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.view;
import android.content.Context;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.loader.FlutterLoader;
/**
* A legacy class to initialize the Flutter engine.
*
* @deprecated Replaced by {@link io.flutter.embedding.engine.loader.FlutterLoader}.
*/
@Deprecated
public class FlutterMain {
public static class Settings {
private String logTag;
@Nullable
public String getLogTag() {
return logTag;
}
/**
* Set the tag associated with Flutter app log messages.
*
* @param tag Log tag.
*/
public void setLogTag(String tag) {
logTag = tag;
}
}
/**
* Starts initialization of the native system.
*
* @param applicationContext The Android application context.
*/
public static void startInitialization(@NonNull Context applicationContext) {
FlutterInjector.instance().flutterLoader().startInitialization(applicationContext);
}
/**
* Starts initialization of the native system.
*
* <p>This loads the Flutter engine's native library to enable subsequent JNI calls. This also
* starts locating and unpacking Dart resources packaged in the app's APK.
*
* <p>Calling this method multiple times has no effect.
*
* @param applicationContext The Android application context.
* @param settings Configuration settings.
*/
public static void startInitialization(
@NonNull Context applicationContext, @NonNull Settings settings) {
FlutterLoader.Settings newSettings = new FlutterLoader.Settings();
newSettings.setLogTag(settings.getLogTag());
FlutterInjector.instance().flutterLoader().startInitialization(applicationContext, newSettings);
}
/**
* Blocks until initialization of the native system has completed.
*
* <p>Calling this method multiple times has no effect.
*
* @param applicationContext The Android application context.
* @param args Flags sent to the Flutter runtime.
*/
public static void ensureInitializationComplete(
@NonNull Context applicationContext, @Nullable String[] args) {
FlutterInjector.instance()
.flutterLoader()
.ensureInitializationComplete(applicationContext, args);
}
/**
* Same as {@link #ensureInitializationComplete(Context, String[])} but waiting on a background
* thread, then invoking {@code callback} on the {@code callbackHandler}.
*/
public static void ensureInitializationCompleteAsync(
@NonNull Context applicationContext,
@Nullable String[] args,
@NonNull Handler callbackHandler,
@NonNull Runnable callback) {
FlutterInjector.instance()
.flutterLoader()
.ensureInitializationCompleteAsync(applicationContext, args, callbackHandler, callback);
}
@NonNull
public static String findAppBundlePath() {
return FlutterInjector.instance().flutterLoader().findAppBundlePath();
}
@Deprecated
@Nullable
public static String findAppBundlePath(@NonNull Context applicationContext) {
return FlutterInjector.instance().flutterLoader().findAppBundlePath();
}
/**
* Returns the file name for the given asset. The returned file name can be used to access the
* asset in the APK through the {@link android.content.res.AssetManager} API.
*
* @param asset the name of the asset. The name can be hierarchical
* @return the filename to be used with {@link android.content.res.AssetManager}
*/
@NonNull
public static String getLookupKeyForAsset(@NonNull String asset) {
return FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(asset);
}
/**
* Returns the file name for the given asset which originates from the specified packageName. The
* returned file name can be used to access the asset in the APK through the {@link
* android.content.res.AssetManager} API.
*
* @param asset the name of the asset. The name can be hierarchical
* @param packageName the name of the package from which the asset originates
* @return the file name to be used with {@link android.content.res.AssetManager}
*/
@NonNull
public static String getLookupKeyForAsset(@NonNull String asset, @NonNull String packageName) {
return FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(asset, packageName);
}
}

View File

@ -1,204 +0,0 @@
// 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.view;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import io.flutter.Log;
import io.flutter.app.FlutterPluginRegistry;
import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
import io.flutter.plugin.common.*;
import java.nio.ByteBuffer;
/**
* @deprecated {@link io.flutter.embedding.android.FlutterView} is the new API that now replaces
* this class. See https://flutter.dev/go/android-project-migration for more migration details.
*/
@Deprecated
public class FlutterNativeView implements BinaryMessenger {
private static final String TAG = "FlutterNativeView";
private final FlutterPluginRegistry mPluginRegistry;
private final DartExecutor dartExecutor;
private FlutterView mFlutterView;
private final FlutterJNI mFlutterJNI;
private final Context mContext;
private boolean applicationIsRunning;
private final FlutterUiDisplayListener flutterUiDisplayListener =
new FlutterUiDisplayListener() {
@Override
public void onFlutterUiDisplayed() {
if (mFlutterView == null) {
return;
}
mFlutterView.onFirstFrame();
}
@Override
public void onFlutterUiNoLongerDisplayed() {
// no-op
}
};
public FlutterNativeView(@NonNull Context context) {
this(context, false);
}
public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) {
if (isBackgroundView) {
Log.w(TAG, "'isBackgroundView' is no longer supported and will be ignored");
}
mContext = context;
mPluginRegistry = new FlutterPluginRegistry(this, context);
mFlutterJNI = new FlutterJNI();
mFlutterJNI.addIsDisplayingFlutterUiListener(flutterUiDisplayListener);
this.dartExecutor = new DartExecutor(mFlutterJNI, context.getAssets());
mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
attach(this);
assertAttached();
}
public void detachFromFlutterView() {
mPluginRegistry.detach();
mFlutterView = null;
}
public void destroy() {
mPluginRegistry.destroy();
dartExecutor.onDetachedFromJNI();
mFlutterView = null;
mFlutterJNI.removeIsDisplayingFlutterUiListener(flutterUiDisplayListener);
mFlutterJNI.detachFromNativeAndReleaseResources();
applicationIsRunning = false;
}
@NonNull
public DartExecutor getDartExecutor() {
return dartExecutor;
}
@NonNull
public FlutterPluginRegistry getPluginRegistry() {
return mPluginRegistry;
}
public void attachViewAndActivity(FlutterView flutterView, Activity activity) {
mFlutterView = flutterView;
mPluginRegistry.attach(flutterView, activity);
}
public boolean isAttached() {
return mFlutterJNI.isAttached();
}
public void assertAttached() {
if (!isAttached()) throw new AssertionError("Platform view is not attached");
}
public void runFromBundle(FlutterRunArguments args) {
if (args.entrypoint == null) {
throw new AssertionError("An entrypoint must be specified");
}
assertAttached();
if (applicationIsRunning)
throw new AssertionError("This Flutter engine instance is already running an application");
mFlutterJNI.runBundleAndSnapshotFromLibrary(
args.bundlePath,
args.entrypoint,
args.libraryPath,
mContext.getResources().getAssets(),
null);
applicationIsRunning = true;
}
public boolean isApplicationRunning() {
return applicationIsRunning;
}
@Deprecated
public static String getObservatoryUri() {
return FlutterJNI.getVMServiceUri();
}
public static String getVMServiceUri() {
return FlutterJNI.getVMServiceUri();
}
@Override
@UiThread
public TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options) {
return dartExecutor.getBinaryMessenger().makeBackgroundTaskQueue(options);
}
@Override
@UiThread
public void send(String channel, ByteBuffer message) {
dartExecutor.getBinaryMessenger().send(channel, message);
}
@Override
@UiThread
public void send(String channel, ByteBuffer message, BinaryReply callback) {
if (!isAttached()) {
Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
return;
}
dartExecutor.getBinaryMessenger().send(channel, message, callback);
}
@Override
@UiThread
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
dartExecutor.getBinaryMessenger().setMessageHandler(channel, handler);
}
@Override
@UiThread
public void setMessageHandler(String channel, BinaryMessageHandler handler, TaskQueue taskQueue) {
dartExecutor.getBinaryMessenger().setMessageHandler(channel, handler, taskQueue);
}
@Override
public void enableBufferingIncomingMessages() {}
@Override
public void disableBufferingIncomingMessages() {}
/*package*/ FlutterJNI getFlutterJNI() {
return mFlutterJNI;
}
private void attach(FlutterNativeView view) {
mFlutterJNI.attachToNative();
dartExecutor.onAttachedToJNI();
}
private final class EngineLifecycleListenerImpl implements EngineLifecycleListener {
// Called by native to notify right before the engine is restarted (cold reload).
@SuppressWarnings("unused")
public void onPreEngineRestart() {
if (mFlutterView != null) {
mFlutterView.resetAccessibilityTree();
}
if (mPluginRegistry == null) {
return;
}
mPluginRegistry.onPreEngineRestart();
}
public void onEngineWillDestroy() {
// The old embedding doesn't actually have a FlutterEngine. It interacts with the JNI
// directly.
}
}
}

View File

@ -1,997 +0,0 @@
// 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.view;
import static io.flutter.Build.API_LEVELS;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.SurfaceTexture;
import android.os.Build;
import android.os.Handler;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.DisplayCutout;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewStructure;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.window.BackEvent;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.UiThread;
import io.flutter.Log;
import io.flutter.app.FlutterPluginRegistry;
import io.flutter.embedding.android.AndroidTouchProcessor;
import io.flutter.embedding.android.KeyboardManager;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.SurfaceTextureWrapper;
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
import io.flutter.embedding.engine.systemchannels.BackGestureChannel;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
import io.flutter.embedding.engine.systemchannels.NavigationChannel;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
import io.flutter.embedding.engine.systemchannels.SystemChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.plugin.common.ActivityLifecycleListener;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.plugin.localization.LocalizationPlugin;
import io.flutter.plugin.mouse.MouseCursorPlugin;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.plugin.platform.PlatformViewsController;
import io.flutter.util.ViewUtils;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
/**
* Deprecated Android view containing a Flutter app.
*
* @deprecated {@link io.flutter.embedding.android.FlutterView} is the new API that now replaces
* this class. See https://flutter.dev/go/android-project-migration for more migration details.
*/
@Deprecated
public class FlutterView extends SurfaceView
implements BinaryMessenger,
TextureRegistry,
MouseCursorPlugin.MouseCursorViewDelegate,
KeyboardManager.ViewDelegate {
/**
* Interface for those objects that maintain and expose a reference to a {@code FlutterView} (such
* as a full-screen Flutter activity).
*
* <p>This indirection is provided to support applications that use an activity other than {@link
* io.flutter.app.FlutterActivity} (e.g. Android v4 support library's {@code FragmentActivity}).
* It allows Flutter plugins to deal in this interface and not require that the activity be a
* subclass of {@code FlutterActivity}.
*/
public interface Provider {
/**
* Returns a reference to the Flutter view maintained by this object. This may be {@code null}.
*
* @return a reference to the Flutter view maintained by this object.
*/
FlutterView getFlutterView();
}
private static final String TAG = "FlutterView";
static final class ViewportMetrics {
float devicePixelRatio = 1.0f;
int physicalWidth = 0;
int physicalHeight = 0;
int physicalViewPaddingTop = 0;
int physicalViewPaddingRight = 0;
int physicalViewPaddingBottom = 0;
int physicalViewPaddingLeft = 0;
int physicalViewInsetTop = 0;
int physicalViewInsetRight = 0;
int physicalViewInsetBottom = 0;
int physicalViewInsetLeft = 0;
int systemGestureInsetTop = 0;
int systemGestureInsetRight = 0;
int systemGestureInsetBottom = 0;
int systemGestureInsetLeft = 0;
int physicalTouchSlop = -1;
}
private final DartExecutor dartExecutor;
private final FlutterRenderer flutterRenderer;
private final NavigationChannel navigationChannel;
private final BackGestureChannel backGestureChannel;
private final LifecycleChannel lifecycleChannel;
private final LocalizationChannel localizationChannel;
private final PlatformChannel platformChannel;
private final SettingsChannel settingsChannel;
private final SystemChannel systemChannel;
private final InputMethodManager mImm;
private final TextInputPlugin mTextInputPlugin;
private final LocalizationPlugin mLocalizationPlugin;
private final MouseCursorPlugin mMouseCursorPlugin;
private final KeyboardManager mKeyboardManager;
private final AndroidTouchProcessor androidTouchProcessor;
private AccessibilityBridge mAccessibilityNodeProvider;
private final SurfaceHolder.Callback mSurfaceCallback;
private final ViewportMetrics mMetrics;
private final List<ActivityLifecycleListener> mActivityLifecycleListeners;
private final List<FirstFrameListener> mFirstFrameListeners;
private final AtomicLong nextTextureId = new AtomicLong(0L);
private FlutterNativeView mNativeView;
private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not
private boolean didRenderFirstFrame = false;
private final AccessibilityBridge.OnAccessibilityChangeListener onAccessibilityChangeListener =
new AccessibilityBridge.OnAccessibilityChangeListener() {
@Override
public void onAccessibilityChanged(
boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled);
}
};
public FlutterView(Context context) {
this(context, null);
}
public FlutterView(Context context, AttributeSet attrs) {
this(context, attrs, null);
}
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
super(context, attrs);
Activity activity = ViewUtils.getActivity(getContext());
if (activity == null) {
throw new IllegalArgumentException("Bad context");
}
if (nativeView == null) {
mNativeView = new FlutterNativeView(activity.getApplicationContext());
} else {
mNativeView = nativeView;
}
dartExecutor = mNativeView.getDartExecutor();
flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI());
mIsSoftwareRenderingEnabled = mNativeView.getFlutterJNI().getIsSoftwareRenderingEnabled();
mMetrics = new ViewportMetrics();
mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
mMetrics.physicalTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setFocusable(true);
setFocusableInTouchMode(true);
mNativeView.attachViewAndActivity(this, activity);
mSurfaceCallback =
new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
assertAttached();
mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
assertAttached();
mNativeView.getFlutterJNI().onSurfaceChanged(width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
assertAttached();
mNativeView.getFlutterJNI().onSurfaceDestroyed();
}
};
getHolder().addCallback(mSurfaceCallback);
mActivityLifecycleListeners = new ArrayList<>();
mFirstFrameListeners = new ArrayList<>();
// Create all platform channels
navigationChannel = new NavigationChannel(dartExecutor);
backGestureChannel = new BackGestureChannel(dartExecutor);
lifecycleChannel = new LifecycleChannel(dartExecutor);
localizationChannel = new LocalizationChannel(dartExecutor);
platformChannel = new PlatformChannel(dartExecutor);
systemChannel = new SystemChannel(dartExecutor);
settingsChannel = new SettingsChannel(dartExecutor);
// Create and set up plugins
PlatformPlugin platformPlugin = new PlatformPlugin(activity, platformChannel);
addActivityLifecycleListener(
new ActivityLifecycleListener() {
@Override
public void onPostResume() {
platformPlugin.updateSystemUiOverlays();
}
});
mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
PlatformViewsController platformViewsController =
mNativeView.getPluginRegistry().getPlatformViewsController();
mTextInputPlugin =
new TextInputPlugin(
this,
new TextInputChannel(dartExecutor),
new ScribeChannel(dartExecutor),
platformViewsController);
mKeyboardManager = new KeyboardManager(this);
if (Build.VERSION.SDK_INT >= API_LEVELS.API_24) {
mMouseCursorPlugin = new MouseCursorPlugin(this, new MouseCursorChannel(dartExecutor));
} else {
mMouseCursorPlugin = null;
}
mLocalizationPlugin = new LocalizationPlugin(context, localizationChannel);
androidTouchProcessor =
new AndroidTouchProcessor(flutterRenderer, /*trackMotionEvents=*/ false);
platformViewsController.attachToFlutterRenderer(flutterRenderer);
mNativeView
.getPluginRegistry()
.getPlatformViewsController()
.attachTextInputPlugin(mTextInputPlugin);
mNativeView.getFlutterJNI().setLocalizationPlugin(mLocalizationPlugin);
// Send initial platform information to Dart
mLocalizationPlugin.sendLocalesToFlutter(getResources().getConfiguration());
sendUserPlatformSettingsToDart();
}
@NonNull
public DartExecutor getDartExecutor() {
return dartExecutor;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.e(TAG, "dispatchKeyEvent: " + event.toString());
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
// Tell Android to start tracking this event.
getKeyDispatcherState().startTracking(event, this);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
// Stop tracking the event.
getKeyDispatcherState().handleUpEvent(event);
}
// If the key processor doesn't handle it, then send it on to the
// superclass. The key processor will typically handle all events except
// those where it has re-dispatched the event after receiving a reply from
// the framework that the framework did not handle it.
return (isAttached() && mKeyboardManager.handleEvent(event)) || super.dispatchKeyEvent(event);
}
public FlutterNativeView getFlutterNativeView() {
return mNativeView;
}
public FlutterPluginRegistry getPluginRegistry() {
return mNativeView.getPluginRegistry();
}
public String getLookupKeyForAsset(String asset) {
return FlutterMain.getLookupKeyForAsset(asset);
}
public String getLookupKeyForAsset(String asset, String packageName) {
return FlutterMain.getLookupKeyForAsset(asset, packageName);
}
public void addActivityLifecycleListener(ActivityLifecycleListener listener) {
mActivityLifecycleListeners.add(listener);
}
public void onStart() {
lifecycleChannel.appIsInactive();
}
public void onPause() {
lifecycleChannel.appIsInactive();
}
public void onPostResume() {
for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
listener.onPostResume();
}
lifecycleChannel.appIsResumed();
}
public void onStop() {
lifecycleChannel.appIsPaused();
}
public void onMemoryPressure() {
mNativeView.getFlutterJNI().notifyLowMemoryWarning();
systemChannel.sendMemoryPressureWarning();
}
/**
* Returns true if the Flutter experience associated with this {@code FlutterView} has rendered
* its first frame, or false otherwise.
*/
public boolean hasRenderedFirstFrame() {
return didRenderFirstFrame;
}
/**
* Provide a listener that will be called once when the FlutterView renders its first frame to the
* underlaying SurfaceView.
*/
public void addFirstFrameListener(FirstFrameListener listener) {
mFirstFrameListeners.add(listener);
}
/** Remove an existing first frame listener. */
public void removeFirstFrameListener(FirstFrameListener listener) {
mFirstFrameListeners.remove(listener);
}
@Override
public void enableBufferingIncomingMessages() {}
@Override
public void disableBufferingIncomingMessages() {}
/**
* Reverts this back to the {@link SurfaceView} defaults, at the back of its window and opaque.
*/
public void disableTransparentBackground() {
setZOrderOnTop(false);
getHolder().setFormat(PixelFormat.OPAQUE);
}
public void setInitialRoute(String route) {
navigationChannel.setInitialRoute(route);
}
public void pushRoute(String route) {
navigationChannel.pushRoute(route);
}
public void popRoute() {
navigationChannel.popRoute();
}
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
public void startBackGesture(@NonNull BackEvent backEvent) {
backGestureChannel.startBackGesture(backEvent);
}
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
public void updateBackGestureProgress(@NonNull BackEvent backEvent) {
backGestureChannel.updateBackGestureProgress(backEvent);
}
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
public void commitBackGesture() {
backGestureChannel.commitBackGesture();
}
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
public void cancelBackGesture() {
backGestureChannel.cancelBackGesture();
}
private void sendUserPlatformSettingsToDart() {
// Lookup the current brightness of the Android OS.
boolean isNightModeOn =
(getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
SettingsChannel.PlatformBrightness brightness =
isNightModeOn
? SettingsChannel.PlatformBrightness.dark
: SettingsChannel.PlatformBrightness.light;
settingsChannel
.startMessage()
.setTextScaleFactor(getResources().getConfiguration().fontScale)
.setUse24HourFormat(DateFormat.is24HourFormat(getContext()))
.setPlatformBrightness(brightness)
.send();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mLocalizationPlugin.sendLocalesToFlutter(newConfig);
sendUserPlatformSettingsToDart();
}
float getDevicePixelRatio() {
return mMetrics.devicePixelRatio;
}
public FlutterNativeView detach() {
if (!isAttached()) return null;
getHolder().removeCallback(mSurfaceCallback);
mNativeView.detachFromFlutterView();
FlutterNativeView view = mNativeView;
mNativeView = null;
return view;
}
public void destroy() {
if (!isAttached()) return;
getHolder().removeCallback(mSurfaceCallback);
releaseAccessibilityNodeProvider();
mNativeView.destroy();
mNativeView = null;
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return mTextInputPlugin.createInputConnection(this, mKeyboardManager, outAttrs);
}
@Override
public boolean checkInputConnectionProxy(View view) {
return mNativeView
.getPluginRegistry()
.getPlatformViewsController()
.checkInputConnectionProxy(view);
}
@Override
public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
super.onProvideAutofillVirtualStructure(structure, flags);
mTextInputPlugin.onProvideAutofillVirtualStructure(structure, flags);
}
@Override
public void autofill(SparseArray<AutofillValue> values) {
mTextInputPlugin.autofill(values);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isAttached()) {
return super.onTouchEvent(event);
}
requestUnbufferedDispatch(event);
return androidTouchProcessor.onTouchEvent(event);
}
@Override
public boolean onHoverEvent(MotionEvent event) {
if (!isAttached()) {
return super.onHoverEvent(event);
}
boolean handled = mAccessibilityNodeProvider.onAccessibilityHoverEvent(event);
if (!handled) {
// TODO(ianh): Expose hover events to the platform,
// implementing ADD, REMOVE, etc.
}
return handled;
}
/**
* Invoked by Android when a generic motion event occurs, e.g., joystick movement, mouse hover,
* track pad touches, scroll wheel movements, etc.
*
* <p>Flutter handles all of its own gesture detection and processing, therefore this method
* forwards all {@link MotionEvent} data from Android to Flutter.
*/
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
boolean handled =
isAttached() && androidTouchProcessor.onGenericMotionEvent(event, getContext());
return handled ? true : super.onGenericMotionEvent(event);
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
mMetrics.physicalWidth = width;
mMetrics.physicalHeight = height;
updateViewportMetrics();
super.onSizeChanged(width, height, oldWidth, oldHeight);
}
// TODO(garyq): Add support for notch cutout API
// Decide if we want to zero the padding of the sides. When in Landscape orientation,
// android may decide to place the software navigation bars on the side. When the nav
// bar is hidden, the reported insets should be removed to prevent extra useless space
// on the sides.
private enum ZeroSides {
NONE,
LEFT,
RIGHT,
BOTH
}
private ZeroSides calculateShouldZeroSides() {
// We get both orientation and rotation because rotation is all 4
// rotations relative to default rotation while orientation is portrait
// or landscape. By combining both, we can obtain a more precise measure
// of the rotation.
Context context = getContext();
int orientation = context.getResources().getConfiguration().orientation;
int rotation =
((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay()
.getRotation();
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
if (rotation == Surface.ROTATION_90) {
return ZeroSides.RIGHT;
} else if (rotation == Surface.ROTATION_270) {
// In android API >= 23, the nav bar always appears on the "bottom" (USB) side.
return Build.VERSION.SDK_INT >= API_LEVELS.API_23 ? ZeroSides.LEFT : ZeroSides.RIGHT;
}
// Ambiguous orientation due to landscape left/right default. Zero both sides.
else if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
return ZeroSides.BOTH;
}
}
// Square orientation deprecated in API 16, we will not check for it and return false
// to be safe and not remove any unique padding for the devices that do use it.
return ZeroSides.NONE;
}
// TODO(garyq): Use new Android R getInsets API
// TODO(garyq): The keyboard detection may interact strangely with
// https://github.com/flutter/flutter/issues/22061
// Uses inset heights and screen heights as a heuristic to determine if the insets should
// be padded. When the on-screen keyboard is detected, we want to include the full inset
// but when the inset is just the hidden nav bar, we want to provide a zero inset so the space
// can be used.
private int guessBottomKeyboardInset(WindowInsets insets) {
int screenHeight = getRootView().getHeight();
// Magic number due to this being a heuristic. This should be replaced, but we have not
// found a clean way to do it yet (Sept. 2018)
final double keyboardHeightRatioHeuristic = 0.18;
if (insets.getSystemWindowInsetBottom() < screenHeight * keyboardHeightRatioHeuristic) {
// Is not a keyboard, so return zero as inset.
return 0;
} else {
// Is a keyboard, so return the full inset.
return insets.getSystemWindowInsetBottom();
}
}
// This callback is not present in API < 20, which means lower API devices will see
// the wider than expected padding when the status and navigation bars are hidden.
// The annotations to suppress "InlinedApi" and "NewApi" lints prevent lint warnings
// caused by usage of Android Q APIs. These calls are safe because they are
// guarded.
@Override
@SuppressLint({"InlinedApi", "NewApi"})
public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
// getSystemGestureInsets() was introduced in API 29 and immediately deprecated in 30.
if (Build.VERSION.SDK_INT == API_LEVELS.API_29) {
Insets systemGestureInsets = insets.getSystemGestureInsets();
mMetrics.systemGestureInsetTop = systemGestureInsets.top;
mMetrics.systemGestureInsetRight = systemGestureInsets.right;
mMetrics.systemGestureInsetBottom = systemGestureInsets.bottom;
mMetrics.systemGestureInsetLeft = systemGestureInsets.left;
}
boolean statusBarVisible = (SYSTEM_UI_FLAG_FULLSCREEN & getWindowSystemUiVisibility()) == 0;
boolean navigationBarVisible =
(SYSTEM_UI_FLAG_HIDE_NAVIGATION & getWindowSystemUiVisibility()) == 0;
if (Build.VERSION.SDK_INT >= API_LEVELS.API_30) {
int mask = 0;
if (navigationBarVisible) {
mask = mask | android.view.WindowInsets.Type.navigationBars();
}
if (statusBarVisible) {
mask = mask | android.view.WindowInsets.Type.statusBars();
}
Insets uiInsets = insets.getInsets(mask);
mMetrics.physicalViewPaddingTop = uiInsets.top;
mMetrics.physicalViewPaddingRight = uiInsets.right;
mMetrics.physicalViewPaddingBottom = uiInsets.bottom;
mMetrics.physicalViewPaddingLeft = uiInsets.left;
Insets imeInsets = insets.getInsets(android.view.WindowInsets.Type.ime());
mMetrics.physicalViewInsetTop = imeInsets.top;
mMetrics.physicalViewInsetRight = imeInsets.right;
mMetrics.physicalViewInsetBottom = imeInsets.bottom; // Typically, only bottom is non-zero
mMetrics.physicalViewInsetLeft = imeInsets.left;
Insets systemGestureInsets =
insets.getInsets(android.view.WindowInsets.Type.systemGestures());
mMetrics.systemGestureInsetTop = systemGestureInsets.top;
mMetrics.systemGestureInsetRight = systemGestureInsets.right;
mMetrics.systemGestureInsetBottom = systemGestureInsets.bottom;
mMetrics.systemGestureInsetLeft = systemGestureInsets.left;
// TODO(garyq): Expose the full rects of the display cutout.
// Take the max of the display cutout insets and existing padding to merge them
DisplayCutout cutout = insets.getDisplayCutout();
if (cutout != null) {
Insets waterfallInsets = cutout.getWaterfallInsets();
mMetrics.physicalViewPaddingTop =
Math.max(
Math.max(mMetrics.physicalViewPaddingTop, waterfallInsets.top),
cutout.getSafeInsetTop());
mMetrics.physicalViewPaddingRight =
Math.max(
Math.max(mMetrics.physicalViewPaddingRight, waterfallInsets.right),
cutout.getSafeInsetRight());
mMetrics.physicalViewPaddingBottom =
Math.max(
Math.max(mMetrics.physicalViewPaddingBottom, waterfallInsets.bottom),
cutout.getSafeInsetBottom());
mMetrics.physicalViewPaddingLeft =
Math.max(
Math.max(mMetrics.physicalViewPaddingLeft, waterfallInsets.left),
cutout.getSafeInsetLeft());
}
} else {
// We zero the left and/or right sides to prevent the padding the
// navigation bar would have caused.
ZeroSides zeroSides = ZeroSides.NONE;
if (!navigationBarVisible) {
zeroSides = calculateShouldZeroSides();
}
// Status bar (top), navigation bar (bottom) and left/right system insets should
// partially obscure the content (padding).
mMetrics.physicalViewPaddingTop = statusBarVisible ? insets.getSystemWindowInsetTop() : 0;
mMetrics.physicalViewPaddingRight =
zeroSides == ZeroSides.RIGHT || zeroSides == ZeroSides.BOTH
? 0
: insets.getSystemWindowInsetRight();
mMetrics.physicalViewPaddingBottom =
navigationBarVisible && guessBottomKeyboardInset(insets) == 0
? insets.getSystemWindowInsetBottom()
: 0;
mMetrics.physicalViewPaddingLeft =
zeroSides == ZeroSides.LEFT || zeroSides == ZeroSides.BOTH
? 0
: insets.getSystemWindowInsetLeft();
// Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
mMetrics.physicalViewInsetTop = 0;
mMetrics.physicalViewInsetRight = 0;
mMetrics.physicalViewInsetBottom = guessBottomKeyboardInset(insets);
mMetrics.physicalViewInsetLeft = 0;
}
updateViewportMetrics();
return super.onApplyWindowInsets(insets);
}
private boolean isAttached() {
return mNativeView != null && mNativeView.isAttached();
}
void assertAttached() {
if (!isAttached()) throw new AssertionError("Platform view is not attached");
}
private void preRun() {
resetAccessibilityTree();
}
void resetAccessibilityTree() {
if (mAccessibilityNodeProvider != null) {
mAccessibilityNodeProvider.reset();
}
}
private void postRun() {}
public void runFromBundle(FlutterRunArguments args) {
assertAttached();
preRun();
mNativeView.runFromBundle(args);
postRun();
}
/**
* Return the most recent frame as a bitmap.
*
* @return A bitmap.
*/
public Bitmap getBitmap() {
assertAttached();
return mNativeView.getFlutterJNI().getBitmap();
}
private void updateViewportMetrics() {
if (!isAttached()) return;
mNativeView
.getFlutterJNI()
.setViewportMetrics(
mMetrics.devicePixelRatio,
mMetrics.physicalWidth,
mMetrics.physicalHeight,
mMetrics.physicalViewPaddingTop,
mMetrics.physicalViewPaddingRight,
mMetrics.physicalViewPaddingBottom,
mMetrics.physicalViewPaddingLeft,
mMetrics.physicalViewInsetTop,
mMetrics.physicalViewInsetRight,
mMetrics.physicalViewInsetBottom,
mMetrics.physicalViewInsetLeft,
mMetrics.systemGestureInsetTop,
mMetrics.systemGestureInsetRight,
mMetrics.systemGestureInsetBottom,
mMetrics.systemGestureInsetLeft,
mMetrics.physicalTouchSlop,
new int[0],
new int[0],
new int[0]);
}
// Called by FlutterNativeView to notify first Flutter frame rendered.
public void onFirstFrame() {
didRenderFirstFrame = true;
// Allow listeners to remove themselves when they are called.
List<FirstFrameListener> listeners = new ArrayList<>(mFirstFrameListeners);
for (FirstFrameListener listener : listeners) {
listener.onFirstFrame();
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
PlatformViewsController platformViewsController =
getPluginRegistry().getPlatformViewsController();
mAccessibilityNodeProvider =
new AccessibilityBridge(
this,
new AccessibilityChannel(dartExecutor, getFlutterNativeView().getFlutterJNI()),
(AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE),
getContext().getContentResolver(),
platformViewsController);
mAccessibilityNodeProvider.setOnAccessibilityChangeListener(onAccessibilityChangeListener);
resetWillNotDraw(
mAccessibilityNodeProvider.isAccessibilityEnabled(),
mAccessibilityNodeProvider.isTouchExplorationEnabled());
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
releaseAccessibilityNodeProvider();
}
// TODO(mattcarroll): Confer with Ian as to why we need this method. Delete if possible, otherwise
// add comments.
private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
if (!mIsSoftwareRenderingEnabled) {
setWillNotDraw(!(isAccessibilityEnabled || isTouchExplorationEnabled));
} else {
setWillNotDraw(false);
}
}
@Override
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
if (mAccessibilityNodeProvider != null && mAccessibilityNodeProvider.isAccessibilityEnabled()) {
return mAccessibilityNodeProvider;
} else {
// TODO(goderbauer): when a11y is off this should return a one-off snapshot of
// the a11y
// tree.
return null;
}
}
private void releaseAccessibilityNodeProvider() {
if (mAccessibilityNodeProvider != null) {
mAccessibilityNodeProvider.release();
mAccessibilityNodeProvider = null;
}
}
// -------- Start: Mouse -------
@Override
@TargetApi(API_LEVELS.API_24)
@RequiresApi(API_LEVELS.API_24)
@NonNull
public PointerIcon getSystemPointerIcon(int type) {
return PointerIcon.getSystemIcon(getContext(), type);
}
// -------- End: Mouse -------
// -------- Start: Keyboard -------
@Override
public BinaryMessenger getBinaryMessenger() {
return this;
}
@Override
public boolean onTextInputKeyEvent(@NonNull KeyEvent keyEvent) {
return mTextInputPlugin.handleKeyEvent(keyEvent);
}
@Override
public void redispatch(@NonNull KeyEvent keyEvent) {
getRootView().dispatchKeyEvent(keyEvent);
}
// -------- End: Keyboard -------
@Override
@UiThread
public TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options) {
return null;
}
@Override
@UiThread
public void send(String channel, ByteBuffer message) {
send(channel, message, null);
}
@Override
@UiThread
public void send(String channel, ByteBuffer message, BinaryReply callback) {
if (!isAttached()) {
Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
return;
}
mNativeView.send(channel, message, callback);
}
@Override
@UiThread
public void setMessageHandler(@NonNull String channel, @NonNull BinaryMessageHandler handler) {
mNativeView.setMessageHandler(channel, handler);
}
@Override
@UiThread
public void setMessageHandler(
@NonNull String channel,
@NonNull BinaryMessageHandler handler,
@NonNull TaskQueue taskQueue) {
mNativeView.setMessageHandler(channel, handler, taskQueue);
}
/** Listener will be called on the Android UI thread once when Flutter renders the first frame. */
public interface FirstFrameListener {
void onFirstFrame();
}
@Override
@NonNull
public TextureRegistry.SurfaceTextureEntry createSurfaceTexture() {
final SurfaceTexture surfaceTexture = new SurfaceTexture(0);
return registerSurfaceTexture(surfaceTexture);
}
@Override
@NonNull
public ImageTextureEntry createImageTexture() {
throw new UnsupportedOperationException("Image textures are not supported in this mode.");
}
@NonNull
@Override
public SurfaceProducer createSurfaceProducer() {
throw new UnsupportedOperationException(
"SurfaceProducer textures are not supported in this mode.");
}
@Override
@NonNull
public TextureRegistry.SurfaceTextureEntry registerSurfaceTexture(
@NonNull SurfaceTexture surfaceTexture) {
surfaceTexture.detachFromGLContext();
final SurfaceTextureRegistryEntry entry =
new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), surfaceTexture);
mNativeView.getFlutterJNI().registerTexture(entry.id(), entry.textureWrapper());
return entry;
}
final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry {
private final long id;
private final SurfaceTextureWrapper textureWrapper;
private boolean released;
SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture) {
this.id = id;
this.textureWrapper = new SurfaceTextureWrapper(surfaceTexture);
// The callback relies on being executed on the UI thread (unsynchronised read of
// mNativeView
// and also the engine code check for platform thread in
// Shell::OnPlatformViewMarkTextureFrameAvailable),
// so we explicitly pass a Handler for the current thread.
this.surfaceTexture().setOnFrameAvailableListener(onFrameListener, new Handler());
}
private SurfaceTexture.OnFrameAvailableListener onFrameListener =
new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture texture) {
if (released || mNativeView == null) {
// Even though we make sure to unregister the callback before releasing, as of Android
// O
// SurfaceTexture has a data race when accessing the callback, so the callback may
// still be called by a stale reference after released==true and mNativeView==null.
return;
}
mNativeView
.getFlutterJNI()
.markTextureFrameAvailable(SurfaceTextureRegistryEntry.this.id);
}
};
public SurfaceTextureWrapper textureWrapper() {
return textureWrapper;
}
@NonNull
@Override
public SurfaceTexture surfaceTexture() {
return textureWrapper.surfaceTexture();
}
@Override
public long id() {
return id;
}
@Override
public void release() {
if (released) {
return;
}
released = true;
// The ordering of the next 3 calls is important:
// First we remove the frame listener, then we release the SurfaceTexture, and only after we
// unregister
// the texture which actually deletes the GL texture.
// Otherwise onFrameAvailableListener might be called after mNativeView==null
// (https://github.com/flutter/flutter/issues/20951). See also the check in onFrameAvailable.
surfaceTexture().setOnFrameAvailableListener(null);
textureWrapper.release();
mNativeView.getFlutterJNI().unregisterTexture(id);
}
}
}

View File

@ -13,8 +13,8 @@ import androidx.annotation.Nullable;
// TODO(mattcarroll): re-evalute docs in this class and add nullability annotations.
/**
* Registry of backend textures used with a single {@link FlutterView} instance. Entries may be
* embedded into the Flutter view using the <a
* Registry of backend textures used with a single {@link io.flutter.embedding.android.FlutterView}
* instance. Entries may be embedded into the Flutter view using the <a
* href="https://api.flutter.dev/flutter/widgets/Texture-class.html">Texture</a> widget.
*/
public interface TextureRegistry {

View File

@ -1,157 +0,0 @@
// 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.plugins.shim;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding;
import io.flutter.embedding.engine.plugins.PluginRegistry;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(AndroidJUnit4.class)
public class ShimPluginRegistryTest {
@Mock private FlutterEngine mockFlutterEngine;
@Mock private FlutterPluginBinding mockFlutterPluginBinding;
@Mock private ActivityPluginBinding mockActivityPluginBinding;
@Mock private PluginRegistry mockPluginRegistry;
@Mock private Context mockApplicationContext;
@Mock private Activity mockActivity;
@Before
public void setup() {
MockitoAnnotations.openMocks(this);
when(mockFlutterEngine.getPlugins()).thenReturn(mockPluginRegistry);
when(mockFlutterPluginBinding.getApplicationContext()).thenReturn(mockApplicationContext);
when(mockActivityPluginBinding.getActivity()).thenReturn(mockActivity);
}
@SuppressWarnings("deprecation")
// Test is intentionally verifying deprecated behavior.
@Test
public void itSuppliesOldAPIsViaTheNewFlutterPluginBinding() {
ShimPluginRegistry registryUnderTest = new ShimPluginRegistry(mockFlutterEngine);
// Fully qualifed name because imports can not have deprecation supression.
// This is the consumption side of the old plugins.
io.flutter.plugin.common.PluginRegistry.Registrar registrarUnderTest =
registryUnderTest.registrarFor("test");
ArgumentCaptor<FlutterPlugin> shimAggregateCaptor =
ArgumentCaptor.forClass(FlutterPlugin.class);
// A single shim aggregate was added as a new plugin to the FlutterEngine's PluginRegistry.
verify(mockPluginRegistry).add(shimAggregateCaptor.capture());
// This is really a ShimRegistrarAggregate acting as a FlutterPlugin which is the
// intermediate consumption side of the new plugin inside the shim.
FlutterPlugin shimAggregateUnderTest = shimAggregateCaptor.getValue();
// The FlutterPluginBinding is the supply side of the new plugin.
shimAggregateUnderTest.onAttachedToEngine(mockFlutterPluginBinding);
// Consume something from the old plugin API.
assertEquals(mockApplicationContext, registrarUnderTest.context());
// Check that the value comes from the supply side of the new plugin.
verify(mockFlutterPluginBinding).getApplicationContext();
}
@SuppressWarnings("deprecation")
// Test is intentionally verifying deprecated behavior.
@Test
public void itSuppliesMultipleOldPlugins() {
ShimPluginRegistry registryUnderTest = new ShimPluginRegistry(mockFlutterEngine);
// Fully qualifed name because imports can not have deprecation supression.
io.flutter.plugin.common.PluginRegistry.Registrar registrarUnderTest1 =
registryUnderTest.registrarFor("test1");
io.flutter.plugin.common.PluginRegistry.Registrar registrarUnderTest2 =
registryUnderTest.registrarFor("test2");
ArgumentCaptor<FlutterPlugin> shimAggregateCaptor =
ArgumentCaptor.forClass(FlutterPlugin.class);
verify(mockPluginRegistry).add(shimAggregateCaptor.capture());
// There's only one aggregate for many old plugins.
FlutterPlugin shimAggregateUnderTest = shimAggregateCaptor.getValue();
// The FlutterPluginBinding is the supply side of the new plugin.
shimAggregateUnderTest.onAttachedToEngine(mockFlutterPluginBinding);
// Since the 2 old plugins are supplied by the same intermediate FlutterPlugin, they should
// get the same value.
assertEquals(registrarUnderTest1.context(), registrarUnderTest2.context());
verify(mockFlutterPluginBinding, times(2)).getApplicationContext();
}
@SuppressWarnings("deprecation")
// Test is intentionally verifying deprecated behavior.
@Test
public void itCanOnlySupplyActivityBindingWhenUpstreamActivityIsAttached() {
ShimPluginRegistry registryUnderTest = new ShimPluginRegistry(mockFlutterEngine);
io.flutter.plugin.common.PluginRegistry.Registrar registrarUnderTest =
registryUnderTest.registrarFor("test");
ArgumentCaptor<FlutterPlugin> shimAggregateCaptor =
ArgumentCaptor.forClass(FlutterPlugin.class);
verify(mockPluginRegistry).add(shimAggregateCaptor.capture());
FlutterPlugin shimAggregateAsPlugin = shimAggregateCaptor.getValue();
ActivityAware shimAggregateAsActivityAware = (ActivityAware) shimAggregateCaptor.getValue();
// Nothing is retrievable when nothing is attached.
assertNull(registrarUnderTest.context());
assertNull(registrarUnderTest.activity());
shimAggregateAsPlugin.onAttachedToEngine(mockFlutterPluginBinding);
assertEquals(mockApplicationContext, registrarUnderTest.context());
assertNull(registrarUnderTest.activity());
shimAggregateAsActivityAware.onAttachedToActivity(mockActivityPluginBinding);
// Now context is the activity context.
assertEquals(mockActivity, registrarUnderTest.activeContext());
assertEquals(mockActivity, registrarUnderTest.activity());
shimAggregateAsActivityAware.onDetachedFromActivityForConfigChanges();
assertEquals(mockApplicationContext, registrarUnderTest.activeContext());
assertNull(registrarUnderTest.activity());
shimAggregateAsActivityAware.onReattachedToActivityForConfigChanges(mockActivityPluginBinding);
assertEquals(mockActivity, registrarUnderTest.activeContext());
assertEquals(mockActivity, registrarUnderTest.activity());
shimAggregateAsActivityAware.onDetachedFromActivity();
assertEquals(mockApplicationContext, registrarUnderTest.activeContext());
assertNull(registrarUnderTest.activity());
// Attach an activity again.
shimAggregateAsActivityAware.onAttachedToActivity(mockActivityPluginBinding);
assertEquals(mockActivity, registrarUnderTest.activeContext());
assertEquals(mockActivity, registrarUnderTest.activity());
// Now rip out the whole engine.
shimAggregateAsPlugin.onDetachedFromEngine(mockFlutterPluginBinding);
// And everything should have been made unavailable.
assertNull(registrarUnderTest.activeContext());
assertNull(registrarUnderTest.activity());
}
}

View File

@ -3,7 +3,6 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="io.flutter.app.FlutterApplication"
android:label="Scenarios App"
android:supportsRtl="true"
android:theme="@style/AppTheme"

View File

@ -66,7 +66,6 @@ def main():
classpath.append(args.build_config_path)
packages = [
'io.flutter.app',
'io.flutter.embedding.android',
'io.flutter.embedding.engine',
'io.flutter.embedding.engine.dart',
@ -78,7 +77,6 @@ def main():
'io.flutter.embedding.engine.plugins.contentprovider',
'io.flutter.embedding.engine.plugins.lifecycle',
'io.flutter.embedding.engine.plugins.service',
'io.flutter.embedding.engine.plugins.shim',
'io.flutter.embedding.engine.renderer',
'io.flutter.embedding.engine.systemchannels',
'io.flutter.plugin.common',