Commit d9ac3683 authored by yangwu.jia's avatar yangwu.jia

Boost 1.9升级

parent 570f4acb
...@@ -26,6 +26,7 @@ android { ...@@ -26,6 +26,7 @@ android {
buildToolsVersion '27.0.3' buildToolsVersion '27.0.3'
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
lintOptions { lintOptions {
...@@ -34,9 +35,11 @@ android { ...@@ -34,9 +35,11 @@ android {
} }
dependencies { dependencies {
implementation 'com.alibaba:fastjson:1.2.41' implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:26.1.0' implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support:support-v4:28.0.0'
implementation 'android.arch.lifecycle:common-java8:1.1.1'
} }
ext { ext {
......
...@@ -28,12 +28,13 @@ import android.content.Context; ...@@ -28,12 +28,13 @@ import android.content.Context;
import com.idlefish.flutterboost.interfaces.IFlutterEngineProvider; import com.idlefish.flutterboost.interfaces.IFlutterEngineProvider;
import com.idlefish.flutterboost.interfaces.IStateListener; import com.idlefish.flutterboost.interfaces.IStateListener;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterShellArgs; import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.view.FlutterMain; import io.flutter.view.FlutterMain;
public class BoostEngineProvider implements IFlutterEngineProvider { public class BoostEngineProvider implements IFlutterEngineProvider {
private BoostFlutterEngine mEngine = null; private FlutterEngine mEngine = null;
public BoostEngineProvider() {} public BoostEngineProvider() {}
...@@ -43,7 +44,7 @@ public class BoostEngineProvider implements IFlutterEngineProvider { ...@@ -43,7 +44,7 @@ public class BoostEngineProvider implements IFlutterEngineProvider {
} }
@Override @Override
public BoostFlutterEngine provideEngine(Context context) { public FlutterEngine provideEngine(Context context) {
Utils.assertCallOnMainThread(); Utils.assertCallOnMainThread();
if (mEngine == null) { if (mEngine == null) {
...@@ -51,25 +52,25 @@ public class BoostEngineProvider implements IFlutterEngineProvider { ...@@ -51,25 +52,25 @@ public class BoostEngineProvider implements IFlutterEngineProvider {
FlutterMain.ensureInitializationComplete( FlutterMain.ensureInitializationComplete(
context.getApplicationContext(), flutterShellArgs.toArray()); context.getApplicationContext(), flutterShellArgs.toArray());
mEngine = createEngine(context.getApplicationContext()); mEngine = new FlutterEngine(context.getApplicationContext());
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener; // final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if(stateListener != null) { // if(stateListener != null) {
stateListener.onEngineCreated(mEngine); // stateListener.onEngineCreated(mEngine);
} // }
} }
return mEngine; return mEngine;
} }
@Override @Override
public BoostFlutterEngine tryGetEngine() { public FlutterEngine tryGetEngine() {
return mEngine; return mEngine;
} }
public static void assertEngineRunning(){ public static void assertEngineRunning(){
final BoostFlutterEngine engine = FlutterBoost.singleton().engineProvider().tryGetEngine(); final FlutterEngine engine = NewFlutterBoost.instance().engineProvider().tryGetEngine();
if(engine == null || !engine.isRunning()) { // if(engine == null || !engine.isRunning()) {
throw new RuntimeException("engine is not running yet!"); // throw new RuntimeException("engine is not running yet!");
} // }
} }
} }
...@@ -30,122 +30,114 @@ import io.flutter.view.FlutterView; ...@@ -30,122 +30,114 @@ import io.flutter.view.FlutterView;
import io.flutter.view.TextureRegistry; import io.flutter.view.TextureRegistry;
public class BoostFlutterEngine extends FlutterEngine { public class BoostFlutterEngine extends FlutterEngine {
protected final Context mContext;
protected final BoostPluginRegistry mBoostPluginRegistry;
protected final DartExecutor.DartEntrypoint mEntrypoint;
protected final String mInitRoute;
private final FakeRender mFakeRender;
protected WeakReference<Activity> mCurrentActivityRef; protected WeakReference<Activity> mCurrentActivityRef;
public BoostFlutterEngine(@NonNull Context context) { public BoostFlutterEngine(@NonNull Context context) {
this(context, null, null);
}
public BoostFlutterEngine(@NonNull Context context, DartExecutor.DartEntrypoint entrypoint, String initRoute) {
super(context); super(context);
mContext = context.getApplicationContext();
mBoostPluginRegistry = new BoostPluginRegistry(this, context);
if (entrypoint != null) {
mEntrypoint = entrypoint;
} else {
mEntrypoint = defaultDartEntrypoint(context);
}
if (initRoute != null) {
mInitRoute = initRoute;
} else {
mInitRoute = defaultInitialRoute(context);
}
FlutterJNI flutterJNI = null;
try {
Field field = FlutterEngine.class.getDeclaredField("flutterJNI");
field.setAccessible(true);
flutterJNI = (FlutterJNI) field.get(this);
} catch (Throwable t) {
try {
for(Field field:FlutterEngine.class.getDeclaredFields()) {
field.setAccessible(true);
Object o = field.get(this);
if(o instanceof FlutterJNI) {
flutterJNI = (FlutterJNI)o;
}
}
if(flutterJNI == null) {
throw new RuntimeException("FlutterJNI not found");
} }
}catch (Throwable it){
Debuger.exception(it);
}
}
mFakeRender = new FakeRender(flutterJNI);
}
public void startRun(@Nullable Activity activity) {
mCurrentActivityRef = new WeakReference<>(activity);
if (!getDartExecutor().isExecutingDart()) { // public BoostFlutterEngine(@NonNull Context context, DartExecutor.DartEntrypoint entrypoint, String initRoute) {
// super(context);
Debuger.log("engine start running..."); // mContext = context.getApplicationContext();
//// mBoostPluginRegistry = new BoostPluginRegistry(this, context);
getNavigationChannel().setInitialRoute(mInitRoute); //
getDartExecutor().executeDartEntrypoint(mEntrypoint); // if (entrypoint != null) {
// mEntrypoint = entrypoint;
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener; // } else {
if (stateListener != null) { // mEntrypoint = defaultDartEntrypoint(context);
stateListener.onEngineStarted(this); // }
} //
// if (initRoute != null) {
FlutterBoost.singleton().platform().registerPlugins(mBoostPluginRegistry); // mInitRoute = initRoute;
// } else {
if(activity != null) { // mInitRoute = defaultInitialRoute(context);
FlutterRenderer.ViewportMetrics metrics = new FlutterRenderer.ViewportMetrics(); // }
metrics.devicePixelRatio = activity.getResources().getDisplayMetrics().density; //
final View decor = activity.getWindow().getDecorView(); // FlutterJNI flutterJNI = null;
if(decor != null) { // try {
metrics.width = decor.getWidth(); // Field field = FlutterEngine.class.getDeclaredField("flutterJNI");
metrics.height = decor.getHeight(); // field.setAccessible(true);
} //
// flutterJNI = (FlutterJNI) field.get(this);
if (metrics.width <= 0 || metrics.height <= 0) { // } catch (Throwable t) {
metrics.width = Utils.getMetricsWidth(activity); // try {
metrics.height = Utils.getMetricsHeight(activity); // for(Field field:FlutterEngine.class.getDeclaredFields()) {
} // field.setAccessible(true);
// Object o = field.get(this);
metrics.paddingTop = Utils.getStatusBarHeight(activity); //
metrics.paddingRight = 0; // if(o instanceof FlutterJNI) {
metrics.paddingBottom = 0; // flutterJNI = (FlutterJNI)o;
metrics.paddingLeft = 0; // }
metrics.viewInsetTop = 0; // }
metrics.viewInsetRight = 0; //
metrics.viewInsetBottom = 0; // if(flutterJNI == null) {
metrics.viewInsetLeft = 0; // throw new RuntimeException("FlutterJNI not found");
// }
getRenderer().setViewportMetrics(metrics); // }catch (Throwable it){
} // Debuger.exception(it);
} // }
} // }
// mFakeRender = new FakeRender(flutterJNI);
// }
// public void startRun(@Nullable Activity activity) {
// mCurrentActivityRef = new WeakReference<>(activity);
//
// if (!getDartExecutor().isExecutingDart()) {
//
// Debuger.log("engine start running...");
//
// getNavigationChannel().setInitialRoute(mInitRoute);
// getDartExecutor().executeDartEntrypoint(mEntrypoint);
//
// final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
// if (stateListener != null) {
// stateListener.onEngineStarted(this);
// }
//
//// FlutterBoost.singleton().platform().registerPlugins(mBoostPluginRegistry);
//
// if(activity != null) {
// FlutterRenderer.ViewportMetrics metrics = new FlutterRenderer.ViewportMetrics();
// metrics.devicePixelRatio = activity.getResources().getDisplayMetrics().density;
// final View decor = activity.getWindow().getDecorView();
// if(decor != null) {
// metrics.width = decor.getWidth();
// metrics.height = decor.getHeight();
// }
//
// if (metrics.width <= 0 || metrics.height <= 0) {
// metrics.width = Utils.getMetricsWidth(activity);
// metrics.height = Utils.getMetricsHeight(activity);
// }
//
// metrics.paddingTop = Utils.getStatusBarHeight(activity);
// metrics.paddingRight = 0;
// metrics.paddingBottom = 0;
// metrics.paddingLeft = 0;
// metrics.viewInsetTop = 0;
// metrics.viewInsetRight = 0;
// metrics.viewInsetBottom = 0;
// metrics.viewInsetLeft = 0;
//
// getRenderer().setViewportMetrics(metrics);
// }
// }
// }
protected DartExecutor.DartEntrypoint defaultDartEntrypoint(Context context) { protected DartExecutor.DartEntrypoint defaultDartEntrypoint(Context context) {
return new DartExecutor.DartEntrypoint( return DartExecutor.DartEntrypoint.createDefault();
context.getResources().getAssets(),
FlutterMain.findAppBundlePath(context),
"main");
} }
protected String defaultInitialRoute(Context context) { protected String defaultInitialRoute(Context context) {
return "/"; return "/";
} }
public BoostPluginRegistry getBoostPluginRegistry() { // public BoostPluginRegistry getBoostPluginRegistry() {
return mBoostPluginRegistry; // return mBoostPluginRegistry;
} // }
public boolean isRunning() { public boolean isRunning() {
return getDartExecutor().isExecutingDart(); return getDartExecutor().isExecutingDart();
...@@ -165,14 +157,14 @@ public class BoostFlutterEngine extends FlutterEngine { ...@@ -165,14 +157,14 @@ public class BoostFlutterEngine extends FlutterEngine {
} }
if (hit) { if (hit) {
return mFakeRender; return null;
} else { } else {
return super.getRenderer(); return super.getRenderer();
} }
} }
public class BoostPluginRegistry extends FlutterPluginRegistry { public class BoostPluginRegistry extends FlutterPluginRegistry {
private final FlutterEngine mEngine; private FlutterEngine mEngine;
public BoostPluginRegistry(FlutterEngine engine, Context context) { public BoostPluginRegistry(FlutterEngine engine, Context context) {
super(engine, context); super(engine, context);
......
...@@ -136,7 +136,7 @@ public class BoostFlutterView extends FrameLayout { ...@@ -136,7 +136,7 @@ public class BoostFlutterView extends FrameLayout {
mFlutterView.addOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener); mFlutterView.addOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener);
mFlutterEngine.startRun((Activity)getContext()); // mFlutterEngine.startRun((Activity)getContext());
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener; final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if(stateListener != null) { if(stateListener != null) {
...@@ -185,7 +185,8 @@ public class BoostFlutterView extends FrameLayout { ...@@ -185,7 +185,8 @@ public class BoostFlutterView extends FrameLayout {
} }
protected BoostFlutterEngine createFlutterEngine(Context context) { protected BoostFlutterEngine createFlutterEngine(Context context) {
return FlutterBoost.singleton().engineProvider().provideEngine(context); // return FlutterBoost.singleton().engineProvider().provideEngine(context);
return null;
} }
public void addFirstFrameRendered(OnFirstFrameRenderedListener listener) { public void addFirstFrameRendered(OnFirstFrameRenderedListener listener) {
...@@ -310,7 +311,7 @@ public class BoostFlutterView extends FrameLayout { ...@@ -310,7 +311,7 @@ public class BoostFlutterView extends FrameLayout {
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (mFlutterEngine != null) { if (mFlutterEngine != null) {
mFlutterEngine.getPluginRegistry().onRequestPermissionsResult(requestCode, permissions, grantResults); // mFlutterEngine.getPluginRegistry().onRequestPermissionsResult(requestCode, permissions, grantResults);
} else { } else {
Debuger.log("onRequestPermissionResult() invoked before BoostFlutterView was attached to an Activity."); Debuger.log("onRequestPermissionResult() invoked before BoostFlutterView was attached to an Activity.");
} }
...@@ -319,7 +320,7 @@ public class BoostFlutterView extends FrameLayout { ...@@ -319,7 +320,7 @@ public class BoostFlutterView extends FrameLayout {
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
if (mFlutterEngine != null) { if (mFlutterEngine != null) {
mFlutterEngine.getPluginRegistry().onNewIntent(intent); // mFlutterEngine.getPluginRegistry().onNewIntent(intent);
} else { } else {
Debuger.log("onNewIntent() invoked before BoostFlutterView was attached to an Activity."); Debuger.log("onNewIntent() invoked before BoostFlutterView was attached to an Activity.");
} }
...@@ -328,7 +329,7 @@ public class BoostFlutterView extends FrameLayout { ...@@ -328,7 +329,7 @@ public class BoostFlutterView extends FrameLayout {
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mFlutterEngine != null) { if (mFlutterEngine != null) {
mFlutterEngine.getPluginRegistry().onActivityResult(requestCode, resultCode, data); // mFlutterEngine.getPluginRegistry().onActivityResult(requestCode, resultCode, data);
} else { } else {
Debuger.log("onActivityResult() invoked before BoostFlutterView was attached to an Activity."); Debuger.log("onActivityResult() invoked before BoostFlutterView was attached to an Activity.");
} }
...@@ -336,7 +337,7 @@ public class BoostFlutterView extends FrameLayout { ...@@ -336,7 +337,7 @@ public class BoostFlutterView extends FrameLayout {
public void onUserLeaveHint() { public void onUserLeaveHint() {
if (mFlutterEngine != null) { if (mFlutterEngine != null) {
mFlutterEngine.getPluginRegistry().onUserLeaveHint(); // mFlutterEngine.getPluginRegistry().onUserLeaveHint();
} else { } else {
Debuger.log("onUserLeaveHint() invoked before BoostFlutterView was attached to an Activity."); Debuger.log("onUserLeaveHint() invoked before BoostFlutterView was attached to an Activity.");
} }
......
package com.idlefish.flutterboost;
import android.app.Activity;
import android.content.Context;
import com.idlefish.flutterboost.interfaces.IContainerRecord;
import io.flutter.app.FlutterPluginRegistry;
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.view.FlutterView;
import io.flutter.view.TextureRegistry;
import java.lang.ref.WeakReference;
public class BoostPluginRegistry extends FlutterPluginRegistry {
protected WeakReference<Activity> mCurrentActivityRef;
private FlutterEngine mEngine;
public BoostPluginRegistry(FlutterEngine engine, Context context) {
super(engine, context);
mEngine = engine;
}
public PluginRegistry.Registrar registrarFor(String pluginKey) {
return new BoostRegistrar(mEngine, super.registrarFor(pluginKey));
}
public class BoostRegistrar implements PluginRegistry.Registrar {
private final PluginRegistry.Registrar mRegistrar;
private final FlutterEngine mEngine;
BoostRegistrar(FlutterEngine engine, PluginRegistry.Registrar registrar) {
mRegistrar = registrar;
mEngine = engine;
}
@Override
public Activity activity() {
Activity activity;
IContainerRecord record;
record = FlutterBoost.singleton().containerManager().getCurrentTopRecord();
if (record == null) {
record = FlutterBoost.singleton().containerManager().getLastGenerateRecord();
}
if (record == null) {
activity = FlutterBoost.singleton().currentActivity();
} else {
activity = record.getContainer().getContextActivity();
}
if (activity == null && mCurrentActivityRef != null) {
activity = mCurrentActivityRef.get();
}
if (activity == null) {
throw new RuntimeException("current has no valid Activity yet");
}
return activity;
}
@Override
public Context context() {
return mRegistrar.context();
}
@Override
public Context activeContext() {
return mRegistrar.activeContext();
}
@Override
public BinaryMessenger messenger() {
return mEngine.getDartExecutor();
}
@Override
public TextureRegistry textures() {
return mEngine.getRenderer();
}
@Override
public PlatformViewRegistry platformViewRegistry() {
return mRegistrar.platformViewRegistry();
}
@Override
public FlutterView view() {
throw new RuntimeException("should not use!!!");
}
@Override
public String lookupKeyForAsset(String s) {
return mRegistrar.lookupKeyForAsset(s);
}
@Override
public String lookupKeyForAsset(String s, String s1) {
return mRegistrar.lookupKeyForAsset(s, s1);
}
@Override
public PluginRegistry.Registrar publish(Object o) {
return mRegistrar.publish(o);
}
@Override
public PluginRegistry.Registrar addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener requestPermissionsResultListener) {
return mRegistrar.addRequestPermissionsResultListener(requestPermissionsResultListener);
}
@Override
public PluginRegistry.Registrar addActivityResultListener(PluginRegistry.ActivityResultListener activityResultListener) {
return mRegistrar.addActivityResultListener(activityResultListener);
}
@Override
public PluginRegistry.Registrar addNewIntentListener(PluginRegistry.NewIntentListener newIntentListener) {
return mRegistrar.addNewIntentListener(newIntentListener);
}
@Override
public PluginRegistry.Registrar addUserLeaveHintListener(PluginRegistry.UserLeaveHintListener userLeaveHintListener) {
return mRegistrar.addUserLeaveHintListener(userLeaveHintListener);
}
@Override
public PluginRegistry.Registrar addViewDestroyListener(PluginRegistry.ViewDestroyListener viewDestroyListener) {
return mRegistrar.addViewDestroyListener(viewDestroyListener);
}
}
}
...@@ -76,7 +76,7 @@ public class ContainerRecord implements IContainerRecord { ...@@ -76,7 +76,7 @@ public class ContainerRecord implements IContainerRecord {
} }
mState = STATE_CREATED; mState = STATE_CREATED;
mContainer.getBoostFlutterView().onResume(); // mContainer.getBoostFlutterView().onResume();
mProxy.create(); mProxy.create();
} }
...@@ -129,15 +129,15 @@ public class ContainerRecord implements IContainerRecord { ...@@ -129,15 +129,15 @@ public class ContainerRecord implements IContainerRecord {
mProxy.destroy(); mProxy.destroy();
mContainer.getBoostFlutterView().onDestroy(); // mContainer.getBoostFlutterView().onDestroy();
mManager.removeRecord(this); mManager.removeRecord(this);
mManager.setContainerResult(this,-1,-1,null); mManager.setContainerResult(this,-1,-1,null);
if (!mManager.hasContainerAppear()) { if (!mManager.hasContainerAppear()) {
mContainer.getBoostFlutterView().onPause(); // mContainer.getBoostFlutterView().onPause();
mContainer.getBoostFlutterView().onStop(); // mContainer.getBoostFlutterView().onStop();
} }
} }
...@@ -154,45 +154,80 @@ public class ContainerRecord implements IContainerRecord { ...@@ -154,45 +154,80 @@ public class ContainerRecord implements IContainerRecord {
map.put("name", mContainer.getContainerUrl()); map.put("name", mContainer.getContainerUrl());
map.put("uniqueId", mUniqueId); map.put("uniqueId", mUniqueId);
FlutterBoost.singleton().channel().sendEvent("lifecycle", map); NewFlutterBoost.instance().channel().sendEvent("lifecycle", map);
mContainer.getBoostFlutterView().onBackPressed(); // mContainer.getBoostFlutterView().onBackPressed();
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
mContainer.getBoostFlutterView().onRequestPermissionsResult(requestCode, permissions, grantResults);
} }
@Override @Override
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
mContainer.getBoostFlutterView().onNewIntent(intent);
} }
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
mContainer.getBoostFlutterView().onActivityResult(requestCode, resultCode, data);
} }
@Override @Override
public void onContainerResult(int requestCode, int resultCode, Map<String, Object> result) { public void onContainerResult(int requestCode, int resultCode, Map<String, Object> result) {
mManager.setContainerResult(this, requestCode,resultCode, result);
} }
@Override @Override
public void onUserLeaveHint() { public void onUserLeaveHint() {
mContainer.getBoostFlutterView().onUserLeaveHint();
} }
@Override @Override
public void onTrimMemory(int level) { public void onTrimMemory(int level) {
mContainer.getBoostFlutterView().onTrimMemory(level);
} }
@Override @Override
public void onLowMemory() { public void onLowMemory() {
mContainer.getBoostFlutterView().onLowMemory();
} }
//
// @Override
// public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// mContainer.getBoostFlutterView().onRequestPermissionsResult(requestCode, permissions, grantResults);
// }
//
// @Override
// public void onNewIntent(Intent intent) {
// mContainer.getBoostFlutterView().onNewIntent(intent);
// }
//
// @Override
// public void onActivityResult(int requestCode, int resultCode, Intent data) {
// mContainer.getBoostFlutterView().onActivityResult(requestCode, resultCode, data);
// }
//
// @Override
// public void onContainerResult(int requestCode, int resultCode, Map<String, Object> result) {
// mManager.setContainerResult(this, requestCode,resultCode, result);
// }
//
// @Override
// public void onUserLeaveHint() {
// mContainer.getBoostFlutterView().onUserLeaveHint();
// }
//
// @Override
// public void onTrimMemory(int level) {
// mContainer.getBoostFlutterView().onTrimMemory(level);
// }
//
// @Override
// public void onLowMemory() {
// mContainer.getBoostFlutterView().onLowMemory();
// }
private class MethodChannelProxy { private class MethodChannelProxy {
...@@ -252,7 +287,7 @@ public class ContainerRecord implements IContainerRecord { ...@@ -252,7 +287,7 @@ public class ContainerRecord implements IContainerRecord {
args.put("pageName", url); args.put("pageName", url);
args.put("params", params); args.put("params", params);
args.put("uniqueId", uniqueId); args.put("uniqueId", uniqueId);
FlutterBoost.singleton().channel().invokeMethod(method, args); NewFlutterBoost.instance().channel().invokeMethod(method, args);
} }
public void invokeChannelUnsafe(String method, String url, Map params, String uniqueId) { public void invokeChannelUnsafe(String method, String url, Map params, String uniqueId) {
...@@ -260,7 +295,7 @@ public class ContainerRecord implements IContainerRecord { ...@@ -260,7 +295,7 @@ public class ContainerRecord implements IContainerRecord {
args.put("pageName", url); args.put("pageName", url);
args.put("params", params); args.put("params", params);
args.put("uniqueId", uniqueId); args.put("uniqueId", uniqueId);
FlutterBoost.singleton().channel().invokeMethodUnsafe(method, args); NewFlutterBoost.instance().channel().invokeMethodUnsafe(method, args);
} }
} }
......
...@@ -51,11 +51,11 @@ public class FlutterBoost { ...@@ -51,11 +51,11 @@ public class FlutterBoost {
sInstance = new FlutterBoost(platform); sInstance = new FlutterBoost(platform);
} }
if (platform.whenEngineStart() == IPlatform.IMMEDIATELY) { // if (platform.whenEngineStart() == IPlatform.IMMEDIATELY) {
sInstance.mEngineProvider // sInstance.mEngineProvider
.provideEngine(platform.getApplication()) // .provideEngine(platform.getApplication())
.startRun(null); // .startRun(null);
} // }
} }
public static FlutterBoost singleton() { public static FlutterBoost singleton() {
...@@ -84,9 +84,9 @@ public class FlutterBoost { ...@@ -84,9 +84,9 @@ public class FlutterBoost {
mEngineProvider = provider; mEngineProvider = provider;
platform.getApplication().registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks()); platform.getApplication().registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks());
BoostChannel.addActionAfterRegistered(new BoostChannel.ActionAfterRegistered() { FlutterBoostPlugin.addActionAfterRegistered(new FlutterBoostPlugin.ActionAfterRegistered() {
@Override @Override
public void onChannelRegistered(BoostChannel channel) { public void onChannelRegistered(FlutterBoostPlugin channel) {
channel.addMethodCallHandler(new BoostMethodHandler()); channel.addMethodCallHandler(new BoostMethodHandler());
} }
}); });
...@@ -104,8 +104,8 @@ public class FlutterBoost { ...@@ -104,8 +104,8 @@ public class FlutterBoost {
return sInstance.mPlatform; return sInstance.mPlatform;
} }
public BoostChannel channel() { public FlutterBoostPlugin channel() {
return BoostChannel.singleton(); return FlutterBoostPlugin.singleton();
} }
public Activity currentActivity() { public Activity currentActivity() {
...@@ -124,9 +124,9 @@ public class FlutterBoost { ...@@ -124,9 +124,9 @@ public class FlutterBoost {
@Override @Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (platform().whenEngineStart() == IPlatform.ANY_ACTIVITY_CREATED) { if (platform().whenEngineStart() == IPlatform.ANY_ACTIVITY_CREATED) {
sInstance.mEngineProvider // sInstance.mEngineProvider
.provideEngine(activity) // .provideEngine(activity)
.startRun(activity); // .startRun(activity);
} }
} }
......
...@@ -2,6 +2,7 @@ package com.idlefish.flutterboost; ...@@ -2,6 +2,7 @@ package com.idlefish.flutterboost;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.idlefish.flutterboost.interfaces.IStateListener; import com.idlefish.flutterboost.interfaces.IStateListener;
import java.io.Serializable; import java.io.Serializable;
...@@ -14,42 +15,42 @@ import io.flutter.plugin.common.MethodCall; ...@@ -14,42 +15,42 @@ import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugin.common.PluginRegistry;
public class BoostChannel { public class FlutterBoostPlugin {
private static BoostChannel sInstance; private static FlutterBoostPlugin sInstance;
private final MethodChannel mMethodChannel; private final MethodChannel mMethodChannel;
private final Set<MethodChannel.MethodCallHandler> mMethodCallHandlers = new HashSet<>(); private final Set<MethodChannel.MethodCallHandler> mMethodCallHandlers = new HashSet<>();
private final Map<String,Set<EventListener>> mEventListeners = new HashMap<>(); private final Map<String, Set<EventListener>> mEventListeners = new HashMap<>();
private static final Set<ActionAfterRegistered> sActions = new HashSet<>(); private static final Set<ActionAfterRegistered> sActions = new HashSet<>();
public static BoostChannel singleton() { public static FlutterBoostPlugin singleton() {
if (sInstance == null) { if (sInstance == null) {
throw new RuntimeException("BoostChannel not register yet"); throw new RuntimeException("FlutterBoostPlugin not register yet");
} }
return sInstance; return sInstance;
} }
public static void addActionAfterRegistered(ActionAfterRegistered action) { public static void addActionAfterRegistered(ActionAfterRegistered action) {
if(action == null) return; if (action == null) return;
if(sInstance == null) { if (sInstance == null) {
sActions.add(action); sActions.add(action);
}else{ } else {
action.onChannelRegistered(sInstance); action.onChannelRegistered(sInstance);
} }
} }
public static void registerWith(PluginRegistry.Registrar registrar) { public static void registerWith(PluginRegistry.Registrar registrar) {
sInstance = new BoostChannel(registrar); sInstance = new FlutterBoostPlugin(registrar);
for(ActionAfterRegistered a : sActions) { for (ActionAfterRegistered a : sActions) {
a.onChannelRegistered(sInstance); a.onChannelRegistered(sInstance);
} }
if(FlutterBoost.sInstance != null) { if (FlutterBoost.sInstance != null) {
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener; final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if (stateListener != null) { if (stateListener != null) {
stateListener.onChannelRegistered(registrar, sInstance); stateListener.onChannelRegistered(registrar, sInstance);
...@@ -59,7 +60,7 @@ public class BoostChannel { ...@@ -59,7 +60,7 @@ public class BoostChannel {
sActions.clear(); sActions.clear();
} }
private BoostChannel(PluginRegistry.Registrar registrar){ private FlutterBoostPlugin(PluginRegistry.Registrar registrar) {
mMethodChannel = new MethodChannel(registrar.messenger(), "flutter_boost"); mMethodChannel = new MethodChannel(registrar.messenger(), "flutter_boost");
mMethodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() { mMethodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
...@@ -78,26 +79,29 @@ public class BoostChannel { ...@@ -78,26 +79,29 @@ public class BoostChannel {
} }
} }
if(listeners != null) { if (listeners != null) {
for(Object o:listeners) { for (Object o : listeners) {
((EventListener)o).onEvent(name,args); ((EventListener) o).onEvent(name, args);
} }
} }
}else{ } else {
Object[] handlers; Object[] handlers;
synchronized (mMethodCallHandlers) { synchronized (mMethodCallHandlers) {
handlers = mMethodCallHandlers.toArray(); handlers = mMethodCallHandlers.toArray();
} }
for(Object o:handlers) { for (Object o : handlers) {
((MethodChannel.MethodCallHandler)o).onMethodCall(methodCall,result); ((MethodChannel.MethodCallHandler) o).onMethodCall(methodCall, result);
} }
} }
} }
}); });
addMethodCallHandler(new BoostMethodHandler());
} }
public void invokeMethodUnsafe(final String name,Serializable args){ public void invokeMethodUnsafe(final String name, Serializable args) {
invokeMethod(name, args, new MethodChannel.Result() { invokeMethod(name, args, new MethodChannel.Result() {
@Override @Override
public void success(@Nullable Object o) { public void success(@Nullable Object o) {
...@@ -106,17 +110,17 @@ public class BoostChannel { ...@@ -106,17 +110,17 @@ public class BoostChannel {
@Override @Override
public void error(String s, @Nullable String s1, @Nullable Object o) { public void error(String s, @Nullable String s1, @Nullable Object o) {
Debuger.log("invoke method "+name+" error:"+s+" | "+s1); Debuger.log("invoke method " + name + " error:" + s + " | " + s1);
} }
@Override @Override
public void notImplemented() { public void notImplemented() {
Debuger.log("invoke method "+name+" notImplemented"); Debuger.log("invoke method " + name + " notImplemented");
} }
}); });
} }
public void invokeMethod(final String name,Serializable args){ public void invokeMethod(final String name, Serializable args) {
invokeMethod(name, args, new MethodChannel.Result() { invokeMethod(name, args, new MethodChannel.Result() {
@Override @Override
public void success(@Nullable Object o) { public void success(@Nullable Object o) {
...@@ -125,18 +129,18 @@ public class BoostChannel { ...@@ -125,18 +129,18 @@ public class BoostChannel {
@Override @Override
public void error(String s, @Nullable String s1, @Nullable Object o) { public void error(String s, @Nullable String s1, @Nullable Object o) {
Debuger.exception("invoke method "+name+" error:"+s+" | "+s1); Debuger.exception("invoke method " + name + " error:" + s + " | " + s1);
} }
@Override @Override
public void notImplemented() { public void notImplemented() {
Debuger.exception("invoke method "+name+" notImplemented"); Debuger.exception("invoke method " + name + " notImplemented");
} }
}); });
} }
public void invokeMethod(final String name,Serializable args,MethodChannel.Result result){ public void invokeMethod(final String name, Serializable args, MethodChannel.Result result) {
if("__event__".equals(name)) { if ("__event__".equals(name)) {
Debuger.exception("method name should not be __event__"); Debuger.exception("method name should not be __event__");
} }
...@@ -144,7 +148,7 @@ public class BoostChannel { ...@@ -144,7 +148,7 @@ public class BoostChannel {
} }
public void addMethodCallHandler(MethodChannel.MethodCallHandler handler) { public void addMethodCallHandler(MethodChannel.MethodCallHandler handler) {
synchronized (mMethodCallHandlers){ synchronized (mMethodCallHandlers) {
mMethodCallHandlers.add(handler); mMethodCallHandlers.add(handler);
} }
} }
...@@ -156,29 +160,29 @@ public class BoostChannel { ...@@ -156,29 +160,29 @@ public class BoostChannel {
} }
public void addEventListener(String name, EventListener listener) { public void addEventListener(String name, EventListener listener) {
synchronized (mEventListeners){ synchronized (mEventListeners) {
Set<EventListener> set = mEventListeners.get(name); Set<EventListener> set = mEventListeners.get(name);
if(set == null) { if (set == null) {
set = new HashSet<>(); set = new HashSet<>();
} }
set.add(listener); set.add(listener);
mEventListeners.put(name,set); mEventListeners.put(name, set);
} }
} }
public void removeEventListener(EventListener listener) { public void removeEventListener(EventListener listener) {
synchronized (mEventListeners) { synchronized (mEventListeners) {
for(Set<EventListener> set:mEventListeners.values()) { for (Set<EventListener> set : mEventListeners.values()) {
set.remove(listener); set.remove(listener);
} }
} }
} }
public void sendEvent(String name,Map args){ public void sendEvent(String name, Map args) {
Map event = new HashMap(); Map event = new HashMap();
event.put("name",name); event.put("name", name);
event.put("arguments",args); event.put("arguments", args);
mMethodChannel.invokeMethod("__event__",event); mMethodChannel.invokeMethod("__event__", event);
} }
public interface EventListener { public interface EventListener {
...@@ -186,6 +190,87 @@ public class BoostChannel { ...@@ -186,6 +190,87 @@ public class BoostChannel {
} }
public interface ActionAfterRegistered { public interface ActionAfterRegistered {
void onChannelRegistered(BoostChannel channel); void onChannelRegistered(FlutterBoostPlugin channel);
}
class BoostMethodHandler implements MethodChannel.MethodCallHandler {
@Override
public void onMethodCall(MethodCall methodCall, final MethodChannel.Result result) {
FlutterViewContainerManager mManager = (FlutterViewContainerManager) NewFlutterBoost.instance().containerManager();
switch (methodCall.method) {
case "pageOnStart": {
Map<String, Object> pageInfo = new HashMap<>();
try {
IContainerRecord record = mManager.getCurrentTopRecord();
if (record == null) {
record = mManager.getLastGenerateRecord();
}
if (record != null) {
pageInfo.put("name", record.getContainer().getContainerUrl());
pageInfo.put("params", record.getContainer().getContainerUrlParams());
pageInfo.put("uniqueId", record.uniqueId());
}
result.success(pageInfo);
} catch (Throwable t) {
result.error("no flutter page found!", t.getMessage(), t);
}
}
break;
case "openPage": {
try {
Map<String, Object> params = methodCall.argument("urlParams");
Map<String, Object> exts = methodCall.argument("exts");
String url = methodCall.argument("url");
mManager.openContainer(url, params, exts, new FlutterViewContainerManager.OnResult() {
@Override
public void onResult(Map<String, Object> rlt) {
if (result != null) {
result.success(rlt);
}
}
});
} catch (Throwable t) {
result.error("open page error", t.getMessage(), t);
}
}
break;
case "closePage": {
try {
String uniqueId = methodCall.argument("uniqueId");
Map<String, Object> resultData = methodCall.argument("result");
Map<String, Object> exts = methodCall.argument("exts");
mManager.closeContainer(uniqueId, resultData, exts);
result.success(true);
} catch (Throwable t) {
result.error("close page error", t.getMessage(), t);
}
}
break;
case "onShownContainerChanged": {
try {
String newId = methodCall.argument("newName");
String oldId = methodCall.argument("oldName");
mManager.onShownContainerChanged(newId, oldId);
result.success(true);
} catch (Throwable t) {
result.error("onShownContainerChanged", t.getMessage(), t);
}
}
break;
default: {
result.notImplemented();
}
}
}
} }
} }
...@@ -111,9 +111,9 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -111,9 +111,9 @@ public class FlutterViewContainerManager implements IContainerManager {
} }
void openContainer(String url, Map<String, Object> urlParams, Map<String, Object> exts,OnResult onResult) { void openContainer(String url, Map<String, Object> urlParams, Map<String, Object> exts,OnResult onResult) {
Context context = FlutterBoost.singleton().currentActivity(); Context context = NewFlutterBoost.instance().currentActivity();
if(context == null) { if(context == null) {
context = FlutterBoost.singleton().platform().getApplication(); context =NewFlutterBoost.instance().platform().getApplication();
} }
if(urlParams == null) { if(urlParams == null) {
...@@ -132,7 +132,7 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -132,7 +132,7 @@ public class FlutterViewContainerManager implements IContainerManager {
mOnResults.put(uniqueId,onResult); mOnResults.put(uniqueId,onResult);
} }
FlutterBoost.singleton().platform().openContainer(context,url,urlParams,requestCode,exts); NewFlutterBoost.instance().platform().openContainer(context,url,urlParams,requestCode,exts);
} }
IContainerRecord closeContainer(String uniqueId, Map<String, Object> result,Map<String,Object> exts) { IContainerRecord closeContainer(String uniqueId, Map<String, Object> result,Map<String,Object> exts) {
...@@ -148,7 +148,7 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -148,7 +148,7 @@ public class FlutterViewContainerManager implements IContainerManager {
Debuger.exception("closeContainer can not find uniqueId:" + uniqueId); Debuger.exception("closeContainer can not find uniqueId:" + uniqueId);
} }
FlutterBoost.singleton().platform().closeContainer(targetRecord,result,exts); NewFlutterBoost.instance().platform().closeContainer(targetRecord,result,exts);
return targetRecord; return targetRecord;
} }
......
package com.idlefish.flutterboost;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.idlefish.flutterboost.interfaces.*;
import io.flutter.app.FlutterPluginRegistry;
import io.flutter.embedding.engine.FlutterEngine;
import java.util.HashMap;
import java.util.Map;
public class NewFlutterBoost {
private Platform mPlatform;
private FlutterViewContainerManager mManager;
private IFlutterEngineProvider mEngineProvider;
private Activity mCurrentActiveActivity;
static NewFlutterBoost sInstance = null;
public static NewFlutterBoost instance() {
if (sInstance == null) {
sInstance = new NewFlutterBoost();
}
return sInstance;
}
public void init(Platform platform) {
mPlatform = platform;
mManager = new FlutterViewContainerManager();
mEngineProvider = platform.engineProvider();
platform.getApplication().registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (mPlatform.whenEngineStart() == IPlatform.ANY_ACTIVITY_CREATED) {
}
}
@Override
public void onActivityStarted(Activity activity) {
if (mCurrentActiveActivity == null) {
Debuger.log("Application entry foreground");
if (NewFlutterBoost.instance().engineProvider().tryGetEngine() != null) {
HashMap<String, String> map = new HashMap<>();
map.put("type", "foreground");
channel().sendEvent("lifecycle", map);
}
}
mCurrentActiveActivity = activity;
}
@Override
public void onActivityResumed(Activity activity) {
mCurrentActiveActivity = activity;
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
if (mCurrentActiveActivity == activity) {
Debuger.log("Application entry background");
if (mEngineProvider.tryGetEngine() != null) {
HashMap<String, String> map = new HashMap<>();
map.put("type", "background");
channel().sendEvent("lifecycle", map);
}
mCurrentActiveActivity = null;
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
if (mCurrentActiveActivity == activity) {
Debuger.log("Application entry background");
if (mEngineProvider.tryGetEngine() != null) {
HashMap<String, String> map = new HashMap<>();
map.put("type", "background");
channel().sendEvent("lifecycle", map);
}
mCurrentActiveActivity = null;
}
}
});
BoostPluginRegistry registry=new BoostPluginRegistry(this.engineProvider().provideEngine(mPlatform.getApplication()),
mPlatform.getApplication());
mPlatform.registerPlugins(registry);
}
public static class ConfigBuilder {
protected static final String DEFAULT_DART_ENTRYPOINT = "main";
protected static final String DEFAULT_INITIAL_ROUTE = "/";
private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT;
private String initialRoute = DEFAULT_INITIAL_ROUTE;
private boolean isDebug = false;
private int whenEngineStart = 1;
private Application mApp;
private INativeRouter router = null;
public ConfigBuilder(Application app, INativeRouter router) {
this.router = router;
this.mApp = app;
}
public ConfigBuilder dartEntrypoint(@NonNull String dartEntrypoint) {
this.dartEntrypoint = dartEntrypoint;
return this;
}
public ConfigBuilder isDebug(boolean isDebug) {
this.isDebug = isDebug;
return this;
}
public ConfigBuilder whenEngineStart(@NonNull int whenEngineStart) {
this.whenEngineStart = whenEngineStart;
return this;
}
public Platform build() {
Platform platform = new Platform() {
@Override
public Application getApplication() {
return ConfigBuilder.this.mApp;
}
public boolean isDebug() {
return ConfigBuilder.this.isDebug;
}
@Override
public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
router.openContainer(context, url, urlParams, requestCode, exts);
}
@Override
public IFlutterEngineProvider engineProvider() {
return new BoostEngineProvider();
}
public int whenEngineStart() {
return ConfigBuilder.this.whenEngineStart;
}
};
return platform;
}
}
public IFlutterEngineProvider engineProvider() {
return sInstance.mEngineProvider;
}
public IContainerManager containerManager() {
return sInstance.mManager;
}
public IPlatform platform() {
return sInstance.mPlatform;
}
public FlutterBoostPlugin channel() {
return FlutterBoostPlugin.singleton();
}
public Activity currentActivity() {
return sInstance.mCurrentActiveActivity;
}
public IFlutterViewContainer findContainerById(String id) {
return mManager.findContainerById(id);
}
}
...@@ -16,7 +16,7 @@ public class StateListener implements IStateListener { ...@@ -16,7 +16,7 @@ public class StateListener implements IStateListener {
} }
@Override @Override
public void onChannelRegistered(PluginRegistry.Registrar registrar, BoostChannel channel) { public void onChannelRegistered(PluginRegistry.Registrar registrar, FlutterBoostPlugin channel) {
Debuger.log(">>onFlutterViewInited"); Debuger.log(">>onFlutterViewInited");
} }
......
...@@ -449,7 +449,7 @@ public class XFlutterView extends FrameLayout { ...@@ -449,7 +449,7 @@ public class XFlutterView extends FrameLayout {
this.flutterEngine = flutterEngine; this.flutterEngine = flutterEngine;
// initialize PlatformViewsController // initialize PlatformViewsController
this.flutterEngine.getPluginRegistry().getPlatformViewsController().attach(getContext(),flutterEngine.getRenderer(),flutterEngine.getDartExecutor()); // this.flutterEngine.getPluginRegistry().getPlatformViewsController().attach(getContext(),flutterEngine.getRenderer(),flutterEngine.getDartExecutor());
// Instruct our FlutterRenderer that we are now its designated RenderSurface. // Instruct our FlutterRenderer that we are now its designated RenderSurface.
this.flutterEngine.getRenderer().attachToRenderSurface(renderSurface); this.flutterEngine.getRenderer().attachToRenderSurface(renderSurface);
...@@ -533,8 +533,8 @@ public class XFlutterView extends FrameLayout { ...@@ -533,8 +533,8 @@ public class XFlutterView extends FrameLayout {
Log.d(TAG, "Detaching from Flutter Engine"); Log.d(TAG, "Detaching from Flutter Engine");
// detach platformviews in page in case memory leak // detach platformviews in page in case memory leak
flutterEngine.getPluginRegistry().getPlatformViewsController().detach(); // flutterEngine.getPluginRegistry().getPlatformViewsController().detach();
flutterEngine.getPluginRegistry().getPlatformViewsController().onFlutterViewDestroyed(); // flutterEngine.getPluginRegistry().getPlatformViewsController().onFlutterViewDestroyed();
// Inform the Android framework that it should retrieve a new InputConnection // Inform the Android framework that it should retrieve a new InputConnection
// now that the engine is detached. The new InputConnection will be null, which // now that the engine is detached. The new InputConnection will be null, which
......
package com.idlefish.flutterboost; package com.idlefish.flutterboost;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.text.Editable; import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.Selection; import android.text.Selection;
...@@ -59,6 +61,11 @@ public class XTextInputPlugin { ...@@ -59,6 +61,11 @@ public class XTextInputPlugin {
setTextInputClient(textInputClientId, configuration); setTextInputClient(textInputClientId, configuration);
} }
@Override
public void setPlatformViewClient(int i) {
}
@Override @Override
public void setEditingState(TextInputChannel.TextEditState editingState) { public void setEditingState(TextInputChannel.TextEditState editingState) {
setTextInputEditingState(mView, editingState); setTextInputEditingState(mView, editingState);
...@@ -206,6 +213,7 @@ public class XTextInputPlugin { ...@@ -206,6 +213,7 @@ public class XTextInputPlugin {
} }
} }
@RequiresApi(api = Build.VERSION_CODES.CUPCAKE)
private void setTextInputEditingState(View view, TextInputChannel.TextEditState state) { private void setTextInputEditingState(View view, TextInputChannel.TextEditState state) {
if (!mRestartInputPending && state.text.equals(mEditable.toString())) { if (!mRestartInputPending && state.text.equals(mEditable.toString())) {
applyStateToSelection(state); applyStateToSelection(state);
......
...@@ -117,7 +117,8 @@ public abstract class BoostFlutterActivity extends Activity implements IFlutterV ...@@ -117,7 +117,8 @@ public abstract class BoostFlutterActivity extends Activity implements IFlutterV
} }
protected BoostFlutterEngine createFlutterEngine(){ protected BoostFlutterEngine createFlutterEngine(){
return FlutterBoost.singleton().engineProvider().provideEngine(this); // return FlutterBoost.singleton().engineProvider().provideEngine(this);
return null;
} }
protected BoostFlutterView createFlutterView(BoostFlutterEngine engine){ protected BoostFlutterView createFlutterView(BoostFlutterEngine engine){
...@@ -247,10 +248,10 @@ public abstract class BoostFlutterActivity extends Activity implements IFlutterV ...@@ -247,10 +248,10 @@ public abstract class BoostFlutterActivity extends Activity implements IFlutterV
return this; return this;
} }
@Override // @Override
public BoostFlutterView getBoostFlutterView() { // public BoostFlutterView getBoostFlutterView() {
return mFlutterView; // return mFlutterView;
} // }
@Override @Override
public void finishContainer(Map<String,Object> result) { public void finishContainer(Map<String,Object> result) {
......
...@@ -9,6 +9,11 @@ import java.util.Map; ...@@ -9,6 +9,11 @@ import java.util.Map;
public class BoostFlutterDefaultActivity extends BoostFlutterActivity { public class BoostFlutterDefaultActivity extends BoostFlutterActivity {
@Override
public FlutterSplashView getBoostFlutterView() {
return null;
}
@Override @Override
public String getContainerUrl() { public String getContainerUrl() {
return getIntent().getStringExtra("url"); return getIntent().getStringExtra("url");
......
...@@ -67,7 +67,8 @@ abstract public class BoostFlutterFragment extends Fragment implements IFlutterV ...@@ -67,7 +67,8 @@ abstract public class BoostFlutterFragment extends Fragment implements IFlutterV
} }
protected BoostFlutterEngine createFlutterEngine(){ protected BoostFlutterEngine createFlutterEngine(){
return FlutterBoost.singleton().engineProvider().provideEngine(getContext()); // return FlutterBoost.singleton().engineProvider().provideEngine(getContext());
return null;
} }
protected BoostFlutterView createFlutterView(BoostFlutterEngine engine){ protected BoostFlutterView createFlutterView(BoostFlutterEngine engine){
...@@ -140,10 +141,10 @@ abstract public class BoostFlutterFragment extends Fragment implements IFlutterV ...@@ -140,10 +141,10 @@ abstract public class BoostFlutterFragment extends Fragment implements IFlutterV
return getActivity(); return getActivity();
} }
@Override // @Override
public BoostFlutterView getBoostFlutterView() { // public BoostFlutterView getBoostFlutterView() {
return mFlutterView; // return mFlutterView;
} // }
@Override @Override
public void finishContainer(Map<String,Object> result) { public void finishContainer(Map<String,Object> result) {
......
package com.idlefish.flutterboost.containers;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.arch.lifecycle.Lifecycle;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.idlefish.flutterboost.BoostFlutterView;
import com.idlefish.flutterboost.FlutterBoost;
import com.idlefish.flutterboost.NewFlutterBoost;
import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.idlefish.flutterboost.interfaces.IOperateSyncer;
import io.flutter.Log;
import io.flutter.app.FlutterActivity;
import io.flutter.embedding.android.*;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterEngineCache;
import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.view.FlutterMain;
import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer {
private static final String TAG = "FlutterActivityAndFragmentDelegate";
@NonNull
private Host host;
@Nullable
private FlutterEngine flutterEngine;
@Nullable
private FlutterSplashView flutterSplashView;
@Nullable
private FlutterView flutterView;
@Nullable
private PlatformPlugin platformPlugin;
private boolean isFlutterEngineFromHost;
protected IOperateSyncer mSyncer;
@NonNull
private final OnFirstFrameRenderedListener onFirstFrameRenderedListener = new OnFirstFrameRenderedListener() {
@Override
public void onFirstFrameRendered() {
host.onFirstFrameRendered();
}
};
FlutterActivityAndFragmentDelegate(@NonNull Host host) {
this.host = host;
}
void release() {
this.host = null;
this.flutterEngine = null;
this.flutterView = null;
this.platformPlugin = null;
}
@Nullable
FlutterEngine getFlutterEngine() {
return flutterEngine;
}
void onAttach(@NonNull Context context) {
ensureAlive();
initializeFlutter(context);
// When "retain instance" is true, the FlutterEngine will survive configuration
// changes. Therefore, we create a new one only if one does not already exist.
if (flutterEngine == null) {
setupFlutterEngine();
}
// Regardless of whether or not a FlutterEngine already existed, the PlatformPlugin
// is bound to a specific Activity. Therefore, it needs to be created and configured
// every time this Fragment attaches to a new Activity.
// TODO(mattcarroll): the PlatformPlugin needs to be reimagined because it implicitly takes
// control of the entire window. This is unacceptable for non-fullscreen
// use-cases.
platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
if (host.shouldAttachEngineToActivity()) {
// Notify any plugins that are currently attached to our FlutterEngine that they
// are now attached to an Activity.
//
// Passing this Fragment's Lifecycle should be sufficient because as long as this Fragment
// is attached to its Activity, the lifecycles should be in sync. Once this Fragment is
// detached from its Activity, that Activity will be detached from the FlutterEngine, too,
// which means there shouldn't be any possibility for the Fragment Lifecycle to get out of
// sync with the Activity. We use the Fragment's Lifecycle because it is possible that the
// attached Activity is not a LifecycleOwner.
Log.d(TAG, "Attaching FlutterEngine to the Activity that owns this Fragment.");
flutterEngine.getActivityControlSurface().attachToActivity(
host.getActivity(),
host.getLifecycle()
);
}
host.configureFlutterEngine(flutterEngine);
}
private void initializeFlutter(@NonNull Context context) {
FlutterMain.ensureInitializationComplete(
context.getApplicationContext(),
host.getFlutterShellArgs().toArray()
);
}
private void setupFlutterEngine() {
Log.d(TAG, "Setting up FlutterEngine.");
// First, check if the host wants to use a cached FlutterEngine.
String cachedEngineId = host.getCachedEngineId();
if (cachedEngineId != null) {
flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
isFlutterEngineFromHost = true;
if (flutterEngine == null) {
throw new IllegalStateException("The requested cached FlutterEngine did not exist in the FlutterEngineCache: '" + cachedEngineId + "'");
}
return;
}
// Second, defer to subclasses for a custom FlutterEngine.
flutterEngine = host.provideFlutterEngine(host.getContext());
if (flutterEngine != null) {
isFlutterEngineFromHost = true;
return;
}
// Our host did not provide a custom FlutterEngine. Create a FlutterEngine to back our
// FlutterView.
Log.d(TAG, "No preferred FlutterEngine was provided. Creating a new FlutterEngine for"
+ " this FlutterFragment.");
flutterEngine = new FlutterEngine(host.getContext());
isFlutterEngineFromHost = false;
}
@SuppressLint("ResourceType")
@NonNull
View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.v(TAG, "Creating FlutterView.");
mSyncer = NewFlutterBoost.instance().containerManager().generateSyncer(this);
ensureAlive();
flutterView = new FlutterView(host.getActivity(), host.getRenderMode(), host.getTransparencyMode());
flutterView.addOnFirstFrameRenderedListener(onFirstFrameRenderedListener);
flutterSplashView = new FlutterSplashView(host.getContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
flutterSplashView.setId(View.generateViewId());
} else {
// TODO(mattcarroll): Find a better solution to this ID. This is a random, static ID.
// It might conflict with other Views, and it means that only a single FlutterSplashView
// can exist in a View hierarchy at one time.
flutterSplashView.setId(486947586);
}
flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());
mSyncer.onCreate();
return flutterSplashView;
}
void onStart() {
Log.v(TAG, "onStart()");
ensureAlive();
// We post() the code that attaches the FlutterEngine to our FlutterView because there is
// some kind of blocking logic on the native side when the surface is connected. That lag
// causes launching Activitys to wait a second or two before launching. By post()'ing this
// behavior we are able to move this blocking logic to after the Activity's launch.
// TODO(mattcarroll): figure out how to avoid blocking the MAIN thread when connecting a surface
new Handler().post(new Runnable() {
@Override
public void run() {
Log.v(TAG, "Attaching FlutterEngine to FlutterView.");
flutterView.attachToFlutterEngine(flutterEngine);
doInitialFlutterViewRun();
}
});
}
private void doInitialFlutterViewRun() {
// Don't attempt to start a FlutterEngine if we're using a cached FlutterEngine.
if (host.getCachedEngineId() != null) {
return;
}
if (flutterEngine.getDartExecutor().isExecutingDart()) {
// No warning is logged because this situation will happen on every config
// change if the developer does not choose to retain the Fragment instance.
// So this is expected behavior in many cases.
return;
}
Log.d(TAG, "Executing Dart entrypoint: " + host.getDartEntrypointFunctionName()
+ ", and sending initial route: " + host.getInitialRoute());
// The engine needs to receive the Flutter app's initial route before executing any
// Dart code to ensure that the initial route arrives in time to be applied.
if (host.getInitialRoute() != null) {
flutterEngine.getNavigationChannel().setInitialRoute(host.getInitialRoute());
}
// Configure the Dart entrypoint and execute it.
DartExecutor.DartEntrypoint entrypoint = new DartExecutor.DartEntrypoint(
host.getAppBundlePath(),
host.getDartEntrypointFunctionName()
);
flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint);
}
void onResume() {
mSyncer.onAppear();
Log.v(TAG, "onResume()");
ensureAlive();
flutterEngine.getLifecycleChannel().appIsResumed();
}
void onPostResume() {
Log.v(TAG, "onPostResume()");
ensureAlive();
if (flutterEngine != null) {
if (platformPlugin != null) {
// TODO(mattcarroll): find a better way to handle the update of UI overlays than calling through
// to platformPlugin. We're implicitly entangling the Window, Activity, Fragment,
// and engine all with this one call.
platformPlugin.updateSystemUiOverlays();
}
} else {
Log.w(TAG, "onPostResume() invoked before FlutterFragment was attached to an Activity.");
}
}
void onPause() {
Log.v(TAG, "onPause()");
mSyncer.onDisappear();
ensureAlive();
flutterEngine.getLifecycleChannel().appIsInactive();
}
void onStop() {
Log.v(TAG, "onStop()");
ensureAlive();
flutterEngine.getLifecycleChannel().appIsPaused();
flutterView.detachFromFlutterEngine();
}
void onDestroyView() {
Log.v(TAG, "onDestroyView()");
mSyncer.onDestroy();
ensureAlive();
flutterView.removeOnFirstFrameRenderedListener(onFirstFrameRenderedListener);
}
void onDetach() {
Log.v(TAG, "onDetach()");
ensureAlive();
if (host.shouldAttachEngineToActivity()) {
// Notify plugins that they are no longer attached to an Activity.
Log.d(TAG, "Detaching FlutterEngine from the Activity that owns this Fragment.");
if (host.getActivity().isChangingConfigurations()) {
flutterEngine.getActivityControlSurface().detachFromActivityForConfigChanges();
} else {
flutterEngine.getActivityControlSurface().detachFromActivity();
}
}
// Null out the platformPlugin to avoid a possible retain cycle between the plugin, this Fragment,
// and this Fragment's Activity.
if (platformPlugin != null) {
platformPlugin.destroy();
platformPlugin = null;
}
// Destroy our FlutterEngine if we're not set to retain it.
if (host.shouldDestroyEngineWithHost()) {
flutterEngine.destroy();
if (host.getCachedEngineId() != null) {
FlutterEngineCache.getInstance().remove(host.getCachedEngineId());
}
flutterEngine = null;
}
}
void onBackPressed() {
mSyncer.onBackPressed();
ensureAlive();
// if (flutterEngine != null) {
// Log.v(TAG, "Forwarding onBackPressed() to FlutterEngine.");
// flutterEngine.getNavigationChannel().popRoute();
// } else {
// Log.w(TAG, "Invoked onBackPressed() before FlutterFragment was attached to an Activity.");
// }
}
void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
mSyncer.onRequestPermissionsResult(requestCode, permissions, grantResults);
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding onRequestPermissionsResult() to FlutterEngine:\n"
+ "requestCode: " + requestCode + "\n"
+ "permissions: " + Arrays.toString(permissions) + "\n"
+ "grantResults: " + Arrays.toString(grantResults));
flutterEngine.getActivityControlSurface().onRequestPermissionsResult(requestCode, permissions, grantResults);
} else {
Log.w(TAG, "onRequestPermissionResult() invoked before FlutterFragment was attached to an Activity.");
}
}
void onNewIntent(@NonNull Intent intent) {
mSyncer.onNewIntent(intent);
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding onNewIntent() to FlutterEngine.");
flutterEngine.getActivityControlSurface().onNewIntent(intent);
} else {
Log.w(TAG, "onNewIntent() invoked before FlutterFragment was attached to an Activity.");
}
}
void onActivityResult(int requestCode, int resultCode, Intent data) {
mSyncer.onActivityResult(requestCode,resultCode,data);
Map<String,Object> result = null;
if(data != null) {
Serializable rlt = data.getSerializableExtra(RESULT_KEY);
if(rlt instanceof Map) {
result = (Map<String,Object>)rlt;
}
}
mSyncer.onContainerResult(requestCode,resultCode,result);
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding onActivityResult() to FlutterEngine:\n"
+ "requestCode: " + requestCode + "\n"
+ "resultCode: " + resultCode + "\n"
+ "data: " + data);
flutterEngine.getActivityControlSurface().onActivityResult(requestCode, resultCode, data);
} else {
Log.w(TAG, "onActivityResult() invoked before FlutterFragment was attached to an Activity.");
}
}
void onUserLeaveHint() {
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding onUserLeaveHint() to FlutterEngine.");
flutterEngine.getActivityControlSurface().onUserLeaveHint();
} else {
Log.w(TAG, "onUserLeaveHint() invoked before FlutterFragment was attached to an Activity.");
}
}
void onTrimMemory(int level) {
mSyncer.onTrimMemory(level);
ensureAlive();
if (flutterEngine != null) {
// 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) {
Log.v(TAG, "Forwarding onTrimMemory() to FlutterEngine. Level: " + level);
flutterEngine.getSystemChannel().sendMemoryPressureWarning();
}
} else {
Log.w(TAG, "onTrimMemory() invoked before FlutterFragment was attached to an Activity.");
}
}
void onLowMemory() {
Log.v(TAG, "Forwarding onLowMemory() to FlutterEngine.");
mSyncer.onLowMemory();
ensureAlive();
flutterEngine.getSystemChannel().sendMemoryPressureWarning();
}
/**
* Ensures that this delegate has not been {@link #release()}'ed.
* <p>
* An {@code IllegalStateException} is thrown if this delegate has been {@link #release()}'ed.
*/
private void ensureAlive() {
if (host == null) {
throw new IllegalStateException("Cannot execute method on a destroyed FlutterActivityAndFragmentDelegate.");
}
}
@Override
public Activity getContextActivity() {
return (Activity)this.host;
}
@Override
public FlutterSplashView getBoostFlutterView() {
return this.flutterSplashView;
}
@Override
public void finishContainer(Map<String, Object> result) {
Activity activity= (Activity)this.host;
activity.finish();
}
@Override
public String getContainerUrl() {
return "flutterPage";
}
@Override
public Map getContainerUrlParams() {
return null;
}
@Override
public void onContainerShown() {
}
@Override
public void onContainerHidden() {
}
/**
* The {@link FlutterActivity} or {@link FlutterFragment} that owns this
* {@code FlutterActivityAndFragmentDelegate}.
*/
/* package */ interface Host extends SplashScreenProvider, FlutterEngineProvider, FlutterEngineConfigurator {
/**
* Returns the {@link Context} that backs the host {@link Activity} or {@code Fragment}.
*/
@NonNull
Context getContext();
/**
* Returns the host {@link Activity} or the {@code Activity} that is currently attached
* to the host {@code Fragment}.
*/
@Nullable
Activity getActivity();
/**
* Returns the {@link Lifecycle} that backs the host {@link Activity} or {@code Fragment}.
*/
@NonNull
Lifecycle getLifecycle();
/**
* Returns the {@link FlutterShellArgs} that should be used when initializing Flutter.
*/
@NonNull
FlutterShellArgs getFlutterShellArgs();
/**
* Returns the ID of a statically cached {@link FlutterEngine} to use within this
* delegate's host, or {@code null} if this delegate's host does not want to
* use a cached {@link FlutterEngine}.
*/
@Nullable
String getCachedEngineId();
/**
* Returns true if the {@link FlutterEngine} used in this delegate should be destroyed
* when the host/delegate are destroyed.
* <p>
* The default value is {@code true} in cases where {@code FlutterFragment} created its own
* {@link FlutterEngine}, and {@code false} in cases where a cached {@link FlutterEngine} was
* provided.
*/
boolean shouldDestroyEngineWithHost();
/**
* Returns the Dart entrypoint that should run when a new {@link FlutterEngine} is
* created.
*/
@NonNull
String getDartEntrypointFunctionName();
/**
* Returns the path to the app bundle where the Dart code exists.
*/
@NonNull
String getAppBundlePath();
/**
* Returns the initial route that Flutter renders.
*/
@Nullable
String getInitialRoute();
/**
* Returns the {@link FlutterView.RenderMode} used by the {@link FlutterView} that
* displays the {@link FlutterEngine}'s content.
*/
@NonNull
FlutterView.RenderMode getRenderMode();
/**
* Returns the {@link FlutterView.TransparencyMode} used by the {@link FlutterView} that
* displays the {@link FlutterEngine}'s content.
*/
@NonNull
FlutterView.TransparencyMode getTransparencyMode();
@Nullable
SplashScreen provideSplashScreen();
/**
* Returns the {@link FlutterEngine} that should be rendered to a {@link FlutterView}.
* <p>
* If {@code null} is returned, a new {@link FlutterEngine} will be created automatically.
*/
@Nullable
FlutterEngine provideFlutterEngine(@NonNull Context context);
/**
* Hook for the host to create/provide a {@link PlatformPlugin} if the associated
* Flutter experience should control system chrome.
*/
@Nullable
PlatformPlugin providePlatformPlugin(@Nullable Activity activity, @NonNull FlutterEngine flutterEngine);
/**
* Hook for the host to configure the {@link FlutterEngine} as desired.
*/
void configureFlutterEngine(@NonNull FlutterEngine flutterEngine);
/**
* Returns true if the {@link FlutterEngine}'s plugin system should be connected to the
* host {@link Activity}, allowing plugins to interact with it.
*/
boolean shouldAttachEngineToActivity();
/**
* Invoked by this delegate when its {@link FlutterView} has rendered its first Flutter
* frame.
*/
void onFirstFrameRendered();
}
}
package com.idlefish.flutterboost.containers;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import com.idlefish.flutterboost.*;
import com.idlefish.flutterboost.interfaces.IStateListener;
import io.flutter.Log;
import io.flutter.embedding.android.FlutterView;
import io.flutter.embedding.android.SplashScreen;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
/**
* {@code View} that displays a {@link SplashScreen} until a given {@link FlutterView}
* renders its first frame.
*/
public class FlutterSplashView extends FrameLayout {
private static String TAG = "FlutterSplashView";
private FlutterEngine mFlutterEngine;
@Nullable
private SplashScreen splashScreen;
@Nullable
private FlutterView flutterView;
@Nullable
private View splashScreenView;
@Nullable
private Bundle splashScreenState;
@Nullable
private String transitioningIsolateId;
@Nullable
private String previousCompletedSplashIsolate;
@NonNull
private final FlutterView.FlutterEngineAttachmentListener flutterEngineAttachmentListener = new FlutterView.FlutterEngineAttachmentListener() {
@Override
public void onFlutterEngineAttachedToFlutterView(@NonNull FlutterEngine engine) {
flutterView.removeFlutterEngineAttachmentListener(this);
displayFlutterViewWithSplash(flutterView, splashScreen);
}
@Override
public void onFlutterEngineDetachedFromFlutterView() {
}
};
@NonNull
private final OnFirstFrameRenderedListener onFirstFrameRenderedListener = new OnFirstFrameRenderedListener() {
@Override
public void onFirstFrameRendered() {
if (splashScreen != null) {
transitionToFlutter();
}
}
};
@NonNull
private final Runnable onTransitionComplete = new Runnable() {
@Override
public void run() {
removeView(splashScreenView);
previousCompletedSplashIsolate = transitioningIsolateId;
}
};
public FlutterSplashView(@NonNull Context context) {
this(context, null, 0);
}
public FlutterSplashView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FlutterSplashView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setSaveEnabled(true);
if (mFlutterEngine == null) {
mFlutterEngine = NewFlutterBoost.instance().engineProvider().provideEngine(context);
}
}
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.previousCompletedSplashIsolate = previousCompletedSplashIsolate;
savedState.splashScreenState = splashScreen != null ? splashScreen.saveSplashScreenState() : null;
return savedState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
previousCompletedSplashIsolate = savedState.previousCompletedSplashIsolate;
splashScreenState = savedState.splashScreenState;
}
/**
* Displays the given {@code splashScreen} on top of the given {@code flutterView} until
* Flutter has rendered its first frame, then the {@code splashScreen} is transitioned away.
* <p>
* If no {@code splashScreen} is provided, this {@code FlutterSplashView} displays the
* given {@code flutterView} on its own.
*/
public void displayFlutterViewWithSplash(@NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) {
// If we were displaying a previous FlutterView, remove it.
if (this.flutterView != null) {
this.flutterView.removeOnFirstFrameRenderedListener(onFirstFrameRenderedListener);
removeView(this.flutterView);
}
// If we were displaying a previous splash screen View, remove it.
if (splashScreenView != null) {
removeView(splashScreenView);
}
// Display the new FlutterView.
this.flutterView = flutterView;
addView(flutterView);
this.splashScreen = splashScreen;
// Display the new splash screen, if needed.
if (splashScreen != null) {
if (isSplashScreenNeededNow()) {
Log.v(TAG, "Showing splash screen UI.");
// This is the typical case. A FlutterEngine is attached to the FlutterView and we're
// waiting for the first frame to render. Show a splash UI until that happens.
splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
addView(this.splashScreenView);
flutterView.addOnFirstFrameRenderedListener(onFirstFrameRenderedListener);
} else if (isSplashScreenTransitionNeededNow()) {
Log.v(TAG, "Showing an immediate splash transition to Flutter due to previously interrupted transition.");
splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
addView(splashScreenView);
transitionToFlutter();
} else if (!flutterView.isAttachedToFlutterEngine()) {
Log.v(TAG, "FlutterView is not yet attached to a FlutterEngine. Showing nothing until a FlutterEngine is attached.");
flutterView.addFlutterEngineAttachmentListener(flutterEngineAttachmentListener);
}
}
}
/**
* Returns true if current conditions require a splash UI to be displayed.
* <p>
* This method does not evaluate whether a previously interrupted splash transition needs
* to resume. See {@link #isSplashScreenTransitionNeededNow()} to answer that question.
*/
private boolean isSplashScreenNeededNow() {
return flutterView != null
&& flutterView.isAttachedToFlutterEngine()
&& !flutterView.hasRenderedFirstFrame()
&& !hasSplashCompleted();
}
/**
* Returns true if a previous splash transition was interrupted by recreation, e.g., an
* orientation change, and that previous transition should be resumed.
* <p>
* Not all splash screens are capable of remembering their transition progress. In those
* cases, this method will return false even if a previous visual transition was
* interrupted.
*/
private boolean isSplashScreenTransitionNeededNow() {
return flutterView != null
&& flutterView.isAttachedToFlutterEngine()
&& splashScreen != null
&& splashScreen.doesSplashViewRememberItsTransition()
&& wasPreviousSplashTransitionInterrupted();
}
/**
* Returns true if a splash screen was transitioning to a Flutter experience and was then
* interrupted, e.g., by an Android configuration change. Returns false otherwise.
* <p>
* Invoking this method expects that a {@code flutterView} exists and it is attached to a
* {@code FlutterEngine}.
*/
private boolean wasPreviousSplashTransitionInterrupted() {
if (flutterView == null) {
throw new IllegalStateException("Cannot determine if previous splash transition was " +
"interrupted when no FlutterView is set.");
}
if (!flutterView.isAttachedToFlutterEngine()) {
throw new IllegalStateException("Cannot determine if previous splash transition was "
+ "interrupted when no FlutterEngine is attached to our FlutterView. This question "
+ "depends on an isolate ID to differentiate Flutter experiences.");
}
return flutterView.hasRenderedFirstFrame() && !hasSplashCompleted();
}
/**
* Returns true if a splash UI for a specific Flutter experience has already completed.
* <p>
* A "specific Flutter experience" is defined as any experience with the same Dart isolate
* ID. The purpose of this distinction is to prevent a situation where a user gets past a
* splash UI, rotates the device (or otherwise triggers a recreation) and the splash screen
* reappears.
* <p>
* An isolate ID is deemed reasonable as a key for a completion event because a Dart isolate
* cannot be entered twice. Therefore, a single Dart isolate cannot return to an "un-rendered"
* state after having previously rendered content.
*/
private boolean hasSplashCompleted() {
if (flutterView == null) {
throw new IllegalStateException("Cannot determine if splash has completed when no FlutterView "
+ "is set.");
}
if (!flutterView.isAttachedToFlutterEngine()) {
throw new IllegalStateException("Cannot determine if splash has completed when no "
+ "FlutterEngine is attached to our FlutterView. This question depends on an isolate ID "
+ "to differentiate Flutter experiences.");
}
// A null isolate ID on a non-null FlutterEngine indicates that the Dart isolate has not
// been initialized. Therefore, no frame has been rendered for this engine, which means
// no splash screen could have completed yet.
return flutterView.getAttachedFlutterEngine().getDartExecutor().getIsolateServiceId() != null
&& flutterView.getAttachedFlutterEngine().getDartExecutor().getIsolateServiceId().equals(previousCompletedSplashIsolate);
}
/**
* Transitions a splash screen to the Flutter UI.
* <p>
* This method requires that our FlutterView be attached to an engine, and that engine have
* a Dart isolate ID. It also requires that a {@code splashScreen} exist.
*/
private void transitionToFlutter() {
transitioningIsolateId = flutterView.getAttachedFlutterEngine().getDartExecutor().getIsolateServiceId();
Log.v(TAG, "Transitioning splash screen to a Flutter UI. Isolate: " + transitioningIsolateId);
splashScreen.transitionToFlutter(onTransitionComplete);
}
public static class SavedState extends BaseSavedState {
public static Creator CREATOR = new Creator() {
@Override
public SavedState createFromParcel(Parcel source) {
return new SavedState(source);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
private String previousCompletedSplashIsolate;
private Bundle splashScreenState;
SavedState(Parcelable superState) {
super(superState);
}
SavedState(Parcel source) {
super(source);
previousCompletedSplashIsolate = source.readString();
splashScreenState = source.readBundle(getClass().getClassLoader());
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeString(previousCompletedSplashIsolate);
out.writeBundle(splashScreenState);
}
}
public void onAttach() {
Debuger.log("BoostFlutterView onAttach");
flutterView.attachToFlutterEngine(mFlutterEngine);
}
public void onDetach() {
Debuger.log("BoostFlutterView onDetach");
flutterView.detachFromFlutterEngine();
}
}
\ No newline at end of file
package com.idlefish.flutterboost.containers;
import android.app.Activity;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.LifecycleRegistry;
import android.arch.lifecycle.Lifecycle.Event;
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.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import com.idlefish.flutterboost.FlutterBoost;
import com.idlefish.flutterboost.NewFlutterBoost;
import com.idlefish.flutterboost.Utils;
import io.flutter.Log;
import io.flutter.embedding.android.DrawableSplashScreen;
import io.flutter.embedding.android.FlutterView;
import io.flutter.embedding.android.SplashScreen;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.view.FlutterMain;
import java.util.HashMap;
import java.util.Map;
public class NewBoostFlutterActivity extends Activity
implements FlutterActivityAndFragmentDelegate.Host,
LifecycleOwner {
private static final String TAG = "NewBoostFlutterActivity";
// Meta-data arguments, processed from manifest XML.
protected static final String DART_ENTRYPOINT_META_DATA_KEY = "io.flutter.Entrypoint";
protected static final String INITIAL_ROUTE_META_DATA_KEY = "io.flutter.InitialRoute";
protected static final String SPLASH_SCREEN_META_DATA_KEY = "io.flutter.embedding.android.SplashScreenDrawable";
protected static final String NORMAL_THEME_META_DATA_KEY = "io.flutter.embedding.android.NormalTheme";
// Intent extra arguments.
protected static final String EXTRA_DART_ENTRYPOINT = "dart_entrypoint";
protected static final String EXTRA_INITIAL_ROUTE = "initial_route";
protected static final String EXTRA_BACKGROUND_MODE = "background_mode";
protected static final String EXTRA_CACHED_ENGINE_ID = "cached_engine_id";
protected static final String EXTRA_DESTROY_ENGINE_WITH_ACTIVITY = "destroy_engine_with_activity";
// Default configuration.
protected static final String DEFAULT_DART_ENTRYPOINT = "main";
protected static final String DEFAULT_INITIAL_ROUTE = "/";
protected static final String DEFAULT_BACKGROUND_MODE = BackgroundMode.opaque.name();
public static Intent createDefaultIntent(@NonNull Context launchContext) {
return withNewEngine().build(launchContext);
}
public static NewEngineIntentBuilder withNewEngine() {
return new NewEngineIntentBuilder(NewBoostFlutterActivity.class);
}
public static class NewEngineIntentBuilder {
private final Class<? extends NewBoostFlutterActivity> activityClass;
private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT;
private String initialRoute = DEFAULT_INITIAL_ROUTE;
private String backgroundMode = DEFAULT_BACKGROUND_MODE;
private String url = "";
private Map params = null;
protected NewEngineIntentBuilder(@NonNull Class<? extends NewBoostFlutterActivity> activityClass) {
this.activityClass = activityClass;
}
public NewEngineIntentBuilder dartEntrypoint(@NonNull String dartEntrypoint) {
this.dartEntrypoint = dartEntrypoint;
return this;
}
public NewEngineIntentBuilder url (@NonNull String url) {
this.url = url;
return this;
}
public NewEngineIntentBuilder params (@NonNull Map params) {
this.params = params;
return this;
}
public NewEngineIntentBuilder initialRoute(@NonNull String initialRoute) {
this.initialRoute = initialRoute;
return this;
}
public NewEngineIntentBuilder backgroundMode(@NonNull BackgroundMode backgroundMode) {
this.backgroundMode = backgroundMode.name();
return this;
}
public Intent build(@NonNull Context context) {
return new Intent(context, activityClass)
.putExtra(EXTRA_DART_ENTRYPOINT, dartEntrypoint)
.putExtra(EXTRA_INITIAL_ROUTE, initialRoute)
.putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
.putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, false)
.putExtra("url", url)
.putExtra("params", (HashMap)params);
}
}
private FlutterActivityAndFragmentDelegate delegate;
@NonNull
private LifecycleRegistry lifecycle;
public NewBoostFlutterActivity() {
lifecycle = new LifecycleRegistry(this);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
switchLaunchThemeForNormalTheme();
super.onCreate(savedInstanceState);
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this);
configureWindowForTransparency();
setContentView(createFlutterView());
configureStatusBarForFullscreenFlutterExperience();
}
private void switchLaunchThemeForNormalTheme() {
try {
ActivityInfo activityInfo = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
if (activityInfo.metaData != null) {
int normalThemeRID = activityInfo.metaData.getInt(NORMAL_THEME_META_DATA_KEY, -1);
if (normalThemeRID != -1) {
setTheme(normalThemeRID);
}
} else {
Log.d(TAG, "Using the launch theme as normal theme.");
}
} catch (PackageManager.NameNotFoundException exception) {
Log.e(TAG, "Could not read meta-data for FlutterActivity. Using the launch theme as normal theme.");
}
}
@Nullable
@Override
public SplashScreen provideSplashScreen() {
Drawable manifestSplashDrawable = getSplashScreenFromManifest();
if (manifestSplashDrawable != null) {
return new DrawableSplashScreen(manifestSplashDrawable);
} else {
return null;
}
}
/**
* Returns a {@link Drawable} to be used as a splash screen as requested by meta-data in the
* {@code AndroidManifest.xml} file, or null if no such splash screen is requested.
* <p>
* See {@link #SPLASH_SCREEN_META_DATA_KEY} for the meta-data key to be used in a
* manifest file.
*/
@Nullable
@SuppressWarnings("deprecation")
private Drawable getSplashScreenFromManifest() {
try {
ActivityInfo activityInfo = getPackageManager().getActivityInfo(
getComponentName(),
PackageManager.GET_META_DATA | PackageManager.GET_ACTIVITIES
);
Bundle metadata = activityInfo.metaData;
Integer splashScreenId = metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : null;
return splashScreenId != null
? Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP
? getResources().getDrawable(splashScreenId, getTheme())
: getResources().getDrawable(splashScreenId)
: null;
} catch (PackageManager.NameNotFoundException e) {
// This is never expected to happen.
return null;
}
}
/**
* Sets this {@code Activity}'s {@code Window} background to be transparent, and hides the status
* bar, if this {@code Activity}'s desired {@link BackgroundMode} is {@link BackgroundMode#transparent}.
* <p>
* For {@code Activity} transparency to work as expected, the theme applied to this {@code Activity}
* must include {@code <item name="android:windowIsTranslucent">true</item>}.
*/
private void configureWindowForTransparency() {
BackgroundMode backgroundMode = getBackgroundMode();
if (backgroundMode == BackgroundMode.transparent) {
getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
);
}
}
@NonNull
private View createFlutterView() {
return delegate.onCreateView(
null /* inflater */,
null /* container */,
null /* savedInstanceState */);
}
private void configureStatusBarForFullscreenFlutterExperience() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
}
Utils.setStatusBarLightMode(this,true);
}
@Override
protected void onStart() {
super.onStart();
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
delegate.onStart();
}
@Override
protected void onResume() {
super.onResume();
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
delegate.onResume();
}
@Override
public void onPostResume() {
super.onPostResume();
delegate.onPostResume();
}
@Override
protected void onPause() {
super.onPause();
delegate.onPause();
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
}
@Override
protected void onStop() {
super.onStop();
delegate.onStop();
// lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}
@Override
protected void onDestroy() {
super.onDestroy();
delegate.onDestroyView();
delegate.onDetach();
// lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
delegate.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onNewIntent(@NonNull Intent intent) {
// TODO(mattcarroll): change G3 lint rule that forces us to call super
super.onNewIntent(intent);
delegate.onNewIntent(intent);
}
@Override
public void onBackPressed() {
delegate.onBackPressed();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
delegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onUserLeaveHint() {
delegate.onUserLeaveHint();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
delegate.onTrimMemory(level);
}
/**
* {@link FlutterActivityAndFragmentDelegate.Host} method that is used by
* {@link FlutterActivityAndFragmentDelegate} to obtain a {@code Context} reference as
* needed.
*/
@Override
@NonNull
public Context getContext() {
return this;
}
/**
* {@link FlutterActivityAndFragmentDelegate.Host} method that is used by
* {@link FlutterActivityAndFragmentDelegate} to obtain an {@code Activity} reference as
* needed. This reference is used by the delegate to instantiate a {@link FlutterView},
* a {@link PlatformPlugin}, and to determine if the {@code Activity} is changing
* configurations.
*/
@Override
@NonNull
public Activity getActivity() {
return this;
}
/**
* {@link FlutterActivityAndFragmentDelegate.Host} method that is used by
* {@link FlutterActivityAndFragmentDelegate} to obtain a {@code Lifecycle} reference as
* needed. This reference is used by the delegate to provide Flutter plugins with access
* to lifecycle events.
*/
@Override
@NonNull
public Lifecycle getLifecycle() {
return lifecycle;
}
/**
* {@link FlutterActivityAndFragmentDelegate.Host} method that is used by
* {@link FlutterActivityAndFragmentDelegate} to obtain Flutter shell arguments when
* initializing Flutter.
*/
@NonNull
@Override
public FlutterShellArgs getFlutterShellArgs() {
return FlutterShellArgs.fromIntent(getIntent());
}
/**
* Returns the ID of a statically cached {@link FlutterEngine} to use within this
* {@code FlutterActivity}, or {@code null} if this {@code FlutterActivity} does not want to
* use a cached {@link FlutterEngine}.
*/
@Override
@Nullable
public String getCachedEngineId() {
return getIntent().getStringExtra(EXTRA_CACHED_ENGINE_ID);
}
/**
* Returns false if the {@link FlutterEngine} backing this {@code FlutterActivity} should
* outlive this {@code FlutterActivity}, or true to be destroyed when the {@code FlutterActivity}
* is destroyed.
* <p>
* The default value is {@code true} in cases where {@code FlutterActivity} created its own
* {@link FlutterEngine}, and {@code false} in cases where a cached {@link FlutterEngine} was
* provided.
*/
@Override
public boolean shouldDestroyEngineWithHost() {
return getIntent().getBooleanExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, false);
}
/**
* The Dart entrypoint that will be executed as soon as the Dart snapshot is loaded.
* <p>
* This preference can be controlled with 2 methods:
* <ol>
* <li>Pass a {@code String} as {@link #EXTRA_DART_ENTRYPOINT} with the launching {@code Intent}, or</li>
* <li>Set a {@code <meta-data>} called {@link #DART_ENTRYPOINT_META_DATA_KEY} for this
* {@code Activity} in the Android manifest.</li>
* </ol>
* If both preferences are set, the {@code Intent} preference takes priority.
* <p>
* The reason that a {@code <meta-data>} preference is supported is because this {@code Activity}
* might be the very first {@code Activity} launched, which means the developer won't have
* control over the incoming {@code Intent}.
* <p>
* Subclasses may override this method to directly control the Dart entrypoint.
*/
@NonNull
public String getDartEntrypointFunctionName() {
if (getIntent().hasExtra(EXTRA_DART_ENTRYPOINT)) {
return getIntent().getStringExtra(EXTRA_DART_ENTRYPOINT);
}
try {
ActivityInfo activityInfo = getPackageManager().getActivityInfo(
getComponentName(),
PackageManager.GET_META_DATA | PackageManager.GET_ACTIVITIES
);
Bundle metadata = activityInfo.metaData;
String desiredDartEntrypoint = metadata != null ? metadata.getString(DART_ENTRYPOINT_META_DATA_KEY) : null;
return desiredDartEntrypoint != null ? desiredDartEntrypoint : DEFAULT_DART_ENTRYPOINT;
} catch (PackageManager.NameNotFoundException e) {
return DEFAULT_DART_ENTRYPOINT;
}
}
/**
* The initial route that a Flutter app will render upon loading and executing its Dart code.
* <p>
* This preference can be controlled with 2 methods:
* <ol>
* <li>Pass a boolean as {@link #EXTRA_INITIAL_ROUTE} with the launching {@code Intent}, or</li>
* <li>Set a {@code <meta-data>} called {@link #INITIAL_ROUTE_META_DATA_KEY} for this
* {@code Activity} in the Android manifest.</li>
* </ol>
* If both preferences are set, the {@code Intent} preference takes priority.
* <p>
* The reason that a {@code <meta-data>} preference is supported is because this {@code Activity}
* might be the very first {@code Activity} launched, which means the developer won't have
* control over the incoming {@code Intent}.
* <p>
* Subclasses may override this method to directly control the initial route.
*/
@NonNull
public String getInitialRoute() {
if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) {
return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE);
}
try {
ActivityInfo activityInfo = getPackageManager().getActivityInfo(
getComponentName(),
PackageManager.GET_META_DATA | PackageManager.GET_ACTIVITIES
);
Bundle metadata = activityInfo.metaData;
String desiredInitialRoute = metadata != null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null;
return desiredInitialRoute != null ? desiredInitialRoute : DEFAULT_INITIAL_ROUTE;
} catch (PackageManager.NameNotFoundException e) {
return DEFAULT_INITIAL_ROUTE;
}
}
/**
* The path to the bundle that contains this Flutter app's resources, e.g., Dart code snapshots.
* <p>
* When this {@code FlutterActivity} is run by Flutter tooling and a data String is included
* in the launching {@code Intent}, that data String is interpreted as an app bundle path.
* <p>
* By default, the app bundle path is obtained from {@link FlutterMain#findAppBundlePath()}.
* <p>
* Subclasses may override this method to return a custom app bundle path.
*/
@NonNull
public String getAppBundlePath() {
// If this Activity was launched from tooling, and the incoming Intent contains
// a custom app bundle path, return that path.
// TODO(mattcarroll): determine if we should have an explicit FlutterTestActivity instead of conflating.
if (isDebuggable() && Intent.ACTION_RUN.equals(getIntent().getAction())) {
String appBundlePath = getIntent().getDataString();
if (appBundlePath != null) {
return appBundlePath;
}
}
// Return the default app bundle path.
// TODO(mattcarroll): move app bundle resolution into an appropriately named class.
return FlutterMain.findAppBundlePath();
}
/**
* Returns true if Flutter is running in "debug mode", and false otherwise.
* <p>
* Debug mode allows Flutter to operate with hot reload and hot restart. Release mode does not.
*/
private boolean isDebuggable() {
return (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
/**
* {@link FlutterActivityAndFragmentDelegate.Host} method that is used by
* {@link FlutterActivityAndFragmentDelegate} to obtain the desired {@link FlutterView.RenderMode}
* that should be used when instantiating a {@link FlutterView}.
*/
@NonNull
@Override
public FlutterView.RenderMode getRenderMode() {
return getBackgroundMode() == BackgroundMode.opaque
? FlutterView.RenderMode.surface
: FlutterView.RenderMode.texture;
}
/**
* {@link FlutterActivityAndFragmentDelegate.Host} method that is used by
* {@link FlutterActivityAndFragmentDelegate} to obtain the desired
* {@link FlutterView.TransparencyMode} that should be used when instantiating a
* {@link FlutterView}.
*/
@NonNull
@Override
public FlutterView.TransparencyMode getTransparencyMode() {
return getBackgroundMode() == BackgroundMode.opaque
? FlutterView.TransparencyMode.opaque
: FlutterView.TransparencyMode.transparent;
}
/**
* The desired window background mode of this {@code Activity}, which defaults to
* {@link BackgroundMode#opaque}.
*/
@NonNull
protected BackgroundMode getBackgroundMode() {
if (getIntent().hasExtra(EXTRA_BACKGROUND_MODE)) {
return BackgroundMode.valueOf(getIntent().getStringExtra(EXTRA_BACKGROUND_MODE));
} else {
return BackgroundMode.opaque;
}
}
/**
* Hook for subclasses to easily provide a custom {@link FlutterEngine}.
* <p>
* This hook is where a cached {@link FlutterEngine} should be provided, if a cached
* {@link FlutterEngine} is desired.
*/
@Nullable
@Override
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
// No-op. Hook for subclasses.
return NewFlutterBoost.instance().engineProvider().provideEngine(this);
}
/**
* Hook for subclasses to obtain a reference to the {@link FlutterEngine} that is owned
* by this {@code FlutterActivity}.
*/
@Nullable
protected FlutterEngine getFlutterEngine() {
return delegate.getFlutterEngine();
}
@Nullable
@Override
public PlatformPlugin providePlatformPlugin(@Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
if (activity != null) {
return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel());
} else {
return null;
}
}
/**
* Hook for subclasses to easily configure a {@code FlutterEngine}, e.g., register
* plugins.
* <p>
* This method is called after {@link #provideFlutterEngine(Context)}.
*/
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
// No-op. Hook for subclasses.
}
@Override
public boolean shouldAttachEngineToActivity() {
return true;
}
@Override
public void onFirstFrameRendered() {
}
/**
* The mode of the background of a {@code FlutterActivity}, either opaque or transparent.
*/
public enum BackgroundMode {
/**
* Indicates a FlutterActivity with an opaque background. This is the default.
*/
opaque,
/**
* Indicates a FlutterActivity with a transparent background.
*/
transparent
}
}
...@@ -26,6 +26,8 @@ package com.idlefish.flutterboost.interfaces; ...@@ -26,6 +26,8 @@ package com.idlefish.flutterboost.interfaces;
import android.content.Context; import android.content.Context;
import com.idlefish.flutterboost.BoostFlutterEngine; import com.idlefish.flutterboost.BoostFlutterEngine;
import io.flutter.embedding.engine.FlutterEngine;
/** /**
* a flutter engine provider * a flutter engine provider
*/ */
...@@ -36,18 +38,18 @@ public interface IFlutterEngineProvider { ...@@ -36,18 +38,18 @@ public interface IFlutterEngineProvider {
* @param context * @param context
* @return * @return
*/ */
BoostFlutterEngine createEngine(Context context); FlutterEngine createEngine(Context context);
/** /**
* provide a flutter engine * provide a flutter engine
* @param context * @param context
* @return * @return
*/ */
BoostFlutterEngine provideEngine(Context context); FlutterEngine provideEngine(Context context);
/** /**
* may return null * may return null
* @return * @return
*/ */
BoostFlutterEngine tryGetEngine(); FlutterEngine tryGetEngine();
} }
...@@ -26,6 +26,7 @@ package com.idlefish.flutterboost.interfaces; ...@@ -26,6 +26,7 @@ package com.idlefish.flutterboost.interfaces;
import android.app.Activity; import android.app.Activity;
import com.idlefish.flutterboost.BoostFlutterView; import com.idlefish.flutterboost.BoostFlutterView;
import com.idlefish.flutterboost.containers.FlutterSplashView;
import java.util.Map; import java.util.Map;
...@@ -41,7 +42,7 @@ public interface IFlutterViewContainer { ...@@ -41,7 +42,7 @@ public interface IFlutterViewContainer {
* provide a flutter view * provide a flutter view
* @return * @return
*/ */
BoostFlutterView getBoostFlutterView(); FlutterSplashView getBoostFlutterView();
/** /**
* call to destroy the container * call to destroy the container
......
package com.idlefish.flutterboost.interfaces;
import android.content.Context;
import java.util.Map;
public interface INativeRouter {
void openContainer(Context context, String url, Map<String,Object> urlParams, int requestCode, Map<String,Object> exts);
void closeContainer(IContainerRecord record, Map<String,Object> result, Map<String,Object> exts);
}
package com.idlefish.flutterboost.interfaces; package com.idlefish.flutterboost.interfaces;
import com.idlefish.flutterboost.BoostChannel; import com.idlefish.flutterboost.FlutterBoostPlugin;
import com.idlefish.flutterboost.BoostFlutterEngine; import com.idlefish.flutterboost.BoostFlutterEngine;
import com.idlefish.flutterboost.BoostFlutterView; import com.idlefish.flutterboost.BoostFlutterView;
...@@ -9,7 +9,7 @@ import io.flutter.plugin.common.PluginRegistry; ...@@ -9,7 +9,7 @@ import io.flutter.plugin.common.PluginRegistry;
public interface IStateListener { public interface IStateListener {
void onEngineCreated(BoostFlutterEngine engine); void onEngineCreated(BoostFlutterEngine engine);
void onEngineStarted(BoostFlutterEngine engine); void onEngineStarted(BoostFlutterEngine engine);
void onChannelRegistered(PluginRegistry.Registrar registrar, BoostChannel channel); void onChannelRegistered(PluginRegistry.Registrar registrar, FlutterBoostPlugin channel);
void onFlutterViewInited(BoostFlutterEngine engine, BoostFlutterView flutterView); void onFlutterViewInited(BoostFlutterEngine engine, BoostFlutterView flutterView);
void beforeEngineAttach(BoostFlutterEngine engine, BoostFlutterView flutterView); void beforeEngineAttach(BoostFlutterEngine engine, BoostFlutterView flutterView);
void afterEngineAttached(BoostFlutterEngine engine, BoostFlutterView flutterView); void afterEngineAttached(BoostFlutterEngine engine, BoostFlutterView flutterView);
......
...@@ -25,7 +25,7 @@ apply plugin: 'com.android.application' ...@@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 26 compileSdkVersion 28
lintOptions { lintOptions {
disable 'InvalidPackage' disable 'InvalidPackage'
...@@ -35,7 +35,7 @@ android { ...@@ -35,7 +35,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.taobao.idlefish.flutterboostexample" applicationId "com.taobao.idlefish.flutterboostexample"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 26 targetSdkVersion 28
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
...@@ -58,6 +58,6 @@ dependencies { ...@@ -58,6 +58,6 @@ dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:support-v4:26.1.0' implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:appcompat-v7:26.1.0'
} }
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.taobao.idlefish.flutterboostexample"> package="com.taobao.idlefish.flutterboostexample">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
...@@ -33,6 +34,10 @@ ...@@ -33,6 +34,10 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".FlutterPageActivity" android:name=".FlutterPageActivity"
android:theme="@style/Theme.AppCompat" android:theme="@style/Theme.AppCompat"
...@@ -40,6 +45,15 @@ ...@@ -40,6 +45,15 @@
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"/> android:windowSoftInputMode="adjustResize"/>
<activity
android:name="com.idlefish.flutterboost.containers.NewBoostFlutterActivity"
android:theme="@style/Theme.AppCompat"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" >
<!--<meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/avd_hide_password"/>-->
</activity>
<activity <activity
android:name=".FlutterFragmentPageActivity" android:name=".FlutterFragmentPageActivity"
android:theme="@style/Theme.AppCompat" android:theme="@style/Theme.AppCompat"
......
...@@ -4,6 +4,7 @@ import android.os.Bundle; ...@@ -4,6 +4,7 @@ import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.idlefish.flutterboost.containers.BoostFlutterFragment; import com.idlefish.flutterboost.containers.BoostFlutterFragment;
import com.idlefish.flutterboost.containers.FlutterSplashView;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
...@@ -25,6 +26,11 @@ public class FlutterFragment extends BoostFlutterFragment { ...@@ -25,6 +26,11 @@ public class FlutterFragment extends BoostFlutterFragment {
super.setArguments(args); super.setArguments(args);
} }
@Override
public FlutterSplashView getBoostFlutterView() {
return null;
}
@Override @Override
public String getContainerUrl() { public String getContainerUrl() {
return "flutterFragment"; return "flutterFragment";
......
package com.taobao.idlefish.flutterboostexample; package com.taobao.idlefish.flutterboostexample;
import com.idlefish.flutterboost.containers.BoostFlutterActivity; import com.idlefish.flutterboost.containers.BoostFlutterActivity;
import com.idlefish.flutterboost.containers.FlutterSplashView;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class FlutterPageActivity extends BoostFlutterActivity { public class FlutterPageActivity extends BoostFlutterActivity {
@Override
public FlutterSplashView getBoostFlutterView() {
return null;
}
/** /**
* 该方法返回当前Activity在Flutter层对应的name, * 该方法返回当前Activity在Flutter层对应的name,
* 混合栈将会在flutter层根据这个名字,在注册的Route表中查找对应的Widget * 混合栈将会在flutter层根据这个名字,在注册的Route表中查找对应的Widget
......
...@@ -3,66 +3,74 @@ package com.taobao.idlefish.flutterboostexample; ...@@ -3,66 +3,74 @@ package com.taobao.idlefish.flutterboostexample;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import com.idlefish.flutterboost.BoostChannel; import com.idlefish.flutterboost.*;
import com.idlefish.flutterboost.BoostEngineProvider; import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.idlefish.flutterboost.BoostFlutterEngine;
import com.idlefish.flutterboost.FlutterBoost;
import com.idlefish.flutterboost.Platform;
import com.idlefish.flutterboost.interfaces.IFlutterEngineProvider; import com.idlefish.flutterboost.interfaces.IFlutterEngineProvider;
import java.util.Map; import java.util.Map;
import com.idlefish.flutterboost.interfaces.INativeRouter;
import io.flutter.app.FlutterApplication; import io.flutter.app.FlutterApplication;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.view.FlutterMain;
public class MyApplication extends FlutterApplication { public class MyApplication extends FlutterApplication {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
//
// FlutterBoost.init(new Platform() {
//
// @Override
// public Application getApplication() {
// return MyApplication.this;
// }
//
// @Override
// public boolean isDebug() {
// return true;
// }
//
// @Override
// public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
// PageRouter.openPageByUrl(context, url, urlParams, requestCode);
// }
//
// @Override
// public IFlutterEngineProvider engineProvider() {
// return new BoostEngineProvider() {
// @Override
// public BoostFlutterEngine createEngine(Context context) {
// return new BoostFlutterEngine(context);
// }
// };
// }
//
// @Override
// public int whenEngineStart() {
// return ANY_ACTIVITY_CREATED;
// }
// });
FlutterBoost.init(new Platform() { // FlutterBoostPlugin.addActionAfterRegistered(new FlutterBoostPlugin.ActionAfterRegistered() {
// @Override
// public void onChannelRegistered(FlutterBoostPlugin channel) {
// //platform view register should use FlutterPluginRegistry instread of BoostPluginRegistry
// TextPlatformViewPlugin.register(FlutterBoost.singleton().engineProvider().tryGetEngine().getPluginRegistry());
// }
// });
@Override
public Application getApplication() {
return MyApplication.this;
}
@Override
public boolean isDebug() {
return true;
}
INativeRouter router =new INativeRouter() {
@Override @Override
public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) { public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
PageRouter.openPageByUrl(context, url, urlParams, requestCode); PageRouter.openPageByUrl(context,url, urlParams);
}
@Override
public IFlutterEngineProvider engineProvider() {
return new BoostEngineProvider() {
@Override
public BoostFlutterEngine createEngine(Context context) {
return new BoostFlutterEngine(context, new DartExecutor.DartEntrypoint(
context.getResources().getAssets(),
FlutterMain.findAppBundlePath(context),
"main"), "/");
}
};
} }
@Override @Override
public int whenEngineStart() { public void closeContainer(IContainerRecord record, Map<String, Object> result, Map<String, Object> exts) {
return ANY_ACTIVITY_CREATED;
}
});
BoostChannel.addActionAfterRegistered(new BoostChannel.ActionAfterRegistered() {
@Override
public void onChannelRegistered(BoostChannel channel) {
//platform view register should use FlutterPluginRegistry instread of BoostPluginRegistry
TextPlatformViewPlugin.register(FlutterBoost.singleton().engineProvider().tryGetEngine().getPluginRegistry());
} }
}); };
Platform platform= new NewFlutterBoost.ConfigBuilder(this,router).build();
NewFlutterBoost.instance().init(platform);
} }
} }
...@@ -3,6 +3,7 @@ package com.taobao.idlefish.flutterboostexample; ...@@ -3,6 +3,7 @@ package com.taobao.idlefish.flutterboostexample;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.text.TextUtils; import android.text.TextUtils;
import com.idlefish.flutterboost.containers.NewBoostFlutterActivity;
import java.util.Map; import java.util.Map;
...@@ -19,10 +20,11 @@ public class PageRouter { ...@@ -19,10 +20,11 @@ public class PageRouter {
public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) { public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) {
try { try {
if (url.startsWith(FLUTTER_PAGE_URL)) { if (url.startsWith(FLUTTER_PAGE_URL)) {
context.startActivity(new Intent(context, FlutterPageActivity.class));
context.startActivity( NewBoostFlutterActivity.createDefaultIntent(context));
return true; return true;
} else if (url.startsWith(FLUTTER_FRAGMENT_PAGE_URL)) { } else if (url.startsWith(FLUTTER_FRAGMENT_PAGE_URL)) {
context.startActivity(new Intent(context, FlutterFragmentPageActivity.class)); // context.startActivity(new Intent(context, FlutterFragmentPageActivity.class));
return true; return true;
} else if (url.startsWith(NATIVE_PAGE_URL)) { } else if (url.startsWith(NATIVE_PAGE_URL)) {
context.startActivity(new Intent(context, NativePageActivity.class)); context.startActivity(new Intent(context, NativePageActivity.class));
......
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
// //
#import "GeneratedPluginRegistrant.h" #import "GeneratedPluginRegistrant.h"
#import <flutter_boost/BoostChannel.h> #import <flutter_boost/FlutterBoostPlugin.h>
@implementation GeneratedPluginRegistrant @implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry { + (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[BoostChannel registerWithRegistrar:[registry registrarForPlugin:@"BoostChannel"]]; [FlutterBoostPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterBoostPlugin"]];
} }
@end @end
...@@ -36,7 +36,7 @@ class _MyAppState extends State<MyApp> { ...@@ -36,7 +36,7 @@ class _MyAppState extends State<MyApp> {
return MaterialApp( return MaterialApp(
title: 'Flutter Boost example', title: 'Flutter Boost example',
builder: FlutterBoost.init(postPush: _onRoutePushed), builder: FlutterBoost.init(postPush: _onRoutePushed),
home: Container()); home: FlutterRouteWidget());
} }
void _onRoutePushed( void _onRoutePushed(
......
...@@ -19,7 +19,7 @@ dependencies: ...@@ -19,7 +19,7 @@ dependencies:
flutter: flutter:
plugin: plugin:
androidPackage: com.idlefish.flutterboost androidPackage: com.idlefish.flutterboost
pluginClass: BoostChannel pluginClass: FlutterBoostPlugin
# To add assets to your plugin package, add an assets section, like this: # To add assets to your plugin package, add an assets section, like this:
# assets: # assets:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment