Commit 68357859 authored by 余玠's avatar 余玠

Merge branch 'v1.5.4-hotfixes'

# Conflicts:
#	android/src/main/java/com/taobao/idlefish/flutterboost/NavigationService/NavigationServiceRegister.java
#	android/src/main/java/com/taobao/idlefish/flutterboost/containers/BoostFlutterActivity.java
#	example/ios/.symlinks/plugins/flutter_boost
#	example/ios/.symlinks/plugins/xservice_kit
#	example/ios/Podfile.lock
#	example/ios/Runner/DemoRouter.m
#	ios/Classes/Boost/FLBPlatform.h
#	ios/Classes/Container/FLBFlutterViewContainer.m
#	ios/Classes/Messaging/BoostMessageChannel.h
#	ios/Classes/Messaging/BoostMessageChannel.mm
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_closePage.h
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_closePage.mm
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_onFlutterPageResult.h
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_onFlutterPageResult.mm
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_onShownContainerChanged.h
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_onShownContainerChanged.mm
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_openPage.h
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_openPage.mm
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_pageOnStart.h
#	ios/Classes/Messaging/Generated/NavigationService/handlers/NavigationService_pageOnStart.mm
#	lib/AIOService/NavigationService/service/NavigationService.dart
#	lib/AIOService/NavigationService/service/NavigationServiceRegister.dart
#	lib/container/container_manager.dart
parents 5bdda596 a761a6e2
......@@ -130,7 +130,7 @@ Initialize FlutterBoost with FLBPlatform at the beginning of your App.
```objc
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router
onStart:^(FlutterViewController *fvc) {
onStart:^(id engine) {
}];
```
......
......@@ -123,7 +123,7 @@ class _MyAppState extends State<MyApp> {
```的ObjectiveC
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router
onStart:^(FlutterViewController * fvc){
onStart:^(id engine){
}];
```
......
group 'com.taobao.idlefish.flutterboost'
group 'com.idlefish.flutterboost'
version '1.0-SNAPSHOT'
buildscript {
......@@ -23,7 +23,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
buildToolsVersion '27.0.3'
defaultConfig {
minSdkVersion 16
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
......@@ -37,7 +37,6 @@ dependencies {
implementation 'com.alibaba:fastjson:1.2.41'
implementation 'com.android.support:support-v4:26.1.0'
implementation 'com.android.support:appcompat-v7:26.1.0'
provided rootProject.findProject(":xservice_kit")
}
ext {
......
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.idlefish.flutterboost">
<application
android:allowBackup="false">
<activity
android:name="com.taobao.idlefish.flutterboost.BoostFlutterActivity"
android:launchMode="standard"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"/>
</application>
package="com.idlefish.flutterboost">
</manifest>
package com.idlefish.flutterboost;
import android.support.annotation.Nullable;
import com.idlefish.flutterboost.interfaces.IStateListener;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
public class BoostChannel {
private static BoostChannel sInstance;
private final MethodChannel mMethodChannel;
private final Set<MethodChannel.MethodCallHandler> mMethodCallHandlers = new HashSet<>();
private final Map<String,Set<EventListener>> mEventListeners = new HashMap<>();
private static final Set<ActionAfterRegistered> sActions = new HashSet<>();
public static BoostChannel singleton() {
if (sInstance == null) {
throw new RuntimeException("BoostChannel not register yet");
}
return sInstance;
}
public static void addActionAfterRegistered(ActionAfterRegistered action) {
if(action == null) return;
if(sInstance == null) {
sActions.add(action);
}else{
action.onChannelRegistered(sInstance);
}
}
public static void registerWith(PluginRegistry.Registrar registrar) {
sInstance = new BoostChannel(registrar);
for(ActionAfterRegistered a : sActions) {
a.onChannelRegistered(sInstance);
}
if(FlutterBoost.sInstance != null) {
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if (stateListener != null) {
stateListener.onChannelRegistered(registrar, sInstance);
}
}
sActions.clear();
}
private BoostChannel(PluginRegistry.Registrar registrar){
mMethodChannel = new MethodChannel(registrar.messenger(), "flutter_boost");
mMethodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.equals("__event__")) {
String name = methodCall.argument("name");
Map args = methodCall.argument("arguments");
Object[] listeners = null;
synchronized (mEventListeners) {
Set<EventListener> set = mEventListeners.get(name);
if (set != null) {
listeners = set.toArray();
}
}
if(listeners != null) {
for(Object o:listeners) {
((EventListener)o).onEvent(name,args);
}
}
}else{
Object[] handlers;
synchronized (mMethodCallHandlers) {
handlers = mMethodCallHandlers.toArray();
}
for(Object o:handlers) {
((MethodChannel.MethodCallHandler)o).onMethodCall(methodCall,result);
}
}
}
});
}
public void invokeMethodUnsafe(final String name,Serializable args){
invokeMethod(name, args, new MethodChannel.Result() {
@Override
public void success(@Nullable Object o) {
//every thing ok...
}
@Override
public void error(String s, @Nullable String s1, @Nullable Object o) {
Debuger.log("invoke method "+name+" error:"+s+" | "+s1);
}
@Override
public void notImplemented() {
Debuger.log("invoke method "+name+" notImplemented");
}
});
}
public void invokeMethod(final String name,Serializable args){
invokeMethod(name, args, new MethodChannel.Result() {
@Override
public void success(@Nullable Object o) {
//every thing ok...
}
@Override
public void error(String s, @Nullable String s1, @Nullable Object o) {
Debuger.exception("invoke method "+name+" error:"+s+" | "+s1);
}
@Override
public void notImplemented() {
Debuger.exception("invoke method "+name+" notImplemented");
}
});
}
public void invokeMethod(final String name,Serializable args,MethodChannel.Result result){
if("__event__".equals(name)) {
Debuger.exception("method name should not be __event__");
}
mMethodChannel.invokeMethod(name, args, result);
}
public void addMethodCallHandler(MethodChannel.MethodCallHandler handler) {
synchronized (mMethodCallHandlers){
mMethodCallHandlers.add(handler);
}
}
public void removeMethodCallHandler(MethodChannel.MethodCallHandler handler) {
synchronized (mMethodCallHandlers) {
mMethodCallHandlers.remove(handler);
}
}
public void addEventListener(String name, EventListener listener) {
synchronized (mEventListeners){
Set<EventListener> set = mEventListeners.get(name);
if(set == null) {
set = new HashSet<>();
}
set.add(listener);
mEventListeners.put(name,set);
}
}
public void removeEventListener(EventListener listener) {
synchronized (mEventListeners) {
for(Set<EventListener> set:mEventListeners.values()) {
set.remove(listener);
}
}
}
public void sendEvent(String name,Map args){
Map event = new HashMap();
event.put("name",name);
event.put("arguments",args);
mMethodChannel.invokeMethod("__event__",event);
}
public interface EventListener {
void onEvent(String name, Map args);
}
public interface ActionAfterRegistered {
void onChannelRegistered(BoostChannel channel);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.idlefish.flutterboost;
import android.content.Context;
import com.idlefish.flutterboost.interfaces.IFlutterEngineProvider;
import com.idlefish.flutterboost.interfaces.IStateListener;
import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.view.FlutterMain;
public class BoostEngineProvider implements IFlutterEngineProvider {
private BoostFlutterEngine mEngine = null;
public BoostEngineProvider() {}
@Override
public BoostFlutterEngine createEngine(Context context) {
return new BoostFlutterEngine(context.getApplicationContext());
}
@Override
public BoostFlutterEngine provideEngine(Context context) {
Utils.assertCallOnMainThread();
if (mEngine == null) {
FlutterShellArgs flutterShellArgs = new FlutterShellArgs(new String[0]);
FlutterMain.ensureInitializationComplete(
context.getApplicationContext(), flutterShellArgs.toArray());
mEngine = createEngine(context.getApplicationContext());
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if(stateListener != null) {
stateListener.onEngineCreated(mEngine);
}
}
return mEngine;
}
@Override
public BoostFlutterEngine tryGetEngine() {
return mEngine;
}
public static void assertEngineRunning(){
final BoostFlutterEngine engine = FlutterBoost.singleton().engineProvider().tryGetEngine();
if(engine == null || !engine.isRunning()) {
throw new RuntimeException("engine is not running yet!");
}
}
}
package com.idlefish.flutterboost;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.Surface;
import android.view.View;
import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.idlefish.flutterboost.interfaces.IStateListener;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import io.flutter.app.FlutterPluginRegistry;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterView;
import io.flutter.view.TextureRegistry;
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;
public BoostFlutterEngine(@NonNull Context context) {
this(context, null, null);
}
public BoostFlutterEngine(@NonNull Context context, DartExecutor.DartEntrypoint entrypoint, String initRoute) {
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()) {
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) {
return new DartExecutor.DartEntrypoint(
context.getResources().getAssets(),
FlutterMain.findAppBundlePath(context),
"main");
}
protected String defaultInitialRoute(Context context) {
return "/";
}
public BoostPluginRegistry getBoostPluginRegistry() {
return mBoostPluginRegistry;
}
public boolean isRunning() {
return getDartExecutor().isExecutingDart();
}
@NonNull
@Override
public FlutterRenderer getRenderer() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
boolean hit = false;
for (StackTraceElement st : stackTrace) {
if (st.getMethodName().equals("sendViewportMetricsToFlutter")) {
hit = true;
break;
}
}
if (hit) {
return mFakeRender;
} else {
return super.getRenderer();
}
}
public class BoostPluginRegistry extends FlutterPluginRegistry {
private final 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);
}
}
private boolean viewportMetricsEqual(FlutterRenderer.ViewportMetrics a, FlutterRenderer.ViewportMetrics b) {
return a != null && b != null &&
a.height == b.height &&
a.width == b.width &&
a.devicePixelRatio == b.devicePixelRatio &&
a.paddingBottom == b.paddingBottom &&
a.paddingLeft == b.paddingLeft &&
a.paddingRight == b.paddingRight &&
a.paddingTop == b.paddingTop &&
a.viewInsetLeft == b.viewInsetLeft &&
a.viewInsetRight == b.viewInsetRight &&
a.viewInsetTop == b.viewInsetTop &&
a.viewInsetBottom == b.viewInsetBottom;
}
class FakeRender extends FlutterRenderer {
private ViewportMetrics last;
public FakeRender(FlutterJNI flutterJNI) {
super(flutterJNI);
}
@Override
public void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics) {
if (viewportMetrics.width > 0 && viewportMetrics.height > 0 /*&& !viewportMetricsEqual(last, viewportMetrics)*/) {
last = viewportMetrics;
Debuger.log("setViewportMetrics w:" + viewportMetrics.width + " h:" + viewportMetrics.height);
super.setViewportMetrics(viewportMetrics);
}
}
@Override
public void attachToRenderSurface(@NonNull RenderSurface renderSurface) {
Debuger.exception("should never called!");
}
@Override
public void detachFromRenderSurface() {
Debuger.exception("should never called!");
}
@Override
public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) {
Debuger.exception("should never called!");
}
@Override
public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) {
Debuger.exception("should never called!");
}
@Override
public SurfaceTextureEntry createSurfaceTexture() {
Debuger.exception("should never called!");
return null;
}
@Override
public void surfaceCreated(Surface surface) {
Debuger.exception("should never called!");
}
@Override
public void surfaceChanged(int width, int height) {
Debuger.exception("should never called!");
}
@Override
public void surfaceDestroyed() {
Debuger.exception("should never called!");
}
@Override
public Bitmap getBitmap() {
Debuger.exception("should never called!");
return null;
}
@Override
public void dispatchPointerDataPacket(ByteBuffer buffer, int position) {
Debuger.exception("should never called!");
}
@Override
public boolean isSoftwareRenderingEnabled() {
Debuger.exception("should never called!");
return false;
}
@Override
public void setAccessibilityFeatures(int flags) {
Debuger.exception("should never called!");
}
@Override
public void setSemanticsEnabled(boolean enabled) {
Debuger.exception("should never called!");
}
@Override
public void dispatchSemanticsAction(int id, int action, ByteBuffer args, int argsPosition) {
Debuger.exception("should never called!");
}
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.idlefish.flutterboost;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityNodeProvider;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.idlefish.flutterboost.interfaces.IStateListener;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import io.flutter.embedding.android.FlutterView;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.view.AccessibilityBridge;
public class BoostFlutterView extends FrameLayout {
private BoostFlutterEngine mFlutterEngine;
private XFlutterView mFlutterView;
private PlatformPlugin mPlatformPlugin;
private Bundle mArguments;
private RenderingProgressCoverCreator mRenderingProgressCoverCreator;
private View mRenderingProgressCover;
private final List<OnFirstFrameRenderedListener> mFirstFrameRenderedListeners = new LinkedList<>();
private boolean mEngineAttached = false;
private boolean mNeedSnapshotWhenDetach = false;
private SnapshotView mSnapshot;
private final io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener mOnFirstFrameRenderedListener =
new io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener() {
@Override
public void onFirstFrameRendered() {
Debuger.log("BoostFlutterView onFirstFrameRendered");
if(mRenderingProgressCover != null && mRenderingProgressCover.getParent() != null) {
((ViewGroup)mRenderingProgressCover.getParent()).removeView(mRenderingProgressCover);
}
if(mNeedSnapshotWhenDetach) {
mSnapshot.dismissSnapshot(BoostFlutterView.this);
}
final Object[] listeners = mFirstFrameRenderedListeners.toArray();
for (Object obj : listeners) {
((OnFirstFrameRenderedListener) obj).onFirstFrameRendered(BoostFlutterView.this);
}
}
};
private final ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewCompat.requestApplyInsets(mFlutterView);
}
};
public BoostFlutterView(Context context, BoostFlutterEngine engine, Bundle args, RenderingProgressCoverCreator creator) {
super(context);
mFlutterEngine = engine;
mArguments = args;
mRenderingProgressCoverCreator = creator;
init();
}
private void init() {
if (mFlutterEngine == null) {
mFlutterEngine = createFlutterEngine(getContext());
}
if (mArguments == null) {
mArguments = new Bundle();
}
mPlatformPlugin = new PlatformPlugin((Activity) getContext(), mFlutterEngine.getPlatformChannel());
mFlutterView = new XFlutterView(getContext(), getRenderMode(), getTransparencyMode());
addView(mFlutterView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mSnapshot = new SnapshotView(getContext());
if(mRenderingProgressCoverCreator != null) {
mRenderingProgressCover = mRenderingProgressCoverCreator
.createRenderingProgressCover(getContext());
}else{
mRenderingProgressCover = createRenderingProgressCorver();
}
if(mRenderingProgressCover != null) {
addView(mRenderingProgressCover, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
mFlutterView.addOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener);
mFlutterEngine.startRun((Activity)getContext());
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if(stateListener != null) {
stateListener.onFlutterViewInited(mFlutterEngine,this);
}
checkAssert();
}
private void checkAssert(){
try {
Method method = FlutterView.class.getDeclaredMethod("sendViewportMetricsToFlutter");
if(method == null) {
throw new Exception("method: FlutterView.sendViewportMetricsToFlutter not found!");
}
}catch (Throwable t){
Debuger.exception(t);
}
}
protected View createRenderingProgressCorver(){
FrameLayout frameLayout = new FrameLayout(getContext());
frameLayout.setBackgroundColor(Color.WHITE);
LinearLayout linearLayout = new LinearLayout(getContext());
linearLayout.setOrientation(LinearLayout.VERTICAL);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
frameLayout.addView(linearLayout,layoutParams);
ProgressBar progressBar = new ProgressBar(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
linearLayout.addView(progressBar,params);
TextView textView = new TextView(getContext());
params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
textView.setText("Frame Rendering...");
linearLayout.addView(textView,params);
return frameLayout;
}
protected BoostFlutterEngine createFlutterEngine(Context context) {
return FlutterBoost.singleton().engineProvider().provideEngine(context);
}
public void addFirstFrameRendered(OnFirstFrameRenderedListener listener) {
mFirstFrameRenderedListeners.add(listener);
}
public void removeFirstFrameRendered(OnFirstFrameRenderedListener listener) {
mFirstFrameRenderedListeners.remove(listener);
}
protected FlutterView.RenderMode getRenderMode() {
String renderModeName = mArguments.getString("flutterview_render_mode", FlutterView.RenderMode.surface.name());
return FlutterView.RenderMode.valueOf(renderModeName);
}
protected FlutterView.TransparencyMode getTransparencyMode() {
String transparencyModeName = mArguments.getString("flutterview_transparency_mode", FlutterView.TransparencyMode.transparent.name());
return FlutterView.TransparencyMode.valueOf(transparencyModeName);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mPlatformPlugin.onPostResume();
ViewCompat.requestApplyInsets(this);
getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
onDetach();
}
public BoostFlutterEngine getEngine(){
return mFlutterEngine;
}
public void onResume() {
Debuger.log("BoostFlutterView onResume");
// mFlutterEngine.getLifecycleChannel().appIsResumed();
}
// public void onPostResume() {
// Debuger.log("BoostFlutterView onPostResume");
// mPlatformPlugin.onPostResume();
// }
public void onPause() {
Debuger.log("BoostFlutterView onPause");
// mFlutterEngine.getLifecycleChannel().appIsInactive();
}
public void onStop() {
Debuger.log("BoostFlutterView onStop");
// mFlutterEngine.getLifecycleChannel().appIsPaused();
}
public void onAttach() {
Debuger.log("BoostFlutterView onAttach");
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if(stateListener != null) {
stateListener.beforeEngineAttach(mFlutterEngine,this);
}
mFlutterView.attachToFlutterEngine(mFlutterEngine);
mEngineAttached = true;
if(stateListener != null) {
stateListener.afterEngineAttached(mFlutterEngine,this);
}
}
public void toggleSnapshot() {
mSnapshot.toggleSnapshot(this);
}
public void toggleAttach() {
if(mEngineAttached) {
onDetach();
}else{
onAttach();
}
}
public void onDetach() {
Debuger.log("BoostFlutterView onDetach");
if(mNeedSnapshotWhenDetach) {
mSnapshot.showSnapshot(BoostFlutterView.this);
}
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if(stateListener != null) {
stateListener.beforeEngineDetach(mFlutterEngine,this);
}
mFlutterView.detachFromFlutterEngine();
mEngineAttached = false;
if(stateListener != null) {
stateListener.afterEngineDetached(mFlutterEngine,this);
}
}
public void onDestroy() {
Debuger.log("BoostFlutterView onDestroy");
mFlutterView.removeOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener);
AccessibilityBridge bridge=mFlutterView.getAccessibilityBridge();
if(bridge!=null){
bridge.release();
}
}
//混合栈的返回和原来Flutter的返回逻辑不同
public void onBackPressed() {
// Debuger.log("onBackPressed()");
// if (mFlutterEngine != null) {
// mFlutterEngine.getNavigationChannel().popRoute();
// } else {
// Debuger.log("Invoked onBackPressed() before BoostFlutterView was attached to an Activity.");
// }
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (mFlutterEngine != null) {
mFlutterEngine.getPluginRegistry().onRequestPermissionsResult(requestCode, permissions, grantResults);
} else {
Debuger.log("onRequestPermissionResult() invoked before BoostFlutterView was attached to an Activity.");
}
}
public void onNewIntent(Intent intent) {
if (mFlutterEngine != null) {
mFlutterEngine.getPluginRegistry().onNewIntent(intent);
} else {
Debuger.log("onNewIntent() invoked before BoostFlutterView was attached to an Activity.");
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mFlutterEngine != null) {
mFlutterEngine.getPluginRegistry().onActivityResult(requestCode, resultCode, data);
} else {
Debuger.log("onActivityResult() invoked before BoostFlutterView was attached to an Activity.");
}
}
public void onUserLeaveHint() {
if (mFlutterEngine != null) {
mFlutterEngine.getPluginRegistry().onUserLeaveHint();
} else {
Debuger.log("onUserLeaveHint() invoked before BoostFlutterView was attached to an Activity.");
}
}
public void onTrimMemory(int level) {
if (mFlutterEngine != null) {
if (level == 10) {
mFlutterEngine.getSystemChannel().sendMemoryPressureWarning();
}
} else {
Debuger.log("onTrimMemory() invoked before BoostFlutterView was attached to an Activity.");
}
}
public void onLowMemory() {
mFlutterEngine.getSystemChannel().sendMemoryPressureWarning();
}
public static class Builder {
private Context context;
private BoostFlutterEngine engine;
private FlutterView.RenderMode renderMode;
private FlutterView.TransparencyMode transparencyMode;
private RenderingProgressCoverCreator renderingProgressCoverCreator;
public Builder(Context ctx) {
this.context = ctx;
renderMode = FlutterView.RenderMode.surface;
transparencyMode = FlutterView.TransparencyMode.transparent;
}
public Builder flutterEngine(BoostFlutterEngine engine) {
this.engine = engine;
return this;
}
public Builder renderMode(FlutterView.RenderMode renderMode) {
this.renderMode = renderMode;
return this;
}
public Builder renderingProgressCoverCreator(RenderingProgressCoverCreator creator) {
this.renderingProgressCoverCreator = creator;
return this;
}
public Builder transparencyMode(FlutterView.TransparencyMode transparencyMode) {
this.transparencyMode = transparencyMode;
return this;
}
public BoostFlutterView build() {
Bundle args = new Bundle();
args.putString("flutterview_render_mode", renderMode != null ? renderMode.name() : FlutterView.RenderMode.surface.name());
args.putString("flutterview_transparency_mode", transparencyMode != null ? transparencyMode.name() : FlutterView.TransparencyMode.transparent.name());
return new BoostFlutterView(context, engine, args,renderingProgressCoverCreator);
}
}
public interface OnFirstFrameRenderedListener {
void onFirstFrameRendered(BoostFlutterView view);
}
public interface RenderingProgressCoverCreator {
View createRenderingProgressCover(Context context);
}
}
/*
* The MIT License (MIT)
*
*
* Copyright (c) 2019 Alibaba Group
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -21,30 +21,32 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
package com.idlefish.flutterboost;
import android.os.Handler;
import android.content.Intent;
import com.taobao.idlefish.flutterboost.NavigationService.NavigationService;
import com.taobao.idlefish.flutterboost.interfaces.IContainerManager;
import com.taobao.idlefish.flutterboost.interfaces.IContainerRecord;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import java.util.HashMap;
import java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
public class ContainerRecord implements IContainerRecord {
private final IContainerManager mManager;
private final FlutterViewContainerManager mManager;
private final IFlutterViewContainer mContainer;
private final String mUniqueId;
private final Handler mHandler = new Handler();
private int mState = STATE_UNKNOW;
private MethodChannelProxy mProxy = new MethodChannelProxy();
public ContainerRecord(IContainerManager manager, IFlutterViewContainer container) {
mUniqueId = System.currentTimeMillis() + "-" + hashCode();
ContainerRecord(FlutterViewContainerManager manager, IFlutterViewContainer container) {
final Map params = container.getContainerUrlParams();
if(params != null && params.containsKey(IContainerRecord.UNIQ_KEY)) {
mUniqueId = String.valueOf(params.get(IContainerRecord.UNIQ_KEY));
}else{
mUniqueId = genUniqueId(this);
}
mManager = manager;
mContainer = container;
}
......@@ -66,63 +68,141 @@ public class ContainerRecord implements IContainerRecord {
@Override
public void onCreate() {
Utils.assertCallOnMainThread();
BoostEngineProvider.assertEngineRunning();
if (mState != STATE_UNKNOW) {
Debuger.exception("state error");
}
mState = STATE_CREATED;
mContainer.getBoostFlutterView().boostResume();
mContainer.getBoostFlutterView().onResume();
mProxy.create();
}
@Override
public void onAppear() {
Utils.assertCallOnMainThread();
if (mState != STATE_CREATED && mState != STATE_DISAPPEAR) {
Debuger.exception("state error");
}
mState = STATE_APPEAR;
mContainer.getBoostFlutterView().boostResume();
mManager.pushRecord(this);
mContainer.getBoostFlutterView().onAttach();
mProxy.appear();
}
@Override
public void onDisappear() {
mProxy.disappear();
Utils.assertCallOnMainThread();
if (mState != STATE_APPEAR) {
Debuger.exception("state error");
}
mState = STATE_DISAPPEAR;
/**
* Bug workaround:
* If current container is finishing, we should call destroy flutter page early.
*/
if(mContainer.isFinishing()) {
mHandler.post(new Runnable() {
@Override
public void run() {
mProxy.destroy();
}
});
mProxy.disappear();
if(getContainer().getContextActivity().isFinishing()) {
mProxy.destroy();
}
mContainer.getBoostFlutterView().onDetach();
mManager.popRecord(this);
}
@Override
public void onDestroy() {
mProxy.destroy();
Utils.assertCallOnMainThread();
if (mState != STATE_DISAPPEAR) {
Debuger.exception("state error");
}
mState = STATE_DESTROYED;
mProxy.destroy();
mContainer.getBoostFlutterView().onDestroy();
mManager.removeRecord(this);
mManager.setContainerResult(this,-1,-1,null);
if (!mManager.hasContainerAppear()) {
mContainer.getBoostFlutterView().onPause();
mContainer.getBoostFlutterView().onStop();
}
}
@Override
public void onResult(Map Result) {
NavigationService.onNativePageResult(
genResult("onNativePageResult"),
mUniqueId,
mUniqueId,
Result,
mContainer.getContainerParams()
);
public void onBackPressed() {
Utils.assertCallOnMainThread();
if (mState == STATE_UNKNOW || mState == STATE_DESTROYED) {
Debuger.exception("state error");
}
HashMap<String, String> map = new HashMap<>();
map.put("type", "backPressedCallback");
map.put("name", mContainer.getContainerUrl());
map.put("uniqueId", mUniqueId);
FlutterBoost.singleton().channel().sendEvent("lifecycle", map);
mContainer.getBoostFlutterView().onBackPressed();
}
@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 int mState = STATE_UNKNOW;
private void create() {
if (mState == STATE_UNKNOW) {
NavigationService.didInitPageContainer(
genResult("didInitPageContainer"),
mContainer.getContainerName(),
mContainer.getContainerParams(),
invokeChannelUnsafe("didInitPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didInitPageContainer");
......@@ -131,10 +211,9 @@ public class ContainerRecord implements IContainerRecord {
}
private void appear() {
NavigationService.didShowPageContainer(
genResult("didShowPageContainer"),
mContainer.getContainerName(),
mContainer.getContainerParams(),
invokeChannelUnsafe("didShowPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didShowPageContainer");
......@@ -144,10 +223,9 @@ public class ContainerRecord implements IContainerRecord {
private void disappear() {
if (mState < STATE_DISAPPEAR) {
NavigationService.didDisappearPageContainer(
genResult("didDisappearPageContainer"),
mContainer.getContainerName(),
mContainer.getContainerParams(),
invokeChannel("didDisappearPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didDisappearPageContainer");
......@@ -158,10 +236,9 @@ public class ContainerRecord implements IContainerRecord {
private void destroy() {
if (mState < STATE_DESTROYED) {
NavigationService.willDeallocPageContainer(
genResult("willDeallocPageContainer"),
mContainer.getContainerName(),
mContainer.getContainerParams(),
invokeChannel("willDeallocPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("willDeallocPageContainer");
......@@ -169,25 +246,25 @@ public class ContainerRecord implements IContainerRecord {
mState = STATE_DESTROYED;
}
}
}
private MessageResult<Boolean> genResult(final String name) {
return new MessageResult<Boolean>() {
@Override
public void success(Boolean var1) {
//Debuger.log(name + " call success");
}
public void invokeChannel(String method, String url, Map params, String uniqueId) {
HashMap<String, Object> args = new HashMap<>();
args.put("pageName", url);
args.put("params", params);
args.put("uniqueId", uniqueId);
FlutterBoost.singleton().channel().invokeMethod(method, args);
}
@Override
public void error(String var1, String var2, Object var3) {
Debuger.log(name + " call error");
}
public void invokeChannelUnsafe(String method, String url, Map params, String uniqueId) {
HashMap<String, Object> args = new HashMap<>();
args.put("pageName", url);
args.put("params", params);
args.put("uniqueId", uniqueId);
FlutterBoost.singleton().channel().invokeMethodUnsafe(method, args);
}
}
@Override
public void notImplemented() {
Debuger.log(name + " call not Impelemented");
}
};
public static String genUniqueId(Object obj) {
return System.currentTimeMillis() + "-" + obj.hashCode();
}
}
......@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
package com.idlefish.flutterboost;
import android.util.Log;
......@@ -60,7 +60,7 @@ public class Debuger {
public static boolean isDebug(){
try {
return FlutterBoostPlugin.platform().isDebug();
return FlutterBoost.singleton().platform().isDebug();
}catch (Throwable t){
return false;
}
......
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.idlefish.flutterboost;
import android.app.Activity;
import android.app.Application;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import com.idlefish.flutterboost.interfaces.IContainerManager;
import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.idlefish.flutterboost.interfaces.IFlutterEngineProvider;
import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.idlefish.flutterboost.interfaces.IPlatform;
import com.idlefish.flutterboost.interfaces.IStateListener;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class FlutterBoost {
static FlutterBoost sInstance = null;
public static synchronized void init(IPlatform platform) {
if (sInstance == null) {
sInstance = new FlutterBoost(platform);
}
if (platform.whenEngineStart() == IPlatform.IMMEDIATELY) {
sInstance.mEngineProvider
.provideEngine(platform.getApplication())
.startRun(null);
}
}
public static FlutterBoost singleton() {
if (sInstance == null) {
throw new RuntimeException("FlutterBoost not init yet");
}
return sInstance;
}
private final IPlatform mPlatform;
private final FlutterViewContainerManager mManager;
private final IFlutterEngineProvider mEngineProvider;
IStateListener mStateListener;
Activity mCurrentActiveActivity;
private FlutterBoost(IPlatform platform) {
mPlatform = platform;
mManager = new FlutterViewContainerManager();
IFlutterEngineProvider provider = platform.engineProvider();
if(provider == null) {
provider = new BoostEngineProvider();
}
mEngineProvider = provider;
platform.getApplication().registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks());
BoostChannel.addActionAfterRegistered(new BoostChannel.ActionAfterRegistered() {
@Override
public void onChannelRegistered(BoostChannel channel) {
channel.addMethodCallHandler(new BoostMethodHandler());
}
});
}
public IFlutterEngineProvider engineProvider() {
return sInstance.mEngineProvider;
}
public IContainerManager containerManager() {
return sInstance.mManager;
}
public IPlatform platform() {
return sInstance.mPlatform;
}
public BoostChannel channel() {
return BoostChannel.singleton();
}
public Activity currentActivity() {
return sInstance.mCurrentActiveActivity;
}
public IFlutterViewContainer findContainerById(String id) {
return mManager.findContainerById(id);
}
public void setStateListener(@Nullable IStateListener listener){
mStateListener = listener;
}
class ActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (platform().whenEngineStart() == IPlatform.ANY_ACTIVITY_CREATED) {
sInstance.mEngineProvider
.provideEngine(activity)
.startRun(activity);
}
}
@Override
public void onActivityStarted(Activity activity) {
if (mCurrentActiveActivity == null) {
Debuger.log("Application entry foreground");
if (mEngineProvider.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;
}
}
}
class BoostMethodHandler implements MethodChannel.MethodCallHandler {
@Override
public void onMethodCall(MethodCall methodCall, final MethodChannel.Result result) {
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();
}
}
}
}
public static void setBoostResult(Activity activity, HashMap result) {
Intent intent = new Intent();
if (result != null) {
intent.putExtra(IFlutterViewContainer.RESULT_KEY, result);
}
activity.setResult(Activity.RESULT_OK, intent);
}
}
......@@ -21,17 +21,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
package com.idlefish.flutterboost;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.content.Context;
import android.text.TextUtils;
import android.util.SparseArray;
import com.taobao.idlefish.flutterboost.NavigationService.NavigationService;
import com.taobao.idlefish.flutterboost.interfaces.IContainerManager;
import com.taobao.idlefish.flutterboost.interfaces.IContainerRecord;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.idlefish.flutterboost.interfaces.IContainerManager;
import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.idlefish.flutterboost.interfaces.IOperateSyncer;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
......@@ -41,194 +40,127 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterRunArguments;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
public class FlutterViewContainerManager implements IContainerManager {
private final Map<IFlutterViewContainer, IContainerRecord> mRecords = new LinkedHashMap<>();
private final Instrument mInstrument;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Map<IFlutterViewContainer, IContainerRecord> mRecordMap = new LinkedHashMap<>();
private final Set<ContainerRef> mRefs = new HashSet<>();
private final Stack<IContainerRecord> mRecordStack = new Stack<>();
private IContainerRecord mCurrentTopRecord;
private final Map<String,OnResult> mOnResults = new HashMap<>();
FlutterViewContainerManager() {
mInstrument = new Instrument(this);
}
FlutterViewContainerManager() {}
@Override
public PluginRegistry onContainerCreate(IFlutterViewContainer container) {
assertCallOnMainThread();
public IOperateSyncer generateSyncer(IFlutterViewContainer container) {
Utils.assertCallOnMainThread();
ContainerRecord record = new ContainerRecord(this, container);
if (mRecords.put(container, record) != null) {
Debuger.exception("container:" + container.getContainerName() + " already exists!");
return new PluginRegistryImpl(container.getActivity(), container.getBoostFlutterView());
if (mRecordMap.put(container, record) != null) {
Debuger.exception("container:" + container.getContainerUrl() + " already exists!");
}
mRefs.add(new ContainerRef(record.uniqueId(),container));
FlutterMain.ensureInitializationComplete(container.getActivity().getApplicationContext(), null);
BoostFlutterView flutterView = FlutterBoostPlugin.viewProvider().createFlutterView(container);
if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
String appBundlePath = FlutterMain.findAppBundlePath(container.getActivity().getApplicationContext());
if (appBundlePath != null) {
FlutterRunArguments arguments = new FlutterRunArguments();
arguments.bundlePath = appBundlePath;
arguments.entrypoint = "main";
flutterView.runFromBundle(arguments);
}
}
mInstrument.performCreate(record);
return new PluginRegistryImpl(container.getActivity(), container.getBoostFlutterView());
return record;
}
@Override
public void onContainerAppear(IFlutterViewContainer container) {
assertCallOnMainThread();
final IContainerRecord record = mRecords.get(container);
if (record == null) {
Debuger.exception("container:" + container.getContainerName() + " not exists yet!");
return;
void pushRecord(IContainerRecord record) {
if(!mRecordMap.containsValue(record)) {
Debuger.exception("invalid record!");
}
mInstrument.performAppear(record);
mCurrentTopRecord = record;
mRecordStack.push(record);
}
@Override
public void onContainerDisappear(IFlutterViewContainer container) {
assertCallOnMainThread();
final IContainerRecord record = mRecords.get(container);
if (record == null) {
Debuger.exception("container:" + container.getContainerName() + " not exists yet!");
return;
void popRecord(IContainerRecord record) {
if(mRecordStack.peek() == record) {
mRecordStack.pop();
}
mInstrument.performDisappear(record);
}
if (!container.isFinishing()) {
checkIfFlutterViewNeedStopLater();
}
void removeRecord(IContainerRecord record) {
mRecordStack.remove(record);
mRecordMap.remove(record.getContainer());
}
@Override
public void onContainerDestroy(IFlutterViewContainer container) {
assertCallOnMainThread();
void setContainerResult(IContainerRecord record,int requestCode, int resultCode, Map<String,Object> result) {
if (mCurrentTopRecord != null
&& mCurrentTopRecord.getContainer() == container) {
mCurrentTopRecord = null;
IFlutterViewContainer target = findContainerById(record.uniqueId());
if(target == null) {
Debuger.exception("setContainerResult error, url="+record.getContainer().getContainerUrl());
}
final IContainerRecord record = mRecords.remove(container);
if (record == null) {
Debuger.exception("container:" + container.getContainerName() + " not exists yet!");
return;
if (result == null) {
result = new HashMap<>();
}
mInstrument.performDestroy(record);
result.put("_requestCode__",requestCode);
result.put("_resultCode__",resultCode);
checkIfFlutterViewNeedStopLater();
final OnResult onResult = mOnResults.remove(record.uniqueId());
if(onResult != null) {
onResult.onResult(result);
}
}
private void checkIfFlutterViewNeedStopLater() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (!hasContainerAppear()) {
FlutterBoostPlugin.viewProvider().stopFlutterView();
}
}
}, 250);
public IContainerRecord recordOf(IFlutterViewContainer container) {
return mRecordMap.get(container);
}
@Override
public void onBackPressed(IFlutterViewContainer container) {
assertCallOnMainThread();
final IContainerRecord record = mRecords.get(container);
if (record == null) {
Debuger.exception("container:" + container.getContainerName() + " not exists yet!");
return;
void openContainer(String url, Map<String, Object> urlParams, Map<String, Object> exts,OnResult onResult) {
Context context = FlutterBoost.singleton().currentActivity();
if(context == null) {
context = FlutterBoost.singleton().platform().getApplication();
}
Map<String, String> map = new HashMap<>();
map.put("type", "backPressedCallback");
map.put("name", container.getContainerName());
map.put("uniqueId", record.uniqueId());
NavigationService.getService().emitEvent(map);
}
@Override
public void destroyContainerRecord(String name, String uniqueId) {
assertCallOnMainThread();
boolean done = false;
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecords.entrySet()) {
if (TextUtils.equals(uniqueId, entry.getValue().uniqueId())) {
entry.getKey().destroyContainer();
done = true;
break;
}
if(urlParams == null) {
urlParams = new HashMap<>();
}
if (!done) {
Debuger.exception("destroyContainerRecord can not find name:" + name + " uniqueId:" + uniqueId);
int requestCode = 0;
final Object v = urlParams.remove("requestCode");
if(v != null) {
requestCode = Integer.valueOf(String.valueOf(v));
}
}
@Override
public void onContainerResult(IFlutterViewContainer container, Map result) {
final IContainerRecord record = mRecords.get(container);
if (record == null) {
Debuger.exception("container:" + container.getContainerName() + " not exists yet!");
return;
final String uniqueId = ContainerRecord.genUniqueId(url);
urlParams.put(IContainerRecord.UNIQ_KEY,uniqueId);
if(onResult != null) {
mOnResults.put(uniqueId,onResult);
}
record.onResult(result);
FlutterBoost.singleton().platform().openContainer(context,url,urlParams,requestCode,exts);
}
@Override
public void setContainerResult(String uniqueId, Map result) {
if (result == null) {
Debuger.exception("setContainerResult result is null");
return;
}
if (!(result instanceof HashMap)) {
result = new HashMap();
result.putAll(result);
}
boolean done = false;
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecords.entrySet()) {
IContainerRecord closeContainer(String uniqueId, Map<String, Object> result,Map<String,Object> exts) {
IContainerRecord targetRecord = null;
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecordMap.entrySet()) {
if (TextUtils.equals(uniqueId, entry.getValue().uniqueId())) {
entry.getKey().setBoostResult((HashMap) result);
done = true;
targetRecord = entry.getValue();
break;
}
}
if (!done) {
Debuger.exception("setContainerResult can not find uniqueId:" + uniqueId);
if(targetRecord == null) {
Debuger.exception("closeContainer can not find uniqueId:" + uniqueId);
}
FlutterBoost.singleton().platform().closeContainer(targetRecord,result,exts);
return targetRecord;
}
@Override
public IContainerRecord getCurrentTopRecord() {
return mCurrentTopRecord;
if(mRecordStack.isEmpty()) return null;
return mRecordStack.peek();
}
@Override
public IContainerRecord getLastRecord() {
final Collection<IContainerRecord> values = mRecords.values();
public IContainerRecord getLastGenerateRecord() {
final Collection<IContainerRecord> values = mRecordMap.values();
if(!values.isEmpty()) {
final ArrayList<IContainerRecord> array = new ArrayList<>(values);
return array.get(array.size()-1);
......@@ -239,7 +171,7 @@ public class FlutterViewContainerManager implements IContainerManager {
@Override
public IFlutterViewContainer findContainerById(String uniqueId) {
IFlutterViewContainer target = null;
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecords.entrySet()) {
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecordMap.entrySet()) {
if (TextUtils.equals(uniqueId, entry.getValue().uniqueId())) {
target = entry.getKey();
break;
......@@ -257,14 +189,13 @@ public class FlutterViewContainerManager implements IContainerManager {
return target;
}
@Override
public void onShownContainerChanged(String old, String now) {
assertCallOnMainThread();
void onShownContainerChanged(String old, String now) {
Utils.assertCallOnMainThread();
IFlutterViewContainer oldContainer = null;
IFlutterViewContainer nowContainer = null;
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecords.entrySet()) {
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecordMap.entrySet()) {
if (TextUtils.equals(old, entry.getValue().uniqueId())) {
oldContainer = entry.getKey();
}
......@@ -289,9 +220,7 @@ public class FlutterViewContainerManager implements IContainerManager {
@Override
public boolean hasContainerAppear() {
assertCallOnMainThread();
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecords.entrySet()) {
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecordMap.entrySet()) {
if (entry.getValue().getState() == IContainerRecord.STATE_APPEAR) {
return true;
}
......@@ -300,16 +229,6 @@ public class FlutterViewContainerManager implements IContainerManager {
return false;
}
@Override
public void reset() {
}
private void assertCallOnMainThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
Debuger.exception("must call method on main thread");
}
}
public static class ContainerRef {
public final String uniqueId;
public final WeakReference<IFlutterViewContainer> container;
......@@ -320,27 +239,7 @@ public class FlutterViewContainerManager implements IContainerManager {
}
}
public static class PluginRegistryImpl implements PluginRegistry {
final BoostFlutterView mBoostFlutterView;
PluginRegistryImpl(Activity activity, BoostFlutterView flutterView) {
mBoostFlutterView = flutterView;
}
@Override
public Registrar registrarFor(String pluginKey) {
return mBoostFlutterView.getPluginRegistry().registrarFor(pluginKey);
}
@Override
public boolean hasPlugin(String key) {
return mBoostFlutterView.getPluginRegistry().hasPlugin(key);
}
@Override
public <T> T valuePublishedByPlugin(String pluginKey) {
return mBoostFlutterView.getPluginRegistry().valuePublishedByPlugin(pluginKey);
}
interface OnResult {
void onResult(Map<String,Object> result);
}
}
package com.idlefish.flutterboost;
import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.idlefish.flutterboost.interfaces.IFlutterEngineProvider;
import com.idlefish.flutterboost.interfaces.IPlatform;
import java.lang.reflect.Method;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
public abstract class Platform implements IPlatform {
@Override
public boolean isDebug() {
return false;
}
@Override
public void closeContainer(IContainerRecord record, Map<String, Object> result, Map<String, Object> exts) {
if(record == null) return;
record.getContainer().finishContainer(result);
}
@Override
public IFlutterEngineProvider engineProvider() {
return new BoostEngineProvider();
}
@Override
public void registerPlugins(PluginRegistry registry) {
try {
Class clz = Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
Method method = clz.getDeclaredMethod("registerWith",PluginRegistry.class);
method.invoke(null,registry);
}catch (Throwable t){
throw new RuntimeException(t);
}
}
@Override
public int whenEngineStart() {
return ANY_ACTIVITY_CREATED;
}
}
package com.idlefish.flutterboost;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import java.util.LinkedList;
import java.util.List;
public class SnapshotView extends FrameLayout {
private ImageView mImg;
public SnapshotView(@NonNull Context context) {
super(context);
init();
}
public SnapshotView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public SnapshotView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
setBackgroundColor(Color.RED);
mImg = new ImageView(getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER;
mImg.setScaleType(ImageView.ScaleType.FIT_XY);
mImg.setLayoutParams(params);
addView(mImg);
}
public void toggleSnapshot(BoostFlutterView flutterView){
if (!dismissSnapshot(flutterView)) {
showSnapshot(flutterView);
}
}
public boolean showSnapshot(BoostFlutterView flutterView){
if(flutterView == null) return false;
dismissSnapshot(flutterView);
final Bitmap bitmap = flutterView.getEngine().getRenderer().getBitmap();
if(bitmap == null || bitmap.isRecycled()) {
return false;
}
mImg.setImageBitmap(bitmap);
flutterView.addView(this,new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
Debuger.log("showSnapshot");
return true;
}
public boolean dismissSnapshot(BoostFlutterView flutterView){
List<View> snapshots = new LinkedList<>();
for(int index = 0;index < flutterView.getChildCount();index++){
View view = flutterView.getChildAt(index);
if(view instanceof SnapshotView) {
snapshots.add(view);
}
}
if(snapshots.isEmpty()) {
return false;
}else{
for(View v:snapshots) {
flutterView.removeView(v);
}
Debuger.log("dismissSnapshot");
return true;
}
}
}
package com.idlefish.flutterboost;
import com.idlefish.flutterboost.interfaces.IStateListener;
import io.flutter.plugin.common.PluginRegistry;
public class StateListener implements IStateListener {
@Override
public void onEngineCreated(BoostFlutterEngine engine) {
Debuger.log(">>onEngineCreated");
}
@Override
public void onEngineStarted(BoostFlutterEngine engine) {
Debuger.log(">>onEngineStarted");
}
@Override
public void onChannelRegistered(PluginRegistry.Registrar registrar, BoostChannel channel) {
Debuger.log(">>onFlutterViewInited");
}
@Override
public void onFlutterViewInited(BoostFlutterEngine engine, BoostFlutterView flutterView) {
Debuger.log(">>onFlutterViewInited");
}
@Override
public void beforeEngineAttach(BoostFlutterEngine engine, BoostFlutterView flutterView) {
Debuger.log(">>beforeEngineAttach");
}
@Override
public void afterEngineAttached(BoostFlutterEngine engine, BoostFlutterView flutterView) {
Debuger.log(">>afterEngineAttached");
}
@Override
public void beforeEngineDetach(BoostFlutterEngine engine, BoostFlutterView flutterView) {
Debuger.log(">>beforeEngineDetach");
}
@Override
public void afterEngineDetached(BoostFlutterEngine engine, BoostFlutterView flutterView) {
Debuger.log(">>afterEngineDetached");
}
}
package com.idlefish.flutterboost;
import android.app.Activity;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by wangchende on 15-9-7.
*/
public class StatusbarColorUtils {
private static Method mSetStatusBarColorIcon;
private static Method mSetStatusBarDarkIcon;
private static Field mStatusBarColorFiled;
private static int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0;
static {
try {
mSetStatusBarColorIcon = Activity.class.getMethod("setStatusBarDarkIcon", int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
mSetStatusBarDarkIcon = Activity.class.getMethod("setStatusBarDarkIcon", boolean.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
mStatusBarColorFiled = WindowManager.LayoutParams.class.getField("statusBarColor");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
try {
Field field = View.class.getField("SYSTEM_UI_FLAG_LIGHT_STATUS_BAR");
SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = field.getInt(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 判断颜色是否偏黑色
*
* @param color 颜色
* @param level 级别
* @return
*/
public static boolean isBlackColor(int color, int level) {
int grey = toGrey(color);
return grey < level;
}
/**
* 颜色转换成灰度值
*
* @param rgb 颜色
* @return 灰度值
*/
public static int toGrey(int rgb) {
int blue = rgb & 0x000000FF;
int green = (rgb & 0x0000FF00) >> 8;
int red = (rgb & 0x00FF0000) >> 16;
return (red * 38 + green * 75 + blue * 15) >> 7;
}
/**
* 设置状态栏字体图标颜色
*
* @param activity 当前activity
* @param color 颜色
*/
public static void setStatusBarDarkIcon(Activity activity, int color) {
if (mSetStatusBarColorIcon != null) {
try {
mSetStatusBarColorIcon.invoke(activity, color);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} else {
boolean whiteColor = isBlackColor(color, 50);
if (mStatusBarColorFiled != null) {
setStatusBarDarkIcon(activity, whiteColor, whiteColor);
setStatusBarDarkIcon(activity.getWindow(), color);
} else {
setStatusBarDarkIcon(activity, whiteColor);
}
}
}
/**
* 设置状态栏字体图标颜色(只限全屏非activity情况)
*
* @param window 当前窗口
* @param color 颜色
*/
public static void setStatusBarDarkIcon(Window window, int color) {
try {
setStatusBarColor(window, color);
if (Build.VERSION.SDK_INT > 22) {
setStatusBarDarkIcon(window.getDecorView(), true);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置状态栏字体图标颜色
*
* @param activity 当前activity
* @param dark 是否深色 true为深色 false 为白色
*/
public static void setStatusBarDarkIcon(Activity activity, boolean dark) {
setStatusBarDarkIcon(activity, dark, true);
}
private static boolean changeMeizuFlag(WindowManager.LayoutParams winParams, String flagName, boolean on) {
try {
Field f = winParams.getClass().getDeclaredField(flagName);
f.setAccessible(true);
int bits = f.getInt(winParams);
Field f2 = winParams.getClass().getDeclaredField("meizuFlags");
f2.setAccessible(true);
int meizuFlags = f2.getInt(winParams);
int oldFlags = meizuFlags;
if (on) {
meizuFlags |= bits;
} else {
meizuFlags &= ~bits;
}
if (oldFlags != meizuFlags) {
f2.setInt(winParams, meizuFlags);
return true;
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
return false;
}
/**
* 设置状态栏颜色
*
* @param view
* @param dark
*/
private static void setStatusBarDarkIcon(View view, boolean dark) {
int oldVis = view.getSystemUiVisibility();
int newVis = oldVis;
if (dark) {
newVis |= SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else {
newVis &= ~SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
}
if (newVis != oldVis) {
view.setSystemUiVisibility(newVis);
}
}
/**
* 设置状态栏颜色
*
* @param window
* @param color
*/
private static void setStatusBarColor(Window window, int color) {
WindowManager.LayoutParams winParams = window.getAttributes();
if (mStatusBarColorFiled != null) {
try {
int oldColor = mStatusBarColorFiled.getInt(winParams);
if (oldColor != color) {
mStatusBarColorFiled.set(winParams, color);
window.setAttributes(winParams);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
/**
* 设置状态栏字体图标颜色(只限全屏非activity情况)
*
* @param window 当前窗口
* @param dark 是否深色 true为深色 false 为白色
*/
public static void setStatusBarDarkIcon(Window window, boolean dark) {
if (Build.VERSION.SDK_INT < 23) {
changeMeizuFlag(window.getAttributes(), "MEIZU_FLAG_DARK_STATUS_BAR_ICON", dark);
} else {
View decorView = window.getDecorView();
if (decorView != null) {
setStatusBarDarkIcon(decorView, dark);
setStatusBarColor(window, 0);
}
}
}
private static void setStatusBarDarkIcon(Activity activity, boolean dark, boolean flag) {
if (mSetStatusBarDarkIcon != null) {
try {
mSetStatusBarDarkIcon.invoke(activity, dark);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} else {
if (flag) {
setStatusBarDarkIcon(activity.getWindow(), dark);
}
}
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.idlefish.flutterboost;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Build;
import android.os.Looper;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Utils {
public static void assertCallOnMainThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
Debuger.exception("must call method on main thread");
}
}
public static void saveBitmap(Bitmap bm,File path, String name) {
try {
File f = new File(path,name);
if (!path.exists()) {
if(!path.mkdirs()) {
throw new Exception("mkdir except");
}
if(!f.createNewFile()){
throw new Exception("createNewFile except");
}
}
FileOutputStream out = new FileOutputStream(f);
bm.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
Debuger.exception("saved bitmap:"+f.getAbsolutePath());
} catch (Throwable t){
throw new RuntimeException(t);
}
}
public static boolean checkImageValid(final Bitmap bitmap) {
if (null == bitmap) {
return false;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
int [] checkPixels = new int[18];
for (int i=0; i<5; i++) {
int colCount = 4 - i%2;
for (int j=0; j<colCount; j++) {
checkPixels[i*3 + j + (i+1)/2] = pixels[(i + 1)*(height/6)*width + (j + 1)*(width/(colCount + 1))];
}
}
float[][] checkHSV = new float[checkPixels.length][3];
for (int i=0; i<checkPixels.length; i++) {
int clr = checkPixels[i];
int red = (clr & 0x00ff0000) >> 16; // 取高两位
int green = (clr & 0x0000ff00) >> 8; // 取中两位
int blue = clr & 0x000000ff;
Color.RGBToHSV(red, green, blue, checkHSV[i]);
}
int diffCount = 0;
for (int i=0; i<checkPixels.length; i++) {
for (int j=i+1; j<checkPixels.length; j++) {
double d = Math.sqrt(Math.pow(checkHSV[i][0] - checkHSV[j][0], 2.0)
+ Math.pow(checkHSV[i][1] - checkHSV[j][1], 2.0)
+ Math.pow(checkHSV[i][2] - checkHSV[j][2], 2.0));
if (d >= 1) {
diffCount++;
}
}
}
if (diffCount <= 10) {
return false;
} else {
return true;
}
}
public static int getMetricsWidth(Context context) {
//尝试拿真实的屏幕分辨率
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
WindowManager windowMgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (null != windowMgr) {
DisplayMetrics metrics = new DisplayMetrics();
windowMgr.getDefaultDisplay().getMetrics(metrics);
if (metrics.widthPixels > 0 && metrics.heightPixels > 0) {
return metrics.widthPixels;
}
}
}
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return metrics.widthPixels;
}
public static int getMetricsHeight(Context context) {
//尝试拿真实的屏幕分辨率
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
WindowManager windowMgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (null != windowMgr) {
DisplayMetrics metrics = new DisplayMetrics();
windowMgr.getDefaultDisplay().getMetrics(metrics);
if (metrics.widthPixels > 0 && metrics.heightPixels > 0) {
return metrics.heightPixels;
}
}
}
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return metrics.heightPixels;
}
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public static void setStatusBarLightMode(Activity activity, boolean dark) {
try {
String pp = Build.MANUFACTURER;
if (pp == null) pp = "unknow";
pp = pp.toLowerCase();
android.util.Log.e("ImmerseTheme","current MANUFACTURER="+pp);
if (pp.contains("xiaomi") || pp.contains("redmi")) {
setMIUISetStatusBarLightMode(activity, dark);
} else if (pp.contains("meizu")) {
StatusbarColorUtils.setStatusBarDarkIcon(activity,true);
}else{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
android.util.Log.e("ImmerseTheme", "setStatusBarLightMode");
if(dark) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
}catch (Throwable t){
Debuger.exception(t);
}
}
private static void setMIUISetStatusBarLightMode(Activity activity, boolean dark) {
try {
if (isCurrentMIUIVersionBiggerAndEqual("V9") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
android.util.Log.e("ImmerseTheme", "setMIUISetStatusBarLightMode MIUI > 9");
if(dark) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
} else if (activity.getWindow() != null) {
android.util.Log.e("ImmerseTheme", "setMIUISetStatusBarLightMode MIUI < 9");
Window window = activity.getWindow();
Class clazz = window.getClass();
int darkModeFlag = 0;
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (dark) {
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体
} else {
extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
}
}
} catch (Exception e) {
Debuger.exception(e);
}
}
public static boolean isCurrentMIUIVersionBiggerAndEqual(String version) {
if (TextUtils.isEmpty(version)) return false;
//V9
int version2 = Integer.parseInt(version.substring(1));
int version1 = 0;
String systemVersion = getMIUISystemVersion();
if (!TextUtils.isEmpty(systemVersion) && systemVersion.length() > 1) {
version1 = Integer.parseInt(systemVersion.substring(1));
}
return version1 != 0 && version2 != 0 && version1 >= version2;
}
public static String getMIUISystemVersion() {
String line;
BufferedReader input = null;
try {
Process p = Runtime.getRuntime().exec("getprop ro.miui.ui.version.name");
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
}
}
}
return line;
}
}
\ No newline at end of file
package com.idlefish.flutterboost;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Build;
import android.os.LocaleList;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import io.flutter.embedding.android.AndroidKeyProcessor;
import io.flutter.embedding.android.AndroidTouchProcessor;
import io.flutter.embedding.android.FlutterSurfaceView;
import io.flutter.embedding.android.FlutterTextureView;
import io.flutter.embedding.android.FlutterView;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.view.AccessibilityBridge;
public class XFlutterView extends FrameLayout {
private static final String TAG = "XFlutterView";
// Behavior configuration of this FlutterView.
@NonNull
private FlutterView.RenderMode renderMode;
@Nullable
private FlutterView.TransparencyMode transparencyMode;
// Internal view hierarchy references.
@Nullable
private FlutterRenderer.RenderSurface renderSurface;
// Connections to a Flutter execution context.
@Nullable
private FlutterEngine flutterEngine;
// Components that process various types of Android View input and events,
// possibly storing intermediate state, and communicating those events to Flutter.
//
// These components essentially add some additional behavioral logic on top of
// existing, stateless system channels, e.g., KeyEventChannel, TextInputChannel, etc.
@Nullable
private TextInputPlugin textInputPlugin;
@Nullable
private AndroidKeyProcessor androidKeyProcessor;
@Nullable
private AndroidTouchProcessor androidTouchProcessor;
@Nullable
private AccessibilityBridge accessibilityBridge;
// Directly implemented View behavior that communicates with Flutter.
private final FlutterRenderer.ViewportMetrics viewportMetrics = new FlutterRenderer.ViewportMetrics();
private final AccessibilityBridge.OnAccessibilityChangeListener onAccessibilityChangeListener = new AccessibilityBridge.OnAccessibilityChangeListener() {
@Override
public void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled);
}
};
public XFlutterView(@NonNull Context context) {
this(context, null, null, null);
}
public XFlutterView(@NonNull Context context, @NonNull FlutterView.RenderMode renderMode) {
this(context, null, renderMode, null);
}
public XFlutterView(@NonNull Context context, @NonNull FlutterView.TransparencyMode transparencyMode) {
this(context, null, FlutterView.RenderMode.surface, transparencyMode);
}
/**
* Constructs a {@code FlutterView} programmatically, without any XML attributes, and allows
* a selection of {@link #renderMode} and {@link #transparencyMode}.
*/
public XFlutterView(@NonNull Context context, @NonNull FlutterView.RenderMode renderMode, @NonNull FlutterView.TransparencyMode transparencyMode) {
this(context, null, renderMode, transparencyMode);
}
/**
* Constructs a {@code FlutterSurfaceView} in an XML-inflation-compliant manner.
*
* // TODO(mattcarroll): expose renderMode in XML when build system supports R.attr
*/
public XFlutterView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, null, null);
}
private XFlutterView(@NonNull Context context, @Nullable AttributeSet attrs, @Nullable FlutterView.RenderMode renderMode, @Nullable FlutterView.TransparencyMode transparencyMode) {
super(context, attrs);
this.renderMode = renderMode == null ? FlutterView.RenderMode.surface : renderMode;
this.transparencyMode = transparencyMode != null ? transparencyMode : FlutterView.TransparencyMode.opaque;
init();
}
private void init() {
Log.d(TAG, "Initializing FlutterView");
switch (renderMode) {
case surface:
Log.d(TAG, "Internally creating a FlutterSurfaceView.");
FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(getContext(), transparencyMode == FlutterView.TransparencyMode.transparent);
renderSurface = flutterSurfaceView;
addView(flutterSurfaceView);
break;
case texture:
Log.d(TAG, "Internally creating a FlutterTextureView.");
FlutterTextureView flutterTextureView = new FlutterTextureView(getContext());
renderSurface = flutterTextureView;
addView(flutterTextureView);
break;
}
// FlutterView needs to be focusable so that the InputMethodManager can interact with it.
setFocusable(true);
setFocusableInTouchMode(true);
}
/**
* Adds the given {@code listener} to this {@code FlutterView}, to be notified upon Flutter's
* first rendered frame.
*/
public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) {
renderSurface.addOnFirstFrameRenderedListener(listener);
}
/**
* Removes the given {@code listener}, which was previously added with
* {@link #addOnFirstFrameRenderedListener(OnFirstFrameRenderedListener)}.
*/
public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) {
renderSurface.removeOnFirstFrameRenderedListener(listener);
}
//------- Start: Process View configuration that Flutter cares about. ------
/**
* Sends relevant configuration data from Android to Flutter when the Android
* {@link Configuration} changes.
*
* The Android {@link Configuration} might change as a result of device orientation
* change, device language change, device text scale factor change, etc.
*/
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
sendLocalesToFlutter(newConfig);
sendUserSettingsToFlutter();
}
/**
* Invoked when this {@code FlutterView} changes size, including upon initial
* measure.
*
* The initial measure reports an {@code oldWidth} and {@code oldHeight} of zero.
*
* Flutter cares about the width and height of the view that displays it on the host
* platform. Therefore, when this method is invoked, the new width and height are
* communicated to Flutter as the "physical size" of the view that displays Flutter's
* UI.
*/
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
viewportMetrics.width = width;
viewportMetrics.height = height;
sendViewportMetricsToFlutter();
}
/**
* Invoked when Android's desired window insets change, i.e., padding.
*
* Flutter does not use a standard {@code View} hierarchy and therefore Flutter is
* unaware of these insets. Therefore, this method calculates the viewport metrics
* that Flutter should use and then sends those metrics to Flutter.
*
* This callback is not present in API < 20, which means lower API devices will see
* the wider than expected padding when the status and navigation bars are hidden.
*/
@Override
@TargetApi(20)
@RequiresApi(20)
public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
WindowInsets newInsets = super.onApplyWindowInsets(insets);
// Status bar (top) and left/right system insets should partially obscure the content (padding).
viewportMetrics.paddingTop = insets.getSystemWindowInsetTop();
viewportMetrics.paddingRight = insets.getSystemWindowInsetRight();
viewportMetrics.paddingBottom = 0;
viewportMetrics.paddingLeft = insets.getSystemWindowInsetLeft();
// Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
viewportMetrics.viewInsetTop = 0;
viewportMetrics.viewInsetRight = 0;
viewportMetrics.viewInsetBottom = insets.getSystemWindowInsetBottom();
viewportMetrics.viewInsetLeft = 0;
sendViewportMetricsToFlutter();
return newInsets;
}
/**
* Invoked when Android's desired window insets change, i.e., padding.
*
* {@code fitSystemWindows} is an earlier version of
* {@link #onApplyWindowInsets(WindowInsets)}. See that method for more details
* about how window insets relate to Flutter.
*/
@Override
@SuppressWarnings("deprecation")
protected boolean fitSystemWindows(Rect insets) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
// Status bar, left/right system insets partially obscure content (padding).
viewportMetrics.paddingTop = insets.top;
viewportMetrics.paddingRight = insets.right;
viewportMetrics.paddingBottom = 0;
viewportMetrics.paddingLeft = insets.left;
// Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
viewportMetrics.viewInsetTop = 0;
viewportMetrics.viewInsetRight = 0;
viewportMetrics.viewInsetBottom = insets.bottom;
viewportMetrics.viewInsetLeft = 0;
sendViewportMetricsToFlutter();
return true;
} else {
return super.fitSystemWindows(insets);
}
}
//------- End: Process View configuration that Flutter cares about. --------
//-------- Start: Process UI I/O that Flutter cares about. -------
/**
* Creates an {@link InputConnection} to work with a {@link android.view.inputmethod.InputMethodManager}.
*
* Any {@code View} that can take focus or process text input must implement this
* method by returning a non-null {@code InputConnection}. Flutter may render one or
* many focusable and text-input widgets, therefore {@code FlutterView} must support
* an {@code InputConnection}.
*
* The {@code InputConnection} returned from this method comes from a
* {@link TextInputPlugin}, which is owned by this {@code FlutterView}. A
* {@link TextInputPlugin} exists to encapsulate the nuances of input communication,
* rather than spread that logic throughout this {@code FlutterView}.
*/
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (!isAttachedToFlutterEngine()) {
return super.onCreateInputConnection(outAttrs);
}
return textInputPlugin.createInputConnection(this, outAttrs);
}
/**
* Invoked when key is released.
*
* This method is typically invoked in response to the release of a physical
* keyboard key or a D-pad button. It is generally not invoked when a virtual
* software keyboard is used, though a software keyboard may choose to invoke
* this method in some situations.
*
* {@link KeyEvent}s are sent from Android to Flutter. {@link AndroidKeyProcessor}
* may do some additional work with the given {@link KeyEvent}, e.g., combine this
* {@code keyCode} with the previous {@code keyCode} to generate a unicode combined
* character.
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (!isAttachedToFlutterEngine()) {
return super.onKeyUp(keyCode, event);
}
androidKeyProcessor.onKeyUp(event);
return super.onKeyUp(keyCode, event);
}
/**
* Invoked when key is pressed.
*
* This method is typically invoked in response to the press of a physical
* keyboard key or a D-pad button. It is generally not invoked when a virtual
* software keyboard is used, though a software keyboard may choose to invoke
* this method in some situations.
*
* {@link KeyEvent}s are sent from Android to Flutter. {@link AndroidKeyProcessor}
* may do some additional work with the given {@link KeyEvent}, e.g., combine this
* {@code keyCode} with the previous {@code keyCode} to generate a unicode combined
* character.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (!isAttachedToFlutterEngine()) {
return super.onKeyDown(keyCode, event);
}
androidKeyProcessor.onKeyDown(event);
return super.onKeyDown(keyCode, event);
}
/**
* Invoked by Android when a user touch event occurs.
*
* Flutter handles all of its own gesture detection and processing, therefore this
* method forwards all {@link MotionEvent} data from Android to Flutter.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isAttachedToFlutterEngine()) {
return super.onTouchEvent(event);
}
// TODO(abarth): This version check might not be effective in some
// versions of Android that statically compile code and will be upset
// at the lack of |requestUnbufferedDispatch|. Instead, we should factor
// version-dependent code into separate classes for each supported
// version and dispatch dynamically.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
requestUnbufferedDispatch(event);
}
return androidTouchProcessor.onTouchEvent(event);
}
/**
* Invoked by Android when a generic motion event occurs, e.g., joystick movement, mouse hover,
* track pad touches, scroll wheel movements, etc.
*
* Flutter handles all of its own gesture detection and processing, therefore this
* method forwards all {@link MotionEvent} data from Android to Flutter.
*/
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
boolean handled = isAttachedToFlutterEngine() && androidTouchProcessor.onGenericMotionEvent(event);
return handled ? true : super.onGenericMotionEvent(event);
}
/**
* Invoked by Android when a hover-compliant input system causes a hover event.
*
* An example of hover events is a stylus sitting near an Android screen. As the
* stylus moves from outside a {@code View} to hover over a {@code View}, or move
* around within a {@code View}, or moves from over a {@code View} to outside a
* {@code View}, a corresponding {@link MotionEvent} is reported via this method.
*
* Hover events can be used for accessibility touch exploration and therefore are
* processed here for accessibility purposes.
*/
@Override
public boolean onHoverEvent(MotionEvent event) {
if (!isAttachedToFlutterEngine()) {
return super.onHoverEvent(event);
}
boolean handled = accessibilityBridge.onAccessibilityHoverEvent(event);
if (!handled) {
// TODO(ianh): Expose hover events to the platform,
// implementing ADD, REMOVE, etc.
}
return handled;
}
//-------- End: Process UI I/O that Flutter cares about. ---------
//-------- Start: Accessibility -------
@Override
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
if (accessibilityBridge != null && accessibilityBridge.isAccessibilityEnabled()) {
return accessibilityBridge;
} else {
// TODO(goderbauer): when a11y is off this should return a one-off snapshot of
// the a11y
// tree.
return null;
}
}
public AccessibilityBridge getAccessibilityBridge() {
if (accessibilityBridge != null) {
return accessibilityBridge;
} else {
// TODO(goderbauer): when a11y is off this should return a one-off snapshot of
// the a11y
// tree.
return null;
}
}
// TODO(mattcarroll): Confer with Ian as to why we need this method. Delete if possible, otherwise add comments.
private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
if(flutterEngine==null) return;
if(flutterEngine.getRenderer()==null) return;
if (!flutterEngine.getRenderer().isSoftwareRenderingEnabled()) {
setWillNotDraw(!(isAccessibilityEnabled || isTouchExplorationEnabled));
} else {
setWillNotDraw(false);
}
}
//-------- End: Accessibility ---------
/**
* Connects this {@code FlutterView} to the given {@link FlutterEngine}.
*
* This {@code FlutterView} will begin rendering the UI painted by the given {@link FlutterEngine}.
* This {@code FlutterView} will also begin forwarding interaction events from this
* {@code FlutterView} to the given {@link FlutterEngine}, e.g., user touch events, accessibility
* events, keyboard events, and others.
*
* See {@link #detachFromFlutterEngine()} for information on how to detach from a
* {@link FlutterEngine}.
*/
public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
Log.d(TAG, "attachToFlutterEngine()");
if (isAttachedToFlutterEngine()) {
if (flutterEngine == this.flutterEngine) {
// We are already attached to this FlutterEngine
Log.d(TAG, "Already attached to this engine. Doing nothing.");
return;
}
// Detach from a previous FlutterEngine so we can attach to this new one.
Log.d(TAG, "Currently attached to a different engine. Detaching.");
detachFromFlutterEngine();
}
this.flutterEngine = flutterEngine;
// Instruct our FlutterRenderer that we are now its designated RenderSurface.
this.flutterEngine.getRenderer().attachToRenderSurface(renderSurface);
// Initialize various components that know how to process Android View I/O
// in a way that Flutter understands.
textInputPlugin = new TextInputPlugin(
this,
this.flutterEngine.getDartExecutor()
);
androidKeyProcessor = new AndroidKeyProcessor(
this.flutterEngine.getKeyEventChannel(),
textInputPlugin
);
androidTouchProcessor = new AndroidTouchProcessor(this.flutterEngine.getRenderer());
accessibilityBridge = new AccessibilityBridge(
this,
flutterEngine.getAccessibilityChannel(),
(AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE),
getContext().getContentResolver(),
// TODO(mattcaroll): plumb the platform views controller to the accessibility bridge.
// https://github.com/flutter/flutter/issues/29618
null
);
accessibilityBridge.setOnAccessibilityChangeListener(onAccessibilityChangeListener);
resetWillNotDraw(
accessibilityBridge.isAccessibilityEnabled(),
accessibilityBridge.isTouchExplorationEnabled()
);
// Inform the Android framework that it should retrieve a new InputConnection
// now that an engine is attached.
// TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin
textInputPlugin.getInputMethodManager().restartInput(this);
// Push View and Context related information from Android to Flutter.
sendUserSettingsToFlutter();
sendLocalesToFlutter(getResources().getConfiguration());
sendViewportMetricsToFlutter();
}
/**
* Disconnects this {@code FlutterView} from a previously attached {@link FlutterEngine}.
*
* This {@code FlutterView} will clear its UI and stop forwarding all events to the previously-attached
* {@link FlutterEngine}. This includes touch events, accessibility events, keyboard events,
* and others.
*
* See {@link #attachToFlutterEngine(FlutterEngine)} for information on how to attach a
* {@link FlutterEngine}.
*/
public void detachFromFlutterEngine() {
Log.d(TAG, "detachFromFlutterEngine()");
if (!isAttachedToFlutterEngine()) {
Log.d(TAG, "Not attached to an engine. Doing nothing.");
return;
}
Log.d(TAG, "Detaching from Flutter Engine");
// Inform the Android framework that it should retrieve a new InputConnection
// now that the engine is detached. The new InputConnection will be null, which
// signifies that this View does not process input (until a new engine is attached).
// TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin
textInputPlugin.getInputMethodManager().restartInput(this);
// Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface.
flutterEngine.getRenderer().detachFromRenderSurface();
flutterEngine = null;
// TODO(mattcarroll): clear the surface when JNI doesn't blow up
// if (isSurfaceAvailableForRendering) {
// Canvas canvas = surfaceHolder.lockCanvas();
// canvas.drawColor(Color.RED);
// surfaceHolder.unlockCanvasAndPost(canvas);
// }
}
private boolean isAttachedToFlutterEngine() {
return flutterEngine != null;
}
/**
* Send the current {@link Locale} configuration to Flutter.
*
* FlutterEngine must be non-null when this method is invoked.
*/
@SuppressWarnings("deprecation")
private void sendLocalesToFlutter(Configuration config) {
List<Locale> locales = new ArrayList<>();
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
LocaleList localeList = config.getLocales();
int localeCount = localeList.size();
for (int index = 0; index < localeCount; ++index) {
Locale locale = localeList.get(index);
locales.add(locale);
}
} else {
locales.add(config.locale);
}
if(flutterEngine!=null&&flutterEngine.getLocalizationChannel()!=null){
flutterEngine.getLocalizationChannel().sendLocales(locales);
}
}
/**
* Send various user preferences of this Android device to Flutter.
*
* For example, sends the user's "text scale factor" preferences, as well as the user's clock
* format preference.
*
* FlutterEngine must be non-null when this method is invoked.
*/
private void sendUserSettingsToFlutter() {
flutterEngine.getSettingsChannel().startMessage()
.setTextScaleFactor(getResources().getConfiguration().fontScale)
.setUse24HourFormat(DateFormat.is24HourFormat(getContext()))
.send();
}
// TODO(mattcarroll): consider introducing a system channel for this communication instead of JNI
private void sendViewportMetricsToFlutter() {
Log.d(TAG, "sendViewportMetricsToFlutter()");
if (!isAttachedToFlutterEngine()) {
Log.w(TAG, "Tried to send viewport metrics from Android to Flutter but this FlutterView was not attached to a FlutterEngine.");
return;
}
viewportMetrics.devicePixelRatio = getResources().getDisplayMetrics().density;
flutterEngine.getRenderer().setViewportMetrics(viewportMetrics);
}
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.idlefish.flutterboost.containers;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.idlefish.flutterboost.BoostFlutterEngine;
import com.idlefish.flutterboost.BoostFlutterView;
import com.idlefish.flutterboost.FlutterBoost;
import com.idlefish.flutterboost.Utils;
import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.idlefish.flutterboost.interfaces.IOperateSyncer;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import io.flutter.embedding.android.FlutterView;
import io.flutter.plugin.platform.PlatformPlugin;
public abstract class BoostFlutterActivity extends Activity implements IFlutterViewContainer {
protected BoostFlutterEngine mFlutterEngine;
protected BoostFlutterView mFlutterView;
protected IOperateSyncer mSyncer;
private Handler mHandler = new Handler(Looper.getMainLooper());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
configureWindowForTransparency();
mSyncer = FlutterBoost.singleton().containerManager().generateSyncer(this);
mFlutterEngine = createFlutterEngine();
mFlutterView = createFlutterView(mFlutterEngine);
setContentView(mFlutterView);
mSyncer.onCreate();
configureStatusBarForFullscreenFlutterExperience();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mHandler.post(new Runnable() {
@Override
public void run() {
configureStatusBarForFullscreenFlutterExperience();
}
});
}
protected void configureWindowForTransparency() {
if (isBackgroundTransparent()) {
getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
);
}
}
protected 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);
}
protected BoostFlutterEngine createFlutterEngine(){
return FlutterBoost.singleton().engineProvider().provideEngine(this);
}
protected BoostFlutterView createFlutterView(BoostFlutterEngine engine){
BoostFlutterView.Builder builder = new BoostFlutterView.Builder(this);
return builder.flutterEngine(engine)
.renderMode(FlutterView.RenderMode.texture)
.transparencyMode(isBackgroundTransparent() ?
FlutterView.TransparencyMode.transparent :
FlutterView.TransparencyMode.opaque)
.renderingProgressCoverCreator(new BoostFlutterView.RenderingProgressCoverCreator() {
@Override
public View createRenderingProgressCover(Context context) {
return BoostFlutterActivity.this.createRenderingProgressCover();
}
})
.build();
}
protected boolean isBackgroundTransparent(){
return false;
}
protected View createRenderingProgressCover(){
FrameLayout frameLayout = new FrameLayout(this);
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
frameLayout.addView(linearLayout,layoutParams);
ProgressBar progressBar = new ProgressBar(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
linearLayout.addView(progressBar,params);
TextView textView = new TextView(this);
params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_HORIZONTAL;
textView.setText("Frame Rendering...");
linearLayout.addView(textView,params);
return frameLayout;
}
@Override
protected void onResume() {
super.onResume();
mSyncer.onAppear();
mFlutterEngine.getLifecycleChannel().appIsResumed();
}
@Override
protected void onPause() {
mSyncer.onDisappear();
super.onPause();
mFlutterEngine.getLifecycleChannel().appIsInactive();
}
@Override
protected void onDestroy() {
mSyncer.onDestroy();
super.onDestroy();
}
@Override
public void onBackPressed() {
mSyncer.onBackPressed();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mSyncer.onNewIntent(intent);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, 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);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
mSyncer.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
mSyncer.onTrimMemory(level);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mSyncer.onLowMemory();
}
@Override
protected void onUserLeaveHint() {
super.onUserLeaveHint();
mSyncer.onUserLeaveHint();
}
@Override
public Activity getContextActivity() {
return this;
}
@Override
public BoostFlutterView getBoostFlutterView() {
return mFlutterView;
}
@Override
public void finishContainer(Map<String,Object> result) {
if(result != null) {
FlutterBoost.setBoostResult(this,new HashMap<>(result));
finish();
}else{
finish();
}
}
@Override
public void onContainerShown() {}
@Override
public void onContainerHidden() {}
}
......@@ -21,140 +21,138 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.containers;
package com.idlefish.flutterboost.containers;
import android.content.Context;
import android.graphics.Color;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.view.Window;
import android.view.WindowManager;
import com.taobao.idlefish.flutterboost.BoostFlutterView;
import com.taobao.idlefish.flutterboost.Debuger;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.idlefish.flutterboost.BoostFlutterEngine;
import com.idlefish.flutterboost.BoostFlutterView;
import com.idlefish.flutterboost.FlutterBoost;
import com.idlefish.flutterboost.Utils;
import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.idlefish.flutterboost.interfaces.IOperateSyncer;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.embedding.android.FlutterView;
import io.flutter.plugin.platform.PlatformPlugin;
abstract public class BoostFlutterFragment extends Fragment implements IFlutterViewContainer {
FlutterContent mContent;
PluginRegistry mRegistry;
boolean resumed = false;
protected BoostFlutterEngine mFlutterEngine;
protected BoostFlutterView mFlutterView;
protected IOperateSyncer mSyncer;
@Nullable
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mSyncer = FlutterBoost.singleton().containerManager().generateSyncer(this);
mFlutterEngine = createFlutterEngine();
mFlutterView = createFlutterView(mFlutterEngine);
mSyncer.onCreate();
mRegistry = FlutterBoostPlugin.containerManager().onContainerCreate(this);
onRegisterPlugins(mRegistry);
return mFlutterView;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
mContent = new FlutterContent(getActivity());
return mContent;
protected BoostFlutterEngine createFlutterEngine(){
return FlutterBoost.singleton().engineProvider().provideEngine(getContext());
}
protected BoostFlutterView createFlutterView(BoostFlutterEngine engine){
BoostFlutterView.Builder builder = new BoostFlutterView.Builder(getContextActivity());
return builder.flutterEngine(engine)
.renderMode(FlutterView.RenderMode.texture)
.transparencyMode(FlutterView.TransparencyMode.opaque)
.build();
}
@Override
public void onResume() {
super.onResume();
if (!resumed) {
resumed = true;
FlutterBoostPlugin.containerManager().onContainerAppear(this);
mContent.attachFlutterView(getBoostFlutterView());
Log.e("FlutterBoost", "FlutterMenuFragment resume");
}
mSyncer.onAppear();
mFlutterEngine.getLifecycleChannel().appIsResumed();
}
@Override
public void onPause() {
mSyncer.onDisappear();
super.onPause();
if (resumed) {
resumed = false;
mContent.snapshot();
FlutterBoostPlugin.containerManager().onContainerDisappear(this);
Log.e("FlutterBoost", "FlutterMenuFragment stop");
}
mFlutterEngine.getLifecycleChannel().appIsInactive();
}
@Override
public void onDestroy() {
mSyncer.onDestroy();
super.onDestroy();
if (mContent != null) {
mContent.destroy();
}
FlutterBoostPlugin.containerManager().onContainerDestroy(this);
}
@Override
public void onContainerShown() {
mContent.onContainerShown();
public void onBackPressed() {
mSyncer.onBackPressed();
}
@Override
public void onContainerHidden() {
mContent.onContainerHidden();
public void onNewIntent(Intent intent) {
mSyncer.onNewIntent(intent);
}
@Override
public BoostFlutterView getBoostFlutterView() {
return FlutterBoostPlugin.viewProvider().createFlutterView(this);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mSyncer.onActivityResult(requestCode,resultCode,data);
}
@Override
public boolean isFinishing() {
return getActivity().isFinishing();
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
mSyncer.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected View createSplashScreenView() {
FrameLayout layout = new FrameLayout(getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER;
layout.addView(new ProgressBar(getContext()),params);
return layout;
public void onTrimMemory(int level) {
mSyncer.onTrimMemory(level);
}
protected View createFlutterInitCoverView() {
View initCover = new View(getActivity());
initCover.setBackgroundColor(Color.WHITE);
return initCover;
@Override
public void onLowMemory() {
super.onLowMemory();
mSyncer.onLowMemory();
}
@Override
public void setBoostResult(HashMap result) {
public void onUserLeaveHint() {
mSyncer.onUserLeaveHint();
}
class FlutterContent extends FlutterViewStub {
@Override
public Activity getContextActivity() {
return getActivity();
}
public FlutterContent(Context context) {
super(context);
}
@Override
public BoostFlutterView getBoostFlutterView() {
return mFlutterView;
}
@Override
public View createFlutterInitCoverView() {
return BoostFlutterFragment.this.createFlutterInitCoverView();
}
@Override
public void finishContainer(Map<String,Object> result) {
getFragmentManager().popBackStack();
}
@Override
public BoostFlutterView getBoostFlutterView() {
return BoostFlutterFragment.this.getBoostFlutterView();
}
@Override
public void onContainerShown() {}
@Override
public View createSplashScreenView() {
return BoostFlutterFragment.this.createSplashScreenView();
}
}
@Override
public void onContainerHidden() {}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.idlefish.flutterboost.interfaces;
public interface IContainerManager {
IOperateSyncer generateSyncer(IFlutterViewContainer container);
IContainerRecord getCurrentTopRecord();
IContainerRecord getLastGenerateRecord();
IFlutterViewContainer findContainerById(String uniqueId);
boolean hasContainerAppear();
}
\ No newline at end of file
......@@ -21,14 +21,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.interfaces;
import java.util.Map;
package com.idlefish.flutterboost.interfaces;
/**
* a container record, which use map a flutter page
*/
public interface IContainerRecord {
public interface IContainerRecord extends IOperateSyncer{
String UNIQ_KEY = "__container_uniqueId_key__";
int STATE_UNKNOW = 0;
int STATE_CREATED = 1;
int STATE_APPEAR = 2;
......@@ -38,9 +39,4 @@ public interface IContainerRecord {
String uniqueId();
IFlutterViewContainer getContainer();
int getState();
void onCreate();
void onAppear();
void onDisappear();
void onDestroy();
void onResult(Map Result);
}
......@@ -21,32 +21,33 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
package com.idlefish.flutterboost.interfaces;
import android.content.Context;
import io.flutter.view.FlutterNativeView;
public class BoostFlutterNativeView extends FlutterNativeView {
public BoostFlutterNativeView(Context context) {
super(context);
}
public void detachFromFlutterView() {
//do nothing...
}
public void detach() {
//do nothing...
}
@Override
public void destroy() {
//do nothing...
}
public void boostDestroy() {
super.destroy();
}
import com.idlefish.flutterboost.BoostFlutterEngine;
/**
* a flutter engine provider
*/
public interface IFlutterEngineProvider {
/**
* create flutter engine, we just hold a single instance now
* @param context
* @return
*/
BoostFlutterEngine createEngine(Context context);
/**
* provide a flutter engine
* @param context
* @return
*/
BoostFlutterEngine provideEngine(Context context);
/**
* may return null
* @return
*/
BoostFlutterEngine tryGetEngine();
}
......@@ -21,15 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.interfaces;
package com.idlefish.flutterboost.interfaces;
import android.app.Activity;
import com.taobao.idlefish.flutterboost.BoostFlutterView;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
import com.idlefish.flutterboost.BoostFlutterView;
import java.util.Map;
/**
* a container which contains the flutter view
......@@ -37,7 +35,7 @@ import io.flutter.plugin.common.PluginRegistry;
public interface IFlutterViewContainer {
String RESULT_KEY = "_flutter_result_";
Activity getActivity();
Activity getContextActivity();
/**
* provide a flutter view
......@@ -48,19 +46,19 @@ public interface IFlutterViewContainer {
/**
* call to destroy the container
*/
void destroyContainer();
void finishContainer(Map<String,Object> result);
/**
* container name
* @return
*/
String getContainerName();
String getContainerUrl();
/**
* container params
* @return
*/
Map getContainerParams();
Map getContainerUrlParams();
/**
* callback when container shown
......@@ -71,22 +69,4 @@ public interface IFlutterViewContainer {
* callback when container hidden
*/
void onContainerHidden();
/**
* is container finishing
* @return
*/
boolean isFinishing();
/**
* call by flutter side to set result
* @param result
*/
void setBoostResult(HashMap result);
/**
* register flutter plugins
* @param registry
*/
void onRegisterPlugins(PluginRegistry registry);
}
package com.idlefish.flutterboost.interfaces;
import android.content.Intent;
import java.util.Map;
public interface IOperateSyncer {
void onCreate();
void onAppear();
void onDisappear();
void onDestroy();
void onBackPressed();
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
void onNewIntent(Intent intent);
void onActivityResult(int requestCode, int resultCode, Intent data);
void onContainerResult(int requestCode, int resultCode, Map<String,Object> result);
void onUserLeaveHint();
void onTrimMemory(int level);
void onLowMemory();
}
......@@ -21,31 +21,29 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.interfaces;
package com.idlefish.flutterboost.interfaces;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
/**
* APIs that platform(Android App) must provide
*/
public interface IPlatform {
int IMMEDIATELY = 0; //立即启动引擎
int ANY_ACTIVITY_CREATED = 1; //当有任何Activity创建时,启动引擎
/**
* get current application
* @return
*/
Application getApplication();
/**
* get main activity, which must always exist at the bottom of task stack.
* @return
*/
Activity getMainActivity();
/**
* debug or not
* @return
......@@ -53,18 +51,25 @@ public interface IPlatform {
boolean isDebug();
/**
* start a new activity from flutter page, you may need a page router with url
* @param context
* @param url
* @param requestCode
* register plugins
* @return
*/
boolean startActivity(Context context,String url,int requestCode);
void registerPlugins(PluginRegistry registry);
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);
IFlutterEngineProvider engineProvider();
/**
* settings, no use
* @return
*
* IMMEDIATELY //立即
* ANY_ACTIVITY_CREATED //当有任何Activity创建的时候
* LAZY //懒加载,尽可能延后
*/
Map getSettings();
int whenEngineStart();
}
package com.idlefish.flutterboost.interfaces;
import com.idlefish.flutterboost.BoostChannel;
import com.idlefish.flutterboost.BoostFlutterEngine;
import com.idlefish.flutterboost.BoostFlutterView;
import io.flutter.plugin.common.PluginRegistry;
public interface IStateListener {
void onEngineCreated(BoostFlutterEngine engine);
void onEngineStarted(BoostFlutterEngine engine);
void onChannelRegistered(PluginRegistry.Registrar registrar, BoostChannel channel);
void onFlutterViewInited(BoostFlutterEngine engine, BoostFlutterView flutterView);
void beforeEngineAttach(BoostFlutterEngine engine, BoostFlutterView flutterView);
void afterEngineAttached(BoostFlutterEngine engine, BoostFlutterView flutterView);
void beforeEngineDetach(BoostFlutterEngine engine, BoostFlutterView flutterView);
void afterEngineDetached(BoostFlutterEngine engine, BoostFlutterView flutterView);
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.WindowInsets;
import com.taobao.idlefish.flutterboost.NavigationService.NavigationService;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterView;
public class BoostFlutterView extends FlutterView {
private boolean mFirstFrameCalled = false;
private boolean mResumed = false;
private WindowInsets mCurrentWindowInsets;
private BoostCallback mBoostCallback;
public BoostFlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
super(context, attrs, nativeView);
super.addFirstFrameListener(new FirstFrameListener() {
@Override
public void onFirstFrame() {
mFirstFrameCalled = true;
}
});
try {
Field field = FlutterView.class.getDeclaredField("mSurfaceCallback");
field.setAccessible(true);
SurfaceHolder.Callback cb = (SurfaceHolder.Callback)field.get(this);
getHolder().removeCallback(cb);
mBoostCallback = new BoostCallback(cb);
getHolder().addCallback(mBoostCallback);
}catch (Throwable t){
Debuger.exception(t);
}
}
@Override
public void onStart() {
//do nothing...
}
@Override
public void onPostResume() {
//do nothing...
requestFocus();
}
@Override
public void onPause() {
//do nothing...
}
@Override
public void onStop() {
//do nothing...
}
@Override
public FlutterNativeView detach() {
//do nothing...
return getFlutterNativeView();
}
@Override
public void destroy() {
//do nothing...
}
@Override
public Bitmap getBitmap() {
if(getFlutterNativeView() == null || !getFlutterNativeView().isAttached()) {
Debuger.exception("FlutterView not attached!");
return null;
}
return super.getBitmap();
}
public boolean firstFrameCalled() {
return mFirstFrameCalled;
}
public void boostResume() {
if (!mResumed) {
mResumed = true;
super.onPostResume();
Debuger.log("resume flutter view");
}
}
public void boostStop() {
if (mResumed) {
super.onStop();
Debuger.log("stop flutter view");
mResumed = false;
}
}
public boolean isResumed() {
return mResumed;
}
public void boostDestroy() {
super.destroy();
}
public void scheduleFrame(){
if (mResumed) {
Map<String,String> map = new HashMap<>();
map.put("type","scheduleFrame");
NavigationService.getService().emitEvent(map);
}
}
class BoostCallback implements SurfaceHolder.Callback {
final SurfaceHolder.Callback mCallback;
BoostCallback(SurfaceHolder.Callback cb){
this.mCallback = cb;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
//Debuger.log("flutterView surfaceCreated");
try {
mCallback.surfaceCreated(holder);
}catch (Throwable t){
Debuger.exception(t);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//Debuger.log("flutterView surfaceChanged");
try {
mCallback.surfaceChanged(holder,format,width,height);
scheduleFrame();
}catch (Throwable t){
Debuger.exception(t);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
//Debuger.log("flutterView surfaceDestroyed");
try {
mCallback.surfaceDestroyed(holder);
}catch (Throwable t){
Debuger.exception(t);
}
}
}
@Override
protected void onAttachedToWindow() {
//Debuger.log("flutterView onAttachedToWindow");
super.onAttachedToWindow();
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// final WindowInsets windowInsets = getRootWindowInsets();
// if(windowInsets != null) {
// if(mCurrentWindowInsets == null ||
// !TextUtils.equals(windowInsets.toString(),mCurrentWindowInsets.toString())) {
// Debuger.log("setWindowInsets "+windowInsets.toString());
//
// mCurrentWindowInsets = windowInsets;
// super.onApplyWindowInsets(mCurrentWindowInsets);
// }
// }
// }else {
// ViewCompat.requestApplyInsets(this);
// }
ViewCompat.requestApplyInsets(this);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import com.alibaba.fastjson.JSON;
import com.taobao.idlefish.flutterboost.NavigationService.NavigationService;
import com.taobao.idlefish.flutterboost.interfaces.IContainerRecord;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.taobao.idlefish.flutterboost.loader.ServiceLoader;
import com.taobao.idlefish.flutterboost.interfaces.IContainerManager;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewProvider;
import com.taobao.idlefish.flutterboost.interfaces.IPlatform;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.view.FlutterView;
public class FlutterBoostPlugin implements MethodChannel.MethodCallHandler, Application.ActivityLifecycleCallbacks {
private static FlutterBoostPlugin sInstance = null;
private static int kRid = 0;
public static synchronized void init(IPlatform platform) {
if (sInstance == null) {
sInstance = new FlutterBoostPlugin(platform);
platform.getApplication().registerActivityLifecycleCallbacks(sInstance);
ServiceLoader.load();
}
}
public static void registerWith(PluginRegistry.Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_boost");
channel.setMethodCallHandler(sInstance);
}
public static IFlutterViewProvider viewProvider() {
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet");
}
return sInstance.mViewProvider;
}
public static IContainerManager containerManager() {
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet");
}
return sInstance.mManager;
}
public static IPlatform platform() {
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet");
}
return sInstance.mPlatform;
}
public static Activity currentActivity() {
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet");
}
return sInstance.mCurrentActiveActivity;
}
private final IPlatform mPlatform;
private final IContainerManager mManager;
private final IFlutterViewProvider mViewProvider;
private final PageResultMediator mMediator;
private Activity mCurrentActiveActivity;
private FlutterBoostPlugin(IPlatform platform) {
mPlatform = platform;
mViewProvider = new FlutterViewProvider(platform);
mManager = new FlutterViewContainerManager();
mMediator = new PageResultMediator();
}
public IFlutterViewContainer findContainerById(String id) {
return mManager.findContainerById(id);
}
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
public static void openPage(Context context, String url, final Map params, int requestCode) {
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet!");
}
Context ctx = context;
if (ctx == null) {
ctx = currentActivity();
}
if (ctx == null) {
ctx = sInstance.mPlatform.getMainActivity();
}
if (ctx == null) {
ctx = sInstance.mPlatform.getApplication();
}
sInstance.mPlatform.startActivity(ctx, concatUrl(url, params), requestCode);
}
public static void openPage(Context context, String url, final Map params, int requestCode,PageResultHandler handler) {
if(handler != null){
String rid = createResultId();
sInstance.mMediator.setHandler(rid,handler);
params.put("result_id",rid);
}
openPage(context,url,params,requestCode);
}
private static String createResultId(){
kRid += 2;
return "result_id_" + kRid;
}
public static void onPageResult(String key , Map resultData, Map params){
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet!");
}
sInstance.mMediator.onPageResult(key,resultData,params);
}
public static void setHandler(String key, PageResultHandler handler) {
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet!");
}
sInstance.mMediator.setHandler(key, handler);
}
public static void removeHandler(String key) {
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet!");
}
sInstance.mMediator.removeHandler(key);
}
private static String concatUrl(String url, Map params) {
if (params == null || params.isEmpty()) return url;
Uri uri = Uri.parse(url);
Uri.Builder builder = uri.buildUpon();
for (Object key : params.keySet()) {
Object value = params.get(key);
if (value != null) {
String str;
if (value instanceof Map || value instanceof List) {
try {
str = URLEncoder.encode(JSON.toJSONString(value), "utf-8");
} catch (UnsupportedEncodingException e) {
str = value.toString();
}
} else {
str = value.toString();
}
builder.appendQueryParameter(String.valueOf(key), str);
}
}
return builder.build().toString();
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
if (mCurrentActiveActivity == null) {
Debuger.log("Application entry foreground");
if (mViewProvider.tryGetFlutterView() != null) {
Map<String, String> map = new HashMap<>();
map.put("type", "foreground");
NavigationService.getService().emitEvent(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 (mViewProvider.tryGetFlutterView() != null) {
Map<String, String> map = new HashMap<>();
map.put("type", "background");
NavigationService.getService().emitEvent(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 (mViewProvider.tryGetFlutterView() != null) {
Map<String, String> map = new HashMap<>();
map.put("type", "background");
NavigationService.getService().emitEvent(map);
}
mCurrentActiveActivity = null;
}
//reset view provider when single instance context is destroyed
// final FlutterView flutterView = mViewProvider.tryGetFlutterView();
// if(flutterView != null) {
// Activity ctxActivity = (Activity)flutterView.getContext();
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// if((ctxActivity.isDestroyed() || ctxActivity == activity) &&
// mManager.getLastRecord() == null) {
// mViewProvider.reset();
// }
// }
// }
}
public static void setBoostResult(Activity activity, HashMap result) {
Intent intent = new Intent();
if (result != null) {
intent.putExtra(IFlutterViewContainer.RESULT_KEY, result);
}
activity.setResult(Activity.RESULT_OK, intent);
}
public static void onBoostResult(IFlutterViewContainer container, int requestCode, int resultCode, Intent intent) {
Map map = new HashMap();
if (intent != null) {
map.put("result", intent.getSerializableExtra(IFlutterViewContainer.RESULT_KEY));
}
map.put("requestCode", requestCode);
map.put("responseCode", resultCode);
containerManager().onContainerResult(container, map);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
import android.app.Activity;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewProvider;
import com.taobao.idlefish.flutterboost.interfaces.IPlatform;
public class FlutterViewProvider implements IFlutterViewProvider {
private final IPlatform mPlatform;
private BoostFlutterNativeView mFlutterNativeView = null;
private BoostFlutterView mFlutterView = null;
FlutterViewProvider(IPlatform platform){
mPlatform = platform;
}
@Override
public BoostFlutterView createFlutterView(IFlutterViewContainer container) {
Activity activity = mPlatform.getMainActivity();
if(activity == null) {
Debuger.log("create Flutter View not with MainActivity");
activity = container.getActivity();
}
if (mFlutterView == null) {
mFlutterView = new BoostFlutterView(activity, null, createFlutterNativeView(container));
}
return mFlutterView;
}
@Override
public BoostFlutterNativeView createFlutterNativeView(IFlutterViewContainer container) {
if (mFlutterNativeView == null) {
mFlutterNativeView = new BoostFlutterNativeView(container.getActivity().getApplicationContext());
}
return mFlutterNativeView;
}
@Override
public BoostFlutterView tryGetFlutterView() {
return mFlutterView;
}
@Override
public void stopFlutterView() {
final BoostFlutterView view = mFlutterView;
if(view != null) {
view.boostStop();
}
}
@Override
public void reset() {
if(mFlutterNativeView != null) {
mFlutterNativeView.boostDestroy();
mFlutterNativeView = null;
}
if(mFlutterView != null) {
mFlutterView.boostDestroy();
mFlutterView = null;
}
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
import com.taobao.idlefish.flutterboost.interfaces.IContainerRecord;
import com.taobao.idlefish.flutterboost.interfaces.IContainerManager;
public class Instrument {
private final IContainerManager mManager;
Instrument(IContainerManager manager) {
mManager = manager;
}
public void performCreate(IContainerRecord record) {
final int currentState = record.getState();
if (currentState != IContainerRecord.STATE_UNKNOW) {
Debuger.exception("performCreate state error, current state:" + currentState);
return;
}
record.onCreate();
}
public void performAppear(IContainerRecord record) {
final int currentState = record.getState();
if (currentState != IContainerRecord.STATE_CREATED
&& currentState != IContainerRecord.STATE_DISAPPEAR) {
Debuger.exception("performAppear state error, current state:" + currentState);
return;
}
record.onAppear();
}
public void performDisappear(IContainerRecord record) {
final int currentState = record.getState();
if (currentState != IContainerRecord.STATE_APPEAR) {
Debuger.exception("performDisappear state error , current state:" + currentState);
return;
}
record.onDisappear();
}
public void performDestroy(IContainerRecord record) {
final int currentState = record.getState();
if (currentState != IContainerRecord.STATE_DISAPPEAR) {
Debuger.exception("performDestroy state error, current state:" + currentState);
}
record.onDestroy();
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.NavigationService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageHandler;
import fleamarket.taobao.com.xservicekit.service.ServiceTemplate;
import fleamarket.taobao.com.xservicekit.service.ServiceGateway;
import io.flutter.plugin.common.MethodChannel;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
public class NavigationService {
private static final ServiceTemplate mService = new ServiceTemplate("NavigationService");
public static ServiceTemplate getService(){
return mService;
}
public static void register(){
ServiceGateway.sharedInstance().addService(mService);
}
public static void onNativePageResult(final MessageResult<Boolean> result ,String uniqueId,String key,Map resultData,Map params){
Map<String,Object> args = new HashMap<>();
args.put("uniqueId",uniqueId);
args.put("key",key);
args.put("resultData",resultData);
args.put("params",params);
mService.invoke("onNativePageResult", args, mService.methodChannelName(), new MethodChannel.Result() {
@Override
public void success(Object o) {
if (o instanceof Boolean){
result.success((Boolean)o);
}else{
result.error("return type error code dart code","","");
}
}
@Override
public void error(String s, String s1, Object o) {
if (result != null){
result.error(s,s1,o);
}
}
@Override
public void notImplemented() {
if (result != null){
result.notImplemented();
}
}
});
}
public static void didShowPageContainer(final MessageResult<Boolean> result ,String pageName,Map params,String uniqueId){
Map<String,Object> args = new HashMap<>();
args.put("pageName",pageName);
args.put("params",params);
args.put("uniqueId",uniqueId);
mService.invoke("didShowPageContainer", args, mService.methodChannelName(), new MethodChannel.Result() {
@Override
public void success(Object o) {
if (o instanceof Boolean){
result.success((Boolean)o);
}else{
result.error("return type error code dart code","","");
}
}
@Override
public void error(String s, String s1, Object o) {
if (result != null){
result.error(s,s1,o);
}
}
@Override
public void notImplemented() {
if (result != null){
result.notImplemented();
}
}
});
}
public static void willShowPageContainer(final MessageResult<Boolean> result ,String pageName,Map params,String uniqueId){
Map<String,Object> args = new HashMap<>();
args.put("pageName",pageName);
args.put("params",params);
args.put("uniqueId",uniqueId);
mService.invoke("willShowPageContainer", args, mService.methodChannelName(), new MethodChannel.Result() {
@Override
public void success(Object o) {
if (o instanceof Boolean){
result.success((Boolean)o);
}else{
result.error("return type error code dart code","","");
}
}
@Override
public void error(String s, String s1, Object o) {
if (result != null){
result.error(s,s1,o);
}
}
@Override
public void notImplemented() {
if (result != null){
result.notImplemented();
}
}
});
}
public static void willDisappearPageContainer(final MessageResult<Boolean> result ,String pageName,Map params,String uniqueId){
Map<String,Object> args = new HashMap<>();
args.put("pageName",pageName);
args.put("params",params);
args.put("uniqueId",uniqueId);
mService.invoke("willDisappearPageContainer", args, mService.methodChannelName(), new MethodChannel.Result() {
@Override
public void success(Object o) {
if (o instanceof Boolean){
result.success((Boolean)o);
}else{
result.error("return type error code dart code","","");
}
}
@Override
public void error(String s, String s1, Object o) {
if (result != null){
result.error(s,s1,o);
}
}
@Override
public void notImplemented() {
if (result != null){
result.notImplemented();
}
}
});
}
public static void didDisappearPageContainer(final MessageResult<Boolean> result ,String pageName,Map params,String uniqueId){
Map<String,Object> args = new HashMap<>();
args.put("pageName",pageName);
args.put("params",params);
args.put("uniqueId",uniqueId);
mService.invoke("didDisappearPageContainer", args, mService.methodChannelName(), new MethodChannel.Result() {
@Override
public void success(Object o) {
if (o instanceof Boolean){
result.success((Boolean)o);
}else{
result.error("return type error code dart code","","");
}
}
@Override
public void error(String s, String s1, Object o) {
if (result != null){
result.error(s,s1,o);
}
}
@Override
public void notImplemented() {
if (result != null){
result.notImplemented();
}
}
});
}
public static void didInitPageContainer(final MessageResult<Boolean> result ,String pageName,Map params,String uniqueId){
Map<String,Object> args = new HashMap<>();
args.put("pageName",pageName);
args.put("params",params);
args.put("uniqueId",uniqueId);
mService.invoke("didInitPageContainer", args, mService.methodChannelName(), new MethodChannel.Result() {
@Override
public void success(Object o) {
if (o instanceof Boolean){
result.success((Boolean)o);
}else{
result.error("return type error code dart code","","");
}
}
@Override
public void error(String s, String s1, Object o) {
if (result != null){
result.error(s,s1,o);
}
}
@Override
public void notImplemented() {
if (result != null){
result.notImplemented();
}
}
});
}
public static void willDeallocPageContainer(final MessageResult<Boolean> result ,String pageName,Map params,String uniqueId){
Map<String,Object> args = new HashMap<>();
args.put("pageName",pageName);
args.put("params",params);
args.put("uniqueId",uniqueId);
mService.invoke("willDeallocPageContainer", args, mService.methodChannelName(), new MethodChannel.Result() {
@Override
public void success(Object o) {
if (o instanceof Boolean){
result.success((Boolean)o);
}else{
result.error("return type error code dart code","","");
}
}
@Override
public void error(String s, String s1, Object o) {
if (result != null){
result.error(s,s1,o);
}
}
@Override
public void notImplemented() {
if (result != null){
result.notImplemented();
}
}
});
}
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.NavigationService;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import com.taobao.idlefish.flutterboost.FlutterViewContainerManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageHandler;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
import fleamarket.taobao.com.xservicekit.service.ServiceGateway;
public class NavigationService_closePage implements MessageHandler<Boolean>{
private Object mContext = null;
private boolean onCall(MessageResult<Boolean> result,String uniqueId,String pageName,Map params,Boolean animated){
FlutterBoostPlugin.containerManager().destroyContainerRecord(pageName,uniqueId);
result.success(true);
return true;
}
//==================Do not edit code blow!==============
@Override
public boolean onMethodCall(String name, Map args, MessageResult<Boolean> result) {
this.onCall(result,(String)args.get("uniqueId"),(String)args.get("pageName"),(Map)args.get("params"),(Boolean)args.get("animated"));
return true;
}
@Override
public List<String> handleMessageNames() {
List<String> h = new ArrayList<>();
h.add("closePage");
return h;
}
@Override
public Object getContext() {
return mContext;
}
@Override
public void setContext(Object obj) {
mContext = obj;
}
@Override
public String service() {
return "NavigationService";
}
public static void register(){
ServiceGateway.sharedInstance().registerHandler(new NavigationService_closePage());
}
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.NavigationService;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageHandler;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
import fleamarket.taobao.com.xservicekit.service.ServiceGateway;
public class NavigationService_onFlutterPageResult implements MessageHandler<Boolean>{
private Object mContext = null;
private boolean onCall(MessageResult<Boolean> result,String uniqueId,String key,Map resultData,Map params){
FlutterBoostPlugin.containerManager().setContainerResult(uniqueId,resultData);
result.success(true);
return true;
}
//==================Do not edit code blow!==============
@Override
public boolean onMethodCall(String name, Map args, MessageResult<Boolean> result) {
this.onCall(result,(String)args.get("uniqueId"),(String)args.get("key"),(Map)args.get("resultData"),(Map)args.get("params"));
return true;
}
@Override
public List<String> handleMessageNames() {
List<String> h = new ArrayList<>();
h.add("onFlutterPageResult");
return h;
}
@Override
public Object getContext() {
return mContext;
}
@Override
public void setContext(Object obj) {
mContext = obj;
}
@Override
public String service() {
return "NavigationService";
}
public static void register(){
ServiceGateway.sharedInstance().registerHandler(new NavigationService_onFlutterPageResult());
}
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.NavigationService;
import com.taobao.idlefish.flutterboost.Debuger;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageHandler;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
import fleamarket.taobao.com.xservicekit.service.ServiceGateway;
public class NavigationService_onShownContainerChanged implements MessageHandler<Boolean>{
private Object mContext = null;
private boolean onCall(MessageResult<Boolean> result,String now,String old,Map params){
//Add your handler code here.
FlutterBoostPlugin.containerManager().onShownContainerChanged(old,now);
return true;
}
//==================Do not edit code blow!==============
@Override
public boolean onMethodCall(String name, Map args, MessageResult<Boolean> result) {
this.onCall(result,(String)args.get("newName"),(String)args.get("oldName"),(Map)args.get("params"));
return true;
}
@Override
public List<String> handleMessageNames() {
List<String> h = new ArrayList<>();
h.add("onShownContainerChanged");
return h;
}
@Override
public Object getContext() {
return mContext;
}
@Override
public void setContext(Object obj) {
mContext = obj;
}
@Override
public String service() {
return "NavigationService";
}
public static void register(){
ServiceGateway.sharedInstance().registerHandler(new NavigationService_onShownContainerChanged());
}
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.NavigationService;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageHandler;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
import fleamarket.taobao.com.xservicekit.service.ServiceGateway;
public class NavigationService_openPage implements MessageHandler<Boolean>{
private Object mContext = null;
private boolean onCall(MessageResult<Boolean> result,String pageName,Map params,Boolean animated){
FlutterBoostPlugin.openPage(null,pageName,params,0);
if(result != null){
result.success(true);
}
return true;
}
//==================Do not edit code blow!==============
@Override
public boolean onMethodCall(String name, Map args, MessageResult<Boolean> result) {
this.onCall(result,(String)args.get("pageName"),(Map)args.get("params"),(Boolean)args.get("animated"));
return true;
}
@Override
public List<String> handleMessageNames() {
List<String> h = new ArrayList<>();
h.add("openPage");
return h;
}
@Override
public Object getContext() {
return mContext;
}
@Override
public void setContext(Object obj) {
mContext = obj;
}
@Override
public String service() {
return "NavigationService";
}
public static void register(){
ServiceGateway.sharedInstance().registerHandler(new NavigationService_openPage());
}
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.NavigationService;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import com.taobao.idlefish.flutterboost.FlutterViewContainerManager;
import com.taobao.idlefish.flutterboost.interfaces.IContainerRecord;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageHandler;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
import fleamarket.taobao.com.xservicekit.service.ServiceGateway;
public class NavigationService_pageOnStart implements MessageHandler<Map>{
private Object mContext = null;
private boolean onCall(MessageResult<Map> result,Map params){
Map<String,Object> pageInfo = new HashMap<>();
try {
IContainerRecord record = FlutterBoostPlugin
.containerManager().getCurrentTopRecord();
if(record == null) {
record = FlutterBoostPlugin.containerManager().getLastRecord();
}
pageInfo.put("name",record.getContainer().getContainerName());
pageInfo.put("params",record.getContainer().getContainerParams());
pageInfo.put("uniqueId",record.uniqueId());
result.success(pageInfo);
}catch (Throwable t){
result.success(pageInfo);
}
return true;
}
//==================Do not edit code blow!==============
@Override
public boolean onMethodCall(String name, Map args, MessageResult<Map> result) {
this.onCall(result,(Map)args.get("params"));
return true;
}
@Override
public List<String> handleMessageNames() {
List<String> h = new ArrayList<>();
h.add("pageOnStart");
return h;
}
@Override
public Object getContext() {
return mContext;
}
@Override
public void setContext(Object obj) {
mContext = obj;
}
@Override
public String service() {
return "NavigationService";
}
public static void register(){
ServiceGateway.sharedInstance().registerHandler(new NavigationService_pageOnStart());
}
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
import java.util.Map;
public interface PageResultHandler {
void onResult(String key , Map resultData);
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
import com.taobao.idlefish.flutterboost.NavigationService.NavigationService;
import java.util.HashMap;
import java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
class PageResultMediator {
private Map<String,PageResultHandler> _handlers = new HashMap<>();
void onPageResult(String key , Map resultData,Map params){
if(key == null) return;
if(_handlers.containsKey(key)){
_handlers.get(key).onResult(key,resultData);
_handlers.remove(key);
}else{
if(params == null || !params.containsKey("forward")){
if(params == null){
params = new HashMap();
}
params.put("forward",1);
NavigationService.onNativePageResult(new MessageResult<Boolean>() {
@Override
public void success(Boolean var1) {
}
@Override
public void error(String var1, String var2, Object var3) {
}
@Override
public void notImplemented() {
}
},key,key,resultData,params);
}else{
int forward = (Integer) params.get("forward");
params.put("forward",++forward);
if(forward <= 2){
NavigationService.onNativePageResult(new MessageResult<Boolean>() {
@Override
public void success(Boolean var1) {
}
@Override
public void error(String var1, String var2, Object var3) {
}
@Override
public void notImplemented() {
}
},key,key,resultData,params);
}
}
}
}
void setHandler(String key, PageResultHandler handler){
if(key == null || handler == null) return;
_handlers.put(key,handler);
}
void removeHandler(String key){
if(key == null) return;;
_handlers.remove(key);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost;
import android.graphics.Bitmap;
import android.graphics.Color;
public class Utils {
public static boolean checkImageValid(final Bitmap bitmap) {
if (null == bitmap) {
return false;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
int [] checkPixels = new int[18];
for (int i=0; i<5; i++) {
int colCount = 4 - i%2;
for (int j=0; j<colCount; j++) {
checkPixels[i*3 + j + (i+1)/2] = pixels[(i + 1)*(height/6)*width + (j + 1)*(width/(colCount + 1))];
}
}
float[][] checkHSV = new float[checkPixels.length][3];
for (int i=0; i<checkPixels.length; i++) {
int clr = checkPixels[i];
int red = (clr & 0x00ff0000) >> 16; // 取高两位
int green = (clr & 0x0000ff00) >> 8; // 取中两位
int blue = clr & 0x000000ff;
Color.RGBToHSV(red, green, blue, checkHSV[i]);
}
int diffCount = 0;
for (int i=0; i<checkPixels.length; i++) {
for (int j=i+1; j<checkPixels.length; j++) {
double d = Math.sqrt(Math.pow(checkHSV[i][0] - checkHSV[j][0], 2.0)
+ Math.pow(checkHSV[i][1] - checkHSV[j][1], 2.0)
+ Math.pow(checkHSV[i][2] - checkHSV[j][2], 2.0));
if (d >= 1) {
diffCount++;
}
}
}
if (diffCount <= 10) {
return false;
} else {
return true;
}
}
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.containers;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import com.taobao.idlefish.flutterboost.BoostFlutterView;
import com.taobao.idlefish.flutterboost.Debuger;
abstract public class FlutterViewStub extends FrameLayout {
public static final Handler sHandler = new ProcessHandler(Looper.getMainLooper());
protected Bitmap mBitmap;
protected ImageView mSnapshot;
protected FrameLayout mStub;
protected View mCover;
protected View mSplashScreenView;
public FlutterViewStub(Context context) {
super(context);
mStub = new FrameLayout(context);
mStub.setBackgroundColor(Color.WHITE);
addView(mStub, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mSnapshot = new ImageView(context);
mSnapshot.setScaleType(ImageView.ScaleType.FIT_CENTER);
mSnapshot.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mCover = createFlutterInitCoverView();
addView(mCover, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
final BoostFlutterView flutterView = getBoostFlutterView();
if (!flutterView.firstFrameCalled()) {
mSplashScreenView = createSplashScreenView();
addView(mSplashScreenView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
public void onContainerShown() {
Debuger.log("onContainerShown");
sHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (mSplashScreenView != null) {
FlutterViewStub.this.removeView(mSplashScreenView);
mSplashScreenView = null;
}
if (mCover != null) {
FlutterViewStub.this.removeView(mCover);
}
if (mSnapshot.getParent() == FlutterViewStub.this) {
FlutterViewStub.this.removeView(mSnapshot);
mSnapshot.setImageBitmap(null);
if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
mBitmap = null;
}
}
getBoostFlutterView().scheduleFrame();
getBoostFlutterView().requestFocus();
getBoostFlutterView().invalidate();
}
}, 167);
}
public void onContainerHidden() {
//Debuger.log("onContainerHidden");
}
public void snapshot() {
if (mStub.getChildCount() <= 0) return;
if (mSnapshot.getParent() != null) return;
BoostFlutterView flutterView = (BoostFlutterView) mStub.getChildAt(0);
mBitmap = flutterView.getBitmap();
if (mBitmap != null && !mBitmap.isRecycled()) {
mSnapshot.setImageBitmap(mBitmap);
addView(mSnapshot);
}
}
public void attachFlutterView(final BoostFlutterView flutterView) {
if (flutterView.getParent() != mStub) {
sHandler.removeMessages(ProcessHandler.MSG_DETACH);
Debuger.log("attachFlutterView");
if (flutterView.getParent() != null) {
((ViewGroup) flutterView.getParent()).removeView(flutterView);
}
mStub.addView(flutterView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
public void detachFlutterView() {
if (mStub.getChildCount() <= 0) return;
final BoostFlutterView flutterView = (BoostFlutterView) mStub.getChildAt(0);
if (flutterView == null) return;
if (mSnapshot.getParent() == null) {
mBitmap = flutterView.getBitmap();
if (mBitmap != null && !mBitmap.isRecycled()) {
mSnapshot.setImageBitmap(mBitmap);
Debuger.log("snapshot view");
addView(mSnapshot);
}
}
Message msg = new Message();
msg.what = ProcessHandler.MSG_DETACH;
msg.obj = new Runnable() {
@Override
public void run() {
if (flutterView.getParent() != null && flutterView.getParent() == mStub) {
Debuger.log("detachFlutterView");
mStub.removeView(flutterView);
}
}
};
sHandler.sendMessageDelayed(msg,18);
}
public void destroy() {
removeAllViews();
mSnapshot.setImageBitmap(null);
if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
mBitmap = null;
}
}
protected View createFlutterInitCoverView() {
View initCover = new View(getContext());
initCover.setBackgroundColor(Color.WHITE);
return initCover;
}
protected View createSplashScreenView() {
FrameLayout layout = new FrameLayout(getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER;
layout.addView(new ProgressBar(getContext()),params);
return layout;
}
abstract protected BoostFlutterView getBoostFlutterView();
public static class ProcessHandler extends Handler {
static final int MSG_DETACH = 175101;
ProcessHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.obj instanceof Runnable) {
Runnable run = (Runnable) msg.obj;
run.run();
}
}
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.interfaces;
import android.app.Activity;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
public interface IContainerManager {
/**
* call by native side when container create
* @param container
* @return
*/
PluginRegistry onContainerCreate(IFlutterViewContainer container);
/**
* call by native side when container appear
* @param container
* @return
*/
void onContainerAppear(IFlutterViewContainer container);
/**
* call by native side when container disappear
* @param container
* @return
*/
void onContainerDisappear(IFlutterViewContainer container);
/**
* call by native side when container destroy
* @param container
* @return
*/
void onContainerDestroy(IFlutterViewContainer container);
/**
* call by native side when back key pressed
* @param container
* @return
*/
void onBackPressed(IFlutterViewContainer container);
/**
* call by flutter side when need destroy container
* @param name
* @param uq
*/
void destroyContainerRecord(String name,String uq);
/**
* call by native side when container handle a result (onActivityResult)
* @param container
* @param result
*/
void onContainerResult(IFlutterViewContainer container,Map result);
/**
* call by flutter side when flutter want set a result for request (setResult)
* @param uniqueId
* @param result
*/
void setContainerResult(String uniqueId,Map result);
/**
* get current interactive container
* @return
*/
IContainerRecord getCurrentTopRecord();
/**
* get last created container
* @return
*/
IContainerRecord getLastRecord();
/**
* find a container
* @param uniqueId
* @return
*/
IFlutterViewContainer findContainerById(String uniqueId);
/**
* call by flutter side when a container shown or hidden
* @param old
* @param now
*/
void onShownContainerChanged(String old,String now);
/**
* is any container appear now
* @return
*/
boolean hasContainerAppear();
/**
* no use
*/
void reset();
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.interfaces;
import com.taobao.idlefish.flutterboost.BoostFlutterNativeView;
import com.taobao.idlefish.flutterboost.BoostFlutterView;
/**
* a flutter view provider
*/
public interface IFlutterViewProvider {
/**
* create flutter view, we just hold a single instance now
* @param container
* @return
*/
BoostFlutterView createFlutterView(IFlutterViewContainer container);
/**
* single instance also
* @param container
* @return
*/
BoostFlutterNativeView createFlutterNativeView(IFlutterViewContainer container);
/**
* may return null
* @return
*/
BoostFlutterView tryGetFlutterView();
/**
* release flutter view
*/
void stopFlutterView();
/**
* reset all refrence
*/
void reset();
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.taobao.idlefish.flutterboost.loader;
public class ServiceLoader {
public static void load(){
com.taobao.idlefish.flutterboost.NavigationService.NavigationServiceRegister.register();
}
}
\ No newline at end of file
......@@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 27
compileSdkVersion 26
lintOptions {
disable 'InvalidPackage'
......@@ -35,7 +35,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.taobao.idlefish.flutterboostexample"
minSdkVersion 16
targetSdkVersion 27
targetSdkVersion 26
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
......@@ -58,6 +58,6 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:support-v4:26.1.0'
implementation 'com.android.support:appcompat-v7:26.1.0'
}
......@@ -7,6 +7,10 @@
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
......
......@@ -3,14 +3,11 @@ package com.taobao.idlefish.flutterboostexample;
import android.os.Bundle;
import android.support.annotation.Nullable;
import com.taobao.idlefish.flutterboost.containers.BoostFlutterFragment;
import com.idlefish.flutterboost.containers.BoostFlutterFragment;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class FlutterFragment extends BoostFlutterFragment {
@Override
......@@ -29,27 +26,17 @@ public class FlutterFragment extends BoostFlutterFragment {
}
@Override
public void onRegisterPlugins(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
@Override
public String getContainerName() {
public String getContainerUrl() {
return "flutterFragment";
}
@Override
public Map getContainerParams() {
public Map getContainerUrlParams() {
Map<String,String> params = new HashMap<>();
params.put("tag",getArguments().getString("tag"));
return params;
}
@Override
public void destroyContainer() {
}
public static FlutterFragment instance(String tag){
FlutterFragment fragment = new FlutterFragment();
fragment.setTabTag(tag);
......
package com.taobao.idlefish.flutterboostexample;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import io.flutter.plugin.platform.PlatformPlugin;
public class FlutterFragmentPageActivity extends AppCompatActivity {
public class FlutterFragmentPageActivity extends AppCompatActivity implements View.OnClickListener {
private FlutterFragment mFragment;
private View mTab1;
private View mTab2;
private View mTab3;
private View mTab4;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
......@@ -32,11 +39,49 @@ public class FlutterFragmentPageActivity extends AppCompatActivity {
setContentView(R.layout.flutter_fragment_page);
mFragment = FlutterFragment.instance("hello");
mTab1 = findViewById(R.id.tab1);
mTab2 = findViewById(R.id.tab2);
mTab3 = findViewById(R.id.tab3);
mTab4 = findViewById(R.id.tab4);
mTab1.setOnClickListener(this);
mTab2.setOnClickListener(this);
mTab3.setOnClickListener(this);
mTab4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
mTab1.setBackgroundColor(Color.WHITE);
mTab2.setBackgroundColor(Color.WHITE);
mTab3.setBackgroundColor(Color.WHITE);
mTab4.setBackgroundColor(Color.WHITE);
if(mTab1 == v) {
mTab1.setBackgroundColor(Color.YELLOW);
mFragment = FlutterFragment.instance("tab1");
}else if(mTab2 == v) {
mTab2.setBackgroundColor(Color.YELLOW);
mFragment = FlutterFragment.instance("tab2");
}else if(mTab3 == v) {
mTab3.setBackgroundColor(Color.YELLOW);
mFragment = FlutterFragment.instance("tab3");
}else{
mTab4.setBackgroundColor(Color.YELLOW);
mFragment = FlutterFragment.instance("tab4");
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_stub,mFragment)
.commit();
}
@Override
protected void onResume() {
super.onResume();
mTab1.performClick();
}
}
package com.taobao.idlefish.flutterboostexample;
import com.taobao.idlefish.flutterboost.containers.BoostFlutterActivity;
import com.idlefish.flutterboost.containers.BoostFlutterActivity;
import java.util.HashMap;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class FlutterPageActivity extends BoostFlutterActivity {
@Override
public void onRegisterPlugins(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
/**
* 该方法返回当前Activity在Flutter层对应的name,
* 混合栈将会在flutter层根据这个名字,在注册的Route表中查找对应的Widget
......@@ -31,7 +23,7 @@ public class FlutterPageActivity extends BoostFlutterActivity {
* @return
*/
@Override
public String getContainerName() {
public String getContainerUrl() {
return "flutterPage";
}
......@@ -50,7 +42,7 @@ public class FlutterPageActivity extends BoostFlutterActivity {
* @return
*/
@Override
public Map getContainerParams() {
public Map getContainerUrlParams() {
Map<String,String> params = new HashMap<>();
params.put("aaa","bbb");
return params;
......
......@@ -6,10 +6,9 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
......@@ -45,13 +44,14 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
@Override
public void onClick(View v) {
Map params = new HashMap();
//Add some params if needed.
if (v == mOpenNative) {
PageRouter.openPageByUrl(this, PageRouter.NATIVE_PAGE_URL);
PageRouter.openPageByUrl(this, PageRouter.NATIVE_PAGE_URL , params);
} else if (v == mOpenFlutter) {
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_URL);
FlutterBoostPlugin.onPageResult("result_id_100",new HashMap(),new HashMap());
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_URL,params);
} else if (v == mOpenFlutterFragment) {
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_FRAGMENT_PAGE_URL);
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_FRAGMENT_PAGE_URL,params);
}
}
}
package com.taobao.idlefish.flutterboostexample;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.taobao.idlefish.flutterboost.Debuger;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import com.taobao.idlefish.flutterboost.interfaces.IPlatform;
import com.idlefish.flutterboost.BoostEngineProvider;
import com.idlefish.flutterboost.BoostFlutterEngine;
import com.idlefish.flutterboost.FlutterBoost;
import com.idlefish.flutterboost.Platform;
import com.idlefish.flutterboost.interfaces.IFlutterEngineProvider;
import java.util.Map;
import io.flutter.app.FlutterApplication;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.view.FlutterMain;
public class MyApplication extends FlutterApplication {
@Override
public void onCreate() {
super.onCreate();
FlutterBoostPlugin.init(new IPlatform() {
FlutterBoost.init(new Platform() {
@Override
public Application getApplication() {
return MyApplication.this;
}
/**
* 获取应用入口的Activity,这个Activity在应用交互期间应该是一直在栈底的
* @return
*/
@Override
public Activity getMainActivity() {
if (MainActivity.sRef != null) {
return MainActivity.sRef.get();
}
return null;
}
@Override
public boolean isDebug() {
return true;
}
/**
* 如果flutter想打开一个本地页面,将会回调这个方法,页面参数将会拼接在url中
*
* 例如:sample://nativePage?aaa=bbb
*
* 参数就是类似 aaa=bbb 这样的键值对
*
* @param context
* @param url
* @param requestCode
* @return
*/
@Override
public boolean startActivity(Context context, String url, int requestCode) {
Debuger.log("startActivity url="+url);
public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
PageRouter.openPageByUrl(context,url,urlParams,requestCode);
}
return PageRouter.openPageByUrl(context,url,requestCode);
@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
public Map getSettings() {
return null;
public int whenEngineStart() {
return ANY_ACTIVITY_CREATED;
}
});
}
......
......@@ -6,6 +6,9 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import java.util.HashMap;
import java.util.Map;
public class NativePageActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mOpenNative;
......@@ -29,12 +32,13 @@ public class NativePageActivity extends AppCompatActivity implements View.OnClic
@Override
public void onClick(View v) {
Map params = new HashMap();
if (v == mOpenNative) {
PageRouter.openPageByUrl(this, PageRouter.NATIVE_PAGE_URL);
PageRouter.openPageByUrl(this, PageRouter.NATIVE_PAGE_URL,params);
} else if (v == mOpenFlutter) {
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_URL);
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_URL,params);
} else if (v == mOpenFlutterFragment) {
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_FRAGMENT_PAGE_URL);
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_FRAGMENT_PAGE_URL,params);
}
}
}
\ No newline at end of file
......@@ -4,17 +4,19 @@ import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import java.util.Map;
public class PageRouter {
public static final String NATIVE_PAGE_URL = "sample://nativePage";
public static final String FLUTTER_PAGE_URL = "sample://flutterPage";
public static final String FLUTTER_FRAGMENT_PAGE_URL = "sample://flutterFragmentPage";
public static boolean openPageByUrl(Context context, String url) {
return openPageByUrl(context, url, 0);
public static boolean openPageByUrl(Context context, String url,Map params) {
return openPageByUrl(context, url,params, 0);
}
public static boolean openPageByUrl(Context context, String url, int requestCode) {
public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) {
try {
if (url.startsWith(FLUTTER_PAGE_URL)) {
context.startActivity(new Intent(context, FlutterPageActivity.class));
......
......@@ -11,4 +11,47 @@
android:layout_weight="1"
android:id="@+id/fragment_stub"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="56dp"
android:text="tab1"
android:id="@+id/tab1"
android:gravity="center"
android:textColor="@android:color/background_dark"
android:layout_weight="1"/>
<TextView
android:layout_width="0dp"
android:layout_height="56dp"
android:text="tab2"
android:id="@+id/tab2"
android:gravity="center"
android:textColor="@android:color/background_dark"
android:layout_weight="1"/>
<TextView
android:layout_width="0dp"
android:layout_height="56dp"
android:text="tab3"
android:id="@+id/tab3"
android:gravity="center"
android:textColor="@android:color/background_dark"
android:layout_weight="1"/>
<TextView
android:layout_width="0dp"
android:layout_height="56dp"
android:text="tab4"
android:id="@+id/tab4"
android:gravity="center"
android:textColor="@android:color/background_dark"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
/Users/zhongchengguo/Desktop/flutter_boost
\ No newline at end of file
/Users/zhongchengguo/.pub-cache/hosted/pub.flutter-io.cn/xservice_kit-0.0.29
\ No newline at end of file
......@@ -49,6 +49,7 @@
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
8F322762730D82C0AD58AC8A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
......@@ -58,6 +59,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A1222E2FC45BEDD8C125BB85 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
C0C20F7D503C2CDF597A19CB /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DF544AA32177253600931378 /* UIViewControllerDemo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIViewControllerDemo.h; sourceTree = "<group>"; };
DF544AA42177253600931378 /* UIViewControllerDemo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIViewControllerDemo.m; sourceTree = "<group>"; };
......@@ -96,6 +98,8 @@
7BDD959A65220A7D194700AF /* Pods */ = {
isa = PBXGroup;
children = (
A1222E2FC45BEDD8C125BB85 /* Pods-Runner.debug.xcconfig */,
8F322762730D82C0AD58AC8A /* Pods-Runner.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
......@@ -176,6 +180,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
3FC74F80EA9D039C70D1F681 /* [CP] Embed Pods Frameworks */,
6CD183C8DCF5E8247072E2F6 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
......@@ -197,6 +202,7 @@
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = HH6T699A6Z;
};
};
};
......@@ -255,7 +261,7 @@
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
);
name = "[CP] Embed Pods Frameworks";
......@@ -267,6 +273,21 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
6CD183C8DCF5E8247072E2F6 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
......@@ -296,7 +317,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
......@@ -444,7 +465,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = HH6T699A6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
......@@ -456,7 +477,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = fleamarket.taobao.com.flutterBoostExample;
PRODUCT_BUNDLE_IDENTIFIER = fleamarket.taobao3.com.flutterBoostExample;
PRODUCT_NAME = "$(TARGET_NAME)";
REEXPORTED_LIBRARY_PATHS = "";
VERSIONING_SYSTEM = "apple-generic";
......@@ -469,7 +490,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = HH6T699A6Z;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
......@@ -481,7 +502,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = fleamarket.taobao.com.flutterBoostExample;
PRODUCT_BUNDLE_IDENTIFIER = fleamarket.taobao3.com.flutterBoostExample;
PRODUCT_NAME = "$(TARGET_NAME)";
REEXPORTED_LIBRARY_PATHS = "";
VERSIONING_SYSTEM = "apple-generic";
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
......@@ -19,6 +19,12 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
DemoRouter *router = [DemoRouter sharedRouter];
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router
onStart:^(FlutterEngine *fvc) {
}];
self.window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
......@@ -37,7 +43,7 @@
UITabBarController *tabVC = [[UITabBarController alloc] init];
UINavigationController *rvc = [[UINavigationController alloc] initWithRootViewController:tabVC];
DemoRouter *router = [DemoRouter sharedRouter];
router.navigationController = rvc;
tabVC.viewControllers = @[vc,fvc];
......@@ -45,10 +51,7 @@
self.window.rootViewController = rvc;
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router
onStart:^(FlutterViewController *fvc) {
}];
......
......@@ -8,10 +8,12 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <flutter_boost/FLBPlatform.h>
#import <flutter_boost/FlutterBoost.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FLBPlatform;
@interface DemoRouter : NSObject<FLBPlatform>
@property (nonatomic,strong) UINavigationController *navigationController;
......
......@@ -21,11 +21,14 @@
return instance;
}
- (void)openPage:(NSString *)name
params:(NSDictionary *)params
animated:(BOOL)animated
completion:(void (^)(BOOL))completion
#pragma mark - Boost 2
- (void)open:(NSString *)name
urlParams:(NSDictionary *)params
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion
{
BOOL animated = [exts[@"animated"] boolValue];
animated = YES;
if([params[@"present"] boolValue]){
FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new;
[vc setName:name params:params];
......@@ -40,14 +43,13 @@
}
}
- (BOOL)accessibilityEnable
{
return YES;
}
- (void)closePage:(NSString *)uid animated:(BOOL)animated params:(NSDictionary *)params completion:(void (^)(BOOL))completion
- (void)close:(NSString *)uid
result:(NSDictionary *)result
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion
{
BOOL animated = [exts[@"animated"] boolValue];
animated = YES;
FLBFlutterViewContainer *vc = (id)self.navigationController.presentedViewController;
if([vc isKindOfClass:FLBFlutterViewContainer.class] && [vc.uniqueIDString isEqual: uid]){
[vc dismissViewControllerAnimated:animated completion:^{}];
......@@ -56,8 +58,26 @@
}
}
- (void)flutterCanPop:(BOOL)canpop {
self.navigationController.interactivePopGestureRecognizer.enabled = !canpop;
#pragma mark - Boost 1
- (void)openPage:(NSString *)name
params:(NSDictionary *)params
animated:(BOOL)animated
completion:(void (^)(BOOL))completion
{
NSMutableDictionary *exts = NSMutableDictionary.new;
exts[@"url"] = name;
exts[@"params"] = params;
exts[@"animated"] = @(animated);
[self open:name urlParams:params exts:exts completion:completion];
return;
}
- (void)closePage:(NSString *)uid animated:(BOOL)animated params:(NSDictionary *)params completion:(void (^)(BOOL))completion
{
NSMutableDictionary *exts = NSMutableDictionary.new;
exts[@"params"] = params;
exts[@"animated"] = @(animated);
[self close:uid result:@{} exts:exts completion:completion];
return;
}
@end
......@@ -3,14 +3,12 @@
//
#import "GeneratedPluginRegistrant.h"
#import <flutter_boost/FlutterBoostPlugin.h>
#import <xservice_kit/XserviceKitPlugin.h>
#import <flutter_boost/BoostChannel.h>
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[FlutterBoostPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterBoostPlugin"]];
[XserviceKitPlugin registerWithRegistrar:[registry registrarForPlugin:@"XserviceKitPlugin"]];
[BoostChannel registerWithRegistrar:[registry registrarForPlugin:@"BoostChannel"]];
}
@end
......@@ -23,17 +23,20 @@
// Do any additional setup after loading the view from its nib.
}
- (IBAction)pushFlutterPage:(id)sender {
[DemoRouter.sharedRouter openPage:@"first" params:@{} animated:YES completion:^(BOOL f){
[FlutterBoostPlugin.sharedInstance onResultForKey:@"result_id_100" resultData:@{} params:@{}];
[DemoRouter.sharedRouter open:@"first"
urlParams:@{}
exts:@{@"animated":@(YES)}
completion:^(BOOL f){
}];
}
- (IBAction)present:(id)sender {
[DemoRouter.sharedRouter openPage:@"second" params:@{@"present":@(YES)} animated:YES completion:^(BOOL f){}];
[DemoRouter.sharedRouter open:@"second"
urlParams:@{@"present":@(YES)}
exts:@{@"animated":@(YES)}
completion:^(BOOL f){}];
// [self dismissViewControllerAnimated:YES completion:completion];
}
......
Future<Map<String,dynamic>> open(String url,{Map<String,dynamic> urlParams,Map<String,dynamic> exts}){
}
void close(String id,{Map<String,dynamic> result,Map<String,dynamic> exts}){
}
\ No newline at end of file
......@@ -29,9 +29,6 @@ class _MyAppState extends State<MyApp> {
return FlutterRouteWidget();
},
});
FlutterBoost.handleOnStartPage();
}
Map<String, WidgetBuilder> routes = {
......@@ -50,29 +47,5 @@ class _MyAppState extends State<MyApp> {
void _onRoutePushed(
String pageName, String uniqueId, Map params, Route route, Future _) {
// List<OverlayEntry> newEntries = route.overlayEntries
// .map((OverlayEntry entry) => OverlayEntry(
// builder: (BuildContext context) {
// final pageWidget = entry.builder(context);
// return Stack(
// children: <Widget>[
// pageWidget,
// Positioned(
// child: Text(
// "pageName:$pageName\npageWidget:${pageWidget.toStringShort()}",
// style: TextStyle(fontSize: 12.0, color: Colors.red),
// ),
// left: 8.0,
// top: 8.0,
// )
// ],
// );
// },
// opaque: entry.opaque,
// maintainState: entry.maintainState))
// .toList(growable: true);
//
// route.overlayEntries.clear();
// route.overlayEntries.addAll(newEntries);
}
}
......@@ -13,8 +13,10 @@ class FirstRouteWidget extends StatelessWidget {
child: Text('Open second route'),
onPressed: () {
FlutterBoost.singleton.openPage("second", {}, animated: true, resultHandler:(String key , Map<dynamic,dynamic> result){
print("did recieve second route result $key $result");
print("open second page!");
FlutterBoost.singleton.open("second").then((Map value){
print("did recieve second route result");
print("did recieve second route result $value");
});
},
......@@ -37,14 +39,9 @@ class SecondRouteWidget extends StatelessWidget {
// Navigate back to first route when tapped.
BoostContainerSettings settings = BoostContainer.of(context).settings;
if(settings.params.containsKey("result_id")){
String rid = settings.params["result_id"];
FlutterBoost.singleton.onPageResult(rid, {"data":"works"},{});
}
FlutterBoost.singleton.closePageForContext(context);
FlutterBoost.singleton.close(settings.uniqueId,result: {"result":"data from second"});
},
child: Text('Go back!'),
child: Text('Go back with result!'),
),
),
);
......@@ -61,7 +58,7 @@ class TabRouteWidget extends StatelessWidget {
body: Center(
child: RaisedButton(
onPressed: () {
FlutterBoost.singleton.openPage("second", {}, animated: true);
FlutterBoost.singleton.open("second");
},
child: Text('Open second route'),
),
......@@ -106,7 +103,7 @@ class FlutterRouteWidget extends StatelessWidget {
///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
///例如:sample://nativePage?aaa=bbb
onTap: () =>
FlutterBoost.singleton.openPage("sample://nativePage", {
FlutterBoost.singleton.open("sample://nativePage", urlParams:{
"query": {"aaa": "bbb"}
}),
),
......@@ -123,7 +120,7 @@ class FlutterRouteWidget extends StatelessWidget {
///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
///例如:sample://nativePage?aaa=bbb
onTap: () =>
FlutterBoost.singleton.openPage("sample://flutterPage", {
FlutterBoost.singleton.open("sample://flutterPage", urlParams:{
"query": {"aaa": "bbb"}
}),
),
......@@ -151,7 +148,7 @@ class FlutterRouteWidget extends StatelessWidget {
style: TextStyle(fontSize: 22.0, color: Colors.black),
)),
onTap: () => FlutterBoost.singleton
.openPage("sample://flutterFragmentPage", {}),
.open("sample://flutterFragmentPage"),
)
],
),
......@@ -200,7 +197,7 @@ class FragmentRouteWidget extends StatelessWidget {
style: TextStyle(fontSize: 22.0, color: Colors.black),
)),
onTap: () =>
FlutterBoost.singleton.openPage("sample://nativePage", {}),
FlutterBoost.singleton.open("sample://nativePage"),
),
InkWell(
child: Container(
......@@ -212,7 +209,7 @@ class FragmentRouteWidget extends StatelessWidget {
style: TextStyle(fontSize: 22.0, color: Colors.black),
)),
onTap: () =>
FlutterBoost.singleton.openPage("sample://flutterPage", {}),
FlutterBoost.singleton.open("sample://flutterPage"),
),
InkWell(
child: Container(
......@@ -224,7 +221,7 @@ class FragmentRouteWidget extends StatelessWidget {
style: TextStyle(fontSize: 22.0, color: Colors.black),
)),
onTap: () => FlutterBoost.singleton
.openPage("sample://flutterFragmentPage", {}),
.open("sample://flutterFragmentPage"),
)
],
),
......
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
TestPage({Key key, this.title = "Input Test"}) : super(key: key);
final String title;
@override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
bottom: false,
child: ListView(
children: <Widget>[
Container(
child: Text(
'You have pushed the button this many times:',
),
margin: const EdgeInsets.all(8.0),
alignment: Alignment.center,
),
Container(
child: Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
margin: const EdgeInsets.all(8.0),
alignment: Alignment.center,
),
Container(
child: TextField(
minLines: 2,
maxLines: 10,
),
padding: const EdgeInsets.all(8.0),
),
TestTextField(),
Container(
child: Container(
color: Colors.red,
width: double.infinity,
height: 128.0,
),
padding: const EdgeInsets.all(8.0),
),
Container(
child: Container(
color: Colors.orange,
width: double.infinity,
height: 128.0,
),
padding: const EdgeInsets.all(8.0),
),
Container(
child: Container(
color: Colors.green,
width: double.infinity,
height: 128.0,
),
padding: const EdgeInsets.all(8.0),
),
Container(
child: Container(
color: Colors.blue,
width: double.infinity,
height: 128.0,
),
padding: const EdgeInsets.all(8.0),
),
Container(
child: Container(
color: Colors.yellow,
width: double.infinity,
height: 128.0,
),
padding: const EdgeInsets.all(8.0),
),
Container(
child: TextField(
minLines: 2,
maxLines: 10,
),
padding: const EdgeInsets.all(8.0),
),
TestTextField(),
],
)),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class TestTextField extends StatefulWidget {
@override
_TestTextFieldState createState() => _TestTextFieldState();
}
class _TestTextFieldState extends State<TestTextField> {
FocusNode _node;
PersistentBottomSheetController _controller;
@override
void initState() {
// TODO: implement initState
super.initState();
_node = FocusNode();
_node.addListener(() {
if (_node.hasFocus) {
print('showBottomSheet');
_controller = Scaffold.of(context)
.showBottomSheet((BuildContext ctx) => Container(
width: double.infinity,
height: 36.0,
color: Colors.deepPurple,
));
} else {
if (_controller != null) {
//Navigator.of(context).pop();
print('closeBottomSheet');
_controller.close();
}
_controller = null;
}
});
}
@override
Widget build(BuildContext context) {
return Container(
child: TextField(
minLines: 2,
maxLines: 10,
focusNode: _node,
),
padding: const EdgeInsets.all(8.0),
);
}
}
/*
* The MIT License (MIT)
*
*
* Copyright (c) 2019 Alibaba Group
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -23,15 +23,11 @@
*/
#import <Foundation/Foundation.h>
#import "FLBFlutterViewProvider.h"
#import "FLBAbstractFactory.h"
NS_ASSUME_NONNULL_BEGIN
#if RELEASE_1_0
@interface FLBFlutterEngine : NSObject<FLBFlutterViewProvider>
@interface FLBFactory : NSObject<FLBAbstractFactory>
@end
#endif
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBFactory.h"
#import "FLBFlutterApplication.h"
#import "FLBFlutterViewContainer.h"
@implementation FLBFactory
- (id<FLBFlutterApplicationInterface>)createApplication:(id<FLBPlatform>)platform
{
return FLBFlutterApplication.new;
}
- (id<FLBFlutterContainer>)createFlutterContainer
{
return FLBFlutterViewContainer.new;
}
@end
......@@ -23,17 +23,13 @@
*/
#import <Foundation/Foundation.h>
#import "FLBFlutterViewProvider.h"
#import <Flutter/Flutter.h>
#import "FLBPlatform.h"
#import "FLBFlutterProvider.h"
#import "FLBFlutterApplicationInterface.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLBViewProviderFactory : NSObject
- (id<FLBFlutterViewProvider>)createViewProvider;
- (id<FLBFlutterViewProvider>)createViewProviderWithPlatform:(id<FLBPlatform>)platform;
@interface FLBFlutterApplication : NSObject<FLBFlutterApplicationInterface>
@property (nonatomic,strong) id<FLBPlatform> platform;
@end
NS_ASSUME_NONNULL_END
......@@ -24,17 +24,15 @@
#import "FLBFlutterApplication.h"
#import "FlutterBoost.h"
#import "FLBFlutterViewContainerManager.h"
#import "FLBViewProviderFactory.h"
#import "FLBFlutterContainerManager.h"
#import "FLBFlutterEngine.h"
@interface FLBFlutterApplication()
@property (nonatomic,strong) FLBFlutterViewContainerManager *manager;
@property (nonatomic,strong) id<FLBFlutterViewProvider> viewProvider;
@property (nonatomic,strong) NSMutableDictionary *pageBuilders;
@property (nonatomic,copy) FLBPageBuilder defaultPageBuilder;
@property (nonatomic,assign) BOOL isRendering;
@property (nonatomic,strong) FLBFlutterContainerManager *manager;
@property (nonatomic,strong) id<FLBFlutterProvider> viewProvider;
@property (nonatomic,assign) BOOL isRunning;
@property (nonatomic,strong) NSMutableDictionary *pageResultCallbacks;
@property (nonatomic,strong) NSMutableDictionary *callbackCache;
@end
......@@ -55,32 +53,47 @@
return _isRunning;
}
- (id)flutterProvider
{
return _viewProvider;
}
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform
onStart:(void (^)(FlutterViewController * _Nonnull))callback
onStart:(void (^)(id<FlutterBinaryMessenger,FlutterTextureRegistry,FlutterPluginRegistry> _Nonnull))callback
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
self.platform = platform;
self.viewProvider = [[FLBViewProviderFactory new] createViewProviderWithPlatform:platform];
[self.viewProvider resume];
self.isRendering = YES;
self.viewProvider = [[FLBFlutterEngine alloc] initWithPlatform:platform];
self.isRunning = YES;
if(callback) callback(self.viewProvider.viewController);
if(callback) callback(self.viewProvider.engine);
});
}
- (instancetype)init
{
if (self = [super init]) {
_manager = [FLBFlutterViewContainerManager new];
_pageBuilders = [NSMutableDictionary new];
_manager = [FLBFlutterContainerManager new];
_pageResultCallbacks = NSMutableDictionary.new;
_callbackCache = NSMutableDictionary.new;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (UIView *)flutterView
......@@ -88,23 +101,25 @@
return [self flutterViewController].view;
}
- (FlutterViewController *)flutterViewController
{
return self.viewProvider.viewController;
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self.viewProvider didEnterBackground];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self.viewProvider willEnterForeground];
}
- (BOOL)contains:(FLBFlutterViewContainer *)vc
- (BOOL)contains:(id<FLBFlutterContainer>)vc
{
return [_manager contains:vc];
}
- (void)addUniqueViewController:(FLBFlutterViewContainer *)vc
- (void)addUniqueViewController:(id<FLBFlutterContainer>)vc
{
return [_manager addUnique:vc];
}
- (void)removeViewController:(FLBFlutterViewContainer *)vc
- (void)removeViewController:(id<FLBFlutterContainer>)vc
{
return [_manager remove:vc];
}
......@@ -117,22 +132,12 @@
- (void)pause
{
if(!_isRendering) return;
[self.viewProvider pause];
_isRendering = NO;
}
- (void)resume
{
if(_isRendering) return;
[self.viewProvider resume];
_isRendering = YES;
}
- (void)inactive
......@@ -140,10 +145,70 @@
[self.viewProvider inactive];
}
- (void)setAccessibilityEnable:(BOOL)enable
- (FlutterViewController *)flutterViewController
{
return self.flutterProvider.engine.viewController;
}
- (void)close:(NSString *)uniqueId
result:(NSDictionary *)result
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion
{
[self.platform close:uniqueId
result:result
exts:exts
completion:completion];
if(_pageResultCallbacks[uniqueId]){
void (^cb)(NSDictionary *) = _pageResultCallbacks[uniqueId];
cb(result);
[_pageResultCallbacks removeObjectForKey:uniqueId];
}
}
- (void)open:(NSString *)url
urlParams:(NSDictionary *)urlParams
exts:(NSDictionary *)exts
reult:(void (^)(NSDictionary *))resultCallback
completion:(void (^)(BOOL))completion
{
NSString *cid = urlParams[@"__calback_id__"];
if(!cid){
static int64_t sCallbackID = 1;
cid = @(sCallbackID).stringValue;
sCallbackID += 2;
}
_callbackCache[cid] = resultCallback;
[self.platform open:url
urlParams:urlParams
exts:exts
completion:completion];
}
- (void)didInitPageContainer:(NSString *)url
params:(NSDictionary *)urlParams
uniqueId:(NSString *)uniqueId
{
[self.viewProvider setAccessibilityEnable:enable];
NSString *cid = urlParams[@"__calback_id__"];
if(cid && _callbackCache[cid]){
_pageResultCallbacks[uniqueId] = _callbackCache[cid];
[_callbackCache removeObjectForKey:cid];
}
}
- (void)willDeallocPageContainer:(NSString *)url
params:(NSDictionary *)params
uniqueId:(NSString *)uniqueId
{
if(_pageResultCallbacks[uniqueId]){
void (^cb)(NSDictionary *) = _pageResultCallbacks[uniqueId];
cb(@{});
[_pageResultCallbacks removeObjectForKey:uniqueId];
}
}
@end
......@@ -23,15 +23,11 @@
*/
#import <Foundation/Foundation.h>
#import "FLBFlutterViewProvider.h"
#import "FLBFlutterProvider.h"
#import "FLBPlatform.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterEngineOld : NSObject<FLBFlutterViewProvider>
@interface FLBFlutterEngine : NSObject<FLBFlutterProvider>
- (instancetype)initWithPlatform:(id<FLBPlatform>)platform;
@end
NS_ASSUME_NONNULL_END
......@@ -24,34 +24,39 @@
#import "FLBFlutterEngine.h"
#import <Flutter/Flutter.h>
#import "FLBFlutterViewControllerAdaptor.h"
#import "FLBFlutterViewContainer.h"
#import "BoostMessageChannel.h"
#if RELEASE_1_0
@interface FLBFlutterEngine()
@property (nonatomic,strong) FLBFlutterViewControllerAdaptor *viewController;
@property (nonatomic,strong) FlutterEngine *engine;
@property (nonatomic,strong) FLBFlutterViewContainer *dummy;
@end
@implementation FLBFlutterEngine
- (instancetype)init
- (instancetype)initWithPlatform:(id<FLBPlatform>)platform
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if (self = [super init]) {
_engine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];
[_engine runWithEntrypoint:nil];
_viewController = [[FLBFlutterViewControllerAdaptor alloc] initWithEngine:_engine
nibName:nil
bundle:nil];
[_viewController view];
if(platform &&
[platform respondsToSelector: @selector(entryForDart)] &&
platform.entryForDart){
[_engine runWithEntrypoint:platform.entryForDart];
}else{
[_engine runWithEntrypoint:nil];
}
_dummy = [[FLBFlutterViewContainer alloc] initWithEngine:_engine
nibName:nil
bundle:nil];
Class clazz = NSClassFromString(@"GeneratedPluginRegistrant");
if (clazz) {
if ([clazz respondsToSelector:NSSelectorFromString(@"registerWithRegistry:")]) {
[clazz performSelector:NSSelectorFromString(@"registerWithRegistry:")
withObject:_viewController];
withObject:_engine];
}
}
}
......@@ -60,23 +65,21 @@
#pragma clang diagnostic pop
}
- (FlutterViewController *)viewController
- (instancetype)init
{
return _viewController;
return [self initWithPlatform:nil];
}
- (void)pause
{
//TODO: [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"];
[self.viewController boost_viewWillDisappear:NO];
[self.viewController boost_viewDidDisappear:NO];
[[_engine lifecycleChannel] sendMessage:@"AppLifecycleState.paused"];
[self detach];
}
- (void)resume
{
//TODO: [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"];
[self.viewController boost_viewWillAppear:NO];
[self.viewController boost_viewDidAppear:NO];
[[_engine lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"];
[(FLBFlutterViewContainer *)_engine.viewController surfaceUpdated:YES];
}
- (void)inactive
......@@ -84,10 +87,43 @@
[[_engine lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"];
}
- (void)setAccessibilityEnable:(BOOL)enable
- (void)didEnterBackground
{
[BoostMessageChannel sendEvent:@"background"
arguments:nil];
}
- (void)willEnterForeground
{
[BoostMessageChannel sendEvent:@"foreground"
arguments:nil];
}
- (FlutterEngine *)engine
{
self.viewController.accessibilityEnable = enable;
return _engine;
}
- (void)atacheToViewController:(FlutterViewController *)vc
{
if(_engine.viewController != vc){
_engine.viewController = vc;
}
}
- (void)detach
{
if(_engine.viewController != _dummy){
_engine.viewController = _dummy;
}
}
- (void)prepareEngineIfNeeded
{
[self detach];
[_dummy surfaceUpdated:YES];
}
@end
#endif
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
#import "FLBFlutterContainer.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterViewContainer : FlutterViewController<FLBFlutterContainer>
- (void)surfaceUpdated:(BOOL)appeared;
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBFlutterViewContainer.h"
#import "FLBFlutterApplication.h"
#import "BoostMessageChannel.h"
#import "FLBFlutterContainerManager.h"
#import "FlutterBoostPlugin_private.h"
#define FLUTTER_APP [FlutterBoostPlugin sharedInstance].application
#define FLUTTER_VIEW FLUTTER_APP.flutterViewController.view
#define FLUTTER_VC FLUTTER_APP.flutterViewController
@interface FLBFlutterViewContainer ()
@property (nonatomic,copy,readwrite) NSString *name;
@property (nonatomic,strong,readwrite) NSDictionary *params;
@property (nonatomic,assign) long long identifier;
@end
@implementation FLBFlutterViewContainer
- (instancetype)init
{
[FLUTTER_APP.flutterProvider prepareEngineIfNeeded];
if(self = [super initWithEngine:FLUTTER_APP.flutterProvider.engine
nibName:nil
bundle:nil]){
[self _setup];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super initWithCoder: aDecoder]) {
NSAssert(NO, @"unsupported init method!");
[self _setup];
}
return self;
}
- (void)setName:(NSString *)name params:(NSDictionary *)params
{
if(!_name && name){
_name = name;
_params = params;
[BoostMessageChannel didInitPageContainer:^(NSNumber *r) {}
pageName:name
params:params
uniqueId:[self uniqueIDString]];
}
}
static NSUInteger kInstanceCounter = 0;
+ (NSUInteger)instanceCounter
{
return kInstanceCounter;
}
+ (void)instanceCounterIncrease
{
kInstanceCounter++;
if(kInstanceCounter == 1){
[FLUTTER_APP resume];
}
}
+ (void)instanceCounterDecrease
{
kInstanceCounter--;
if([self.class instanceCounter] == 0){
[FLUTTER_APP pause];
}
}
- (NSString *)uniqueIDString
{
return @(_identifier).stringValue;
}
- (void)_setup
{
static long long sCounter = 0;
_identifier = sCounter++;
[self.class instanceCounterIncrease];
}
- (void)dealloc
{
[self notifyWillDealloc];
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)notifyWillDealloc
{
[BoostMessageChannel willDeallocPageContainer:^(NSNumber *r) {}
pageName:_name params:_params
uniqueId:[self uniqueIDString]];
[FLUTTER_APP removeViewController:self];
[self.class instanceCounterDecrease];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
}
#pragma mark - ScreenShots
- (BOOL)isFlutterViewAttatched
{
return FLUTTER_VIEW.superview == self.view;
}
- (void)attatchFlutterEngine
{
[FLUTTER_APP.flutterProvider prepareEngineIfNeeded];
[FLUTTER_APP.flutterProvider atacheToViewController:self];
}
- (void)detatchFlutterEngine
{
[FLUTTER_APP.flutterProvider detach];
}
#pragma mark - Life circle methods
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[FLUTTER_APP resume];
}
- (void)viewWillAppear:(BOOL)animated
{
if([FLUTTER_APP contains:self]){
[self detatchFlutterEngine];
}else{
[self attatchFlutterEngine];
}
[FLUTTER_APP resume];
//For new page we should attach flutter view in view will appear
//for better performance.
[BoostMessageChannel willShowPageContainer:^(NSNumber *result) {}
pageName:_name
params:_params
uniqueId:self.uniqueIDString];
//Save some first time page info.
if(![FlutterBoostPlugin sharedInstance].fPagename){
[FlutterBoostPlugin sharedInstance].fPagename = _name;
[FlutterBoostPlugin sharedInstance].fPageId = self.uniqueIDString;
[FlutterBoostPlugin sharedInstance].fParams = _params;
}
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[FLUTTER_APP addUniqueViewController:self];
//Ensure flutter view is attached.
[self attatchFlutterEngine];
[FLUTTER_APP resume];
[BoostMessageChannel didShowPageContainer:^(NSNumber *result) {}
pageName:_name
params:_params
uniqueId:self.uniqueIDString];
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[BoostMessageChannel willDisappearPageContainer:^(NSNumber *result) {}
pageName:_name
params:_params
uniqueId:self.uniqueIDString];
[[[UIApplication sharedApplication] keyWindow] endEditing:YES];
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[FLUTTER_APP resume];
[BoostMessageChannel didDisappearPageContainer:^(NSNumber *result) {}
pageName:_name
params:_params
uniqueId:self.uniqueIDString];
}
- (void)installSplashScreenViewIfNecessary {
//Do nothing.
}
- (BOOL)loadDefaultSplashScreenView
{
return NO;
}
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBFlutterEngineOld.h"
#import "FLBFlutterViewControllerAdaptor.h"
#import <objc/runtime.h>
@interface FLBFlutterEngineOld()
@property (nonatomic,strong) FLBFlutterViewControllerAdaptor *viewController;
@end
@implementation FLBFlutterEngineOld
- (instancetype)initWithPlatform:(id<FLBPlatform>)platform
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if (self = [super init]) {
Class class = [FLBFlutterViewControllerAdaptor class];
SEL originalSelector = @selector(onAccessibilityStatusChanged:);
SEL swizzledSelector = @selector(fixed_onAccessibilityStatusChanged:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
_viewController = [FLBFlutterViewControllerAdaptor new];
if([platform respondsToSelector:@selector(accessibilityEnable)]){
_viewController.accessibilityEnable = [platform accessibilityEnable];
}else{
_viewController.accessibilityEnable = YES;
}
[_viewController view];
Class clazz = NSClassFromString(@"GeneratedPluginRegistrant");
if (clazz) {
if ([clazz respondsToSelector:NSSelectorFromString(@"registerWithRegistry:")]) {
[clazz performSelector:NSSelectorFromString(@"registerWithRegistry:")
withObject:_viewController];
}
}
}
return self;
#pragma clang diagnostic pop
}
- (FlutterViewController *)viewController
{
return _viewController;
}
- (void)pause
{
[self.viewController boost_viewWillDisappear:NO];
[self.viewController boost_viewDidDisappear:NO];
}
- (void)resume
{
[self.viewController boost_viewWillAppear:NO];
[self.viewController boost_viewDidAppear:NO];
}
- (void)inactive
{
NSString *channel = @"flutter/lifecycle";
NSString *message = @"AppLifecycleState.inactive";
NSData *data = [[FlutterStringCodec sharedInstance] encode:message];
[self.viewController sendOnChannel:channel message:data];
}
- (void)resumeFlutterOnly
{
NSString *channel = @"flutter/lifecycle";
NSString *message = @"AppLifecycleState.resumed";
NSData *data = [[FlutterStringCodec sharedInstance] encode:message];
[self.viewController sendOnChannel:channel message:data];
}
- (void)setAccessibilityEnable:(BOOL)enable
{
self.viewController.accessibilityEnable = enable;
}
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterViewControllerAdaptor : FlutterViewController
- (void)boost_viewWillAppear:(BOOL)animated;
- (void)boost_viewDidAppear:(BOOL)animated;
- (void)boost_viewWillDisappear:(BOOL)animated;
- (void)boost_viewDidDisappear:(BOOL)animated;
@property (nonatomic,assign) BOOL accessibilityEnable;
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBFlutterViewControllerAdaptor.h"
#import <objc/runtime.h>
@interface FLBFlutterViewControllerAdaptor ()
@end
@implementation FLBFlutterViewControllerAdaptor
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// Do any additional setup after loading the view.
}
- (void)viewWillAppear:(BOOL)animated
{
//Left blank intentionally.
}
- (void)viewDidAppear:(BOOL)animated
{
//Left blank intentionally.
}
- (void)viewWillDisappear:(BOOL)animated
{
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
//Avoid super call intentionally.
}
- (void)viewDidDisappear:(BOOL)animated
{
//Avoid super call intentionally.
[[[UIApplication sharedApplication] keyWindow] endEditing:YES];
}
- (void)boost_viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)boost_viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)boost_viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)boost_viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (UIEdgeInsets)paddingEdgeInsets{
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
if (@available(iOS 11, *)) {
edgeInsets = UIEdgeInsetsMake(0, self.view.safeAreaInsets.left, self.view.safeAreaInsets.bottom, self.view.safeAreaInsets.right);
} else {
edgeInsets = UIEdgeInsetsZero;
}
return edgeInsets;
}
- (void)installSplashScreenViewIfNecessary {
//Override this to avoid unnecessary splash Screen.
}
- (void)fixed_onAccessibilityStatusChanged:(NSNotification*)notification {
if(self.accessibilityEnable){
[self fixed_onAccessibilityStatusChanged:notification];
}
}
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBViewProviderFactory.h"
#import "FLBFlutterEngine.h"
#import "FLBFlutterEngineOld.h"
#import "FLBPlatform.h"
@implementation FLBViewProviderFactory
- (id<FLBFlutterViewProvider>)createViewProviderWithPlatform:(id<FLBPlatform>)platform
{
#if RELEASE_1_0
return [FLBFlutterEngine new];
#else
return [[FLBFlutterEngineOld alloc] initWithPlatform:platform];
#endif
}
- (id<FLBFlutterViewProvider>)createViewProvider
{
#if RELEASE_1_0
return [FLBFlutterEngine new];
#else
return [FLBFlutterEngineOld new];
#endif
}
@end
/*
* The MIT License (MIT)
*
*
* Copyright (c) 2019 Alibaba Group
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -21,14 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FLBViewControllerResultHandler <NSObject>
@required
- (void)onRecievedResult:(NSDictionary *)resultData forKey:(NSString *)key;
@interface BoostChannel : NSObject<FlutterPlugin>
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "BoostChannel.h"
#import "FlutterBoost.h"
@implementation BoostChannel
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[FlutterBoostPlugin registerWithRegistrar:registrar];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
[FlutterBoostPlugin.sharedInstance handleMethodCall:call result:result];
}
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import "FLBFlutterApplicationInterface.h"
#import "FLBFlutterProvider.h"
#import "FLBPlatform.h"
#import "FLBFlutterContainer.h"
NS_ASSUME_NONNULL_BEGIN
@protocol FLBAbstractFactory <NSObject>
@required
- (id<FLBFlutterApplicationInterface>)createApplication:(id<FLBPlatform>)platform;
- (id<FLBFlutterContainer>)createFlutterContainer;
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef bool (^FLBCollectionFilter)(id value);
@interface FLBCollectionHelper : NSObject
+ (NSDictionary *)deepCopyNSDictionary:(NSDictionary *)origin filter:(FLBCollectionFilter)filter;
+ (NSArray *)deepCopyNSArray:(NSArray *)array filter:(FLBCollectionFilter)filter;
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
*
* Copyright (c) 2019 Alibaba Group
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -21,61 +21,57 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'package:flutter_boost/support/logger.dart';
import 'package:flutter_boost/AIOService/NavigationService/service/NavigationService.dart';
typedef void PageResultHandler(String key , Map<String,dynamic> result);
typedef VoidCallback = void Function();
#import "FLBCollectionHelper.h"
class PageResultMediator{
@implementation FLBCollectionHelper
static int _resultId = 0;
String createResultId(){
_resultId++;
return "result_id_$_resultId";
}
bool isResultId(String rid){
if(rid == null) return false;
return rid.contains("result_id");
}
Map<String,PageResultHandler> _handlers = Map();
void onPageResult(String key , Map<String,dynamic> resultData, Map params){
if(key == null) return;
+ (NSDictionary *)deepCopyNSDictionary:(NSDictionary *)origin filter:(FLBCollectionFilter)filter
{
if(origin.count < 1) return origin;
NSMutableDictionary *copyed = [NSMutableDictionary new];
[origin enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//Filter: Do not include invalid things
if(filter && !filter(obj)) return;
if([obj isKindOfClass: NSDictionary.class]){
copyed[key] = [self deepCopyNSDictionary:obj filter:filter];
}else if([obj isKindOfClass:NSArray.class]){
copyed[key] = [self deepCopyNSArray:obj filter:filter];
}else{
copyed[key] = obj;
}
}];
return copyed;
}
Logger.log("did receive page result $resultData for page key $key");
+ (NSArray *)deepCopyNSArray:(NSArray *)origin filter:(FLBCollectionFilter)filter
{
if(origin.count < 1) return origin;
NSMutableArray *copyed = [NSMutableArray new];
[origin enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if(_handlers.containsKey(key)){
_handlers[key](key,resultData);
_handlers.remove(key);
}else{
//Cannot find handler consider forward to native.
//Use forward to avoid circle.
if(params == null || !params.containsKey("forward")){
if(params == null){
params = new Map();
//Filter: Do not include invalid things.
if(filter && !filter(obj)) return;
id nObj = nil;
if([obj isKindOfClass: NSDictionary.class]){
nObj = [self deepCopyNSDictionary:obj filter:filter];
}else if([obj isKindOfClass:NSArray.class]){
nObj = [self deepCopyNSArray:obj filter:filter];
}else{
nObj = obj;
}
params["forward"] = 1;
NavigationService.onFlutterPageResult(key, key , resultData, params);
}else{
params["forward"] = params["forward"]+1;
if(params["forward"] <= 2){
NavigationService.onFlutterPageResult(key, key , resultData, params);
if (nObj) {
[copyed addObject:nObj];
}
}
}
}
VoidCallback setPageResultHandler(String key, PageResultHandler handler){
if(key == null || handler == null) return (){};
_handlers[key] = handler;
return (){
_handlers.remove(key);
};
}
}];
return copyed;
}
@end
......@@ -25,12 +25,9 @@
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterAppDelegate : FlutterAppDelegate
// Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController;
@end
NS_ASSUME_NONNULL_END
......@@ -23,13 +23,12 @@
*/
#import "FLBFlutterAppDelegate.h"
#import "FLBFlutterApplication.h"
#import "FlutterBoostPlugin_private.h"
@implementation FLBFlutterAppDelegate
// Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController {
return FLBFlutterApplication.sharedApplication.flutterViewController;
return FlutterBoostPlugin.sharedInstance.application.flutterViewController;
}
@end
/*
* The MIT License (MIT)
*
*
* Copyright (c) 2019 Alibaba Group
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -21,28 +21,30 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
#import "FLBPlatform.h"
#import "FlutterBoost.h"
#import "FLBFlutterProvider.h"
#import "FLBFlutterContainer.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterApplication : NSObject
@protocol FLBFlutterApplicationInterface <NSObject>
@property (nonatomic,strong) id<FLBPlatform> platform;
#pragma mark - Getters
- (void)setAccessibilityEnable:(BOOL)enable;
+ (FLBFlutterApplication *)sharedApplication;
- (id<FLBFlutterProvider>)flutterProvider;
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform
onStart:(void (^)(FlutterViewController *))callback;
onStart:(void (^)(id<FlutterBinaryMessenger,
FlutterTextureRegistry,
FlutterPluginRegistry> engine))callback;
- (FlutterViewController *)flutterViewController;
#pragma mark - Container Management
- (BOOL)contains:(FLBFlutterViewContainer *)vc;
- (void)addUniqueViewController:(FLBFlutterViewContainer *)vc;
- (void)removeViewController:(FLBFlutterViewContainer *)vc;
- (BOOL)contains:(id<FLBFlutterContainer>)vc;
- (void)addUniqueViewController:(id<FLBFlutterContainer>)vc;
- (void)removeViewController:(id<FLBFlutterContainer>)vc;
- (BOOL)isTop:(NSString *)pageId;
#pragma mark - App Control
......@@ -50,6 +52,29 @@ NS_ASSUME_NONNULL_BEGIN
- (void)resume;
- (void)inactive;
- (BOOL)isRunning;
@end
#pragma mark - handle messages
- (void)close:(NSString *)uid
result:(NSDictionary *)result
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion;
- (void)open:(NSString *)url
urlParams:(NSDictionary *)urlParams
exts:(NSDictionary *)exts
reult:(void (^)(NSDictionary *))resultCallback
completion:(void (^)(BOOL))completion;
- (void)didInitPageContainer:(NSString *)url
params:(NSDictionary *)urlParams
uniqueId:(NSString *)uniqueId;
- (void)willDeallocPageContainer:(NSString *)url
params:(NSDictionary *)params
uniqueId:(NSString *)uniqueId;
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FLBFlutterContainer <NSObject>
- (NSString *)name;
- (NSDictionary *)params;
- (NSString *)uniqueIDString;
- (void)setName:(NSString *)name params:(NSDictionary *)params;
@end
NS_ASSUME_NONNULL_END
......@@ -23,17 +23,15 @@
*/
#import <Foundation/Foundation.h>
#import "FLBFlutterViewContainer.h"
#import "FLBFlutterContainer.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterViewContainerManager : NSObject
@interface FLBFlutterContainerManager : NSObject
- (NSString *)peak;
- (void)addUnique:(FLBFlutterViewContainer *)vc;
- (void)remove:(FLBFlutterViewContainer *)vc;
- (BOOL)contains:(FLBFlutterViewContainer *)vc;
- (void)addUnique:(id<FLBFlutterContainer>)vc;
- (void)remove:(id<FLBFlutterContainer>)vc;
- (BOOL)contains:(id<FLBFlutterContainer>)vc;
@end
NS_ASSUME_NONNULL_END
......@@ -22,14 +22,14 @@
* THE SOFTWARE.
*/
#import "FLBFlutterViewContainerManager.h"
#import "FLBFlutterContainerManager.h"
@interface FLBFlutterViewContainerManager()
@interface FLBFlutterContainerManager()
@property (nonatomic,strong) NSMutableArray *idStk;
@property (nonatomic,strong) NSMutableDictionary *existedID;
@end
@implementation FLBFlutterViewContainerManager
@implementation FLBFlutterContainerManager
- (instancetype)init
{
......@@ -41,7 +41,7 @@
return self;
}
- (BOOL)contains:(FLBFlutterViewContainer *)vc
- (BOOL)contains:(id<FLBFlutterContainer>)vc
{
if (vc) {
return _existedID[vc.uniqueIDString]?YES:NO;
......@@ -50,7 +50,7 @@
return NO;
}
- (void)addUnique:(FLBFlutterViewContainer *)vc
- (void)addUnique:(id<FLBFlutterContainer>)vc
{
if (vc) {
if(!_existedID[vc.uniqueIDString]){
......@@ -60,7 +60,7 @@
}
}
- (void)remove:(FLBFlutterViewContainer *)vc
- (void)remove:(id<FLBFlutterContainer>)vc
{
if (vc) {
[_existedID removeObjectForKey:vc.uniqueIDString];
......
......@@ -25,21 +25,19 @@
#import <Foundation/Foundation.h>
@class FlutterViewController;
@class FlutterEngine;
NS_ASSUME_NONNULL_BEGIN
#define RELEASE_1_0 0
@protocol FLBFlutterViewProvider <NSObject>
@protocol FLBFlutterProvider <NSObject>
@required
- (void)setAccessibilityEnable:(BOOL)enable;
- (BOOL)accessibilityEnable;
- (FlutterViewController *)viewController;
- (FlutterEngine *)engine;
- (void)atacheToViewController:(FlutterViewController *)vc;
- (void)detach;
- (void)prepareEngineIfNeeded;
- (void)pause;
- (void)resume;
- (void)inactive;
- (void)didEnterBackground;
- (void)willEnterForeground;
@end
NS_ASSUME_NONNULL_END
......@@ -25,26 +25,19 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FLBPlatform <NSObject>
@optional
//Whether to enable accessibility support. Default value is Yes.
- (BOOL)accessibilityEnable;
// flutter模块是否还可以pop
- (void)flutterCanPop:(BOOL)canpop;
- (NSString *)entryForDart;
@required
- (void)openPage:(NSString *)name
params:(NSDictionary *)params
animated:(BOOL)animated
- (void)open:(NSString *)url
urlParams:(NSDictionary *)urlParams
exts:(NSDictionary *)exts
completion:(void (^)(BOOL finished))completion;
- (void)closePage:(NSString *)uid
animated:(BOOL)animated
params:(NSDictionary *)params
completion:(void (^)(BOOL finished))completion;
- (void)close:(NSString *)uid
result:(NSDictionary *)result
exts:(NSDictionary *)exts
completion:(void (^)(BOOL finished))completion;
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^FLBPageResultHandler)(NSString *, NSDictionary *);
@interface FLBResultMediator : NSObject
- (void)onResultForKey:(NSString *)resultId resultData:(NSDictionary *)resultData params:(NSDictionary *)params;
- (void)setResultHandler:(FLBPageResultHandler)handler forKey:(NSString *)vcid;
- (void)removeHandlerForKey:(NSString *)vcid;
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBResultMediator.h"
#import "Service_NavigationService.h"
@interface FLBResultMediator()
@property (nonatomic,strong) NSMutableDictionary *handlers;
@end
@implementation FLBResultMediator
- (instancetype)init
{
if (self = [super init]) {
_handlers = [NSMutableDictionary new];
}
return self;
}
- (void)onResultForKey:(NSString *)rid
resultData:(NSDictionary *)resultData
params:(nonnull NSDictionary *)params
{
if(!rid) return;
NSString *key = rid;
if(_handlers[key]){
FLBPageResultHandler handler = _handlers[key];
handler(key,resultData);
[_handlers removeObjectForKey: key];
}else{
//Cannot find handler here. Try to forward message to flutter.
//Use forward to avoid circle.
if(!params || !params[@"forward"]){
NSMutableDictionary *tmp = params.mutableCopy;
if(!tmp){
tmp = NSMutableDictionary.new;
}
tmp[@"forward"] = @(1);
params = tmp;
[Service_NavigationService onNativePageResult:^(NSNumber *r) {}
uniqueId:rid
key:rid
resultData:resultData
params:params];
}else{
NSMutableDictionary *tmp = params.mutableCopy;
tmp[@"forward"] = @([params[@"forward"] intValue] + 1);
params = tmp;
if([params[@"forward"] intValue] <= 2){
[Service_NavigationService onNativePageResult:^(NSNumber *r) {}
uniqueId:rid
key:rid
resultData:resultData
params:params];
}
}
}
}
- (void)setResultHandler:(FLBPageResultHandler)handler
forKey:(NSString *)vcid
{
if(!handler || !vcid) return;
_handlers[vcid] = handler;
}
- (void)removeHandlerForKey:(NSString *)vcid
{
if(!vcid) return;
[_handlers removeObjectForKey:vcid];
}
@end
/*
* The MIT License (MIT)
*
*
* Copyright (c) 2019 Alibaba Group
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -21,17 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import "FLBStackCache.h"
@interface FLBStackCacheObjectImg : NSObject<FLBStackCacheObject>
@property (nonatomic,copy) NSString *key;
- (instancetype)initWithImage:(UIImage *)image;
#ifndef FLBTypes_h
#define FLBTypes_h
- (UIImage *)image;
typedef void (^FLBEventListener) (NSString *name ,
NSDictionary *arguments);
typedef void (^FLBVoidCallback)(void);
@end
#endif /* FLBTypes_h */
......@@ -26,8 +26,8 @@
#define FlutterBoost_h
#import "FlutterBoostPlugin.h"
#import "FLBFlutterViewContainer.h"
#import "FLBPlatform.h"
#import "FLBFlutterAppDelegate.h"
#import "FLBFlutterViewContainer.h"
#import "FLBTypes.h"
#endif /* FlutterBoost_h */
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FlutterBoostConfig.h"
@interface FlutterBoostConfig()
@property (nonatomic,assign) BOOL firstView;
@end
@implementation FlutterBoostConfig
+ (instancetype)sharedInstance
{
static FlutterBoostConfig *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [self new];
instance.firstView = YES;
});
return instance;
}
- (void)setFPageId:(NSString *)fPageId
{
_fPageId = fPageId;
_firstView = NO;
}
- (BOOL)firstView
{
return _firstView;
}
@end
......@@ -23,36 +23,27 @@
*/
#import <Flutter/Flutter.h>
#import "FLBFlutterViewContainer.h"
#import "FLBPlatform.h"
typedef FLBFlutterViewContainer * (^FLBPageBuilder)(NSString *name,NSDictionary *params);
#import "FLBTypes.h"
@interface FlutterBoostPlugin : NSObject<FlutterPlugin>
#pragma mark - Initializer
- (void)setAccessibilityEnable:(BOOL)enable;
+ (instancetype)sharedInstance;
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform onStart:(void (^)(FlutterViewController *))callback;
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform
onStart:(void (^)(id<FlutterBinaryMessenger,
FlutterTextureRegistry,
FlutterPluginRegistry> engine))callback;
#pragma mark - Some properties.
- (BOOL)isRunning;
- (FlutterViewController *)currentViewController;
#pragma mark - handing vc result.
- (void)openPage:(NSString *)name
params:(NSDictionary *)params
animated:(BOOL)animated
completion:(void (^)(BOOL finished))completion
resultHandler:(void (^)(NSString *resultId,NSDictionary *rData))resultHandler;
- (FlutterViewController *)currentViewController;
- (void)onResultForKey:(NSString *)vcId
resultData:(NSDictionary *)resultData
params:(NSDictionary *)params;
#pragma mark - broadcast event to/from flutter
- (void)sendEvent:(NSString *)eventName
arguments:(NSDictionary *)arguments;
- (void)setResultHandler:(void (^)(NSString *, NSDictionary *))handler
forKey:(NSString *)result_id;
- (void)removeHandlerForKey:(NSString *)vcid;
- (FLBVoidCallback)addEventListener:(FLBEventListener)listner
forName:(NSString *)name;
@end
......@@ -23,15 +23,83 @@
*/
#import "FlutterBoostPlugin.h"
#import "FLBFlutterApplication.h"
#import "FLBResultMediator.h"
#import "FlutterBoostPlugin_private.h"
#import "FLBFactory.h"
#import "BoostMessageChannel.h"
#import "FLBCollectionHelper.h"
#define NSNull2Nil(_x_) if([_x_ isKindOfClass: NSNull.class]) _x_ = nil;
@interface FlutterBoostPlugin()
@property (nonatomic,strong) FLBResultMediator *resultMediator;
@end
@implementation FlutterBoostPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"flutter_boost"
binaryMessenger:[registrar messenger]];
FlutterBoostPlugin* instance = [self.class sharedInstance];
instance.methodChannel = channel;
[registrar addMethodCallDelegate:instance channel:channel];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"getPlatformVersion" isEqualToString:call.method]) {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
} else if([@"__event__" isEqual: call.method]){
[BoostMessageChannel handleMethodCall:call result:result];
}else if([@"closePage" isEqualToString:call.method]){
NSDictionary *args = [FLBCollectionHelper deepCopyNSDictionary:call.arguments
filter:^bool(id _Nonnull value) {
return ![value isKindOfClass:NSNull.class];
}];
NSDictionary *exts = args[@"exts"];
NSString *uid = args[@"uniqueId"];
NSDictionary *resultData = args[@"result"];
NSNull2Nil(exts);
NSNull2Nil(resultData);
NSNull2Nil(uid);
[[FlutterBoostPlugin sharedInstance].application close:uid
result:resultData
exts:exts
completion:^(BOOL r){
result(@(r));
}];
}else if([@"onShownContainerChanged" isEqualToString:call.method]){
NSString *newName = call.arguments[@"newName"];
if(newName){
[NSNotificationCenter.defaultCenter postNotificationName:@"flutter_boost_container_showed"
object:newName];
}
}else if([@"openPage" isEqualToString:call.method]){
NSDictionary *args = [FLBCollectionHelper deepCopyNSDictionary:call.arguments
filter:^bool(id _Nonnull value) {
return ![value isKindOfClass:NSNull.class];
}];
NSString *url = args[@"url"];
NSDictionary *urlParams = args[@"urlParams"];
NSDictionary *exts = args[@"exts"];
NSNull2Nil(url);
NSNull2Nil(urlParams);
NSNull2Nil(exts);
[[FlutterBoostPlugin sharedInstance].application open:url
urlParams:urlParams
exts:exts
reult:result
completion:^(BOOL r) {}];
}else if([@"pageOnStart" isEqualToString:call.method]){
NSMutableDictionary *pageInfo = [NSMutableDictionary new];
pageInfo[@"name"] =[FlutterBoostPlugin sharedInstance].fPagename;
pageInfo[@"params"] = [FlutterBoostPlugin sharedInstance].fParams;
pageInfo[@"uniqueId"] = [FlutterBoostPlugin sharedInstance].fPageId;
if(result) result(pageInfo);
}else{
result(FlutterMethodNotImplemented);
}
}
+ (instancetype)sharedInstance
{
static id _instance = nil;
......@@ -43,79 +111,57 @@
return _instance;
}
- (instancetype)init
- (id<FLBFlutterApplicationInterface>)application
{
if (self = [super init]) {
_resultMediator = [FLBResultMediator new];
}
return self;
return _application;
}
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"flutter_boost"
binaryMessenger:[registrar messenger]];
FlutterBoostPlugin* instance = [self.class sharedInstance];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"getPlatformVersion" isEqualToString:call.method]) {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
} else {
result(FlutterMethodNotImplemented);
}
- (id<FLBAbstractFactory>)factory
{
return _factory;
}
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform
onStart:(void (^)(FlutterViewController *))callback;
onStart:(void (^)(id<FlutterBinaryMessenger,
FlutterTextureRegistry,
FlutterPluginRegistry> engine))callback;
{
[FLBFlutterApplication.sharedApplication startFlutterWithPlatform:platform
onStart:callback];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
self->_factory = FLBFactory.new;
self->_application = [self->_factory createApplication:platform];
[self->_application startFlutterWithPlatform:platform
onStart:callback];
});
}
- (BOOL)isRunning
{
return [FLBFlutterApplication.sharedApplication isRunning];
}
- (FlutterViewController *)currentViewController
{
return [[FLBFlutterApplication sharedApplication] flutterViewController];
return [self.application isRunning];
}
- (void)openPage:(NSString *)name
params:(NSDictionary *)params animated:(BOOL)animated
completion:(void (^)(BOOL))completion
resultHandler:(void (^)(NSString *, NSDictionary *))resultHandler
{
static int kRid = 0;
NSString *resultId = [NSString stringWithFormat:@"result_id_%d",kRid++];
[_resultMediator setResultHandler:^(NSString * _Nonnull resultId, NSDictionary * _Nonnull resultData) {
if(resultHandler) resultHandler(resultId,resultData);
} forKey:resultId];
}
- (void)onResultForKey:(NSString *)vcId resultData:(NSDictionary *)resultData params:(NSDictionary *)params
- (FlutterViewController *)currentViewController
{
[_resultMediator onResultForKey:vcId resultData:resultData params:params];
return [self.application flutterViewController];
}
- (void)setResultHandler:(void (^)(NSString *, NSDictionary *))handler forKey:(NSString *)vcid
{
[_resultMediator setResultHandler:handler forKey:vcid];
}
- (void)removeHandlerForKey:(NSString *)vcid
#pragma mark - broadcast event to/from flutter
- (void)sendEvent:(NSString *)eventName
arguments:(NSDictionary *)arguments
{
[_resultMediator removeHandlerForKey:vcid];
[BoostMessageChannel sendEvent:eventName
arguments:arguments];
}
- (void)setAccessibilityEnable:(BOOL)enable
- (FLBVoidCallback)addEventListener:(FLBEventListener)listner
forName:(NSString *)name
{
[[FLBFlutterApplication sharedApplication] setAccessibilityEnable:enable];
return [BoostMessageChannel addEventListener:listner
forName:name];
}
@end
/*
* The MIT License (MIT)
*
*
* Copyright (c) 2019 Alibaba Group
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -21,20 +21,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface FlutterBoostConfig : NSObject
#pragma mark - Store first open page
//There are some cases first page messages could be lost.
//So we store the first page info for later usage.
+ (instancetype)sharedInstance;
@property (nonatomic,copy) NSString *fPagename;
#import "FLBFlutterApplicationInterface.h"
#import "FLBAbstractFactory.h"
#import "FlutterBoostPlugin.h"
@interface FlutterBoostPlugin(){
id<FLBFlutterApplicationInterface> _application;
id<FLBAbstractFactory> _factory;
}
@property (nonatomic,strong) FlutterMethodChannel *methodChannel;
@property (nonatomic,copy) NSString *fPageId;
@property (nonatomic,copy) NSString *fPagename;
@property (nonatomic,strong) NSDictionary *fParams;
- (BOOL)firstView;
- (id<FLBFlutterApplicationInterface>)application;
- (id<FLBAbstractFactory>)factory;
@end
NS_ASSUME_NONNULL_END
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <UIKit/UIKit.h>
#import "FLBViewControllerResultHandler.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterViewContainer : UIViewController<FLBViewControllerResultHandler>
@property (nonatomic,copy,readonly) NSString *name;
@property (nonatomic,strong,readonly) NSDictionary *params;
@property (nonatomic,copy,readonly) NSString *uniqueIDString;
/*
You must call this one time to set flutter page name
and params.
*/
- (void)setName:(NSString *)name params:(NSDictionary *)params;
- (void)flutterViewDidAppear:(NSDictionary *)params;
@end
NS_ASSUME_NONNULL_END
......@@ -22,11 +22,12 @@
* THE SOFTWARE.
*/
//Generated by AIOCodeGen do not edit!
#import <xservice_kit/FlutterServiceTemplate.h>
@interface Service_NavigationService : NSObject
+ (FlutterServiceTemplate *)service;
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
#import "FLBTypes.h"
@interface BoostMessageChannel : NSObject
+ (void)onNativePageResult:(void (^)(NSNumber *))result uniqueId:(NSString *)uniqueId key:(NSString *)key resultData:(NSDictionary *)resultData params:(NSDictionary *)params;
+ (void)didShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId;
+ (void)willShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId;
......@@ -34,6 +35,15 @@
+ (void)didDisappearPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId;
+ (void)didInitPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId;
+ (void)willDeallocPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId;
// 获取flutter的canPop
+ (void)canPopPageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId;
+ (void)sendEvent:(NSString *)eventName
arguments:(NSDictionary *)arguments;
+ (FLBVoidCallback)addEventListener:(FLBEventListener)listner
forName:(NSString *)name;
+ (void)handleMethodCall:(FlutterMethodCall *)call
result:(FlutterResult)result;
@end
......@@ -21,30 +21,69 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "BoostMessageChannel.h"
#import "FlutterBoostPlugin_private.h"
//Generated by AIOCodeGen. Do not edit!
#import "Service_NavigationService.h"
#import <xservice_kit/ServiceGateway.h>
#import <xservice_kit/FlutterServiceTemplate.h>
@implementation Service_NavigationService
@implementation BoostMessageChannel
+ (NSMutableDictionary *)lists{
static NSMutableDictionary *_list = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_list = [NSMutableDictionary new];
});
return _list;
}
+ (FlutterServiceTemplate *)service
+ (FlutterMethodChannel *)methodChannel
{
static id _instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[FlutterServiceTemplate alloc] initWithName:@"NavigationService"];
});
return _instance;
return FlutterBoostPlugin.sharedInstance.methodChannel;
}
+ (void)load{
[[ServiceGateway sharedInstance] addService:[self service]];
}
+ (void)sendEvent:(NSString *)eventName
arguments:(NSDictionary *)arguments
{
if(!eventName) return;
NSMutableDictionary *msg = [NSMutableDictionary new];
msg[@"name"] = eventName;
msg[@"arguments"] = arguments;
[self.methodChannel invokeMethod:@"__event__"
arguments:msg
result:^(id r){}];
}
+ (FLBVoidCallback)addEventListener:(FLBEventListener)listner
forName:(NSString *)name
{
if(!name || !listner) return ^{};
NSMutableArray *list = [self lists][name];
if(!list){
list = [NSMutableArray new];
[self lists][name] = list;
}
[list addObject:listner];
return ^{
[list removeObject:listner];
};
}
+ (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
{
if([call.method isEqual:@"__event__"]){
NSString *name = call.arguments[@"name"];
NSDictionary *arguments = call.arguments[@"arguments"];
if(name){
NSMutableArray *list = [self lists][name];
if(list){
for(FLBEventListener l in list){
l(name,arguments);
}
}
}
}
}
+ (void)onNativePageResult:(void (^)(NSNumber *))result uniqueId:(NSString *)uniqueId key:(NSString *)key resultData:(NSDictionary *)resultData params:(NSDictionary *)params
{
NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
......@@ -52,7 +91,7 @@
if(key) tmp[@"key"] = key;
if(resultData) tmp[@"resultData"] = resultData;
if(params) tmp[@"params"] = params;
[self.service invoke:@"onNativePageResult" args:tmp result:^(id tTesult) {
[self.methodChannel invokeMethod:@"onNativePageResult" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
......@@ -65,7 +104,7 @@
if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId;
[self.service invoke:@"didShowPageContainer" args:tmp result:^(id tTesult) {
[self.methodChannel invokeMethod:@"didShowPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
......@@ -78,7 +117,7 @@
if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId;
[self.service invoke:@"willShowPageContainer" args:tmp result:^(id tTesult) {
[self.methodChannel invokeMethod:@"willShowPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
......@@ -91,7 +130,7 @@
if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId;
[self.service invoke:@"willDisappearPageContainer" args:tmp result:^(id tTesult) {
[self.methodChannel invokeMethod:@"willDisappearPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
......@@ -104,7 +143,7 @@
if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId;
[self.service invoke:@"didDisappearPageContainer" args:tmp result:^(id tTesult) {
[self.methodChannel invokeMethod:@"didDisappearPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
......@@ -117,11 +156,15 @@
if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId;
[self.service invoke:@"didInitPageContainer" args:tmp result:^(id tTesult) {
[self.methodChannel invokeMethod:@"didInitPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
}];
[FlutterBoostPlugin.sharedInstance.application didInitPageContainer:pageName
params:params
uniqueId:uniqueId];
}
+ (void)willDeallocPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
......@@ -130,11 +173,15 @@
if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId;
[self.service invoke:@"willDeallocPageContainer" args:tmp result:^(id tTesult) {
[self.methodChannel invokeMethod:@"willDeallocPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
}];
[FlutterBoostPlugin.sharedInstance.application willDeallocPageContainer:pageName
params:params
uniqueId:uniqueId];
}
+ (void)canPopPageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId{
......@@ -146,4 +193,5 @@
}];
}
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
@class FLBStackCache;
@protocol FLBStackCacheObject<NSObject>
@required
@property (nonatomic,copy) NSString *key;
- (BOOL)writeToFileWithKey:(NSString *)key
queue:(dispatch_queue_t)queue
cache:(FLBStackCache *)cache
completion:(void (^)(NSError *err,NSString *path))completion;
+ (BOOL)loadFromFileWithKey:(NSString *)key
queue:(dispatch_queue_t)queue
cache:(FLBStackCache *)cache
completion:(void (^)(NSError *err ,id<FLBStackCacheObject>))completion;
- (BOOL)removeCachedFileWithKey:(NSString *)key
queue:(dispatch_queue_t)queue
cache:(FLBStackCache *)cache
completion:(void (^)(NSError *, NSString *))completion;
@end
@interface FLBStackCache : NSObject
+ (instancetype)sharedInstance;
/**
* Num of objects allowed in memory.
* Default value is set to 2.
*/
@property (nonatomic,assign) NSUInteger inMemoryCount;
#pragma mark - basic operations.
- (void)pushObject:(id<FLBStackCacheObject>)obj key:(NSString *)key;
- (id<FLBStackCacheObject>)remove:(NSString *)key;
- (void)invalidate:(NSString *)key;
- (BOOL)empty;
- (void)clear;
- (id<FLBStackCacheObject>)objectForKey:(NSString *)key;
#pragma mark - Disk thing.
- (NSString *)cacheDir;
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBStackCache.h"
@interface FLBStackCache()
@property (nonatomic,strong) NSMutableArray *keyStack;
@property (nonatomic,strong) NSMutableDictionary *typesMap;
@property (nonatomic,strong) NSMutableDictionary *inMemoryObjectsMap;
@property (nonatomic,strong) NSMutableDictionary *loadinMap;
@property (nonatomic,strong) dispatch_queue_t queueIO;
@end
@implementation FLBStackCache
+ (instancetype)sharedInstance
{
static id sInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sInstance = [self.class new];
});
return sInstance;
}
- (BOOL)empty
{
return _keyStack.count<=0;
}
- (void)clear
{
[self.keyStack removeAllObjects];
[self.inMemoryObjectsMap removeAllObjects];
[self.typesMap removeAllObjects];
NSError *err = nil;
[[NSFileManager defaultManager] removeItemAtPath:self.cacheDir error:&err];
if (err) {
NSLog(@"fail to remove cache dir %@",err);
}
}
- (instancetype)init
{
if (self = [super init]) {
_keyStack = [NSMutableArray new];
_inMemoryObjectsMap = [NSMutableDictionary new];
_loadinMap = [NSMutableDictionary new];
_typesMap = [NSMutableDictionary new];
_queueIO = dispatch_queue_create("Stack cache working queue", NULL);
}
_inMemoryCount = 2;
return self;
}
- (void)pushObject:(id<FLBStackCacheObject>)obj
key:(NSString *)key
{
if (!obj || key.length <= 0) {
return;
}
if(![_keyStack containsObject:key]){
[_keyStack addObject:key];
}else{
//return;
}
obj.key = key;
_typesMap[key] = obj.class;
_inMemoryObjectsMap[key] = obj;
for(NSUInteger i = _keyStack.count - _inMemoryObjectsMap.count ;
i < _keyStack.count && _inMemoryObjectsMap.count > _inMemoryCount;
i++){
NSString *keyToSave = _keyStack[i];
if(_inMemoryObjectsMap[keyToSave]){
id<FLBStackCacheObject> ob = _inMemoryObjectsMap[keyToSave];
[_inMemoryObjectsMap removeObjectForKey:keyToSave];
[ob writeToFileWithKey:keyToSave
queue:_queueIO
cache:self
completion:^(NSError *err, NSString *path) {
if (err) {
NSLog(@"Caching object to file failed!");
}
}];
}
}
}
- (id<FLBStackCacheObject>)objectForKey:(NSString *)key
{
return _inMemoryObjectsMap[key];
}
- (id<FLBStackCacheObject>)remove:(NSString *)key
{
if([self empty]) return nil;
if(![_keyStack containsObject:key]) return nil;
id ob = _inMemoryObjectsMap[key];
[_keyStack removeObject:key];
[_inMemoryObjectsMap removeObjectForKey:key];
[_typesMap removeObjectForKey:key];
[self preloadIfNeeded];
return ob;
}
- (void)invalidate:(NSString *)key
{
if(!key || [self empty]) return;
if(![_keyStack containsObject:key]) return;
id<FLBStackCacheObject> ob = _inMemoryObjectsMap[key];
[ob removeCachedFileWithKey:key
queue:_queueIO
cache:self
completion:^(NSError *err, NSString *k) {
}];
[_inMemoryObjectsMap removeObjectForKey:key];
[self preloadIfNeeded];
}
- (void)preloadIfNeeded
{
for(NSString *key in self.keyStack.reverseObjectEnumerator){
Class typeClass = _typesMap[key];
id cache = _inMemoryObjectsMap[key];
if(typeClass && !cache && [typeClass conformsToProtocol: @protocol(FLBStackCacheObject)]){
_loadinMap[key] = @(YES);
[typeClass loadFromFileWithKey:key
queue:_queueIO
cache:self
completion:^(NSError *err ,id<FLBStackCacheObject> ob){
[self.loadinMap removeObjectForKey:key];
if (ob && !err) {
if(self.typesMap[key]){
self.inMemoryObjectsMap[key] = ob;
}
}else{
NSLog(@"preload object from file failed!");
}
}];
}
if(_inMemoryObjectsMap.count + _loadinMap.count >= _inMemoryCount){
break;
}
}
}
- (NSString *)cacheDir
{
static NSString *cachePath = nil;
if (!cachePath) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
cachePath = [cacheDirectory stringByAppendingPathComponent:@"FlutterScreenshots"];
}
BOOL dir = NO;
if(![[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&dir]){
NSError *eror = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath
withIntermediateDirectories:YES
attributes:nil
error:&eror];
if (eror) {
NSLog(@"%@",eror);
}
}
return cachePath;
}
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBStackCacheObjectImg.h"
@interface FLBStackCacheObjectImg()
@property (nonatomic,strong) UIImage *image;
@end
@implementation FLBStackCacheObjectImg
- (instancetype)initWithImage:(UIImage *)image
{
if (self = [super init]) {
_image = image;
}
return self;
}
+ (BOOL)loadFromFileWithKey:(NSString *)key
queue:(dispatch_queue_t)queue
cache:(FLBStackCache *)cache
completion:(void (^)(NSError *, id<FLBStackCacheObject>))completion
{
dispatch_async(queue, ^{
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self filePathByKey:key dirPath:cache.cacheDir]];
if (completion) {
if (image) {
FLBStackCacheObjectImg *ob = [[FLBStackCacheObjectImg alloc] initWithImage:image];
ob.key = key;
completion(nil,ob);
}else{
completion([NSError new],nil);
}
}
});
return YES;
}
+ (NSString *)filePathByKey:(NSString *)key dirPath:(NSString *)cacheDir
{
key = [key stringByReplacingOccurrencesOfString:@"/" withString:@""];
key = [key stringByReplacingOccurrencesOfString:@":" withString:@""];
NSString *path = [cacheDir stringByAppendingPathComponent:key];
return path;
}
- (BOOL)writeToFileWithKey:(NSString *)key
queue:(dispatch_queue_t)queue
cache:(FLBStackCache *)cache
completion:(void (^)(NSError *, NSString *))completion
{
if(!_image){
return NO;
}
dispatch_async(queue, ^{
NSData *imgData = UIImagePNGRepresentation(self.image);
NSString *filePath = [FLBStackCacheObjectImg filePathByKey:key dirPath:cache.cacheDir];
[imgData writeToFile:filePath atomically:YES];
if (completion) {
completion(nil,key);
}
});
return YES;
}
- (BOOL)removeCachedFileWithKey:(NSString *)key
queue:(dispatch_queue_t)queue
cache:(FLBStackCache *)cache
completion:(void (^)(NSError *, NSString *))completion
{
if(!key){
return NO;
}
dispatch_async(queue, ^{
NSString *filePath = [FLBStackCacheObjectImg filePathByKey:key dirPath:cache.cacheDir];
NSError *err = nil;
[NSFileManager.defaultManager removeItemAtPath:filePath error:&err];
if (completion) {
completion(err,key);
}
});
return YES;
}
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Foundation/Foundation.h>
#define kFLBMemoryInspectorChangedNotification @"__FlutterMemoryInspectorChangedNotification__"
#define kFLBMemoryInspectorKeyCondition @"condition"
typedef NS_ENUM(NSUInteger,FLBMemoryCondition) {
FLBMemoryConditionUnknown,
FLBMemoryConditionNormal,
FLBMemoryConditionLowMemory,
FLBMemoryConditionExtremelyLow,
FLBMemoryConditionAboutToDie
};
@interface FLBMemoryInspector : NSObject
+ (instancetype)sharedInstance;
- (FLBMemoryCondition)currentCondition;
- (int64_t)currentFootPrint;
- (int64_t)deviceMemory;
- (BOOL)smallMemoryDevice;
@end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "FLBMemoryInspector.h"
#include <mach/mach.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#define MB(_v_) (_v_*1024*1024)
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
@interface FLBMemoryInspector()
@property(nonatomic,assign) FLBMemoryCondition condition;
@end
@implementation FLBMemoryInspector
+ (instancetype)sharedInstance
{
static id sInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sInstance = [[self.class alloc] init];
});
return sInstance;
}
static bool isHighterThanIos9(){
bool ret = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"9.0.0");
return ret;
}
static int64_t memoryFootprint()
{
task_vm_info_data_t vmInfo;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
if (result != KERN_SUCCESS)
return -1;
return vmInfo.phys_footprint;
}
static int64_t _memoryWarningLimit()
{
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine =(char *) malloc(size);
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithUTF8String:machine];
free(machine);
if ([platform isEqualToString:@"iPhone5,1"]) return MB(600);
if ([platform isEqualToString:@"iPhone5,2"]) return MB(600);
if ([platform isEqualToString:@"iPhone5,3"]) return MB(600);
if ([platform isEqualToString:@"iPhone5,4"]) return MB(600);
if ([platform isEqualToString:@"iPhone6,1"]) return MB(600);
if ([platform isEqualToString:@"iPhone6,2"]) return MB(600);
if ([platform isEqualToString:@"iPhone7,1"]) return MB(600);
if ([platform isEqualToString:@"iPhone7,2"]) return MB(600);
if ([platform isEqualToString:@"iPhone8,1"]) return MB(1280);
if ([platform isEqualToString:@"iPhone8,2"]) return MB(1280);
if ([platform isEqualToString:@"iPhone8,4"]) return MB(1280);
if ([platform isEqualToString:@"iPhone9,1"]) return MB(1280);
if ([platform isEqualToString:@"iPhone9,2"]) return MB(1950);
if ([platform isEqualToString:@"iPhone9,3"]) return MB(1280);
if ([platform isEqualToString:@"iPhone9,4"]) return MB(1950);
if ([platform isEqualToString:@"iPhone10,1"]) return MB(1280);
if ([platform isEqualToString:@"iPhone10,2"]) return MB(1950);
if ([platform isEqualToString:@"iPhone10,3"]) return MB(1950);
if ([platform isEqualToString:@"iPhone10,4"]) return MB(1280);
if ([platform isEqualToString:@"iPhone10,5"]) return MB(1950);
if ([platform isEqualToString:@"iPhone10,6"]) return MB(1950);
return 0;
}
static int64_t getLimit(){
const static size_t l = _memoryWarningLimit();
return l;
}
- (int64_t)deviceMemory
{
int64_t size = [NSProcessInfo processInfo].physicalMemory;
return size;
}
- (BOOL)smallMemoryDevice
{
if([self deviceMemory] <= MB(1024)){
return YES;
}else{
return NO;
}
}
- (FLBMemoryCondition)currentCondition
{
FLBMemoryCondition newCondition = FLBMemoryConditionUnknown;
if(!isHighterThanIos9() || getLimit() <= 0){
newCondition = FLBMemoryConditionUnknown;
}else if(memoryFootprint() < getLimit() * 0.40){
newCondition = FLBMemoryConditionNormal;
}else if(memoryFootprint() < getLimit() * 0.60){
newCondition = FLBMemoryConditionLowMemory;
}else if(memoryFootprint() < getLimit() * 0.80){
newCondition = FLBMemoryConditionExtremelyLow;
}else{
newCondition = FLBMemoryConditionAboutToDie;
}
if (newCondition != self.condition) {
[[NSNotificationCenter defaultCenter] postNotificationName:kFLBMemoryInspectorChangedNotification
object:@{kFLBMemoryInspectorKeyCondition:@(newCondition)}];
}
self.condition = newCondition;
return newCondition;
}
- (int64_t)currentFootPrint
{
return memoryFootprint();
}
@end
......@@ -3,7 +3,7 @@
#
Pod::Spec.new do |s|
s.name = 'flutter_boost'
s.version = '0.0.1'
s.version = '0.0.2'
s.summary = 'A new Flutter plugin make flutter better to use!'
s.description = <<-DESC
A new Flutter plugin make flutter better to use!
......@@ -15,18 +15,18 @@ A new Flutter plugin make flutter better to use!
s.source_files = 'Classes/**/*.{h,m,mm}'
s.public_header_files =
'Classes/Boost/FlutterBoost.h',
'Classes/Boost/FlutterBoostPlugin.h',
'Classes/Boost/FLBPlatform.h',
'Classes/Boost/FLBFlutterContainer.h',
'Classes/Boost/FLBFlutterAppDelegate.h',
'Classes/Container/FLBFlutterViewContainer.h',
'Classes/Container/FLBViewControllerResultHandler.h'
'Classes/Boost/FLBTypes.h',
'Classes/Boost/FlutterBoost.h',
'Classes/Boost/BoostChannel.h',
'Classes/1.5/FLBFlutterViewContainer.h'
s.dependency 'Flutter'
s.dependency 'xservice_kit'
s.libraries = 'c++'
s.ios.deployment_target = '8.0'
end
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_boost/container/container_coordinator.dart';
import 'package:xservice_kit/ServiceCallHandler.dart';
import 'package:xservice_kit/ServiceGateway.dart';
class NavigationService_didDisappearPageContainer extends ServiceCallHandler {
static void regsiter() {
ServiceGateway.sharedInstance()
.registerHandler(new NavigationService_didDisappearPageContainer());
}
@override
String name() {
return "didDisappearPageContainer";
}
@override
String service() {
return "NavigationService";
}
@override
Future<bool> onMethodCall(MethodCall call) {
return onCall(call.arguments["pageName"], call.arguments["params"],
call.arguments["uniqueId"]);
}
//==============================================Do not edit code above!
Future<bool> onCall(String pageName, Map params, String uniqueId) async {
return ContainerCoordinator.singleton
.nativeContainerDidDisappear(pageName, params, uniqueId);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:xservice_kit/ServiceCallHandler.dart';
import 'package:xservice_kit/ServiceGateway.dart';
import 'package:flutter_boost/container/container_coordinator.dart';
class NavigationService_didInitPageContainer extends ServiceCallHandler {
static void regsiter() {
ServiceGateway.sharedInstance()
.registerHandler(new NavigationService_didInitPageContainer());
}
@override
String name() {
return "didInitPageContainer";
}
@override
String service() {
return "NavigationService";
}
@override
Future<bool> onMethodCall(MethodCall call) {
return onCall(call.arguments["pageName"], call.arguments["params"],
call.arguments["uniqueId"]);
}
//==============================================Do not edit code above!
Future<bool> onCall(String pageName, Map params, String uniqueId) async {
return ContainerCoordinator.singleton
.nativeContainerDidInit(pageName, params, uniqueId);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_boost/container/container_coordinator.dart';
import 'package:xservice_kit/ServiceCallHandler.dart';
import 'package:xservice_kit/ServiceGateway.dart';
class NavigationService_didShowPageContainer extends ServiceCallHandler {
static void regsiter() {
ServiceGateway.sharedInstance()
.registerHandler(new NavigationService_didShowPageContainer());
}
@override
String name() {
return "didShowPageContainer";
}
@override
String service() {
return "NavigationService";
}
@override
Future<bool> onMethodCall(MethodCall call) {
return onCall(call.arguments["pageName"], call.arguments["params"],
call.arguments["uniqueId"]);
}
//==============================================Do not edit code above!
Future<bool> onCall(String pageName, Map params, String uniqueId) async {
return ContainerCoordinator.singleton
.nativeContainerDidShow(pageName, params, uniqueId);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:xservice_kit/ServiceCallHandler.dart';
import 'package:xservice_kit/ServiceGateway.dart';
import 'package:flutter_boost/flutter_boost.dart';
class NavigationService_onNativePageResult extends ServiceCallHandler {
static void regsiter() {
ServiceGateway.sharedInstance().registerHandler(new NavigationService_onNativePageResult());
}
@override
String name() {
return "onNativePageResult";
}
@override
String service() {
return "NavigationService";
}
@override
Future<bool> onMethodCall(MethodCall call) {
return onCall(call.arguments["uniqueId"],call.arguments["key"],call.arguments["resultData"],call.arguments["params"]);
}
//==============================================Do not edit code above!
Future<bool> onCall(String uniqueId,String key,Map resultData,Map params) async{
return FlutterBoost.singleton.onPageResult(key, resultData, params);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_boost/container/container_coordinator.dart';
import 'package:xservice_kit/ServiceCallHandler.dart';
import 'package:xservice_kit/ServiceGateway.dart';
class NavigationService_willDeallocPageContainer extends ServiceCallHandler {
static void regsiter() {
ServiceGateway.sharedInstance()
.registerHandler(new NavigationService_willDeallocPageContainer());
}
@override
String name() {
return "willDeallocPageContainer";
}
@override
String service() {
return "NavigationService";
}
@override
Future<bool> onMethodCall(MethodCall call) {
return onCall(call.arguments["pageName"], call.arguments["params"],
call.arguments["uniqueId"]);
}
//==============================================Do not edit code above!
Future<bool> onCall(String pageName, Map params, String uniqueId) async {
return ContainerCoordinator.singleton
.nativeContainerWillDealloc(pageName, params, uniqueId);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_boost/container/container_coordinator.dart';
import 'package:xservice_kit/ServiceCallHandler.dart';
import 'package:xservice_kit/ServiceGateway.dart';
class NavigationService_willShowPageContainer extends ServiceCallHandler {
static void regsiter() {
ServiceGateway.sharedInstance()
.registerHandler(new NavigationService_willShowPageContainer());
}
@override
String name() {
return "willShowPageContainer";
}
@override
String service() {
return "NavigationService";
}
@override
Future<bool> onMethodCall(MethodCall call) {
return onCall(call.arguments["pageName"], call.arguments["params"],
call.arguments["uniqueId"]);
}
//==============================================Do not edit code above!
Future<bool> onCall(String pageName, Map params, String uniqueId) async {
return ContainerCoordinator.singleton
.nativeContainerWillShow(pageName, params, uniqueId);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import '../NavigationService/service/NavigationServiceRegister.dart';
class ServiceLoader{
static load(){
NavigationServiceRegister.register();
}
}
\ No newline at end of file
......@@ -21,39 +21,97 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'dart:async';
import 'dart:async';
import 'dart:ui';
import 'package:flutter/services.dart';
import 'package:flutter_boost/container/container_coordinator.dart';
import 'package:xservice_kit/ServiceCallHandler.dart';
import 'package:xservice_kit/ServiceGateway.dart';
class NavigationService_willDisappearPageContainer extends ServiceCallHandler {
static void regsiter() {
ServiceGateway.sharedInstance()
.registerHandler(new NavigationService_willDisappearPageContainer());
typedef Future<dynamic> EventListener(String name, Map arguments);
typedef Future<dynamic> MethodHandler(MethodCall call);
class BoostChannel {
final MethodChannel _methodChannel = MethodChannel("flutter_boost");
final Map<String, List<EventListener>> _eventListeners = Map();
final Set<MethodHandler> _methodHandlers = Set();
BoostChannel() {
_methodChannel.setMethodCallHandler((MethodCall call){
if (call.method == "__event__") {
String name = call.arguments["name"];
Map arg = call.arguments["arguments"];
List<EventListener> list = _eventListeners[name];
if (list != null) {
for (EventListener l in list) {
l(name, arg);
}
}
}else{
for(MethodHandler handler in _methodHandlers) {
handler(call);
}
}
return Future.value();
});
}
void sendEvent(String name, Map arguments) {
if (name == null) {
return;
}
if (arguments == null) {
arguments = Map();
}
Map msg = Map();
msg["name"] = name;
msg["arguments"] = arguments;
_methodChannel.invokeMethod("__event__", msg);
}
@override
String name() {
return "willDisappearPageContainer";
Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async {
assert(method != "__event__");
return _methodChannel.invokeMethod<T>(method,arguments);
}
Future<List<T>> invokeListMethod<T>(String method, [ dynamic arguments ]) async {
assert(method != "__event__");
return _methodChannel.invokeListMethod<T>(method,arguments);
}
@override
String service() {
return "NavigationService";
Future<Map<K, V>> invokeMapMethod<K, V>(String method, [ dynamic arguments ]) async {
assert(method != "__event__");
return _methodChannel.invokeMapMethod<K, V>(method,arguments);
}
@override
Future<bool> onMethodCall(MethodCall call) {
return onCall(call.arguments["pageName"], call.arguments["params"],
call.arguments["uniqueId"]);
VoidCallback addEventListener(String name, EventListener listener) {
assert(name != null && listener != null);
List<EventListener> list = _eventListeners[name];
if (list == null) {
list = List();
_eventListeners[name] = list;
}
list.add(listener);
return () {
list.remove(listener);
};
}
//==============================================Do not edit code above!
VoidCallback addMethodHandler(MethodHandler handler) {
assert(handler != null);
_methodHandlers.add(handler);
Future<bool> onCall(String pageName, Map params, String uniqueId) async {
return ContainerCoordinator.singleton
.nativeContainerWillDisappear(pageName, params, uniqueId);
return (){
_methodHandlers.remove(handler);
};
}
}
......@@ -22,11 +22,10 @@
* THE SOFTWARE.
*/
import 'package:flutter/material.dart';
import 'package:flutter_boost/container/container_coordinator.dart';
import 'package:flutter_boost/container/container_manager.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_boost/router/boost_page_route.dart';
import 'package:flutter_boost/support/logger.dart';
import 'container_manager.dart';
import '../flutter_boost.dart';
import 'boost_page_route.dart';
import '../logger.dart';
enum ContainerLifeCycle {
Init,
......@@ -40,9 +39,6 @@ enum ContainerLifeCycle {
typedef void BoostContainerLifeCycleObserver(
ContainerLifeCycle state, BoostContainerSettings settings);
typedef void ResultObserver(
int requestCode, int responseCode, Map<dynamic, dynamic> result);
class BoostContainer extends Navigator {
final BoostContainerSettings settings;
......@@ -117,9 +113,7 @@ class BoostContainer extends Navigator {
}
class BoostContainerState extends NavigatorState {
final Set<VoidCallback> _backPressedListeners = Set<VoidCallback>();
final Set<ResultObserver> _resultObservers = Set<ResultObserver>();
VoidCallback backPressedHandler;
String get uniqueId => widget.settings.uniqueId;
......@@ -138,6 +132,8 @@ class BoostContainerState extends NavigatorState {
@override
BoostContainer get widget => super.widget as BoostContainer;
final List<Route<dynamic>> routerHistory = <Route<dynamic>>[];
ContainerNavigatorObserver findContainerNavigatorObserver(
Navigator navigator) {
for (NavigatorObserver observer in navigator.observers) {
......@@ -149,6 +145,12 @@ class BoostContainerState extends NavigatorState {
return null;
}
@override
void initState() {
super.initState();
backPressedHandler = () => maybePop();
}
@override
void didUpdateWidget(Navigator oldWidget) {
super.didUpdateWidget(oldWidget);
......@@ -160,41 +162,53 @@ class BoostContainerState extends NavigatorState {
void dispose() {
findContainerNavigatorObserver(widget)?.removeBoostNavigatorObserver(
FlutterBoost.containerManager.navigatorObserver);
routerHistory.clear();
super.dispose();
}
void performBackPressed() {
Logger.log('performBackPressed');
if (_backPressedListeners.isEmpty) {
pop();
} else {
for (VoidCallback cb in _backPressedListeners) {
cb();
}
}
backPressedHandler?.call();
}
void performOnResult(Map<dynamic, dynamic> data) {
Logger.log('performOnResult ${data.toString()}');
final int requestCode = data['requestCode'];
final int responseCode = data['responseCode'];
final Map result = data['result'];
for (ResultObserver observer in _resultObservers) {
observer(requestCode, responseCode, result);
@override
Future<bool> maybePop<T extends Object>([T result]) async {
final Route<T> route = routerHistory.last;
final RoutePopDisposition disposition = await route.willPop();
if (mounted) {
switch (disposition) {
case RoutePopDisposition.pop:
pop(result);
return true;
break;
case RoutePopDisposition.doNotPop:
return false;
break;
case RoutePopDisposition.bubble:
pop(result);
return true;
break;
}
}
}
@override
bool pop<T extends Object>([T result]) {
if (routerHistory.length > 1) {
routerHistory.removeLast();
}
if (canPop()) {
return super.pop(result);
} else {
FlutterBoost.singleton.closePage(name, uniqueId, params);
if (T is Map<String, dynamic>) {
FlutterBoost.singleton
.close(uniqueId, result: result as Map<String, dynamic>);
} else {
FlutterBoost.singleton.close(uniqueId);
}
}
return false;
}
......@@ -208,6 +222,8 @@ class BoostContainerState extends NavigatorState {
Future<T> future = super.push<T>(newRoute ?? route);
routerHistory.add(route);
if (FlutterBoost.containerManager.postPushRoute != null) {
FlutterBoost.containerManager
.postPushRoute(name, uniqueId, params, newRoute ?? route, future);
......@@ -216,18 +232,6 @@ class BoostContainerState extends NavigatorState {
return future;
}
VoidCallback addBackPressedListener(VoidCallback listener) {
_backPressedListeners.add(listener);
return () => _backPressedListeners.remove(listener);
}
VoidCallback addResultObserver(ResultObserver observer) {
_resultObservers.add(observer);
return () => _resultObservers.remove(observer);
}
VoidCallback addLifeCycleObserver(BoostContainerLifeCycleObserver observer) {
return FlutterBoost.singleton.addBoostContainerLifeCycleObserver(
(ContainerLifeCycle state, BoostContainerSettings settings) {
......
......@@ -22,22 +22,33 @@
* THE SOFTWARE.
*/
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_boost/AIOService/NavigationService/service/NavigationService.dart';
import 'package:flutter_boost/container/boost_container.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_boost/messaging/native_page_container_event_handler.dart';
import 'package:flutter_boost/support/logger.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart';
import '../channel/boost_channel.dart';
import 'boost_container.dart';
import '../flutter_boost.dart';
import '../logger.dart';
class ContainerCoordinator implements NativePageContainerEventHandler {
static final ContainerCoordinator singleton = ContainerCoordinator();
class ContainerCoordinator {
static ContainerCoordinator get singleton => _instance;
static ContainerCoordinator _instance;
final Map<String, PageBuilder> _pageBuilders = <String, PageBuilder>{};
PageBuilder _defaultPageBuilder;
ContainerCoordinator() {
NavigationService.listenEvent(onChannelEvent);
ContainerCoordinator(BoostChannel channel) {
assert(_instance == null);
_instance = this;
channel.addEventListener("lifecycle",
(String name, Map arguments) => _onChannelEvent(arguments));
channel.addMethodHandler((MethodCall call) => _onMethodCall(call));
}
BoostContainerSettings _createContainerSettings(
......@@ -86,11 +97,13 @@ class ContainerCoordinator implements NativePageContainerEventHandler {
}
}
void onChannelEvent(dynamic event) {
Future<dynamic> _onChannelEvent(dynamic event) {
if (event is Map) {
Map map = event as Map;
Map map = event;
final String type = map['type'];
Logger.log("onEvent $type");
switch (type) {
//Handler back key pressed event.
case 'backPressedCallback':
......@@ -123,11 +136,71 @@ class ContainerCoordinator implements NativePageContainerEventHandler {
break;
}
}
return Future<dynamic>(() {});
}
Future<dynamic> _onMethodCall(MethodCall call) {
Logger.log("onMetohdCall ${call.method}");
switch (call.method) {
case "didInitPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerDidInit(pageName, params, uniqueId);
}
break;
case "willShowPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerWillShow(pageName, params, uniqueId);
}
break;
case "didShowPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
nativeContainerDidShow(pageName, params, uniqueId);
}
break;
case "willDisappearPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerWillDisappear(pageName, params, uniqueId);
}
break;
case "didDisappearPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerDidDisappear(pageName, params, uniqueId);
}
break;
case "willDeallocPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerWillDealloc(pageName, params, uniqueId);
}
break;
case "onNativePageResult":
{}
break;
}
return Future<dynamic>(() {});
}
//Messaging
@override
bool nativeContainerWillShow(String name, Map params, String pageId) {
bool _nativeContainerWillShow(String name, Map params, String pageId) {
if (FlutterBoost.containerManager?.containsContainer(pageId) != true) {
FlutterBoost.containerManager
?.pushContainer(_createContainerSettings(name, params, pageId));
......@@ -136,11 +209,23 @@ class ContainerCoordinator implements NativePageContainerEventHandler {
return true;
}
@override
bool nativeContainerDidShow(String name, Map params, String pageId) {
FlutterBoost.containerManager
?.showContainer(_createContainerSettings(name, params, pageId));
//在Android上对无障碍辅助模式的兼容
if (Platform.isAndroid) {
try {
final SemanticsOwner owner =
WidgetsBinding.instance.pipelineOwner?.semanticsOwner;
final SemanticsNode root = owner?.rootSemanticsNode;
root?.detach();
root?.attach(owner);
} catch (e) {
assert(false, e.toString());
}
}
performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Appear);
......@@ -150,34 +235,30 @@ class ContainerCoordinator implements NativePageContainerEventHandler {
return true;
}
@override
bool nativeContainerWillDisappear(String name, Map params, String pageId) {
bool _nativeContainerWillDisappear(String name, Map params, String pageId) {
return true;
}
@override
bool nativeContainerDidDisappear(String name, Map params, String pageId) {
bool _nativeContainerDidDisappear(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Disappear);
return true;
}
@override
bool nativeContainerDidInit(String name, Map params, String pageId) {
bool _nativeContainerDidInit(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Init);
return true;
}
@override
bool nativeContainerWillDealloc(String name, Map params, String pageId) {
bool _nativeContainerWillDealloc(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Destroy);
FlutterBoost.containerManager?.remove(pageId);
Logger.log(
'native containner dealloc, \nmanager dump:\n${FlutterBoost.containerManager?.dump()}');
'native containner dealloc, \n manager dump:\n${FlutterBoost.containerManager?.dump()}');
return true;
}
......
......@@ -23,12 +23,11 @@
*/
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_boost/AIOService/NavigationService/service/NavigationService.dart';
import 'package:flutter_boost/container/boost_container.dart';
import 'package:flutter_boost/container/container_coordinator.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_boost/support/logger.dart';
import 'package:flutter_boost/container/navigator_observer.dart';
enum ContainerOperation { Push, Onstage, Pop, Remove }
typedef BoostContainerObserver = void Function(
......@@ -137,7 +136,12 @@ class ContainerManagerState extends State<BoostContainerManager> {
void _onShownContainerChanged(String old, String now) {
Logger.log('onShownContainerChanged old:$old now:$now');
NavigationService.onShownContainerChanged(now, old, <dynamic, dynamic>{});
Map<String, dynamic> properties = new Map<String, dynamic>();
properties['newName'] = now;
properties['oldName'] = old;
FlutterBoost.singleton.channel.invokeMethod('onShownContainerChanged',properties);
}
void _refreshOverlayEntries() {
......@@ -225,7 +229,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
.observersOf<BoostContainerObserver>()) {
observer(ContainerOperation.Onstage, _onstage.settings);
}
Logger.log('ContainerObserver didOnstage');
Logger.log('ContainerObserver#2 didOnstage');
} else {
pushContainer(settings);
}
......@@ -267,7 +271,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
.observersOf<BoostContainerObserver>()) {
observer(ContainerOperation.Push, _onstage.settings);
}
Logger.log('ContainerObserver didPush');
Logger.log('ContainerObserver#2 didPush');
}
void pop() {
......@@ -283,7 +287,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
observer(ContainerOperation.Pop, old.settings);
}
Logger.log('ContainerObserver didPop');
Logger.log('ContainerObserver#2 didPop');
}
void remove(String uniqueId) {
......@@ -304,7 +308,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
observer(ContainerOperation.Remove, container.settings);
}
Logger.log('ContainerObserver didRemove');
Logger.log('ContainerObserver#2 didRemove');
}
}
......
......@@ -24,14 +24,10 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_boost/AIOService/NavigationService/service/NavigationService.dart';
import 'package:flutter_boost/container/boost_container.dart';
import 'package:flutter_boost/container/container_manager.dart';
import 'package:flutter_boost/messaging/page_result_mediator.dart';
import 'package:flutter_boost/router/router.dart';
import 'AIOService/loader/ServiceLoader.dart';
import 'container/boost_container.dart';
import 'container/container_manager.dart';
import 'channel/boost_channel.dart';
import 'container/container_coordinator.dart';
import 'observers_holders.dart';
......@@ -41,24 +37,18 @@ export 'container/container_manager.dart';
typedef Widget PageBuilder(String pageName, Map params, String uniqueId);
typedef Route PrePushRoute(
String pageName, String uniqueId, Map params, Route route);
String url, String uniqueId, Map params, Route route);
typedef void PostPushRoute(
String pageName, String uniqueId, Map params, Route route, Future result);
String url, String uniqueId, Map params, Route route, Future result);
class FlutterBoost {
static final FlutterBoost _instance = FlutterBoost();
final GlobalKey<ContainerManagerState> containerManagerKey =
GlobalKey<ContainerManagerState>();
final ObserversHolder _observersHolder = ObserversHolder();
final PageResultMediator _resultMediator = PageResultMediator();
final Router _router = Router();
FlutterBoost() {
_router.resultMediator = _resultMediator;
ServiceLoader.load();
}
final BoostChannel _boostChannel = BoostChannel();
static FlutterBoost get singleton => _instance;
......@@ -67,13 +57,12 @@ class FlutterBoost {
static TransitionBuilder init(
{TransitionBuilder builder,
PrePushRoute prePush,
PostPushRoute postPush}) {
PrePushRoute prePush,
PostPushRoute postPush}) {
return (BuildContext context, Widget child) {
assert(child is Navigator, 'child must be Navigator, what is wrong?');
//Logger.log('Running flutter boost opt!');
final BoostContainerManager manager = BoostContainerManager(
key: _instance.containerManagerKey,
initNavigator: child,
......@@ -90,71 +79,83 @@ class FlutterBoost {
ObserversHolder get observersHolder => _observersHolder;
BoostChannel get channel => _boostChannel;
FlutterBoost(){
ContainerCoordinator(_boostChannel);
}
///Register a default page builder.
void registerDefaultPageBuilder(PageBuilder builder) {
ContainerCoordinator.singleton.registerDefaultPageBuilder(builder);
}
///Register page builder for a key.
void registerPageBuilder(String pageName, PageBuilder builder) {
ContainerCoordinator.singleton.registerPageBuilder(pageName, builder);
}
///Register a map builders
void registerPageBuilders(Map<String, PageBuilder> builders) {
ContainerCoordinator.singleton.registerPageBuilders(builders);
}
Future<bool> openPage(String url, Map params,
{bool animated, PageResultHandler resultHandler}) {
return _router.openPage(url, params,
animated: animated, resultHandler: resultHandler);
}
Future<Map<String,dynamic>> open(String url,{Map<String,dynamic> urlParams,Map<String,dynamic> exts}){
Future<bool> closePage(String url, String pageId, Map params,
{bool animated}) {
return _router.closePage(url, pageId, params, animated: animated);
Map<String, dynamic> properties = new Map<String, dynamic>();
properties["url"] = url;
properties["urlParams"] = urlParams;
properties["exts"] = exts;
return channel.invokeMethod<Map<String,dynamic>>(
'openPage', properties);
}
//Close currentPage page.
Future<bool> closeCurPage(Map params) {
return _router.closeCurPage(params);
}
Future<bool> close(String id,{Map<String,dynamic> result,Map<String,dynamic> exts}){
Future<bool> closePageForContext(BuildContext context) {
BoostContainerSettings settings = BoostContainer.of(context).settings;
return closePage(settings.name, settings.uniqueId, settings.params,
animated: true);
}
assert(id != null);
BoostContainerSettings settings = containerManager?.onstageSettings;
Map<String, dynamic> properties = new Map<String, dynamic>();
///query current top page and show it
static void handleOnStartPage() async {
final Map<dynamic, dynamic> pageInfo =
await NavigationService.pageOnStart(<dynamic, dynamic>{});
if (pageInfo == null || pageInfo.isEmpty) return;
if (pageInfo.containsKey("name") &&
pageInfo.containsKey("params") &&
pageInfo.containsKey("uniqueId")) {
ContainerCoordinator.singleton.nativeContainerDidShow(
pageInfo["name"], pageInfo["params"], pageInfo["uniqueId"]);
if(exts == null){
exts = Map<String,dynamic>();
}
}
exts["params"] = settings.params;
bool onPageResult(String key, Map resultData, Map params) {
if(!exts.containsKey("animated")){
exts["animated"] = true;
}
properties["uniqueId"] = id;
if (result != null) {
properties["result"] = result;
}
if(_resultMediator.isResultId(key)){
_resultMediator.onPageResult(key, resultData,params);
}else{
containerManager?.containerStateOf(key)?.performOnResult(resultData);
if (exts != null) {
properties["exts"] = exts;
}
return true;
return channel.invokeMethod<bool>('closePage', properties);
}
Future<bool> closeCurrent({Map<String,dynamic> result,Map<String,dynamic> exts}) {
BoostContainerSettings settings = containerManager?.onstageSettings;
if(exts == null){
exts = Map<String,dynamic>();
}
exts["params"] = settings.params;
if(!exts.containsKey("animated")){
exts["animated"] = true;
}
return close(settings.uniqueId,result: result,exts: exts);
}
VoidCallback setPageResultHandler(String key, PageResultHandler handler) {
return _resultMediator.setPageResultHandler(key, handler);
Future<bool> closeByContext(BuildContext context,{Map<String,dynamic> result,Map<String,dynamic> exts}) {
BoostContainerSettings settings = containerManager?.onstageSettings;
if(exts == null){
exts = Map<String,dynamic>();
}
exts["params"] = settings.params;
if(!exts.containsKey("animated")){
exts["animated"] = true;
}
return close(settings.uniqueId,result: result,exts: exts);
}
///register for Container changed callbacks
......@@ -169,4 +170,6 @@ class FlutterBoost {
///register callbacks for Navigators push & pop
VoidCallback addBoostNavigatorObserver(BoostNavigatorObserver observer) =>
_observersHolder.addObserver<BoostNavigatorObserver>(observer);
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'package:flutter_boost/AIOService/NavigationService/service/NavigationService.dart';
import 'dart:async';
abstract class MessageProxy{
Future<bool> openPage(String pageName,Map params,bool animated);
Future<bool> closePage(String uniqueId,String pageName,Map params,bool animated);
}
class MessageProxyImp implements MessageProxy{
@override
Future<bool> openPage(String pageName, Map params, bool animated) {
return NavigationService.openPage(pageName, params, animated);
}
@override
Future<bool> closePage(String uniqueId, String pageName, Map params, bool animated) {
return NavigationService.closePage(uniqueId, pageName, params, animated);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
abstract class NativePageContainerEventHandler{
bool nativeContainerWillShow(String name , Map params, String pageId);
bool nativeContainerDidShow(String name , Map params, String pageId);
bool nativeContainerWillDisappear(String name , Map params, String pageId);
bool nativeContainerDidDisappear(String name , Map params, String pageId);
bool nativeContainerWillDealloc(String name , Map params, String pageId);
bool nativeContainerDidInit(String name , Map params, String pageId);
}
\ No newline at end of file
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'dart:async';
import 'package:flutter_boost/container/boost_container.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_boost/messaging/message_proxy.dart';
import 'package:flutter_boost/messaging/page_result_mediator.dart';
import 'package:flutter_boost/support/logger.dart';
class Router {
MessageProxy _msgProxy = MessageProxyImp();
PageResultMediator resultMediator = null;
void setMessageProxy(MessageProxy prx) {
if (prx != null) {
_msgProxy = prx;
}
}
Future<bool> openPage(String url, Map params,
{bool animated = true, PageResultHandler resultHandler}) {
if (resultHandler != null) {
String rid = resultMediator.createResultId();
params["result_id"] = rid;
FlutterBoost.singleton.setPageResultHandler(rid,
(String key, Map<dynamic, dynamic> result) {
Logger.log("Recieved result $result for from page key $key");
if (resultHandler != null) {
resultHandler(key, result);
}
});
}
return _msgProxy.openPage(url, params, animated);
}
Future<bool> closePage(String name, String pageId, Map params,
{bool animated = true}) {
return _msgProxy.closePage(pageId, name, params, animated);
}
//Close currentPage page.
Future<bool> closeCurPage(Map params) {
BoostContainerSettings settings;
final BoostContainerState container =
FlutterBoost.containerManager.onstageContainer;
if (container != null) {
settings = container.settings;
} else {
settings = FlutterBoost.containerManager.onstageSettings;
}
if (settings == null) {
return Future<bool>(() {
return false;
});
}
bool animated = true;
if (params.containsKey("animated")) {
animated = params["animated"] as bool;
}
return _msgProxy.closePage(
settings.uniqueId, settings.name, settings.params, animated);
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Alibaba Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import 'logger.dart';
class Tracer {
static final Tracer singleton = Tracer();
final Map<String, Record> _records = <String, Record>{};
Tracer();
static String mark(String unique, String tag) {
Record record = singleton._records[unique];
if (record == null) {
record = Record()
..unique = unique
..marks = <Mark>[];
singleton._records[unique] = record;
}
record.marks.add(Mark(tag, DateTime.now()));
return record.toString();
}
static void markAndLog(String unique, String tag) {
Logger.log(mark(unique, tag));
}
static String dump(String unique) => singleton._records[unique]?.toString();
}
class Record {
String unique;
List<Mark> marks;
@override
String toString() {
if (marks == null || marks.isEmpty) {
return '';
}
if (marks.length == 1) {
return marks.first.tag;
}
Mark least = marks.first;
String info = 'trace<$unique>#${least.tag}';
for (int i = 1; i < marks.length; i++) {
final Mark mark = marks[i];
info =
'$info=${mark.timeStamp.millisecond - least.timeStamp.millisecond}ms=>${mark.tag}';
least = mark;
}
return info;
}
}
class Mark {
String tag;
DateTime timeStamp;
Mark(this.tag, this.timeStamp);
}
......@@ -11,8 +11,6 @@ dependencies:
flutter:
sdk: flutter
xservice_kit: ^0.0.27
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
......@@ -20,8 +18,8 @@ dependencies:
# The following section is specific to Flutter.
flutter:
plugin:
androidPackage: com.taobao.idlefish.flutterboost
pluginClass: FlutterBoostPlugin
androidPackage: com.idlefish.flutterboost
pluginClass: BoostChannel
# To add assets to your plugin package, add an assets section, like this:
# 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