2018-11-07 12:24:35 -08:00
|
|
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
2017-06-07 12:28:41 -07:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package io.flutter.app;
|
|
|
|
|
|
2017-08-23 12:57:06 -07:00
|
|
|
import android.animation.Animator;
|
|
|
|
|
import android.animation.AnimatorListenerAdapter;
|
2017-06-07 12:28:41 -07:00
|
|
|
import android.app.Activity;
|
2017-11-13 13:56:48 -08:00
|
|
|
import android.app.Application;
|
2017-06-07 12:28:41 -07:00
|
|
|
import android.content.Context;
|
|
|
|
|
import android.content.Intent;
|
2017-08-15 11:01:12 -07:00
|
|
|
import android.content.pm.ActivityInfo;
|
2017-06-08 08:13:52 -07:00
|
|
|
import android.content.pm.ApplicationInfo;
|
2017-08-14 15:44:52 -07:00
|
|
|
import android.content.pm.PackageManager;
|
|
|
|
|
import android.content.pm.PackageManager.NameNotFoundException;
|
2017-06-07 12:28:41 -07:00
|
|
|
import android.content.res.Configuration;
|
2017-08-14 15:44:52 -07:00
|
|
|
import android.content.res.Resources.NotFoundException;
|
|
|
|
|
import android.graphics.drawable.Drawable;
|
2017-06-07 12:28:41 -07:00
|
|
|
import android.os.Build;
|
|
|
|
|
import android.os.Bundle;
|
2017-08-14 15:44:52 -07:00
|
|
|
import android.util.Log;
|
|
|
|
|
import android.util.TypedValue;
|
|
|
|
|
import android.view.View;
|
2017-08-23 12:57:06 -07:00
|
|
|
import android.view.ViewGroup;
|
2017-06-07 12:28:41 -07:00
|
|
|
import android.view.Window;
|
|
|
|
|
import android.view.WindowManager.LayoutParams;
|
|
|
|
|
import io.flutter.plugin.common.PluginRegistry;
|
|
|
|
|
import io.flutter.plugin.platform.PlatformPlugin;
|
|
|
|
|
import io.flutter.util.Preconditions;
|
|
|
|
|
import io.flutter.view.FlutterMain;
|
2017-11-13 13:56:48 -08:00
|
|
|
import io.flutter.view.FlutterNativeView;
|
2018-08-07 12:42:22 -07:00
|
|
|
import io.flutter.view.FlutterRunArguments;
|
2017-06-07 12:28:41 -07:00
|
|
|
import io.flutter.view.FlutterView;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class that performs the actual work of tying Android {@link Activity}
|
|
|
|
|
* instances to Flutter.
|
2017-06-09 07:35:50 -07:00
|
|
|
*
|
|
|
|
|
* <p>This exists as a dedicated class (as opposed to being integrated directly
|
2017-06-07 12:28:41 -07:00
|
|
|
* 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
|
2017-06-09 07:35:50 -07:00
|
|
|
* support library's {@code FragmentActivity}.</p>
|
|
|
|
|
*
|
2017-06-07 12:28:41 -07:00
|
|
|
* <h3>Usage:</h3>
|
2017-06-09 07:35:50 -07:00
|
|
|
* <p>To wire this class up to your activity, simply forward the events defined
|
2017-06-07 12:28:41 -07:00
|
|
|
* in {@link FlutterActivityEvents} from your activity to an instance of this
|
|
|
|
|
* class. Optionally, you can make your activity implement
|
2017-06-09 07:35:50 -07:00
|
|
|
* {@link PluginRegistry} and/or {@link io.flutter.view.FlutterView.Provider}
|
|
|
|
|
* and forward those methods to this class as well.</p>
|
2017-06-07 12:28:41 -07:00
|
|
|
*/
|
|
|
|
|
public final class FlutterActivityDelegate
|
|
|
|
|
implements FlutterActivityEvents,
|
|
|
|
|
FlutterView.Provider,
|
|
|
|
|
PluginRegistry {
|
2019-10-11 11:54:02 -07:00
|
|
|
private static final String SPLASH_SCREEN_META_DATA_KEY =
|
|
|
|
|
"io.flutter.app.android.SplashScreenUntilFirstFrame";
|
2017-08-14 15:44:52 -07:00
|
|
|
private static final String TAG = "FlutterActivityDelegate";
|
|
|
|
|
private static final LayoutParams matchParent =
|
|
|
|
|
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
|
|
|
|
|
2017-06-07 12:28:41 -07:00
|
|
|
/**
|
|
|
|
|
* Specifies the mechanism by which Flutter views are created during the
|
|
|
|
|
* operation of a {@code FlutterActivityDelegate}.
|
2017-06-09 07:35:50 -07:00
|
|
|
*
|
|
|
|
|
* <p>A delegate's view factory will be consulted during
|
2017-06-07 12:28:41 -07:00
|
|
|
* {@link #onCreate(Bundle)}. If it returns {@code null}, then the delegate
|
2017-06-09 07:35:50 -07:00
|
|
|
* will fall back to instantiating a new full-screen {@code FlutterView}.</p>
|
2017-11-13 13:56:48 -08:00
|
|
|
*
|
|
|
|
|
* <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.</p>
|
2017-06-07 12:28:41 -07:00
|
|
|
*/
|
|
|
|
|
public interface ViewFactory {
|
|
|
|
|
FlutterView createFlutterView(Context context);
|
2017-11-13 13:56:48 -08:00
|
|
|
FlutterNativeView createFlutterNativeView();
|
2018-05-15 12:05:54 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook for subclasses to indicate that the {@code FlutterNativeView}
|
|
|
|
|
* returned by {@link #createFlutterNativeView()} should not be destroyed
|
|
|
|
|
* when this activity is destroyed.
|
|
|
|
|
*/
|
|
|
|
|
boolean retainFlutterNativeView();
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final Activity activity;
|
|
|
|
|
private final ViewFactory viewFactory;
|
|
|
|
|
private FlutterView flutterView;
|
2017-08-14 15:44:52 -07:00
|
|
|
private View launchView;
|
2017-06-07 12:28:41 -07:00
|
|
|
|
|
|
|
|
public FlutterActivityDelegate(Activity activity, ViewFactory viewFactory) {
|
|
|
|
|
this.activity = Preconditions.checkNotNull(activity);
|
|
|
|
|
this.viewFactory = Preconditions.checkNotNull(viewFactory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public FlutterView getFlutterView() {
|
|
|
|
|
return flutterView;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-30 11:05:55 -08:00
|
|
|
// The implementation of PluginRegistry forwards to flutterView.
|
2017-06-07 12:28:41 -07:00
|
|
|
@Override
|
|
|
|
|
public boolean hasPlugin(String key) {
|
2017-11-30 11:05:55 -08:00
|
|
|
return flutterView.getPluginRegistry().hasPlugin(key);
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
public <T> T valuePublishedByPlugin(String pluginKey) {
|
2017-11-30 11:05:55 -08:00
|
|
|
return (T) flutterView.getPluginRegistry().valuePublishedByPlugin(pluginKey);
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Registrar registrarFor(String pluginKey) {
|
2017-11-30 11:05:55 -08:00
|
|
|
return flutterView.getPluginRegistry().registrarFor(pluginKey);
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2018-01-02 15:32:53 +01:00
|
|
|
public boolean onRequestPermissionsResult(
|
|
|
|
|
int requestCode, String[] permissions, int[] grantResults) {
|
|
|
|
|
return flutterView.getPluginRegistry().onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-07 12:28:41 -07:00
|
|
|
@Override
|
|
|
|
|
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
2017-11-30 11:05:55 -08:00
|
|
|
return flutterView.getPluginRegistry().onActivityResult(requestCode, resultCode, data);
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
|
|
|
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) {
|
2017-11-13 13:56:48 -08:00
|
|
|
FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
|
|
|
|
|
flutterView = new FlutterView(activity, null, nativeView);
|
2017-08-14 15:44:52 -07:00
|
|
|
flutterView.setLayoutParams(matchParent);
|
2017-08-23 12:57:06 -07:00
|
|
|
activity.setContentView(flutterView);
|
2017-08-14 15:44:52 -07:00
|
|
|
launchView = createLaunchView();
|
2017-08-23 12:57:06 -07:00
|
|
|
if (launchView != null) {
|
|
|
|
|
addLaunchView();
|
|
|
|
|
}
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
|
2018-08-07 12:42:22 -07:00
|
|
|
if (loadIntent(activity.getIntent())) {
|
2017-06-07 12:28:41 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2018-12-13 10:17:37 -08:00
|
|
|
|
2019-08-01 18:13:27 -07:00
|
|
|
String appBundlePath = FlutterMain.findAppBundlePath();
|
2019-01-08 12:44:07 -08:00
|
|
|
if (appBundlePath != null) {
|
|
|
|
|
runBundle(appBundlePath);
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onNewIntent(Intent intent) {
|
2017-06-08 08:13:52 -07:00
|
|
|
// 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)) {
|
2017-11-30 11:05:55 -08:00
|
|
|
flutterView.getPluginRegistry().onNewIntent(intent);
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-08 08:13:52 -07:00
|
|
|
private boolean isDebuggable() {
|
|
|
|
|
return (activity.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-07 12:28:41 -07:00
|
|
|
@Override
|
|
|
|
|
public void onPause() {
|
2017-11-13 13:56:48 -08:00
|
|
|
Application app = (Application) activity.getApplicationContext();
|
|
|
|
|
if (app instanceof FlutterApplication) {
|
|
|
|
|
FlutterApplication flutterApp = (FlutterApplication) app;
|
2018-06-01 10:16:50 -07:00
|
|
|
if (activity.equals(flutterApp.getCurrentActivity())) {
|
2017-11-13 13:56:48 -08:00
|
|
|
flutterApp.setCurrentActivity(null);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-06-07 12:28:41 -07:00
|
|
|
if (flutterView != null) {
|
|
|
|
|
flutterView.onPause();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-04 14:46:57 -07:00
|
|
|
@Override
|
|
|
|
|
public void onStart() {
|
|
|
|
|
if (flutterView != null) {
|
|
|
|
|
flutterView.onStart();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-07 12:28:41 -07:00
|
|
|
@Override
|
|
|
|
|
public void onResume() {
|
2017-11-13 13:56:48 -08:00
|
|
|
Application app = (Application) activity.getApplicationContext();
|
|
|
|
|
if (app instanceof FlutterApplication) {
|
|
|
|
|
FlutterApplication flutterApp = (FlutterApplication) app;
|
|
|
|
|
flutterApp.setCurrentActivity(activity);
|
|
|
|
|
}
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
|
2018-04-13 17:08:00 -07:00
|
|
|
@Override
|
|
|
|
|
public void onStop() {
|
|
|
|
|
flutterView.onStop();
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-07 12:28:41 -07:00
|
|
|
@Override
|
|
|
|
|
public void onPostResume() {
|
|
|
|
|
if (flutterView != null) {
|
|
|
|
|
flutterView.onPostResume();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onDestroy() {
|
2017-11-13 13:56:48 -08:00
|
|
|
Application app = (Application) activity.getApplicationContext();
|
|
|
|
|
if (app instanceof FlutterApplication) {
|
|
|
|
|
FlutterApplication flutterApp = (FlutterApplication) app;
|
2018-06-01 10:16:50 -07:00
|
|
|
if (activity.equals(flutterApp.getCurrentActivity())) {
|
2017-11-13 13:56:48 -08:00
|
|
|
flutterApp.setCurrentActivity(null);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-06-07 12:28:41 -07:00
|
|
|
if (flutterView != null) {
|
2017-11-30 11:05:55 -08:00
|
|
|
final boolean detach =
|
|
|
|
|
flutterView.getPluginRegistry().onViewDestroy(flutterView.getFlutterNativeView());
|
2018-05-15 12:05:54 -07:00
|
|
|
if (detach || viewFactory.retainFlutterNativeView()) {
|
2017-11-13 13:56:48 -08:00
|
|
|
// Detach, but do not destroy the FlutterView if a plugin
|
|
|
|
|
// expressed interest in its FlutterNativeView.
|
|
|
|
|
flutterView.detach();
|
2017-11-30 11:05:55 -08:00
|
|
|
} else {
|
|
|
|
|
flutterView.destroy();
|
2017-11-13 13:56:48 -08:00
|
|
|
}
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onBackPressed() {
|
|
|
|
|
if (flutterView != null) {
|
|
|
|
|
flutterView.popRoute();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onUserLeaveHint() {
|
2017-11-30 11:05:55 -08:00
|
|
|
flutterView.getPluginRegistry().onUserLeaveHint();
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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
|
2019-02-04 19:30:15 -08:00
|
|
|
public void onConfigurationChanged(Configuration newConfig) {}
|
2017-06-07 12:28:41 -07:00
|
|
|
|
|
|
|
|
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.
|
2018-12-17 16:35:10 -08:00
|
|
|
ArrayList<String> args = new ArrayList<>();
|
2017-06-07 12:28:41 -07:00
|
|
|
if (intent.getBooleanExtra("trace-startup", false)) {
|
|
|
|
|
args.add("--trace-startup");
|
|
|
|
|
}
|
|
|
|
|
if (intent.getBooleanExtra("start-paused", false)) {
|
|
|
|
|
args.add("--start-paused");
|
|
|
|
|
}
|
2019-04-18 14:17:25 -07:00
|
|
|
if (intent.getBooleanExtra("disable-service-auth-codes", false)) {
|
|
|
|
|
args.add("--disable-service-auth-codes");
|
2019-04-10 14:38:58 -07:00
|
|
|
}
|
2017-06-07 12:28:41 -07:00
|
|
|
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");
|
|
|
|
|
}
|
2018-02-20 17:42:11 -08:00
|
|
|
if (intent.getBooleanExtra("skia-deterministic-rendering", false)) {
|
|
|
|
|
args.add("--skia-deterministic-rendering");
|
|
|
|
|
}
|
2017-09-13 10:18:45 -07:00
|
|
|
if (intent.getBooleanExtra("trace-skia", false)) {
|
|
|
|
|
args.add("--trace-skia");
|
|
|
|
|
}
|
2019-01-28 17:39:31 -08:00
|
|
|
if (intent.getBooleanExtra("trace-systrace", false)) {
|
|
|
|
|
args.add("--trace-systrace");
|
|
|
|
|
}
|
2019-03-14 12:58:09 -07:00
|
|
|
if (intent.getBooleanExtra("dump-skp-on-shader-compilation", false)) {
|
|
|
|
|
args.add("--dump-skp-on-shader-compilation");
|
|
|
|
|
}
|
2019-10-08 11:51:28 -07:00
|
|
|
if (intent.getBooleanExtra("cache-sksl", false)) {
|
|
|
|
|
args.add("--cache-sksl");
|
|
|
|
|
}
|
2018-06-01 17:34:45 -07:00
|
|
|
if (intent.getBooleanExtra("verbose-logging", false)) {
|
|
|
|
|
args.add("--verbose-logging");
|
|
|
|
|
}
|
2019-06-19 13:13:13 -07:00
|
|
|
final int observatoryPort = intent.getIntExtra("observatory-port", 0);
|
|
|
|
|
if (observatoryPort > 0) {
|
|
|
|
|
args.add("--observatory-port=" + Integer.toString(observatoryPort));
|
|
|
|
|
}
|
|
|
|
|
if (intent.getBooleanExtra("disable-service-auth-codes", false)) {
|
|
|
|
|
args.add("--disable-service-auth-codes");
|
|
|
|
|
}
|
2019-06-04 14:51:29 -07:00
|
|
|
// NOTE: all flags provided with this argument are subject to filtering
|
|
|
|
|
// based on a whitelist in shell/common/switches.cc. If any flag provided
|
|
|
|
|
// is not present in the whitelist, the process will immediately
|
|
|
|
|
// terminate.
|
|
|
|
|
if (intent.hasExtra("dart-flags")) {
|
|
|
|
|
args.add("--dart-flags=" + intent.getStringExtra("dart-flags"));
|
|
|
|
|
}
|
2017-06-07 12:28:41 -07:00
|
|
|
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) {
|
2019-01-08 12:44:07 -08:00
|
|
|
// Fall back to the installation path if no bundle path was specified.
|
2019-08-01 18:13:27 -07:00
|
|
|
appBundlePath = FlutterMain.findAppBundlePath();
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
|
|
|
|
if (route != null) {
|
2017-06-19 15:25:47 -07:00
|
|
|
flutterView.setInitialRoute(route);
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|
2019-01-08 12:44:07 -08:00
|
|
|
|
|
|
|
|
runBundle(appBundlePath);
|
2017-06-07 12:28:41 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 12:44:07 -08:00
|
|
|
private void runBundle(String appBundlePath) {
|
|
|
|
|
if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
|
|
|
|
|
FlutterRunArguments args = new FlutterRunArguments();
|
2019-06-25 14:17:50 -07:00
|
|
|
args.bundlePath = appBundlePath;
|
2019-01-08 12:44:07 -08:00
|
|
|
args.entrypoint = "main";
|
|
|
|
|
flutterView.runFromBundle(args);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-14 15:44:52 -07:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Returns null if no {@code windowBackground} is set for the activity.
|
|
|
|
|
*/
|
|
|
|
|
private View createLaunchView() {
|
2017-08-15 11:01:12 -07:00
|
|
|
if (!showSplashScreenUntilFirstFrame()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2017-08-14 15:44:52 -07:00
|
|
|
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}.
|
|
|
|
|
*
|
2017-08-23 12:57:06 -07:00
|
|
|
* {@code android:windowBackground} is specifically reused instead of a other attributes
|
2017-08-14 15:44:52 -07:00
|
|
|
* because the Android framework can display it fast enough when launching the app as opposed
|
|
|
|
|
* to anything defined in the Activity subclass.
|
|
|
|
|
*
|
|
|
|
|
* Returns null if no {@code windowBackground} is set for the activity.
|
|
|
|
|
*/
|
2017-08-21 17:25:31 -07:00
|
|
|
@SuppressWarnings("deprecation")
|
2017-08-14 15:44:52 -07:00
|
|
|
private Drawable getLaunchScreenDrawableFromActivityTheme() {
|
|
|
|
|
TypedValue typedValue = new TypedValue();
|
|
|
|
|
if (!activity.getTheme().resolveAttribute(
|
|
|
|
|
android.R.attr.windowBackground,
|
|
|
|
|
typedValue,
|
2019-02-04 19:30:15 -08:00
|
|
|
true)) {
|
2017-08-14 15:44:52 -07:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (typedValue.resourceId == 0) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
try {
|
2017-08-21 17:25:31 -07:00
|
|
|
return activity.getResources().getDrawable(typedValue.resourceId);
|
2017-08-14 15:44:52 -07:00
|
|
|
} catch (NotFoundException e) {
|
2017-08-15 11:01:12 -07:00
|
|
|
Log.e(TAG, "Referenced launch screen windowBackground resource does not exist");
|
2017-08-14 15:44:52 -07:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-23 12:57:06 -07:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2017-08-15 11:01:12 -07:00
|
|
|
private Boolean showSplashScreenUntilFirstFrame() {
|
|
|
|
|
try {
|
|
|
|
|
ActivityInfo activityInfo = activity.getPackageManager().getActivityInfo(
|
|
|
|
|
activity.getComponentName(),
|
|
|
|
|
PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES);
|
|
|
|
|
Bundle metadata = activityInfo.metaData;
|
|
|
|
|
return metadata != null && metadata.getBoolean(SPLASH_SCREEN_META_DATA_KEY);
|
|
|
|
|
} catch (NameNotFoundException e) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-14 15:44:52 -07:00
|
|
|
/**
|
2017-08-23 12:57:06 -07:00
|
|
|
* Show and then automatically animate out the launch view.
|
2017-08-14 15:44:52 -07:00
|
|
|
*
|
2017-08-23 12:57:06 -07:00
|
|
|
* 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}.
|
2017-08-14 15:44:52 -07:00
|
|
|
*
|
2017-08-23 12:57:06 -07:00
|
|
|
* Fade it out and remove it when the {@link FlutterView} renders its first frame.
|
2017-08-14 15:44:52 -07:00
|
|
|
*/
|
2017-08-23 12:57:06 -07:00
|
|
|
private void addLaunchView() {
|
2017-08-14 15:44:52 -07:00
|
|
|
if (launchView == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-23 12:57:06 -07:00
|
|
|
activity.addContentView(launchView, matchParent);
|
2017-08-14 15:44:52 -07:00
|
|
|
flutterView.addFirstFrameListener(new FlutterView.FirstFrameListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onFirstFrame() {
|
2017-08-23 12:57:06 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2017-08-14 15:44:52 -07:00
|
|
|
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);
|
|
|
|
|
}
|
2017-06-07 12:28:41 -07:00
|
|
|
}
|