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

Merge branch 'master' into develop

# Conflicts:
#	android/src/main/java/com/idlefish/flutterboost/interfaces/IFlutterEngineProvider.java
#	android/src/main/java/com/taobao/idlefish/flutterboost/ContainerRecord.java
#	android/src/main/java/com/taobao/idlefish/flutterboost/containers/FlutterViewStub.java
parents 1d4ba9f8 c45410e2
...@@ -8,3 +8,5 @@ ...@@ -8,3 +8,5 @@
build/ build/
pubspec.lock pubspec.lock
.flutter-plugins .flutter-plugins
flutter
flutter_boost
## 0.0.1 ## 0.0.1
* TODO: Describe initial release. * TODO: Describe initial release.
## 0.1.5
The main changes are as following:
1. The new version do the page jump (URL route) based on the inherited FlutterViewController or Activity. The jump procedure will create new instance of FlutterView, while the old version just reuse the underlying FlutterView
2. Avoiding keeping and reusing the FlutterView, there is no screenshot and complex attach&detach logical any more. As a result, memory is saved and black or white-screen issue occured in old version all are solved.
3. This version also solved the app life cycle observation issue, we recommend you to use ContainerLifeCycle observer to listen the app enter background or foreground notification instead of WidgetBinding.
4. We did some code refactoring, the main logic became more straightforward.
### API changes
From the point of API changes, we did some refactoring as following:
#### iOS API changes
1. FlutterBoostPlugin's startFlutterWithPlatform function change its parameter from FlutterViewController to Engine
2.
**Before change**
```objectivec
FlutterBoostPlugin
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform onStart:(void (^)(FlutterViewController *))callback;
```
**After change**
```objectivec
FlutterBoostPlugin2
- (void)startFlutterWithPlatform:(id<FLB2Platform>)platform
onStart:(void (^)(id<FlutterBinaryMessenger,
FlutterTextureRegistry,
FlutterPluginRegistry> engine))callback;
```
2. FLBPlatform protocol removed flutterCanPop、accessibilityEnable and added entryForDart
**Before change:**
```objectivec
@protocol FLBPlatform <NSObject>
@optional
//Whether to enable accessibility support. Default value is Yes.
- (BOOL)accessibilityEnable;
// flutter模块是否还可以pop
- (void)flutterCanPop:(BOOL)canpop;
@required
- (void)openPage:(NSString *)name
params:(NSDictionary *)params
animated:(BOOL)animated
completion:(void (^)(BOOL finished))completion;
- (void)closePage:(NSString *)uid
animated:(BOOL)animated
params:(NSDictionary *)params
completion:(void (^)(BOOL finished))completion;
@end
```
**After change:**
```objectivec
@protocol FLB2Platform <NSObject>
@optional
- (NSString *)entryForDart;
@required
- (void)open:(NSString *)url
urlParams:(NSDictionary *)urlParams
exts:(NSDictionary *)exts
completion:(void (^)(BOOL finished))completion;
- (void)close:(NSString *)uid
result:(NSDictionary *)result
exts:(NSDictionary *)exts
completion:(void (^)(BOOL finished))completion;
@end
```
#### Android API changes
Android mainly changed the IPlatform interface and its implementation.
It removed following APIs:
```java
Activity getMainActivity();
boolean startActivity(Context context,String url,int requestCode);
Map getSettings();
```
And added following APIs:
```java
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();
int whenEngineStart();
```
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
<a href="https://mp.weixin.qq.com/s?__biz=MzU4MDUxOTI5NA==&mid=2247484367&idx=1&sn=fcbc485f068dae5de9f68d52607ea08f&chksm=fd54d7deca235ec86249a9e3714ec18be8b2d6dc580cae19e4e5113533a6c5b44dfa5813c4c3&scene=0&subscene=131&clicktime=1551942425&ascene=7&devicetype=android-28&version=2700033b&nettype=ctnet&abtest_cookie=BAABAAoACwASABMABAAklx4AVpkeAMSZHgDWmR4AAAA%3D&lang=zh_CN&pass_ticket=1qvHqOsbLBHv3wwAcw577EHhNjg6EKXqTfnOiFbbbaw%3D&wx_header=1">中文介绍</a> <a href="https://mp.weixin.qq.com/s?__biz=MzU4MDUxOTI5NA==&mid=2247484367&idx=1&sn=fcbc485f068dae5de9f68d52607ea08f&chksm=fd54d7deca235ec86249a9e3714ec18be8b2d6dc580cae19e4e5113533a6c5b44dfa5813c4c3&scene=0&subscene=131&clicktime=1551942425&ascene=7&devicetype=android-28&version=2700033b&nettype=ctnet&abtest_cookie=BAABAAoACwASABMABAAklx4AVpkeAMSZHgDWmR4AAAA%3D&lang=zh_CN&pass_ticket=1qvHqOsbLBHv3wwAcw577EHhNjg6EKXqTfnOiFbbbaw%3D&wx_header=1">中文介绍</a>
</p> </p>
Note: Please checkout the release note for the latest 0.1.50 to see changes [0.1.50 release note](https://github.com/alibaba/flutter_boost/releases)
# FlutterBoost # FlutterBoost
A next-generation Flutter-Native hybrid solution. FlutterBoost is a Flutter plugin which enables hybrid integration of Flutter for your existing native apps with minimum efforts.The philosophy of FlutterBoost is to use Flutter as easy as using a WebView. Managing Native pages and Flutter pages at the same time is non-trivial in an existing App. FlutterBoost takes care of page resolution for you. The only thing you need to care about is the name of the page(usually could be an URL).  A next-generation Flutter-Native hybrid solution. FlutterBoost is a Flutter plugin which enables hybrid integration of Flutter for your existing native apps with minimum efforts.The philosophy of FlutterBoost is to use Flutter as easy as using a WebView. Managing Native pages and Flutter pages at the same time is non-trivial in an existing App. FlutterBoost takes care of page resolution for you. The only thing you need to care about is the name of the page(usually could be an URL). 
<a name="bf647454"></a> <a name="bf647454"></a>
...@@ -20,10 +22,19 @@ You need to add Flutter to your project before moving on. ...@@ -20,10 +22,19 @@ You need to add Flutter to your project before moving on.
Open you pubspec.yaml and add the following line to dependencies: Open you pubspec.yaml and add the following line to dependencies:
```java ```java
flutter_boost: ^0.0.400 flutter_boost: ^0.0.415
```
or you could rely directly on a Github project tag, for example(recommended)
```java
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: '0.0.415'
``` ```
## Integration with Flutter code. ## Integration with Flutter code.
Add init code to you App Add init code to you App
...@@ -61,6 +72,8 @@ class _MyAppState extends State<MyApp> { ...@@ -61,6 +72,8 @@ class _MyAppState extends State<MyApp> {
## Integration with iOS code. ## Integration with iOS code.
Note: You need to add libc++ into "Linked Frameworks and Libraries"
Use FLBFlutterAppDelegate as the superclass of your AppDelegate Use FLBFlutterAppDelegate as the superclass of your AppDelegate
```objc ```objc
...@@ -119,7 +132,7 @@ Initialize FlutterBoost with FLBPlatform at the beginning of your App. ...@@ -119,7 +132,7 @@ Initialize FlutterBoost with FLBPlatform at the beginning of your App.
```objc ```objc
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router [FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router
onStart:^(FlutterViewController *fvc) { onStart:^(id engine) {
}]; }];
``` ```
...@@ -257,5 +270,19 @@ Please see the example for details. ...@@ -257,5 +270,19 @@ Please see the example for details.
# License # License
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
# Acknowledgments ## 关于我们
* Flutter 阿里巴巴-闲鱼技术是国内最早也是最大规模线上运行Flutter的团队。
我们在公众号中为你精选了Flutter独家干货,全面而深入。
内容包括:Flutter的接入、规模化应用、引擎探秘、工程体系、创新技术等教程和开源信息。
**架构/服务端/客户端/前端/算法/质量工程师 在公众号中投递简历,名额不限哦**
欢迎来闲鱼做一个好奇、幸福、有影响力的程序员,简历投递:tino.wjf@alibaba-inc.com
订阅地址
<img src="https://img.alicdn.com/tfs/TB17Ki5XubviK0jSZFNXXaApXXa-656-656.png" width="328px" height="328px">
[For English](https://twitter.com/xianyutech "For English")
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
<img src="flutter_boost.png"> <img src="flutter_boost.png">
</p> </p>
Note: 请查看最新版本0.1.50的release note 确认变更,[0.1.50 release note](https://github.com/alibaba/flutter_boost/releases)。
# FlutterBoost # FlutterBoost
新一代Flutter-Native混合解决方案。 FlutterBoost是一个Flutter插件,它可以轻松地为现有原生应用程序提供Flutter混合集成方案。FlutterBoost的理念是将Flutter像Webview那样来使用。在现有应用程序中同时管理Native页面和Flutter页面并非易事。 FlutterBoost帮你处理页面的映射和跳转,你只需关心页面的名字和参数即可(通常可以是URL)。 新一代Flutter-Native混合解决方案。 FlutterBoost是一个Flutter插件,它可以轻松地为现有原生应用程序提供Flutter混合集成方案。FlutterBoost的理念是将Flutter像Webview那样来使用。在现有应用程序中同时管理Native页面和Flutter页面并非易事。 FlutterBoost帮你处理页面的映射和跳转,你只需关心页面的名字和参数即可(通常可以是URL)。
...@@ -16,12 +19,17 @@ ...@@ -16,12 +19,17 @@
打开pubspec.yaml并将以下行添加到依赖项: 打开pubspec.yaml并将以下行添加到依赖项:
Release 1.0 之前的版本
```json ```json
flutter_boost: ^0.0.400 flutter_boost: ^0.0.415
``` ```
或者可以直接依赖github的项目的版本,Tag,pub发布会有延迟,推荐直接依赖Github项目
```java
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: '0.0.415'
```
## Dart代码的集成 ## Dart代码的集成
将init代码添加到App App 将init代码添加到App App
...@@ -58,6 +66,8 @@ class _MyAppState extends State<MyApp> { ...@@ -58,6 +66,8 @@ class _MyAppState extends State<MyApp> {
## iOS代码集成。 ## iOS代码集成。
注意:需要将libc++ 加入 "Linked Frameworks and Libraries" 中。
使用FLBFlutterAppDelegate作为AppDelegate的超类 使用FLBFlutterAppDelegate作为AppDelegate的超类
```objectivec ```objectivec
...@@ -116,7 +126,7 @@ class _MyAppState extends State<MyApp> { ...@@ -116,7 +126,7 @@ class _MyAppState extends State<MyApp> {
```的ObjectiveC ```的ObjectiveC
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router [FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router
onStart:^(FlutterViewController * fvc){ onStart:^(id engine){
}]; }];
``` ```
...@@ -246,12 +256,23 @@ FlutterBoost.singleton.closePageForContext(context); ...@@ -246,12 +256,23 @@ FlutterBoost.singleton.closePageForContext(context);
# Examples # Examples
更详细的使用例子请参考Demo 更详细的使用例子请参考Demo
# 作者
阿里巴巴闲鱼终端团队
# 许可证 # 许可证
该项目根据MIT许可证授权 - 有关详细信息,请参阅[LICENSE.md](LICENSE.md)文件 该项目根据MIT许可证授权 - 有关详细信息,请参阅[LICENSE.md](LICENSE.md)文件
<a name="Acknowledgments"> </a> <a name="Acknowledgments"> </a>
# 致谢
- Flutter ## 关于我们
阿里巴巴-闲鱼技术是国内最早也是最大规模线上运行Flutter的团队。
我们在公众号中为你精选了Flutter独家干货,全面而深入。
内容包括:Flutter的接入、规模化应用、引擎探秘、工程体系、创新技术等教程和开源信息。
**架构/服务端/客户端/前端/算法/质量工程师 在公众号中投递简历,名额不限哦**
欢迎来闲鱼做一个好奇、幸福、有影响力的程序员,简历投递:tino.wjf@alibaba-inc.com
订阅地址
<img src="https://img.alicdn.com/tfs/TB17Ki5XubviK0jSZFNXXaApXXa-656-656.png" width="328px" height="328px">
[For English](https://twitter.com/xianyutech "For English")
group 'com.taobao.idlefish.flutterboost' group 'com.idlefish.flutterboost'
version '1.0-SNAPSHOT' version '1.0-SNAPSHOT'
buildscript { buildscript {
...@@ -23,7 +23,7 @@ apply plugin: 'com.android.library' ...@@ -23,7 +23,7 @@ apply plugin: 'com.android.library'
android { android {
compileSdkVersion 26 compileSdkVersion 26
buildToolsVersion "26.0.2" buildToolsVersion '27.0.3'
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
...@@ -37,7 +37,6 @@ dependencies { ...@@ -37,7 +37,6 @@ dependencies {
implementation 'com.alibaba:fastjson:1.2.41' implementation 'com.alibaba:fastjson:1.2.41'
implementation 'com.android.support:support-v4:26.1.0' implementation 'com.android.support:support-v4:26.1.0'
implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support:appcompat-v7:26.1.0'
provided rootProject.findProject(":xservice_kit")
} }
ext { ext {
......
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.idlefish.flutterboost"> package="com.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>
</manifest> </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
* 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.Intent;
import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import java.util.HashMap;
import java.util.Map;
public class ContainerRecord implements IContainerRecord {
private final FlutterViewContainerManager mManager;
private final IFlutterViewContainer mContainer;
private final String mUniqueId;
private int mState = STATE_UNKNOW;
private MethodChannelProxy mProxy = new MethodChannelProxy();
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;
}
@Override
public String uniqueId() {
return mUniqueId;
}
@Override
public IFlutterViewContainer getContainer() {
return mContainer;
}
@Override
public int getState() {
return mState;
}
@Override
public void onCreate() {
Utils.assertCallOnMainThread();
BoostEngineProvider.assertEngineRunning();
if (mState != STATE_UNKNOW) {
Debuger.exception("state error");
}
mState = STATE_CREATED;
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;
mManager.pushRecord(this);
mContainer.getBoostFlutterView().onAttach();
mProxy.appear();
}
@Override
public void onDisappear() {
Utils.assertCallOnMainThread();
if (mState != STATE_APPEAR) {
Debuger.exception("state error");
}
mState = STATE_DISAPPEAR;
mProxy.disappear();
if(getContainer().getContextActivity().isFinishing()) {
mProxy.destroy();
}
mContainer.getBoostFlutterView().onDetach();
mManager.popRecord(this);
}
@Override
public void onDestroy() {
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 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) {
invokeChannelUnsafe("didInitPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didInitPageContainer");
mState = STATE_CREATED;
}
}
private void appear() {
invokeChannelUnsafe("didShowPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didShowPageContainer");
mState = STATE_APPEAR;
}
private void disappear() {
if (mState < STATE_DISAPPEAR) {
invokeChannel("didDisappearPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didDisappearPageContainer");
mState = STATE_DISAPPEAR;
}
}
private void destroy() {
if (mState < STATE_DESTROYED) {
invokeChannel("willDeallocPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("willDeallocPageContainer");
mState = STATE_DESTROYED;
}
}
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);
}
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);
}
}
public static String genUniqueId(Object obj) {
return System.currentTimeMillis() + "-" + obj.hashCode();
}
}
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.taobao.idlefish.flutterboost; package com.idlefish.flutterboost;
import android.util.Log; import android.util.Log;
...@@ -60,7 +60,7 @@ public class Debuger { ...@@ -60,7 +60,7 @@ public class Debuger {
public static boolean isDebug(){ public static boolean isDebug(){
try { try {
return FlutterBoostPlugin.platform().isDebug(); return FlutterBoost.singleton().platform().isDebug();
}catch (Throwable t){ }catch (Throwable t){
return false; 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 @@ ...@@ -21,17 +21,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.taobao.idlefish.flutterboost; package com.idlefish.flutterboost;
import android.app.Activity; import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.SparseArray;
import com.taobao.idlefish.flutterboost.NavigationService.NavigationService; import com.idlefish.flutterboost.interfaces.IContainerManager;
import com.taobao.idlefish.flutterboost.interfaces.IContainerManager; import com.idlefish.flutterboost.interfaces.IContainerRecord;
import com.taobao.idlefish.flutterboost.interfaces.IContainerRecord; import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewContainer; import com.idlefish.flutterboost.interfaces.IOperateSyncer;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -41,194 +40,127 @@ import java.util.HashSet; ...@@ -41,194 +40,127 @@ import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.WeakHashMap; import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterRunArguments;
public class FlutterViewContainerManager implements IContainerManager { public class FlutterViewContainerManager implements IContainerManager {
private final Map<IFlutterViewContainer, IContainerRecord> mRecords = new LinkedHashMap<>(); private final Map<IFlutterViewContainer, IContainerRecord> mRecordMap = new LinkedHashMap<>();
private final Instrument mInstrument;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Set<ContainerRef> mRefs = new HashSet<>(); 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() { FlutterViewContainerManager() {}
mInstrument = new Instrument(this);
}
@Override @Override
public PluginRegistry onContainerCreate(IFlutterViewContainer container) { public IOperateSyncer generateSyncer(IFlutterViewContainer container) {
assertCallOnMainThread(); Utils.assertCallOnMainThread();
ContainerRecord record = new ContainerRecord(this, container); ContainerRecord record = new ContainerRecord(this, container);
if (mRecords.put(container, record) != null) { if (mRecordMap.put(container, record) != null) {
Debuger.exception("container:" + container.getContainerName() + " already exists!"); Debuger.exception("container:" + container.getContainerUrl() + " already exists!");
return new PluginRegistryImpl(container.getActivity(), container.getBoostFlutterView());
} }
mRefs.add(new ContainerRef(record.uniqueId(),container)); mRefs.add(new ContainerRef(record.uniqueId(),container));
FlutterMain.ensureInitializationComplete(container.getActivity().getApplicationContext(), null); return record;
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()); void pushRecord(IContainerRecord record) {
if(!mRecordMap.containsValue(record)) {
Debuger.exception("invalid record!");
} }
@Override mRecordStack.push(record);
public void onContainerAppear(IFlutterViewContainer container) {
assertCallOnMainThread();
final IContainerRecord record = mRecords.get(container);
if (record == null) {
Debuger.exception("container:" + container.getContainerName() + " not exists yet!");
return;
} }
mInstrument.performAppear(record); void popRecord(IContainerRecord record) {
mCurrentTopRecord = record; if(mRecordStack.peek() == record) {
mRecordStack.pop();
} }
@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;
}
mInstrument.performDisappear(record);
if (!container.isFinishing()) {
checkIfFlutterViewNeedStopLater();
}
}
@Override
public void onContainerDestroy(IFlutterViewContainer container) {
assertCallOnMainThread();
if (mCurrentTopRecord != null
&& mCurrentTopRecord.getContainer() == container) {
mCurrentTopRecord = null;
} }
final IContainerRecord record = mRecords.remove(container); void removeRecord(IContainerRecord record) {
if (record == null) { mRecordStack.remove(record);
Debuger.exception("container:" + container.getContainerName() + " not exists yet!"); mRecordMap.remove(record.getContainer());
return;
} }
mInstrument.performDestroy(record); void setContainerResult(IContainerRecord record,int requestCode, int resultCode, Map<String,Object> result) {
checkIfFlutterViewNeedStopLater(); IFlutterViewContainer target = findContainerById(record.uniqueId());
if(target == null) {
Debuger.exception("setContainerResult error, url="+record.getContainer().getContainerUrl());
} }
private void checkIfFlutterViewNeedStopLater() { if (result == null) {
mHandler.postDelayed(new Runnable() { result = new HashMap<>();
@Override
public void run() {
if (!hasContainerAppear()) {
FlutterBoostPlugin.viewProvider().stopFlutterView();
}
}
}, 250);
} }
@Override result.put("_requestCode__",requestCode);
public void onBackPressed(IFlutterViewContainer container) { result.put("_resultCode__",resultCode);
assertCallOnMainThread();
final IContainerRecord record = mRecords.get(container); final OnResult onResult = mOnResults.remove(record.uniqueId());
if (record == null) { if(onResult != null) {
Debuger.exception("container:" + container.getContainerName() + " not exists yet!"); onResult.onResult(result);
return;
} }
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 IContainerRecord recordOf(IFlutterViewContainer container) {
public void destroyContainerRecord(String name, String uniqueId) { return mRecordMap.get(container);
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 (!done) { void openContainer(String url, Map<String, Object> urlParams, Map<String, Object> exts,OnResult onResult) {
Debuger.exception("destroyContainerRecord can not find name:" + name + " uniqueId:" + uniqueId); Context context = FlutterBoost.singleton().currentActivity();
} if(context == null) {
context = FlutterBoost.singleton().platform().getApplication();
} }
@Override if(urlParams == null) {
public void onContainerResult(IFlutterViewContainer container, Map result) { urlParams = new HashMap<>();
final IContainerRecord record = mRecords.get(container);
if (record == null) {
Debuger.exception("container:" + container.getContainerName() + " not exists yet!");
return;
} }
record.onResult(result); int requestCode = 0;
final Object v = urlParams.remove("requestCode");
if(v != null) {
requestCode = Integer.valueOf(String.valueOf(v));
} }
@Override final String uniqueId = ContainerRecord.genUniqueId(url);
public void setContainerResult(String uniqueId, Map result) { urlParams.put(IContainerRecord.UNIQ_KEY,uniqueId);
if (result == null) { if(onResult != null) {
Debuger.exception("setContainerResult result is null"); mOnResults.put(uniqueId,onResult);
return;
} }
if (!(result instanceof HashMap)) { FlutterBoost.singleton().platform().openContainer(context,url,urlParams,requestCode,exts);
result = new HashMap();
result.putAll(result);
} }
boolean done = false; IContainerRecord closeContainer(String uniqueId, Map<String, Object> result,Map<String,Object> exts) {
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecords.entrySet()) { IContainerRecord targetRecord = null;
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecordMap.entrySet()) {
if (TextUtils.equals(uniqueId, entry.getValue().uniqueId())) { if (TextUtils.equals(uniqueId, entry.getValue().uniqueId())) {
entry.getKey().setBoostResult((HashMap) result); targetRecord = entry.getValue();
done = true;
break; break;
} }
} }
if (!done) { if(targetRecord == null) {
Debuger.exception("setContainerResult can not find uniqueId:" + uniqueId); Debuger.exception("closeContainer can not find uniqueId:" + uniqueId);
} }
FlutterBoost.singleton().platform().closeContainer(targetRecord,result,exts);
return targetRecord;
} }
@Override @Override
public IContainerRecord getCurrentTopRecord() { public IContainerRecord getCurrentTopRecord() {
return mCurrentTopRecord; if(mRecordStack.isEmpty()) return null;
return mRecordStack.peek();
} }
@Override @Override
public IContainerRecord getLastRecord() { public IContainerRecord getLastGenerateRecord() {
final Collection<IContainerRecord> values = mRecords.values(); final Collection<IContainerRecord> values = mRecordMap.values();
if(!values.isEmpty()) { if(!values.isEmpty()) {
final ArrayList<IContainerRecord> array = new ArrayList<>(values); final ArrayList<IContainerRecord> array = new ArrayList<>(values);
return array.get(array.size()-1); return array.get(array.size()-1);
...@@ -239,7 +171,7 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -239,7 +171,7 @@ public class FlutterViewContainerManager implements IContainerManager {
@Override @Override
public IFlutterViewContainer findContainerById(String uniqueId) { public IFlutterViewContainer findContainerById(String uniqueId) {
IFlutterViewContainer target = null; 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())) { if (TextUtils.equals(uniqueId, entry.getValue().uniqueId())) {
target = entry.getKey(); target = entry.getKey();
break; break;
...@@ -257,14 +189,13 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -257,14 +189,13 @@ public class FlutterViewContainerManager implements IContainerManager {
return target; return target;
} }
@Override void onShownContainerChanged(String old, String now) {
public void onShownContainerChanged(String old, String now) { Utils.assertCallOnMainThread();
assertCallOnMainThread();
IFlutterViewContainer oldContainer = null; IFlutterViewContainer oldContainer = null;
IFlutterViewContainer nowContainer = 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())) { if (TextUtils.equals(old, entry.getValue().uniqueId())) {
oldContainer = entry.getKey(); oldContainer = entry.getKey();
} }
...@@ -289,9 +220,7 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -289,9 +220,7 @@ public class FlutterViewContainerManager implements IContainerManager {
@Override @Override
public boolean hasContainerAppear() { public boolean hasContainerAppear() {
assertCallOnMainThread(); for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecordMap.entrySet()) {
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecords.entrySet()) {
if (entry.getValue().getState() == IContainerRecord.STATE_APPEAR) { if (entry.getValue().getState() == IContainerRecord.STATE_APPEAR) {
return true; return true;
} }
...@@ -300,16 +229,6 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -300,16 +229,6 @@ public class FlutterViewContainerManager implements IContainerManager {
return false; 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 static class ContainerRef {
public final String uniqueId; public final String uniqueId;
public final WeakReference<IFlutterViewContainer> container; public final WeakReference<IFlutterViewContainer> container;
...@@ -320,27 +239,7 @@ public class FlutterViewContainerManager implements IContainerManager { ...@@ -320,27 +239,7 @@ public class FlutterViewContainerManager implements IContainerManager {
} }
} }
public static class PluginRegistryImpl implements PluginRegistry { interface OnResult {
void onResult(Map<String,Object> result);
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);
}
} }
} }
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 @@ ...@@ -21,140 +21,138 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.taobao.idlefish.flutterboost.containers; package com.idlefish.flutterboost.containers;
import android.content.Context; import android.app.Activity;
import android.graphics.Color; import android.content.Intent;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.view.Window;
import android.widget.ProgressBar; import android.view.WindowManager;
import com.taobao.idlefish.flutterboost.BoostFlutterView; import com.idlefish.flutterboost.BoostFlutterEngine;
import com.taobao.idlefish.flutterboost.Debuger; import com.idlefish.flutterboost.BoostFlutterView;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin; import com.idlefish.flutterboost.FlutterBoost;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewContainer; 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 { abstract public class BoostFlutterFragment extends Fragment implements IFlutterViewContainer {
FlutterContent mContent; protected BoostFlutterEngine mFlutterEngine;
PluginRegistry mRegistry; protected BoostFlutterView mFlutterView;
protected IOperateSyncer mSyncer;
boolean resumed = false;
@Nullable
@Override @Override
public void onCreate(Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); mSyncer = FlutterBoost.singleton().containerManager().generateSyncer(this);
mFlutterEngine = createFlutterEngine();
mFlutterView = createFlutterView(mFlutterEngine);
mSyncer.onCreate();
mRegistry = FlutterBoostPlugin.containerManager().onContainerCreate(this); return mFlutterView;
onRegisterPlugins(mRegistry);
} }
@Override protected BoostFlutterEngine createFlutterEngine(){
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return FlutterBoost.singleton().engineProvider().provideEngine(getContext());
super.onCreateView(inflater, container, savedInstanceState); }
mContent = new FlutterContent(getActivity());
return mContent; 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 @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
if (!resumed) { mSyncer.onAppear();
resumed = true; mFlutterEngine.getLifecycleChannel().appIsResumed();
FlutterBoostPlugin.containerManager().onContainerAppear(this);
mContent.attachFlutterView(getBoostFlutterView());
Log.e("FlutterBoost", "FlutterMenuFragment resume");
}
} }
@Override @Override
public void onPause() { public void onPause() {
mSyncer.onDisappear();
super.onPause(); super.onPause();
if (resumed) { mFlutterEngine.getLifecycleChannel().appIsInactive();
resumed = false;
mContent.snapshot();
FlutterBoostPlugin.containerManager().onContainerDisappear(this);
Log.e("FlutterBoost", "FlutterMenuFragment stop");
}
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
mSyncer.onDestroy();
super.onDestroy(); super.onDestroy();
if (mContent != null) {
mContent.destroy();
}
FlutterBoostPlugin.containerManager().onContainerDestroy(this);
} }
@Override public void onBackPressed() {
public void onContainerShown() { mSyncer.onBackPressed();
mContent.onContainerShown();
} }
@Override public void onNewIntent(Intent intent) {
public void onContainerHidden() { mSyncer.onNewIntent(intent);
mContent.onContainerHidden();
} }
@Override @Override
public BoostFlutterView getBoostFlutterView() { public void onActivityResult(int requestCode, int resultCode, Intent data) {
return FlutterBoostPlugin.viewProvider().createFlutterView(this); super.onActivityResult(requestCode, resultCode, data);
mSyncer.onActivityResult(requestCode,resultCode,data);
} }
@Override @Override
public boolean isFinishing() { public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
return getActivity().isFinishing(); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
mSyncer.onRequestPermissionsResult(requestCode, permissions, grantResults);
} }
protected View createSplashScreenView() { public void onTrimMemory(int level) {
FrameLayout layout = new FrameLayout(getContext()); mSyncer.onTrimMemory(level);
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;
}
protected View createFlutterInitCoverView() {
View initCover = new View(getActivity());
initCover.setBackgroundColor(Color.WHITE);
return initCover;
} }
@Override @Override
public void setBoostResult(HashMap result) { public void onLowMemory() {
super.onLowMemory();
mSyncer.onLowMemory();
} }
class FlutterContent extends FlutterViewStub { public void onUserLeaveHint() {
mSyncer.onUserLeaveHint();
public FlutterContent(Context context) {
super(context);
} }
@Override @Override
public View createFlutterInitCoverView() { public Activity getContextActivity() {
return BoostFlutterFragment.this.createFlutterInitCoverView(); return getActivity();
} }
@Override @Override
public BoostFlutterView getBoostFlutterView() { public BoostFlutterView getBoostFlutterView() {
return BoostFlutterFragment.this.getBoostFlutterView(); return mFlutterView;
} }
@Override @Override
public View createSplashScreenView() { public void finishContainer(Map<String,Object> result) {
return BoostFlutterFragment.this.createSplashScreenView(); getFragmentManager().popBackStack();
}
} }
@Override
public void onContainerShown() {}
@Override
public void onContainerHidden() {}
} }
...@@ -21,22 +21,17 @@ ...@@ -21,22 +21,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.idlefish.flutterboost.interfaces;
#import "FLBViewProviderFactory.h" public interface IContainerManager {
#import "FLBFlutterEngine.h"
#import "FLBFlutterEngineOld.h"
IOperateSyncer generateSyncer(IFlutterViewContainer container);
IContainerRecord getCurrentTopRecord();
@implementation FLBViewProviderFactory IContainerRecord getLastGenerateRecord();
- (id<FLBFlutterViewProvider>)createViewProvider IFlutterViewContainer findContainerById(String uniqueId);
{
#if RELEASE_1_0
return [FLBFlutterEngine new];
#else
return [FLBFlutterEngineOld new];
#endif
}
@end boolean hasContainerAppear();
}
\ No newline at end of file
...@@ -21,14 +21,15 @@ ...@@ -21,14 +21,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.taobao.idlefish.flutterboost.interfaces; package com.idlefish.flutterboost.interfaces;
import java.util.Map;
/** /**
* a container record, which use map a flutter page * 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_UNKNOW = 0;
int STATE_CREATED = 1; int STATE_CREATED = 1;
int STATE_APPEAR = 2; int STATE_APPEAR = 2;
...@@ -38,9 +39,4 @@ public interface IContainerRecord { ...@@ -38,9 +39,4 @@ public interface IContainerRecord {
String uniqueId(); String uniqueId();
IFlutterViewContainer getContainer(); IFlutterViewContainer getContainer();
int getState(); int getState();
void onCreate();
void onAppear();
void onDisappear();
void onDestroy();
void onResult(Map Result);
} }
...@@ -21,32 +21,33 @@ ...@@ -21,32 +21,33 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.taobao.idlefish.flutterboost; package com.idlefish.flutterboost.interfaces;
import android.content.Context; import android.content.Context;
import io.flutter.view.FlutterNativeView; import com.idlefish.flutterboost.BoostFlutterEngine;
/**
public class BoostFlutterNativeView extends FlutterNativeView { * a flutter engine provider
*/
public BoostFlutterNativeView(Context context) { public interface IFlutterEngineProvider {
super(context);
}
public void detachFromFlutterView() {
//do nothing...
}
public void detach() { /**
//do nothing... * create flutter engine, we just hold a single instance now
} * @param context
* @return
*/
BoostFlutterEngine createEngine(Context context);
@Override /**
public void destroy() { * provide a flutter engine
//do nothing... * @param context
} * @return
*/
BoostFlutterEngine provideEngine(Context context);
public void boostDestroy() { /**
super.destroy(); * may return null
} * @return
*/
BoostFlutterEngine tryGetEngine();
} }
...@@ -21,15 +21,13 @@ ...@@ -21,15 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.taobao.idlefish.flutterboost.interfaces; package com.idlefish.flutterboost.interfaces;
import android.app.Activity; 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 * a container which contains the flutter view
...@@ -37,7 +35,7 @@ import io.flutter.plugin.common.PluginRegistry; ...@@ -37,7 +35,7 @@ import io.flutter.plugin.common.PluginRegistry;
public interface IFlutterViewContainer { public interface IFlutterViewContainer {
String RESULT_KEY = "_flutter_result_"; String RESULT_KEY = "_flutter_result_";
Activity getActivity(); Activity getContextActivity();
/** /**
* provide a flutter view * provide a flutter view
...@@ -48,19 +46,19 @@ public interface IFlutterViewContainer { ...@@ -48,19 +46,19 @@ public interface IFlutterViewContainer {
/** /**
* call to destroy the container * call to destroy the container
*/ */
void destroyContainer(); void finishContainer(Map<String,Object> result);
/** /**
* container name * container name
* @return * @return
*/ */
String getContainerName(); String getContainerUrl();
/** /**
* container params * container params
* @return * @return
*/ */
Map getContainerParams(); Map getContainerUrlParams();
/** /**
* callback when container shown * callback when container shown
...@@ -71,22 +69,4 @@ public interface IFlutterViewContainer { ...@@ -71,22 +69,4 @@ public interface IFlutterViewContainer {
* callback when container hidden * callback when container hidden
*/ */
void onContainerHidden(); 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 @@ ...@@ -21,31 +21,29 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.taobao.idlefish.flutterboost.interfaces; package com.idlefish.flutterboost.interfaces;
import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import java.util.Map; import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
/** /**
* APIs that platform(Android App) must provide * APIs that platform(Android App) must provide
*/ */
public interface IPlatform { public interface IPlatform {
int IMMEDIATELY = 0; //立即启动引擎
int ANY_ACTIVITY_CREATED = 1; //当有任何Activity创建时,启动引擎
/** /**
* get current application * get current application
* @return * @return
*/ */
Application getApplication(); Application getApplication();
/**
* get main activity, which must always exist at the bottom of task stack.
* @return
*/
Activity getMainActivity();
/** /**
* debug or not * debug or not
* @return * @return
...@@ -53,18 +51,25 @@ public interface IPlatform { ...@@ -53,18 +51,25 @@ public interface IPlatform {
boolean isDebug(); boolean isDebug();
/** /**
* start a new activity from flutter page, you may need a page router with url * register plugins
* @param context
* @param url
* @param requestCode
* @return * @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 * @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.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);
}
}
}
}
}
/*
* 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.os.Handler;
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 java.util.Map;
import fleamarket.taobao.com.xservicekit.handler.MessageResult;
public class ContainerRecord implements IContainerRecord {
private final IContainerManager 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();
mManager = manager;
mContainer = container;
}
@Override
public String uniqueId() {
return mUniqueId;
}
@Override
public IFlutterViewContainer getContainer() {
return mContainer;
}
@Override
public int getState() {
return mState;
}
@Override
public void onCreate() {
mState = STATE_CREATED;
mContainer.getBoostFlutterView().boostResume();
mProxy.create();
}
@Override
public void onAppear() {
mState = STATE_APPEAR;
mContainer.getBoostFlutterView().boostResume();
mProxy.appear();
}
@Override
public void onDisappear() {
mProxy.disappear();
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();
}
});
}
}
@Override
public void onDestroy() {
mProxy.destroy();
mState = STATE_DESTROYED;
}
@Override
public void onResult(Map Result) {
NavigationService.onNativePageResult(
genResult("onNativePageResult"),
mUniqueId,
mUniqueId,
Result,
mContainer.getContainerParams()
);
}
private class MethodChannelProxy {
private int mState = STATE_UNKNOW;
private void create() {
if (mState == STATE_UNKNOW) {
NavigationService.didInitPageContainer(
genResult("didInitPageContainer"),
mContainer.getContainerName(),
mContainer.getContainerParams(),
mUniqueId
);
//Debuger.log("didInitPageContainer");
mState = STATE_CREATED;
}
}
private void appear() {
NavigationService.didShowPageContainer(
genResult("didShowPageContainer"),
mContainer.getContainerName(),
mContainer.getContainerParams(),
mUniqueId
);
//Debuger.log("didShowPageContainer");
mState = STATE_APPEAR;
}
private void disappear() {
if (mState < STATE_DISAPPEAR) {
NavigationService.didDisappearPageContainer(
genResult("didDisappearPageContainer"),
mContainer.getContainerName(),
mContainer.getContainerParams(),
mUniqueId
);
//Debuger.log("didDisappearPageContainer");
mState = STATE_DISAPPEAR;
}
}
private void destroy() {
if (mState < STATE_DESTROYED) {
NavigationService.willDeallocPageContainer(
genResult("willDeallocPageContainer"),
mContainer.getContainerName(),
mContainer.getContainerParams(),
mUniqueId
);
//Debuger.log("willDeallocPageContainer");
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");
}
@Override
public void error(String var1, String var2, Object var3) {
Debuger.log(name + " call error");
}
@Override
public void notImplemented() {
Debuger.log(name + " call not Impelemented");
}
};
}
}
/*
* 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;
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();
}
//Handling page result.
if (sInstance.needResult(params)) {
sInstance.mMediator.setHandler(url, new PageResultHandler() {
@Override
public void onResult(String key, Map resultData) {
NavigationService.onNativePageResult(new MessageResult<Boolean>() {
@Override
public void success(Boolean var1) {
//Doing nothing now.
}
@Override
public void error(String var1, String var2, Object var3) {
//Doing nothing now.
}
@Override
public void notImplemented() {
//Doing nothing now.
}
}, "no use", key, resultData, params);
}
});
}
sInstance.mPlatform.startActivity(ctx, concatUrl(url, params), requestCode);
}
private Boolean needResult(Map params) {
if (params == null) return false;
final String key = "needResult";
if (params.containsKey(key)) {
if (params.get(key) instanceof Boolean) {
return (Boolean) params.get(key);
}
}
return false;
}
public static void onPageResult(String key, Map resultData) {
if (sInstance == null) {
throw new RuntimeException("FlutterBoostPlugin not init yet!");
}
sInstance.mMediator.onPageResult(key, resultData);
}
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;
public class NavigationServiceRegister {
public static void register(){
NavigationService.register();
NavigationService_onShownContainerChanged.register();
NavigationService_onFlutterPageResult.register();
NavigationService_pageOnStart.register();
NavigationService_openPage.register();
NavigationService_closePage.register();
}
}
\ 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){
Map pageParams = null;
int requestCode = 0;
if(params != null && params.get("query") != null) {
pageParams = (Map)params.get("query");
}
if(params != null && params.get("requestCode") != null) {
requestCode = (int)params.get("requestCode");
}
FlutterBoostPlugin.openPage(null,pageName,pageParams,requestCode);
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 java.util.HashMap;
import java.util.Map;
class PageResultMediator {
private Map<String,PageResultHandler> _handlers = new HashMap<>();
void onPageResult(String key , Map resultData){
if(key == null) return;
if(_handlers.containsKey(key)){
_handlers.get(key).onResult(key,resultData);
_handlers.remove(key);
}
}
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.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import com.taobao.idlefish.flutterboost.BoostFlutterView;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import com.taobao.idlefish.flutterboost.interfaces.IFlutterViewContainer;
import java.util.HashMap;
import java.util.Map;
import io.flutter.app.FlutterActivity;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterView;
abstract public class BoostFlutterActivity extends FlutterActivity implements IFlutterViewContainer {
private FlutterContent mFlutterContent;
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
mFlutterContent = new FlutterContent(this);
setContentView(mFlutterContent);
FlutterBoostPlugin.containerManager().onContainerCreate(this);
onRegisterPlugins(this);
}
@Override
protected void onPostResume() {
super.onPostResume();
FlutterBoostPlugin.containerManager().onContainerAppear(BoostFlutterActivity.this);
mFlutterContent.attachFlutterView(getBoostFlutterView());
}
@Override
protected void onPause() {
mFlutterContent.detachFlutterView();
FlutterBoostPlugin.containerManager().onContainerDisappear(this);
super.onPause();
}
@Override
protected void onDestroy() {
FlutterBoostPlugin.containerManager().onContainerDestroy(this);
mFlutterContent.destroy();
super.onDestroy();
}
public FlutterView createFlutterView(Context context) {
return FlutterBoostPlugin.viewProvider().createFlutterView(this);
}
@Override
public FlutterNativeView createFlutterNativeView() {
return FlutterBoostPlugin.viewProvider().createFlutterNativeView(this);
}
@Override
public boolean retainFlutterNativeView() {
return true;
}
protected View createSplashScreenView() {
FrameLayout frameLayout = new FrameLayout(this);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER;
frameLayout.addView(new ProgressBar(this), params);
return frameLayout;
}
protected View createFlutterInitCoverView() {
View initCover = new View(this);
initCover.setBackgroundColor(Color.WHITE);
return initCover;
}
@Override
public void onContainerShown() {
mFlutterContent.onContainerShown();
}
@Override
public void onContainerHidden() {
mFlutterContent.onContainerHidden();
}
@Override
public void onBackPressed() {
FlutterBoostPlugin.containerManager().onBackPressed(this);
}
@Override
public Activity getActivity() {
return this;
}
@Override
public BoostFlutterView getBoostFlutterView() {
return (BoostFlutterView) getFlutterView();
}
@Override
public void destroyContainer() {
finish();
}
@Override
public abstract String getContainerName();
@Override
public abstract Map getContainerParams();
@Override
public void setBoostResult(HashMap result) {
FlutterBoostPlugin.setBoostResult(this, result);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
FlutterBoostPlugin.onBoostResult(this,requestCode,resultCode,data);
}
class FlutterContent extends FlutterViewStub {
public FlutterContent(Context context) {
super(context);
}
@Override
public View createFlutterInitCoverView() {
return BoostFlutterActivity.this.createFlutterInitCoverView();
}
@Override
public BoostFlutterView getBoostFlutterView() {
return BoostFlutterActivity.this.getBoostFlutterView();
}
@Override
public View createSplashScreenView() {
return BoostFlutterActivity.this.createSplashScreenView();
}
}
}
/*
* 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' ...@@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 27 compileSdkVersion 26
lintOptions { lintOptions {
disable 'InvalidPackage' disable 'InvalidPackage'
...@@ -35,7 +35,7 @@ android { ...@@ -35,7 +35,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.taobao.idlefish.flutterboostexample" applicationId "com.taobao.idlefish.flutterboostexample"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 27 targetSdkVersion 26
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
...@@ -58,6 +58,6 @@ dependencies { ...@@ -58,6 +58,6 @@ dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:support-v4:27.1.1' implementation 'com.android.support:support-v4:26.1.0'
implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support:appcompat-v7:26.1.0'
} }
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
--> -->
<uses-permission android:name="android.permission.INTERNET"/> <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 <!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method. calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide 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; ...@@ -3,14 +3,11 @@ package com.taobao.idlefish.flutterboostexample;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; 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.HashMap;
import java.util.Map; import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class FlutterFragment extends BoostFlutterFragment { public class FlutterFragment extends BoostFlutterFragment {
@Override @Override
...@@ -29,27 +26,17 @@ public class FlutterFragment extends BoostFlutterFragment { ...@@ -29,27 +26,17 @@ public class FlutterFragment extends BoostFlutterFragment {
} }
@Override @Override
public void onRegisterPlugins(PluginRegistry registry) { public String getContainerUrl() {
GeneratedPluginRegistrant.registerWith(registry);
}
@Override
public String getContainerName() {
return "flutterFragment"; return "flutterFragment";
} }
@Override @Override
public Map getContainerParams() { public Map getContainerUrlParams() {
Map<String,String> params = new HashMap<>(); Map<String,String> params = new HashMap<>();
params.put("tag",getArguments().getString("tag")); params.put("tag",getArguments().getString("tag"));
return params; return params;
} }
@Override
public void destroyContainer() {
}
public static FlutterFragment instance(String tag){ public static FlutterFragment instance(String tag){
FlutterFragment fragment = new FlutterFragment(); FlutterFragment fragment = new FlutterFragment();
fragment.setTabTag(tag); fragment.setTabTag(tag);
......
package com.taobao.idlefish.flutterboostexample; package com.taobao.idlefish.flutterboostexample;
import android.graphics.Color;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.plugin.platform.PlatformPlugin;
public class FlutterFragmentPageActivity extends AppCompatActivity { public class FlutterFragmentPageActivity extends AppCompatActivity implements View.OnClickListener {
private FlutterFragment mFragment; private FlutterFragment mFragment;
private View mTab1;
private View mTab2;
private View mTab3;
private View mTab4;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE); supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
...@@ -32,11 +39,49 @@ public class FlutterFragmentPageActivity extends AppCompatActivity { ...@@ -32,11 +39,49 @@ public class FlutterFragmentPageActivity extends AppCompatActivity {
setContentView(R.layout.flutter_fragment_page); 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() getSupportFragmentManager()
.beginTransaction() .beginTransaction()
.replace(R.id.fragment_stub,mFragment) .replace(R.id.fragment_stub,mFragment)
.commit(); .commit();
} }
@Override
protected void onResume() {
super.onResume();
mTab1.performClick();
}
} }
package com.taobao.idlefish.flutterboostexample; 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 java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class FlutterPageActivity extends BoostFlutterActivity { public class FlutterPageActivity extends BoostFlutterActivity {
/**
* 该方法返回当前Activity在Flutter层对应的name,
* 混合栈将会在flutter层根据这个名字,在注册的Route表中查找对应的Widget
*
* 在flutter层有注册函数:
* FlutterBoost.singleton.registerPageBuilders({
* 'first': (pageName, params, _) => FirstRouteWidget(),
* 'second': (pageName, params, _) => SecondRouteWidget(),
* ...
* });
*
* 该方法中返回的就是注册的key:first , second
*
* @return
*/
@Override @Override
public void onRegisterPlugins(PluginRegistry registry) { public String getContainerUrl() {
GeneratedPluginRegistrant.registerWith(registry);
}
@Override
public String getContainerName() {
return "flutterPage"; return "flutterPage";
} }
/**
* 该方法返回的参数将会传递给上层的flutter对应的Widget
*
* 在flutter层有注册函数:
* FlutterBoost.singleton.registerPageBuilders({
* 'first': (pageName, params, _) => FirstRouteWidget(),
* 'second': (pageName, params, _) => SecondRouteWidget(),
* ...
* });
*
* 该方法返回的参数就会封装成上面的params
*
* @return
*/
@Override @Override
public Map getContainerParams() { public Map getContainerUrlParams() {
return null; Map<String,String> params = new HashMap<>();
params.put("aaa","bbb");
return params;
} }
} }
...@@ -7,6 +7,8 @@ import android.view.View; ...@@ -7,6 +7,8 @@ import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements View.OnClickListener { public class MainActivity extends AppCompatActivity implements View.OnClickListener {
...@@ -42,12 +44,14 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe ...@@ -42,12 +44,14 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Map params = new HashMap();
//Add some params if needed.
if (v == mOpenNative) { if (v == mOpenNative) {
PageRouter.openPageByUrl(this, PageRouter.NATIVE_PAGE_URL); PageRouter.openPageByUrl(this, PageRouter.NATIVE_PAGE_URL , params);
} else if (v == mOpenFlutter) { } else if (v == mOpenFlutter) {
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_URL); PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_URL,params);
} else if (v == mOpenFlutterFragment) { } 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; package com.taobao.idlefish.flutterboostexample;
import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin; import com.idlefish.flutterboost.BoostEngineProvider;
import com.taobao.idlefish.flutterboost.interfaces.IPlatform; 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 java.util.Map;
import io.flutter.app.FlutterApplication; import io.flutter.app.FlutterApplication;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.view.FlutterMain;
public class MyApplication extends FlutterApplication { public class MyApplication extends FlutterApplication {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
FlutterBoostPlugin.init(new IPlatform() { FlutterBoost.init(new Platform() {
@Override @Override
public Application getApplication() { public Application getApplication() {
return MyApplication.this; return MyApplication.this;
} }
/**
* 获取应用入口的Activity,这个Activity在应用交互期间应该是一直在栈底的
* @return
*/
@Override @Override
public Activity getMainActivity() { public boolean isDebug() {
if (MainActivity.sRef != null) { return true;
return MainActivity.sRef.get();
}
return null;
} }
@Override @Override
public boolean isDebug() { public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
return true; PageRouter.openPageByUrl(context,url,urlParams,requestCode);
} }
@Override @Override
public boolean startActivity(Context context, String url, int requestCode) { public IFlutterEngineProvider engineProvider() {
return PageRouter.openPageByUrl(context,url,requestCode); return new BoostEngineProvider(){
@Override
public BoostFlutterEngine createEngine(Context context) {
return new BoostFlutterEngine(context, new DartExecutor.DartEntrypoint(
context.getResources().getAssets(),
FlutterMain.findAppBundlePath(context),
"main"),"/");
}
};
} }
@Override @Override
public Map getSettings() { public int whenEngineStart() {
return null; return ANY_ACTIVITY_CREATED;
} }
}); });
} }
......
...@@ -6,6 +6,9 @@ import android.support.v7.app.AppCompatActivity; ...@@ -6,6 +6,9 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import java.util.HashMap;
import java.util.Map;
public class NativePageActivity extends AppCompatActivity implements View.OnClickListener { public class NativePageActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mOpenNative; private TextView mOpenNative;
...@@ -29,12 +32,13 @@ public class NativePageActivity extends AppCompatActivity implements View.OnClic ...@@ -29,12 +32,13 @@ public class NativePageActivity extends AppCompatActivity implements View.OnClic
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Map params = new HashMap();
if (v == mOpenNative) { if (v == mOpenNative) {
PageRouter.openPageByUrl(this, PageRouter.NATIVE_PAGE_URL); PageRouter.openPageByUrl(this, PageRouter.NATIVE_PAGE_URL,params);
} else if (v == mOpenFlutter) { } else if (v == mOpenFlutter) {
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_URL); PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_URL,params);
} else if (v == mOpenFlutterFragment) { } 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,25 +4,27 @@ import android.content.Context; ...@@ -4,25 +4,27 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.text.TextUtils; import android.text.TextUtils;
import java.util.Map;
public class PageRouter { public class PageRouter {
public static final String NATIVE_PAGE_URL = "sample://nativePage"; public static final String NATIVE_PAGE_URL = "sample://nativePage";
public static final String FLUTTER_PAGE_URL = "sample://flutterPage"; public static final String FLUTTER_PAGE_URL = "sample://flutterPage";
public static final String FLUTTER_FRAGMENT_PAGE_URL = "sample://flutterFragmentPage"; public static final String FLUTTER_FRAGMENT_PAGE_URL = "sample://flutterFragmentPage";
public static boolean openPageByUrl(Context context, String url) { public static boolean openPageByUrl(Context context, String url,Map params) {
return openPageByUrl(context, url, 0); 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 { try {
if (TextUtils.equals(url, FLUTTER_PAGE_URL)) { if (url.startsWith(FLUTTER_PAGE_URL)) {
context.startActivity(new Intent(context, FlutterPageActivity.class)); context.startActivity(new Intent(context, FlutterPageActivity.class));
return true; return true;
} else if (TextUtils.equals(url, FLUTTER_FRAGMENT_PAGE_URL)) { } else if (url.startsWith(FLUTTER_FRAGMENT_PAGE_URL)) {
context.startActivity(new Intent(context, FlutterFragmentPageActivity.class)); context.startActivity(new Intent(context, FlutterFragmentPageActivity.class));
return true; return true;
} else if (TextUtils.equals(url, NATIVE_PAGE_URL)) { } else if (url.startsWith(NATIVE_PAGE_URL)) {
context.startActivity(new Intent(context, NativePageActivity.class)); context.startActivity(new Intent(context, NativePageActivity.class));
return true; return true;
} else { } else {
......
...@@ -11,4 +11,47 @@ ...@@ -11,4 +11,47 @@
android:layout_weight="1" android:layout_weight="1"
android:id="@+id/fragment_stub"/> 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> </LinearLayout>
/Users/jidong/Documents/FlutterBoost
\ No newline at end of file
/Users/jidong/.pub-cache/hosted/pub.dartlang.org/xservice_kit-0.0.26
\ No newline at end of file
PODS: PODS:
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_boost (0.0.1): - flutter_boost (0.0.2):
- Flutter
- xservice_kit
- xservice_kit (0.0.1):
- Flutter - Flutter
DEPENDENCIES: DEPENDENCIES:
- flutter_boost (from `/Users/jidong/Documents/FlutterBoost/ios`) - Flutter (from `.symlinks/flutter/ios`)
- xservice_kit (from `/Users/jidong/.pub-cache/hosted/pub.dartlang.org/xservice_kit-0.0.26/ios`) - flutter_boost (from `/Users/yujie/Desktop/flutter/flutter_boost/ios`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- Flutter
EXTERNAL SOURCES: EXTERNAL SOURCES:
Flutter:
:path: ".symlinks/flutter/ios"
flutter_boost: flutter_boost:
:path: "/Users/jidong/Documents/FlutterBoost/ios" :path: "/Users/yujie/Desktop/flutter/flutter_boost/ios"
xservice_kit:
:path: "/Users/jidong/.pub-cache/hosted/pub.dartlang.org/xservice_kit-0.0.26/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
Flutter: fa617360ba1995d522016bfa59d68df27ac12e02 Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296
flutter_boost: 24249d12d924d858cf445a84949484bc08bca000 flutter_boost: 91c0b7bfc2f93528edc0658c8c539c595bd2d46f
xservice_kit: a86c64372b3e41e7d8e9b1a0b9139866680f525c
PODFILE CHECKSUM: 2a757a7bdc03b37a2814666652fdff1cf694243f PODFILE CHECKSUM: 2a757a7bdc03b37a2814666652fdff1cf694243f
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
...@@ -45,12 +44,12 @@ ...@@ -45,12 +44,12 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
...@@ -60,6 +59,7 @@ ...@@ -60,6 +59,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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; }; 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>"; }; 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>"; }; DF544AA42177253600931378 /* UIViewControllerDemo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIViewControllerDemo.m; sourceTree = "<group>"; };
...@@ -98,6 +98,8 @@ ...@@ -98,6 +98,8 @@
7BDD959A65220A7D194700AF /* Pods */ = { 7BDD959A65220A7D194700AF /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A1222E2FC45BEDD8C125BB85 /* Pods-Runner.debug.xcconfig */,
8F322762730D82C0AD58AC8A /* Pods-Runner.release.xcconfig */,
); );
name = Pods; name = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -105,7 +107,6 @@ ...@@ -105,7 +107,6 @@
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
3B80C3931E831B6300D905FE /* App.framework */, 3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEBA1CF902C7004384FC /* Flutter.framework */,
...@@ -200,7 +201,7 @@ ...@@ -200,7 +201,7 @@
TargetAttributes = { TargetAttributes = {
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = E83K84RE26; DevelopmentTeam = HH6T699A6Z;
}; };
}; };
}; };
...@@ -232,7 +233,6 @@ ...@@ -232,7 +233,6 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
...@@ -261,7 +261,7 @@ ...@@ -261,7 +261,7 @@
); );
inputPaths = ( inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/Flutter/Flutter.framework", "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
); );
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputPaths = ( outputPaths = (
...@@ -449,7 +449,7 @@ ...@@ -449,7 +449,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = E83K84RE26; DEVELOPMENT_TEAM = HH6T699A6Z;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
...@@ -461,7 +461,7 @@ ...@@ -461,7 +461,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = fleamarket.taobao.com.flutterBoostExample; PRODUCT_BUNDLE_IDENTIFIER = fleamarket.taobao3.com.flutterBoostExample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
REEXPORTED_LIBRARY_PATHS = ""; REEXPORTED_LIBRARY_PATHS = "";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
...@@ -474,7 +474,7 @@ ...@@ -474,7 +474,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = E83K84RE26; DEVELOPMENT_TEAM = HH6T699A6Z;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
...@@ -486,7 +486,7 @@ ...@@ -486,7 +486,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = fleamarket.taobao.com.flutterBoostExample; PRODUCT_BUNDLE_IDENTIFIER = fleamarket.taobao3.com.flutterBoostExample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
REEXPORTED_LIBRARY_PATHS = ""; REEXPORTED_LIBRARY_PATHS = "";
VERSIONING_SYSTEM = "apple-generic"; 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>BuildLocationStyle</key>
<string>UseTargetSettings</string>
</dict>
</plist>
<?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>
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "0"
version = "2.0">
</Bucket>
...@@ -11,8 +11,5 @@ ...@@ -11,8 +11,5 @@
@interface AppDelegate : FLBFlutterAppDelegate <UIApplicationDelegate> @interface AppDelegate : FLBFlutterAppDelegate <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end @end
...@@ -19,6 +19,12 @@ ...@@ -19,6 +19,12 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (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]; self.window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
...@@ -37,19 +43,18 @@ ...@@ -37,19 +43,18 @@
UITabBarController *tabVC = [[UITabBarController alloc] init]; UITabBarController *tabVC = [[UITabBarController alloc] init];
UINavigationController *rvc = [[UINavigationController alloc] initWithRootViewController:tabVC]; UINavigationController *rvc = [[UINavigationController alloc] initWithRootViewController:tabVC];
DemoRouter *router = [DemoRouter sharedRouter];
router.navigationController = rvc; router.navigationController = rvc;
tabVC.viewControllers = @[vc,fvc]; tabVC.viewControllers = @[vc,fvc];
self.window.rootViewController = rvc;
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:router
onStart:^(FlutterViewController *fvc) {
}];
self.window.rootViewController = rvc;
UIButton *nativeButton = [UIButton buttonWithType:UIButtonTypeCustom]; UIButton *nativeButton = [UIButton buttonWithType:UIButtonTypeCustom];
nativeButton.frame = CGRectMake(self.window.frame.size.width * 0.5 - 50, 200, 100, 45); nativeButton.frame = CGRectMake(self.window.frame.size.width * 0.5 - 50, 200, 100, 45);
......
...@@ -8,10 +8,12 @@ ...@@ -8,10 +8,12 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <flutter_boost/FLBPlatform.h> #import <flutter_boost/FlutterBoost.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@protocol FLBPlatform;
@interface DemoRouter : NSObject<FLBPlatform> @interface DemoRouter : NSObject<FLBPlatform>
@property (nonatomic,strong) UINavigationController *navigationController; @property (nonatomic,strong) UINavigationController *navigationController;
......
...@@ -21,26 +21,34 @@ ...@@ -21,26 +21,34 @@
return instance; return instance;
} }
- (void)openPage:(NSString *)name #pragma mark - Boost 2
params:(NSDictionary *)params - (void)open:(NSString *)name
animated:(BOOL)animated urlParams:(NSDictionary *)params
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion completion:(void (^)(BOOL))completion
{ {
BOOL animated = [exts[@"animated"] boolValue];
if([params[@"present"] boolValue]){ if([params[@"present"] boolValue]){
FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new; FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new;
[vc setName:name params:params]; [vc setName:name params:params];
[self.navigationController presentViewController:vc animated:animated completion:^{}]; [self.navigationController presentViewController:vc animated:animated completion:^{
if(completion) completion(YES);
}];
}else{ }else{
FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new; FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new;
[vc setName:name params:params]; [vc setName:name params:params];
[self.navigationController pushViewController:vc animated:animated]; [self.navigationController pushViewController:vc animated:animated];
if(completion) completion(YES);
} }
} }
- (void)close:(NSString *)uid
result:(NSDictionary *)result
- (void)closePage:(NSString *)uid animated:(BOOL)animated params:(NSDictionary *)params completion:(void (^)(BOOL))completion exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion
{ {
BOOL animated = [exts[@"animated"] boolValue];
animated = YES;
FLBFlutterViewContainer *vc = (id)self.navigationController.presentedViewController; FLBFlutterViewContainer *vc = (id)self.navigationController.presentedViewController;
if([vc isKindOfClass:FLBFlutterViewContainer.class] && [vc.uniqueIDString isEqual: uid]){ if([vc isKindOfClass:FLBFlutterViewContainer.class] && [vc.uniqueIDString isEqual: uid]){
[vc dismissViewControllerAnimated:animated completion:^{}]; [vc dismissViewControllerAnimated:animated completion:^{}];
...@@ -48,4 +56,27 @@ ...@@ -48,4 +56,27 @@
[self.navigationController popViewControllerAnimated:animated]; [self.navigationController popViewControllerAnimated:animated];
} }
} }
#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 @end
...@@ -3,14 +3,12 @@ ...@@ -3,14 +3,12 @@
// //
#import "GeneratedPluginRegistrant.h" #import "GeneratedPluginRegistrant.h"
#import <flutter_boost/FlutterBoostPlugin.h> #import <flutter_boost/BoostChannel.h>
#import <xservice_kit/XserviceKitPlugin.h>
@implementation GeneratedPluginRegistrant @implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry { + (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[FlutterBoostPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterBoostPlugin"]]; [BoostChannel registerWithRegistrar:[registry registrarForPlugin:@"BoostChannel"]];
[XserviceKitPlugin registerWithRegistrar:[registry registrarForPlugin:@"XserviceKitPlugin"]];
} }
@end @end
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#import "UIViewControllerDemo.h" #import "UIViewControllerDemo.h"
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#import "DemoRouter.h" #import "DemoRouter.h"
#import <flutter_boost/FlutterBoostPlugin.h>
@interface UIViewControllerDemo () @interface UIViewControllerDemo ()
...@@ -23,13 +23,20 @@ ...@@ -23,13 +23,20 @@
// Do any additional setup after loading the view from its nib. // Do any additional setup after loading the view from its nib.
} }
- (IBAction)pushFlutterPage:(id)sender { - (IBAction)pushFlutterPage:(id)sender {
[DemoRouter.sharedRouter openPage:@"first" params:@{} animated:YES completion:^(BOOL f){}]; [DemoRouter.sharedRouter open:@"first"
urlParams:@{}
exts:@{@"animated":@(YES)}
completion:^(BOOL f){
}];
} }
- (IBAction)present:(id)sender { - (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]; // [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
...@@ -20,18 +20,32 @@ class _MyAppState extends State<MyApp> { ...@@ -20,18 +20,32 @@ class _MyAppState extends State<MyApp> {
'first': (pageName, params, _) => FirstRouteWidget(), 'first': (pageName, params, _) => FirstRouteWidget(),
'second': (pageName, params, _) => SecondRouteWidget(), 'second': (pageName, params, _) => SecondRouteWidget(),
'tab': (pageName, params, _) => TabRouteWidget(), 'tab': (pageName, params, _) => TabRouteWidget(),
'flutterPage': (pageName, params, _) => FlutterRouteWidget(),
'flutterFragment': (pageName, params, _) => FragmentRouteWidget(params), 'flutterFragment': (pageName, params, _) => FragmentRouteWidget(params),
});
FlutterBoost.handleOnStartPage(); ///可以在native层通过 getContainerParams 来传递参数
'flutterPage': (pageName, params, _) {
print("flutterPage params:$params");
return FlutterRouteWidget();
},
});
} }
Map<String, WidgetBuilder> routes = {
"second": (BuildContext context) =>
SecondRouteWidget(),
};
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Flutter Boost example', title: 'Flutter Boost example',
builder: FlutterBoost.init(), builder: FlutterBoost.init(postPush: _onRoutePushed),
routes: routes,
home: Container()); home: Container());
} }
void _onRoutePushed(
String pageName, String uniqueId, Map params, Route route, Future _) {
}
} }
...@@ -12,7 +12,13 @@ class FirstRouteWidget extends StatelessWidget { ...@@ -12,7 +12,13 @@ class FirstRouteWidget extends StatelessWidget {
child: RaisedButton( child: RaisedButton(
child: Text('Open second route'), child: Text('Open second route'),
onPressed: () { onPressed: () {
FlutterBoost.singleton.openPage("second", {}, animated: true);
print("open second page!");
FlutterBoost.singleton.open("second").then((Map value){
print("did recieve second route result");
print("did recieve second route result $value");
});
}, },
), ),
), ),
...@@ -31,9 +37,11 @@ class SecondRouteWidget extends StatelessWidget { ...@@ -31,9 +37,11 @@ class SecondRouteWidget extends StatelessWidget {
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
// Navigate back to first route when tapped. // Navigate back to first route when tapped.
FlutterBoost.singleton.closePageForContext(context);
BoostContainerSettings settings = BoostContainer.of(context).settings;
FlutterBoost.singleton.close(settings.uniqueId,result: {"result":"data from second"});
}, },
child: Text('Go back!'), child: Text('Go back with result!'),
), ),
), ),
); );
...@@ -50,7 +58,7 @@ class TabRouteWidget extends StatelessWidget { ...@@ -50,7 +58,7 @@ class TabRouteWidget extends StatelessWidget {
body: Center( body: Center(
child: RaisedButton( child: RaisedButton(
onPressed: () { onPressed: () {
FlutterBoost.singleton.openPage("second", {}, animated: true); FlutterBoost.singleton.open("second");
}, },
child: Text('Open second route'), child: Text('Open second route'),
), ),
...@@ -60,6 +68,10 @@ class TabRouteWidget extends StatelessWidget { ...@@ -60,6 +68,10 @@ class TabRouteWidget extends StatelessWidget {
} }
class FlutterRouteWidget extends StatelessWidget { class FlutterRouteWidget extends StatelessWidget {
final String message;
FlutterRouteWidget({this.message});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
...@@ -72,7 +84,7 @@ class FlutterRouteWidget extends StatelessWidget { ...@@ -72,7 +84,7 @@ class FlutterRouteWidget extends StatelessWidget {
Container( Container(
margin: const EdgeInsets.only(top: 80.0), margin: const EdgeInsets.only(top: 80.0),
child: Text( child: Text(
"This is a flutter activity", message ?? "This is a flutter activity",
style: TextStyle(fontSize: 28.0, color: Colors.blue), style: TextStyle(fontSize: 28.0, color: Colors.blue),
), ),
alignment: AlignmentDirectional.center, alignment: AlignmentDirectional.center,
...@@ -87,8 +99,13 @@ class FlutterRouteWidget extends StatelessWidget { ...@@ -87,8 +99,13 @@ class FlutterRouteWidget extends StatelessWidget {
'open native page', 'open native page',
style: TextStyle(fontSize: 22.0, color: Colors.black), style: TextStyle(fontSize: 22.0, color: Colors.black),
)), )),
///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
///例如:sample://nativePage?aaa=bbb
onTap: () => onTap: () =>
FlutterBoost.singleton.openPage("sample://nativePage", {}), FlutterBoost.singleton.open("sample://nativePage", urlParams:{
"query": {"aaa": "bbb"}
}),
), ),
InkWell( InkWell(
child: Container( child: Container(
...@@ -99,8 +116,27 @@ class FlutterRouteWidget extends StatelessWidget { ...@@ -99,8 +116,27 @@ class FlutterRouteWidget extends StatelessWidget {
'open flutter page', 'open flutter page',
style: TextStyle(fontSize: 22.0, color: Colors.black), style: TextStyle(fontSize: 22.0, color: Colors.black),
)), )),
///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
///例如:sample://nativePage?aaa=bbb
onTap: () => onTap: () =>
FlutterBoost.singleton.openPage("sample://flutterPage", {}), FlutterBoost.singleton.open("sample://flutterPage", urlParams:{
"query": {"aaa": "bbb"}
}),
),
InkWell(
child: Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
color: Colors.yellow,
child: Text(
'push flutter widget',
style: TextStyle(fontSize: 22.0, color: Colors.black),
)),
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (_) => PushWidget()));
},
), ),
InkWell( InkWell(
child: Container( child: Container(
...@@ -112,7 +148,7 @@ class FlutterRouteWidget extends StatelessWidget { ...@@ -112,7 +148,7 @@ class FlutterRouteWidget extends StatelessWidget {
style: TextStyle(fontSize: 22.0, color: Colors.black), style: TextStyle(fontSize: 22.0, color: Colors.black),
)), )),
onTap: () => FlutterBoost.singleton onTap: () => FlutterBoost.singleton
.openPage("sample://flutterFragmentPage", {}), .open("sample://flutterFragmentPage"),
) )
], ],
), ),
...@@ -161,7 +197,7 @@ class FragmentRouteWidget extends StatelessWidget { ...@@ -161,7 +197,7 @@ class FragmentRouteWidget extends StatelessWidget {
style: TextStyle(fontSize: 22.0, color: Colors.black), style: TextStyle(fontSize: 22.0, color: Colors.black),
)), )),
onTap: () => onTap: () =>
FlutterBoost.singleton.openPage("sample://nativePage", {}), FlutterBoost.singleton.open("sample://nativePage"),
), ),
InkWell( InkWell(
child: Container( child: Container(
...@@ -173,7 +209,7 @@ class FragmentRouteWidget extends StatelessWidget { ...@@ -173,7 +209,7 @@ class FragmentRouteWidget extends StatelessWidget {
style: TextStyle(fontSize: 22.0, color: Colors.black), style: TextStyle(fontSize: 22.0, color: Colors.black),
)), )),
onTap: () => onTap: () =>
FlutterBoost.singleton.openPage("sample://flutterPage", {}), FlutterBoost.singleton.open("sample://flutterPage"),
), ),
InkWell( InkWell(
child: Container( child: Container(
...@@ -185,10 +221,53 @@ class FragmentRouteWidget extends StatelessWidget { ...@@ -185,10 +221,53 @@ class FragmentRouteWidget extends StatelessWidget {
style: TextStyle(fontSize: 22.0, color: Colors.black), style: TextStyle(fontSize: 22.0, color: Colors.black),
)), )),
onTap: () => FlutterBoost.singleton onTap: () => FlutterBoost.singleton
.openPage("sample://flutterFragmentPage", {}), .open("sample://flutterFragmentPage"),
) )
], ],
), ),
); );
} }
} }
class PushWidget extends StatefulWidget {
@override
_PushWidgetState createState() => _PushWidgetState();
}
class _PushWidgetState extends State<PushWidget> {
VoidCallback _backPressedListenerUnsub;
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
// if (_backPressedListenerUnsub == null) {
// _backPressedListenerUnsub =
// BoostContainer.of(context).addBackPressedListener(() {
// if (BoostContainer.of(context).onstage &&
// ModalRoute.of(context).isCurrent) {
// Navigator.pop(context);
// }
// });
// }
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
_backPressedListenerUnsub?.call();
}
@override
Widget build(BuildContext context) {
return FlutterRouteWidget(message:"Pushed Widget");
}
}
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),
);
}
}
...@@ -14,8 +14,6 @@ environment: ...@@ -14,8 +14,6 @@ environment:
dependencies: dependencies:
xservice_kit: ^0.0.18
flutter: flutter:
sdk: flutter sdk: flutter
......
/*
* 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"
@interface FLBFlutterEngineOld()
@property (nonatomic,strong) FLBFlutterViewControllerAdaptor *viewController;
@end
@implementation FLBFlutterEngineOld
- (instancetype)init
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if (self = [super init]) {
_viewController = [FLBFlutterViewControllerAdaptor new];
[_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];
}
@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;
@end
NS_ASSUME_NONNULL_END
...@@ -21,17 +21,12 @@ ...@@ -21,17 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "FLBFlutterViewProvider.h" #import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
#if RELEASE_1_0 @interface BoostChannel : NSObject<FlutterPlugin>
@interface FLBFlutterEngine : NSObject<FLBFlutterViewProvider>
@end @end
#endif
NS_ASSUME_NONNULL_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
...@@ -22,78 +22,56 @@ ...@@ -22,78 +22,56 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#import "FLBFlutterViewControllerAdaptor.h" #import "FLBCollectionHelper.h"
@interface FLBFlutterViewControllerAdaptor ()
@end
@implementation FLBFlutterViewControllerAdaptor
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// Do any additional setup after loading the view.
}
@implementation FLBCollectionHelper
- (void)viewWillAppear:(BOOL)animated + (NSDictionary *)deepCopyNSDictionary:(NSDictionary *)origin filter:(FLBCollectionFilter)filter
{ {
//Left blank intentionally. if(origin.count < 1) return origin;
} NSMutableDictionary *copyed = [NSMutableDictionary new];
[origin enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
- (void)viewDidAppear:(BOOL)animated
{ //Filter: Do not include invalid things
//Left blank intentionally. if(filter && !filter(obj)) return;
}
if([obj isKindOfClass: NSDictionary.class]){
- (void)viewWillDisappear:(BOOL)animated copyed[key] = [self deepCopyNSDictionary:obj filter:filter];
{ }else if([obj isKindOfClass:NSArray.class]){
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil]; copyed[key] = [self deepCopyNSArray:obj filter:filter];
//miss super call intentionally. }else{
} copyed[key] = obj;
}
}];
- (void)viewDidDisappear:(BOOL)animated return copyed;
{
//Miss super call intentionally.
[[[UIApplication sharedApplication] keyWindow] endEditing:YES];
} }
+ (NSArray *)deepCopyNSArray:(NSArray *)origin filter:(FLBCollectionFilter)filter
- (void)boost_viewWillAppear:(BOOL)animated
{ {
[super viewWillAppear:animated]; if(origin.count < 1) return origin;
} NSMutableArray *copyed = [NSMutableArray new];
[origin enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
- (void)boost_viewDidAppear:(BOOL)animated //Filter: Do not include invalid things.
{ if(filter && !filter(obj)) return;
[super viewDidAppear:animated];
}
- (void)boost_viewWillDisappear:(BOOL)animated id nObj = nil;
{
[super viewWillDisappear:animated];
}
- (void)boost_viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
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;
}
- (UIEdgeInsets)paddingEdgeInsets{ if (nObj) {
UIEdgeInsets edgeInsets = UIEdgeInsetsZero; [copyed addObject:nObj];
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 { return copyed;
//Override this to avoid unnecessary splash Screen.
} }
@end @end
...@@ -25,12 +25,9 @@ ...@@ -25,12 +25,9 @@
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterAppDelegate : FlutterAppDelegate @interface FLBFlutterAppDelegate : FlutterAppDelegate
// Returns the key window's rootViewController, if it's a FlutterViewController. // Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil. // Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController; - (FlutterViewController*)rootFlutterViewController;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -23,13 +23,12 @@ ...@@ -23,13 +23,12 @@
*/ */
#import "FLBFlutterAppDelegate.h" #import "FLBFlutterAppDelegate.h"
#import "FLBFlutterApplication.h" #import "FlutterBoostPlugin_private.h"
@implementation FLBFlutterAppDelegate @implementation FLBFlutterAppDelegate
// Returns the key window's rootViewController, if it's a FlutterViewController. // Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil. // Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController { - (FlutterViewController*)rootFlutterViewController {
return FLBFlutterApplication.sharedApplication.flutterViewController; return FlutterBoostPlugin.sharedInstance.application.flutterViewController;
} }
@end @end
...@@ -21,27 +21,30 @@ ...@@ -21,27 +21,30 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#import "FLBPlatform.h" #import "FLBPlatform.h"
#import "FlutterBoost.h" #import "FlutterBoost.h"
#import "FLBFlutterProvider.h"
#import "FLBFlutterContainer.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterApplication : NSObject @protocol FLBFlutterApplicationInterface <NSObject>
@property (nonatomic,strong) id<FLBPlatform> platform; @property (nonatomic,strong) id<FLBPlatform> platform;
#pragma mark - Getters - (id<FLBFlutterProvider>)flutterProvider;
+ (FLBFlutterApplication *)sharedApplication;
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform - (void)startFlutterWithPlatform:(id<FLBPlatform>)platform
onStart:(void (^)(FlutterViewController *))callback; onStart:(void (^)(id<FlutterBinaryMessenger,
FlutterTextureRegistry,
FlutterPluginRegistry> engine))callback;
- (FlutterViewController *)flutterViewController; - (FlutterViewController *)flutterViewController;
#pragma mark - Container Management #pragma mark - Container Management
- (BOOL)contains:(FLBFlutterViewContainer *)vc; - (BOOL)contains:(id<FLBFlutterContainer>)vc;
- (void)addUniqueViewController:(FLBFlutterViewContainer *)vc; - (void)addUniqueViewController:(id<FLBFlutterContainer>)vc;
- (void)removeViewController:(FLBFlutterViewContainer *)vc; - (void)removeViewController:(id<FLBFlutterContainer>)vc;
- (BOOL)isTop:(NSString *)pageId; - (BOOL)isTop:(NSString *)pageId;
#pragma mark - App Control #pragma mark - App Control
...@@ -49,6 +52,29 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -49,6 +52,29 @@ NS_ASSUME_NONNULL_BEGIN
- (void)resume; - (void)resume;
- (void)inactive; - (void)inactive;
- (BOOL)isRunning; - (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 NS_ASSUME_NONNULL_END
...@@ -21,17 +21,13 @@ ...@@ -21,17 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "FLBFlutterViewProvider.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@protocol FLBFlutterContainer <NSObject>
@interface FLBViewProviderFactory : NSObject - (NSString *)name;
- (NSDictionary *)params;
- (id<FLBFlutterViewProvider>)createViewProvider; - (NSString *)uniqueIDString;
- (void)setName:(NSString *)name params:(NSDictionary *)params;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -23,17 +23,15 @@ ...@@ -23,17 +23,15 @@
*/ */
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "FLBFlutterViewContainer.h" #import "FLBFlutterContainer.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterContainerManager : NSObject
@interface FLBFlutterViewContainerManager : NSObject
- (NSString *)peak; - (NSString *)peak;
- (void)addUnique:(FLBFlutterViewContainer *)vc; - (void)addUnique:(id<FLBFlutterContainer>)vc;
- (void)remove:(FLBFlutterViewContainer *)vc; - (void)remove:(id<FLBFlutterContainer>)vc;
- (BOOL)contains:(FLBFlutterViewContainer *)vc; - (BOOL)contains:(id<FLBFlutterContainer>)vc;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -22,14 +22,14 @@ ...@@ -22,14 +22,14 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#import "FLBFlutterViewContainerManager.h" #import "FLBFlutterContainerManager.h"
@interface FLBFlutterViewContainerManager() @interface FLBFlutterContainerManager()
@property (nonatomic,strong) NSMutableArray *idStk; @property (nonatomic,strong) NSMutableArray *idStk;
@property (nonatomic,strong) NSMutableDictionary *existedID; @property (nonatomic,strong) NSMutableDictionary *existedID;
@end @end
@implementation FLBFlutterViewContainerManager @implementation FLBFlutterContainerManager
- (instancetype)init - (instancetype)init
{ {
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
return self; return self;
} }
- (BOOL)contains:(FLBFlutterViewContainer *)vc - (BOOL)contains:(id<FLBFlutterContainer>)vc
{ {
if (vc) { if (vc) {
return _existedID[vc.uniqueIDString]?YES:NO; return _existedID[vc.uniqueIDString]?YES:NO;
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
return NO; return NO;
} }
- (void)addUnique:(FLBFlutterViewContainer *)vc - (void)addUnique:(id<FLBFlutterContainer>)vc
{ {
if (vc) { if (vc) {
if(!_existedID[vc.uniqueIDString]){ if(!_existedID[vc.uniqueIDString]){
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
} }
} }
- (void)remove:(FLBFlutterViewContainer *)vc - (void)remove:(id<FLBFlutterContainer>)vc
{ {
if (vc) { if (vc) {
[_existedID removeObjectForKey:vc.uniqueIDString]; [_existedID removeObjectForKey:vc.uniqueIDString];
......
...@@ -25,19 +25,19 @@ ...@@ -25,19 +25,19 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class FlutterViewController; @class FlutterViewController;
@class FlutterEngine;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@protocol FLBFlutterProvider <NSObject>
#define RELEASE_1_0 0
@protocol FLBFlutterViewProvider <NSObject>
@required @required
- (FlutterViewController *)viewController; - (FlutterEngine *)engine;
- (void)atacheToViewController:(FlutterViewController *)vc;
- (void)detach;
- (void)prepareEngineIfNeeded;
- (void)pause; - (void)pause;
- (void)resume; - (void)resume;
- (void)inactive; - (void)inactive;
- (void)didEnterBackground;
- (void)willEnterForeground;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -25,18 +25,19 @@ ...@@ -25,18 +25,19 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@protocol FLBPlatform <NSObject> @protocol FLBPlatform <NSObject>
@optional
- (NSString *)entryForDart;
@required @required
- (void)openPage:(NSString *)name - (void)open:(NSString *)url
params:(NSDictionary *)params urlParams:(NSDictionary *)urlParams
animated:(BOOL)animated exts:(NSDictionary *)exts
completion:(void (^)(BOOL finished))completion; completion:(void (^)(BOOL finished))completion;
- (void)closePage:(NSString *)uid - (void)close:(NSString *)uid
animated:(BOOL)animated result:(NSDictionary *)result
params:(NSDictionary *)params exts:(NSDictionary *)exts
completion:(void (^)(BOOL finished))completion; completion:(void (^)(BOOL finished))completion;
@end @end
NS_ASSUME_NONNULL_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 *)vcId resultData:(NSDictionary *)resultData;
- (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"
@interface FLBResultMediator()
@property (nonatomic,strong) NSMutableDictionary *handlers;
@end
@implementation FLBResultMediator
- (instancetype)init
{
if (self = [super init]) {
_handlers = [NSMutableDictionary new];
}
return self;
}
- (void)onResultForKey:(NSString *)vcId
resultData:(NSDictionary *)resultData
{
if(!vcId) return;
NSString *key = vcId;
if(_handlers[key]){
FLBPageResultHandler handler = _handlers[key];
handler(key,resultData);
[_handlers removeObjectForKey: key];
}
}
- (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
* 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>
#ifndef FLBTypes_h
#define FLBTypes_h
typedef void (^FLBEventListener) (NSString *name ,
NSDictionary *arguments);
typedef void (^FLBVoidCallback)(void);
#endif /* FLBTypes_h */
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
#define FlutterBoost_h #define FlutterBoost_h
#import "FlutterBoostPlugin.h" #import "FlutterBoostPlugin.h"
#import "FLBFlutterViewContainer.h"
#import "FLBPlatform.h"
#import "FLBFlutterAppDelegate.h" #import "FLBFlutterAppDelegate.h"
#import "FLBFlutterViewContainer.h"
#import "FLBTypes.h"
#endif /* FlutterBoost_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,24 +23,27 @@ ...@@ -23,24 +23,27 @@
*/ */
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#import "FLBFlutterViewContainer.h"
#import "FLBPlatform.h" #import "FLBPlatform.h"
#import "FLBTypes.h"
typedef FLBFlutterViewContainer * (^FLBPageBuilder)(NSString *name,NSDictionary *params);
@interface FlutterBoostPlugin : NSObject<FlutterPlugin> @interface FlutterBoostPlugin : NSObject<FlutterPlugin>
#pragma mark - Initializer #pragma mark - Initializer
+ (instancetype)sharedInstance; + (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. #pragma mark - Some properties.
- (BOOL)isRunning; - (BOOL)isRunning;
- (FlutterViewController *)currentViewController; - (FlutterViewController *)currentViewController;
#pragma mark - handing vc result. #pragma mark - broadcast event to/from flutter
- (void)onResultForKey:(NSString *)vcId resultData:(NSDictionary *)resultData; - (void)sendEvent:(NSString *)eventName
- (void)setResultHandler:(void (^)(NSString *, NSDictionary *))handler forKey:(NSString *)vcid; arguments:(NSDictionary *)arguments;
- (void)removeHandlerForKey:(NSString *)vcid;
- (FLBVoidCallback)addEventListener:(FLBEventListener)listner
forName:(NSString *)name;
@end @end
...@@ -23,15 +23,83 @@ ...@@ -23,15 +23,83 @@
*/ */
#import "FlutterBoostPlugin.h" #import "FlutterBoostPlugin.h"
#import "FLBFlutterApplication.h" #import "FlutterBoostPlugin_private.h"
#import "FLBResultMediator.h" #import "FLBFactory.h"
#import "BoostMessageChannel.h"
#import "FLBCollectionHelper.h"
#define NSNull2Nil(_x_) if([_x_ isKindOfClass: NSNull.class]) _x_ = nil;
@interface FlutterBoostPlugin() @interface FlutterBoostPlugin()
@property (nonatomic,strong) FLBResultMediator *resultMediator;
@end @end
@implementation FlutterBoostPlugin @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 + (instancetype)sharedInstance
{ {
static id _instance = nil; static id _instance = nil;
...@@ -43,62 +111,57 @@ ...@@ -43,62 +111,57 @@
return _instance; return _instance;
} }
- (instancetype)init
{
if (self = [super init]) {
_resultMediator = [FLBResultMediator new];
}
return self; - (id<FLBFlutterApplicationInterface>)application
{
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 { - (id<FLBAbstractFactory>)factory
if ([@"getPlatformVersion" isEqualToString:call.method]) { {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); return _factory;
} else {
result(FlutterMethodNotImplemented);
}
} }
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform - (void)startFlutterWithPlatform:(id<FLBPlatform>)platform
onStart:(void (^)(FlutterViewController *))callback; onStart:(void (^)(id<FlutterBinaryMessenger,
FlutterTextureRegistry,
FlutterPluginRegistry> engine))callback;
{ {
[FLBFlutterApplication.sharedApplication startFlutterWithPlatform:platform static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
self->_factory = FLBFactory.new;
self->_application = [self->_factory createApplication:platform];
[self->_application startFlutterWithPlatform:platform
onStart:callback]; onStart:callback];
});
} }
- (BOOL)isRunning - (BOOL)isRunning
{ {
return [FLBFlutterApplication.sharedApplication isRunning]; return [self.application isRunning];
} }
- (FlutterViewController *)currentViewController - (FlutterViewController *)currentViewController
{ {
return [[FLBFlutterApplication sharedApplication] flutterViewController]; return [self.application flutterViewController];
} }
- (void)onResultForKey:(NSString *)vcId resultData:(NSDictionary *)resultData
{
[_resultMediator onResultForKey:vcId resultData:resultData];
}
- (void)setResultHandler:(void (^)(NSString *, NSDictionary *))handler forKey:(NSString *)vcid #pragma mark - broadcast event to/from flutter
- (void)sendEvent:(NSString *)eventName
arguments:(NSDictionary *)arguments
{ {
[_resultMediator setResultHandler:handler forKey:vcid]; [BoostMessageChannel sendEvent:eventName
arguments:arguments];
} }
- (void)removeHandlerForKey:(NSString *)vcid - (FLBVoidCallback)addEventListener:(FLBEventListener)listner
forName:(NSString *)name
{ {
[_resultMediator removeHandlerForKey:vcid]; return [BoostMessageChannel addEventListener:listner
forName:name];
} }
@end @end
...@@ -21,20 +21,19 @@ ...@@ -21,20 +21,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN #import "FLBFlutterApplicationInterface.h"
#import "FLBAbstractFactory.h"
@interface FlutterBoostConfig : NSObject #import "FlutterBoostPlugin.h"
#pragma mark - Store first open page @interface FlutterBoostPlugin(){
//There are some cases first page messages could be lost. id<FLBFlutterApplicationInterface> _application;
//So we store the first page info for later usage. id<FLBAbstractFactory> _factory;
+ (instancetype)sharedInstance; }
@property (nonatomic,copy) NSString *fPagename; @property (nonatomic,strong) FlutterMethodChannel *methodChannel;
@property (nonatomic,copy) NSString *fPageId; @property (nonatomic,copy) NSString *fPageId;
@property (nonatomic,copy) NSString *fPagename;
@property (nonatomic,strong) NSDictionary *fParams; @property (nonatomic,strong) NSDictionary *fParams;
- (BOOL)firstView; - (id<FLBFlutterApplicationInterface>)application;
- (id<FLBAbstractFactory>)factory;
@end @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
...@@ -23,12 +23,11 @@ ...@@ -23,12 +23,11 @@
*/ */
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "FLBFlutterViewProvider.h" #import "FLBAbstractFactory.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterEngineOld : NSObject<FLBFlutterViewProvider> @interface FLBFactory : NSObject<FLBAbstractFactory>
@end @end
NS_ASSUME_NONNULL_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 "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
/*
* 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 <Flutter/Flutter.h>
#import "FLBPlatform.h"
#import "FLBFlutterProvider.h"
#import "FLBFlutterApplicationInterface.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterApplication : NSObject<FLBFlutterApplicationInterface>
@property (nonatomic,strong) id<FLBPlatform> platform;
@end
NS_ASSUME_NONNULL_END
...@@ -24,17 +24,15 @@ ...@@ -24,17 +24,15 @@
#import "FLBFlutterApplication.h" #import "FLBFlutterApplication.h"
#import "FlutterBoost.h" #import "FlutterBoost.h"
#import "FLBFlutterViewContainerManager.h" #import "FLBFlutterContainerManager.h"
#import "FLBViewProviderFactory.h" #import "FLBFlutterEngine.h"
@interface FLBFlutterApplication() @interface FLBFlutterApplication()
@property (nonatomic,strong) FLBFlutterViewContainerManager *manager; @property (nonatomic,strong) FLBFlutterContainerManager *manager;
@property (nonatomic,strong) id<FLBFlutterViewProvider> viewProvider; @property (nonatomic,strong) id<FLBFlutterProvider> viewProvider;
@property (nonatomic,strong) NSMutableDictionary *pageBuilders;
@property (nonatomic,copy) FLBPageBuilder defaultPageBuilder;
@property (nonatomic,assign) BOOL isRendering;
@property (nonatomic,assign) BOOL isRunning; @property (nonatomic,assign) BOOL isRunning;
@property (nonatomic,strong) NSMutableDictionary *pageResultCallbacks;
@property (nonatomic,strong) NSMutableDictionary *callbackCache;
@end @end
...@@ -55,32 +53,47 @@ ...@@ -55,32 +53,47 @@
return _isRunning; return _isRunning;
} }
- (id)flutterProvider
{
return _viewProvider;
}
- (void)startFlutterWithPlatform:(id<FLBPlatform>)platform - (void)startFlutterWithPlatform:(id<FLBPlatform>)platform
onStart:(void (^)(FlutterViewController * _Nonnull))callback onStart:(void (^)(id<FlutterBinaryMessenger,FlutterTextureRegistry,FlutterPluginRegistry> _Nonnull))callback
{ {
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
self.platform = platform; self.platform = platform;
self.viewProvider = [[FLBViewProviderFactory new] createViewProvider]; self.viewProvider = [[FLBFlutterEngine alloc] initWithPlatform:platform];
[self.viewProvider resume];
self.isRendering = YES;
self.isRunning = YES; self.isRunning = YES;
if(callback) callback(self.viewProvider.viewController); if(callback) callback(self.viewProvider.engine);
}); });
} }
- (instancetype)init - (instancetype)init
{ {
if (self = [super init]) { if (self = [super init]) {
_manager = [FLBFlutterViewContainerManager new]; _manager = [FLBFlutterContainerManager new];
_pageBuilders = [NSMutableDictionary 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; return self;
} }
- (void)dealloc - (void)dealloc
{ {
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
} }
- (UIView *)flutterView - (UIView *)flutterView
...@@ -88,30 +101,30 @@ ...@@ -88,30 +101,30 @@
return [self flutterViewController].view; return [self flutterViewController].view;
} }
- (FlutterViewController *)flutterViewController - (void)applicationDidEnterBackground:(UIApplication *)application {
{ [self.viewProvider didEnterBackground];
return self.viewProvider.viewController;
} }
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self.viewProvider willEnterForeground];
}
- (BOOL)contains:(FLBFlutterViewContainer *)vc - (BOOL)contains:(id<FLBFlutterContainer>)vc
{ {
return [_manager contains:vc]; return [_manager contains:vc];
} }
- (void)addUniqueViewController:(FLBFlutterViewContainer *)vc - (void)addUniqueViewController:(id<FLBFlutterContainer>)vc
{ {
return [_manager addUnique:vc]; return [_manager addUnique:vc];
} }
- (void)removeViewController:(FLBFlutterViewContainer *)vc - (void)removeViewController:(id<FLBFlutterContainer>)vc
{ {
return [_manager remove:vc]; return [_manager remove:vc];
} }
- (BOOL)isTop:(NSString *)pageId - (BOOL)isTop:(NSString *)pageId
{ {
return [_manager.peak isEqual:pageId]; return [_manager.peak isEqual:pageId];
...@@ -119,22 +132,12 @@ ...@@ -119,22 +132,12 @@
- (void)pause - (void)pause
{ {
if(!_isRendering) return;
[self.viewProvider pause]; [self.viewProvider pause];
_isRendering = NO;
} }
- (void)resume - (void)resume
{ {
if(_isRendering) return;
[self.viewProvider resume]; [self.viewProvider resume];
_isRendering = YES;
} }
- (void)inactive - (void)inactive
...@@ -142,6 +145,70 @@ ...@@ -142,6 +145,70 @@
[self.viewProvider inactive]; [self.viewProvider inactive];
} }
- (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
{
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 @end
...@@ -23,12 +23,11 @@ ...@@ -23,12 +23,11 @@
*/ */
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "FLBFlutterProvider.h"
#import "FLBPlatform.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterEngine : NSObject<FLBFlutterProvider>
@protocol FLBViewControllerResultHandler <NSObject> - (instancetype)initWithPlatform:(id<FLBPlatform>)platform;
@required
- (void)onRecievedResult:(NSDictionary *)resultData forKey:(NSString *)key;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -24,34 +24,39 @@ ...@@ -24,34 +24,39 @@
#import "FLBFlutterEngine.h" #import "FLBFlutterEngine.h"
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#import "FLBFlutterViewControllerAdaptor.h" #import "FLBFlutterViewContainer.h"
#import "BoostMessageChannel.h"
#if RELEASE_1_0
@interface FLBFlutterEngine() @interface FLBFlutterEngine()
@property (nonatomic,strong) FLBFlutterViewControllerAdaptor *viewController;
@property (nonatomic,strong) FlutterEngine *engine; @property (nonatomic,strong) FlutterEngine *engine;
@property (nonatomic,strong) FLBFlutterViewContainer *dummy;
@end @end
@implementation FLBFlutterEngine @implementation FLBFlutterEngine
- (instancetype)init - (instancetype)initWithPlatform:(id<FLBPlatform>)platform
{ {
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks" #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if (self = [super init]) { if (self = [super init]) {
_engine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil]; _engine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];
if(platform &&
[platform respondsToSelector: @selector(entryForDart)] &&
platform.entryForDart){
[_engine runWithEntrypoint:platform.entryForDart];
}else{
[_engine runWithEntrypoint:nil]; [_engine runWithEntrypoint:nil];
_viewController = [[FLBFlutterViewControllerAdaptor alloc] initWithEngine:_engine }
_dummy = [[FLBFlutterViewContainer alloc] initWithEngine:_engine
nibName:nil nibName:nil
bundle:nil]; bundle:nil];
[_viewController view];
Class clazz = NSClassFromString(@"GeneratedPluginRegistrant"); Class clazz = NSClassFromString(@"GeneratedPluginRegistrant");
if (clazz) { if (clazz) {
if ([clazz respondsToSelector:NSSelectorFromString(@"registerWithRegistry:")]) { if ([clazz respondsToSelector:NSSelectorFromString(@"registerWithRegistry:")]) {
[clazz performSelector:NSSelectorFromString(@"registerWithRegistry:") [clazz performSelector:NSSelectorFromString(@"registerWithRegistry:")
withObject:_viewController]; withObject:_engine];
} }
} }
} }
...@@ -60,23 +65,21 @@ ...@@ -60,23 +65,21 @@
#pragma clang diagnostic pop #pragma clang diagnostic pop
} }
- (FlutterViewController *)viewController - (instancetype)init
{ {
return _viewController; return [self initWithPlatform:nil];
} }
- (void)pause - (void)pause
{ {
//TODO: [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; [[_engine lifecycleChannel] sendMessage:@"AppLifecycleState.paused"];
[self.viewController boost_viewWillDisappear:NO]; [self detach];
[self.viewController boost_viewDidDisappear:NO];
} }
- (void)resume - (void)resume
{ {
//TODO: [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"]; [[_engine lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"];
[self.viewController boost_viewWillAppear:NO]; [(FLBFlutterViewContainer *)_engine.viewController surfaceUpdated:YES];
[self.viewController boost_viewDidAppear:NO];
} }
- (void)inactive - (void)inactive
...@@ -84,6 +87,43 @@ ...@@ -84,6 +87,43 @@
[[_engine lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; [[_engine lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"];
} }
- (void)didEnterBackground
{
[BoostMessageChannel sendEvent:@"background"
arguments:nil];
}
- (void)willEnterForeground
{
[BoostMessageChannel sendEvent:@"foreground"
arguments:nil];
}
- (FlutterEngine *)engine
{
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 @end
#endif
...@@ -22,10 +22,11 @@ ...@@ -22,10 +22,11 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
//Generated by AIOCodeGen do not edit! #import <Foundation/Foundation.h>
#import "FlutterServiceTemplate.h" #import <Flutter/Flutter.h>
@interface Service_NavigationService : NSObject #import "FLBTypes.h"
+ (FlutterServiceTemplate *)service;
@interface BoostMessageChannel : NSObject
+ (void)onNativePageResult:(void (^)(NSNumber *))result uniqueId:(NSString *)uniqueId key:(NSString *)key resultData:(NSDictionary *)resultData params:(NSDictionary *)params; + (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)didShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId;
...@@ -34,4 +35,15 @@ ...@@ -34,4 +35,15 @@
+ (void)didDisappearPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId; + (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)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; + (void)willDeallocPageContainer:(void (^)(NSNumber *))result pageName:(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 @end
...@@ -21,29 +21,68 @@ ...@@ -21,29 +21,68 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#import "BoostMessageChannel.h"
#import "FlutterBoostPlugin_private.h"
//Generated by AIOCodeGen. Do not edit! @implementation BoostMessageChannel
#import "Service_NavigationService.h" + (NSMutableDictionary *)lists{
#import "ServiceGateway.h" static NSMutableDictionary *_list = nil;
#import "FlutterServiceTemplate.h"
@implementation Service_NavigationService
+ (FlutterServiceTemplate *)service
{
static id _instance = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
_instance = [[FlutterServiceTemplate alloc] initWithName:@"NavigationService"]; _list = [NSMutableDictionary new];
}); });
return _list;
}
return _instance; + (FlutterMethodChannel *)methodChannel
{
return FlutterBoostPlugin.sharedInstance.methodChannel;
} }
+ (void)load{ + (void)sendEvent:(NSString *)eventName
[[ServiceGateway sharedInstance] addService:[self service]]; 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 + (void)onNativePageResult:(void (^)(NSNumber *))result uniqueId:(NSString *)uniqueId key:(NSString *)key resultData:(NSDictionary *)resultData params:(NSDictionary *)params
{ {
...@@ -52,7 +91,7 @@ ...@@ -52,7 +91,7 @@
if(key) tmp[@"key"] = key; if(key) tmp[@"key"] = key;
if(resultData) tmp[@"resultData"] = resultData; if(resultData) tmp[@"resultData"] = resultData;
if(params) tmp[@"params"] = params; 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) { if (result) {
result(tTesult); result(tTesult);
} }
...@@ -65,7 +104,7 @@ ...@@ -65,7 +104,7 @@
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId; 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) { if (result) {
result(tTesult); result(tTesult);
} }
...@@ -78,7 +117,7 @@ ...@@ -78,7 +117,7 @@
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId; 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) { if (result) {
result(tTesult); result(tTesult);
} }
...@@ -91,7 +130,7 @@ ...@@ -91,7 +130,7 @@
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId; 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) { if (result) {
result(tTesult); result(tTesult);
} }
...@@ -104,7 +143,7 @@ ...@@ -104,7 +143,7 @@
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId; 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) { if (result) {
result(tTesult); result(tTesult);
} }
...@@ -117,11 +156,15 @@ ...@@ -117,11 +156,15 @@
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId; 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) { if (result) {
result(tTesult); 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 + (void)willDeallocPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
...@@ -130,11 +173,15 @@ ...@@ -130,11 +173,15 @@
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId; 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) { if (result) {
result(tTesult); result(tTesult);
} }
}]; }];
[FlutterBoostPlugin.sharedInstance.application willDeallocPageContainer:pageName
params:params
uniqueId: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.
*/
// Generated by AIOCodeGen do not edit!
#import "FlutterServiceCallHandler.h"
@interface NavigationService_closePage : FlutterServiceCallHandler
@end
\ 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.
*/
//Generated by AIOCodeGen.
#import "ServiceGateway.h"
#import "NavigationService_closePage.h"
#import "FLBFlutterApplication.h"
@implementation NavigationService_closePage
- (void)onCall:(void (^)(BOOL))result
uniqueId:(NSString *)uniqueId
pageName:(NSString *)pageName
params:(NSDictionary *)params
animated:(NSNumber *)animated
{
//Add your handler code here!
[[FLBFlutterApplication sharedApplication].platform closePage:uniqueId
animated:animated.boolValue
params:params
completion:^(BOOL finished) {
if(result) result(finished);
}];
}
#pragma mark - Do not edit these method.
- (void)__flutter_p_handler_closePage:(NSDictionary *)args result:(void (^)(BOOL))result {
[self onCall:result uniqueId:args[@"uniqueId"] pageName:args[@"pageName"] params:args[@"params"] animated:args[@"animated"]];
}
+ (void)load{
[[ServiceGateway sharedInstance] registerHandler:[NavigationService_closePage new]];
}
- (NSString *)returnType
{
return @"BOOL";
}
- (NSString *)service
{
return @"NavigationService";
}
@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.
*/
// Generated by AIOCodeGen do not edit!
#import "FlutterServiceCallHandler.h"
@interface NavigationService_onFlutterPageResult : FlutterServiceCallHandler
@end
\ 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.
*/
//Generated by AIOCodeGen.
#import "ServiceGateway.h"
#import "NavigationService_onFlutterPageResult.h"
#import "FlutterBoost.h"
@implementation NavigationService_onFlutterPageResult
- (void)onCall:(void (^)(BOOL))result uniqueId:(NSString *)uniqueId key:(NSString *)key resultData:(NSDictionary *)resultData params:(NSDictionary *)params
{
//Add your handler code here!
[FlutterBoostPlugin.sharedInstance onResultForKey:key
resultData:resultData];
}
#pragma mark - Do not edit these method.
- (void)__flutter_p_handler_onFlutterPageResult:(NSDictionary *)args result:(void (^)(BOOL))result {
[self onCall:result uniqueId:args[@"uniqueId"] key:args[@"key"] resultData:args[@"resultData"] params:args[@"params"]];
}
+ (void)load{
[[ServiceGateway sharedInstance] registerHandler:[NavigationService_onFlutterPageResult new]];
}
- (NSString *)returnType
{
return @"BOOL";
}
- (NSString *)service
{
return @"NavigationService";
}
@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.
*/
// Generated by AIOCodeGen do not edit!
#import "FlutterServiceCallHandler.h"
@interface NavigationService_onShownContainerChanged : FlutterServiceCallHandler
@end
\ 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.
*/
//Generated by AIOCodeGen.
#import "ServiceGateway.h"
#import "NavigationService_onShownContainerChanged.h"
@implementation NavigationService_onShownContainerChanged
- (void)onCall:(void (^)(BOOL))result newName:(NSString *)newName oldName:(NSString *)oldName params:(NSDictionary *)params
{
if(newName){
[NSNotificationCenter.defaultCenter postNotificationName:@"flutter_boost_container_showed"
object:newName];
}
}
#pragma mark - Do not edit these method.
- (void)__flutter_p_handler_onShownContainerChanged:(NSDictionary *)args result:(void (^)(BOOL))result {
[self onCall:result newName:args[@"newName"] oldName:args[@"oldName"] params:args[@"params"]];
}
+ (void)load{
[[ServiceGateway sharedInstance] registerHandler:[NavigationService_onShownContainerChanged new]];
}
- (NSString *)returnType
{
return @"BOOL";
}
- (NSString *)service
{
return @"NavigationService";
}
@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.
*/
// Generated by AIOCodeGen do not edit!
#import "FlutterServiceCallHandler.h"
@interface NavigationService_openPage : FlutterServiceCallHandler
@end
\ 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.
*/
//Generated by AIOCodeGen.
#import "ServiceGateway.h"
#import "NavigationService_openPage.h"
#import "FLBFlutterApplication.h"
#import "FlutterBoost.h"
#import "Service_NavigationService.h"
@implementation NavigationService_openPage
- (void)onCall:(void (^)(BOOL))result pageName:(NSString *)pageName params:(NSDictionary *)params animated:(NSNumber *)animated
{
NSString *url = pageName;
if(pageName == nil){
pageName = params[@"url"];
}
if([self needResult:params]){
[FlutterBoostPlugin.sharedInstance setResultHandler:^(NSString *key , NSDictionary *resultData) {
[Service_NavigationService onNativePageResult:^(NSNumber *) {}
uniqueId:@"no use"
key:url
resultData:resultData
params:@{}];
} forKey:url];
}
[[FLBFlutterApplication sharedApplication].platform openPage:pageName
params:params
animated:animated.boolValue
completion:^(BOOL finished) {
if(result)result(YES);
}];
}
- (BOOL)needResult:(NSDictionary *)params
{
NSString *const key = @"needResult";
NSNumber *val = params[key];
if (val && [val isKindOfClass:NSNumber.class]) {
return val.boolValue;
}
return NO;
}
#pragma mark - Do not edit these method.
- (void)__flutter_p_handler_openPage:(NSDictionary *)args result:(void (^)(BOOL))result {
[self onCall:result pageName:args[@"pageName"] params:args[@"params"] animated:args[@"animated"]];
}
+ (void)load{
[[ServiceGateway sharedInstance] registerHandler:[NavigationService_openPage new]];
}
- (NSString *)returnType
{
return @"BOOL";
}
- (NSString *)service
{
return @"NavigationService";
}
@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.
*/
// Generated by AIOCodeGen do not edit!
#import "FlutterServiceCallHandler.h"
@interface NavigationService_pageOnStart : FlutterServiceCallHandler
@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.
*/
//Generated by AIOCodeGen.
#import "ServiceGateway.h"
#import "NavigationService_pageOnStart.h"
#import "FLBFlutterApplication.h"
#import "FlutterBoostConfig.h"
@implementation NavigationService_pageOnStart
- (void)onCall:(void (^)(NSDictionary *))result params:(NSDictionary *)params
{
NSMutableDictionary *pageInfo = [NSMutableDictionary new];
pageInfo[@"name"] =[FlutterBoostConfig sharedInstance].fPagename;
pageInfo[@"params"] = [FlutterBoostConfig sharedInstance].fParams;
pageInfo[@"uniqueId"] = [FlutterBoostConfig sharedInstance].fPageId;
if(result) result(pageInfo);
}
#pragma mark - Do not edit these method.
- (void)__flutter_p_handler_pageOnStart:(NSDictionary *)args result:(void (^)(NSDictionary *))result {
[self onCall:result params:args[@"params"]];
}
+ (void)load{
[[ServiceGateway sharedInstance] registerHandler:[NavigationService_pageOnStart new]];
}
- (NSString *)returnType
{
return @"NSDictionary *";
}
- (NSString *)service
{
return @"NavigationService";
}
@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;
@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;
- (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(!_inMemoryObjectsMap[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)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 <Foundation/Foundation.h>
#import "FLBStackCache.h"
@interface FLBStackCacheObjectImg : NSObject<FLBStackCacheObject>
@property (nonatomic,copy) NSString *key;
- (instancetype)initWithImage:(UIImage *)image;
- (UIImage *)image;
@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;
}
@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
/*
* 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
...@@ -24,38 +24,53 @@ ...@@ -24,38 +24,53 @@
#import "FLBFlutterViewContainer.h" #import "FLBFlutterViewContainer.h"
#import "FLBFlutterApplication.h" #import "FLBFlutterApplication.h"
#import "FLBStackCache.h" #import "BoostMessageChannel.h"
#import "FLBStackCacheObjectImg.h" #import "FLBFlutterContainerManager.h"
#import "FLBMemoryInspector.h" #import "FlutterBoostPlugin_private.h"
#import "Service_NavigationService.h"
#import "FlutterBoostConfig.h"
#define FLUTTER_VIEW [FLBFlutterApplication sharedApplication].flutterViewController.view #define FLUTTER_APP [FlutterBoostPlugin sharedInstance].application
#define FLUTTER_VC [FLBFlutterApplication sharedApplication].flutterViewController #define FLUTTER_VIEW FLUTTER_APP.flutterViewController.view
#define FLUTTER_APP [FLBFlutterApplication sharedApplication] #define FLUTTER_VC FLUTTER_APP.flutterViewController
@interface FLBFlutterViewContainer () @interface FLBFlutterViewContainer ()
@property (nonatomic,copy,readwrite) NSString *name; @property (nonatomic,copy,readwrite) NSString *name;
@property (nonatomic,strong,readwrite) NSDictionary *params; @property (nonatomic,strong,readwrite) NSDictionary *params;
@property (nonatomic,strong) UIImageView *screenShotView;
@property (nonatomic,assign) long long identifier; @property (nonatomic,assign) long long identifier;
@end @end
@implementation FLBFlutterViewContainer @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 - (void)setName:(NSString *)name params:(NSDictionary *)params
{ {
if(!_name && name){ if(!_name && name){
_name = name; _name = name;
_params = params; _params = params;
[Service_NavigationService didInitPageContainer:^(NSNumber *r) {} [BoostMessageChannel didInitPageContainer:^(NSNumber *r) {}
pageName:name pageName:name
params:params params:params
uniqueId:[self uniqueIDString]]; uniqueId:[self uniqueIDString]];
} }
} }
static NSUInteger kInstanceCounter = 0; static NSUInteger kInstanceCounter = 0;
+ (NSUInteger)instanceCounter + (NSUInteger)instanceCounter
...@@ -75,8 +90,7 @@ static NSUInteger kInstanceCounter = 0; ...@@ -75,8 +90,7 @@ static NSUInteger kInstanceCounter = 0;
{ {
kInstanceCounter--; kInstanceCounter--;
if([self.class instanceCounter] == 0){ if([self.class instanceCounter] == 0){
[[FLBStackCache sharedInstance] clear]; [FLUTTER_APP pause];
[[FLBFlutterApplication sharedApplication] pause];
} }
} }
...@@ -85,32 +99,11 @@ static NSUInteger kInstanceCounter = 0; ...@@ -85,32 +99,11 @@ static NSUInteger kInstanceCounter = 0;
return @(_identifier).stringValue; return @(_identifier).stringValue;
} }
- (instancetype)init - (void)_setup
{ {
if(self = [super init]){
static long long sCounter = 0; static long long sCounter = 0;
_identifier = sCounter++; _identifier = sCounter++;
[self.class instanceCounterIncrease]; [self.class instanceCounterIncrease];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(flutterViewDidShow:) name:@"flutter_boost_container_showed"
object:nil];
}
return self;
}
- (void)flutterViewDidAppear:(NSDictionary *)params
{
//Notify flutter view appeared.
}
- (void)flutterViewDidShow:(NSNotification *)notification
{
__weak typeof(self) weakSelf = self;
if([notification.object isEqual: self.uniqueIDString]){
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf showFlutterView];
});
}
} }
- (void)dealloc - (void)dealloc
...@@ -121,179 +114,68 @@ static NSUInteger kInstanceCounter = 0; ...@@ -121,179 +114,68 @@ static NSUInteger kInstanceCounter = 0;
- (void)notifyWillDealloc - (void)notifyWillDealloc
{ {
[Service_NavigationService willDeallocPageContainer:^(NSNumber *r) {} [BoostMessageChannel willDeallocPageContainer:^(NSNumber *r) {}
pageName:_name params:_params pageName:_name params:_params
uniqueId:[self uniqueIDString]]; uniqueId:[self uniqueIDString]];
[[FLBStackCache sharedInstance] remove:self.uniqueIDString]; [FLUTTER_APP removeViewController:self];
[[FLBFlutterApplication sharedApplication] removeViewController:self];
[self.class instanceCounterDecrease]; [self.class instanceCounterDecrease];
} }
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor; self.view.backgroundColor = UIColor.whiteColor;
self.screenShotView = [[UIImageView alloc] initWithFrame:self.view.bounds];
self.screenShotView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.screenShotView];
} }
#pragma mark - ScreenShots #pragma mark - ScreenShots
- (UIImage *)takeScreenShot
{
CGFloat scale = 1;
switch ([FLBMemoryInspector.sharedInstance currentCondition]) {
case FLBMemoryConditionNormal:
scale = 2;
break;
case FLBMemoryConditionLowMemory:
scale = 1;
break;
case FLBMemoryConditionExtremelyLow:
scale = 0.75;
break;
case FLBMemoryConditionAboutToDie:
return [UIImage new];
break;
case FLBMemoryConditionUnknown:
if([[FLBMemoryInspector sharedInstance] smallMemoryDevice]){
scale = 1;
}else{
scale = 2;
}
break;
}
self.screenShotView.opaque = YES;
CGRect flutterBounds = self.view.bounds;
CGSize snapshotSize = CGSizeMake(flutterBounds.size.width ,
flutterBounds.size.height);
UIGraphicsBeginImageContextWithOptions(snapshotSize, NO, scale);
[self.view drawViewHierarchyInRect:flutterBounds
afterScreenUpdates:NO];
UIImage *snapImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapImage;
}
- (void)saveScreenShot
{
UIImage *snapImage = [self takeScreenShot];
if(snapImage){
FLBStackCacheObjectImg *cImg = [[FLBStackCacheObjectImg alloc] initWithImage:snapImage];
[[FLBStackCache sharedInstance] pushObject:cImg key:self.uniqueIDString];
}
}
- (void)clearCurrentScreenShotImage
{
self.screenShotView.image = nil;
}
- (UIImage *)getSavedScreenShot
{
FLBStackCacheObjectImg *cImg = [[FLBStackCache sharedInstance] objectForKey:self.uniqueIDString];
return [cImg image];
}
- (BOOL)isFlutterViewAttatched - (BOOL)isFlutterViewAttatched
{ {
return FLUTTER_VIEW.superview == self.view; return FLUTTER_VIEW.superview == self.view;
} }
- (void)attatchFlutterView - (void)attatchFlutterEngine
{ {
if([self isFlutterViewAttatched]) return; [FLUTTER_APP.flutterProvider prepareEngineIfNeeded];
[FLUTTER_APP.flutterProvider atacheToViewController:self];
[FLUTTER_VC willMoveToParentViewController:nil];
[FLUTTER_VC removeFromParentViewController];
[FLUTTER_VC didMoveToParentViewController:nil];
[FLUTTER_VC willMoveToParentViewController:self];
FLUTTER_VIEW.frame = self.view.bounds;
if(!self.screenShotView.image){
[self.view addSubview: FLUTTER_VIEW];
}else{
[self.view insertSubview:FLUTTER_VIEW belowSubview:self.screenShotView];
}
[self addChildViewController:FLUTTER_VC];
[FLUTTER_VC didMoveToParentViewController:self];
} }
- (BOOL)showSnapShotVew - (void)detatchFlutterEngine
{ {
self.screenShotView.image = [self getSavedScreenShot]; [FLUTTER_APP.flutterProvider detach];
if([self isFlutterViewAttatched]){
NSUInteger fIdx = [self.view.subviews indexOfObject:FLUTTER_VIEW];
NSUInteger sIdx = [self.view.subviews indexOfObject:self.screenShotView];
if(fIdx > sIdx){
[self.view insertSubview:FLUTTER_VIEW
atIndex:0];
}
}
return self.screenShotView.image != nil;
} }
- (void)showFlutterView
{
if(FLUTTER_VIEW.superview != self.view) return;
if([self isFlutterViewAttatched] ){
NSUInteger fIdx = [self.view.subviews indexOfObject:FLUTTER_VIEW];
NSUInteger sIdx = [self.view.subviews indexOfObject:self.screenShotView];
self.screenShotView.backgroundColor = UIColor.clearColor;
if(sIdx > fIdx){
[self.view insertSubview:self.screenShotView belowSubview:FLUTTER_VIEW];
[self flutterViewDidAppear:@{@"uid":self.uniqueIDString?:@""}];
}
}
[self clearCurrentScreenShotImage];
//Remove obsolete screenshot.
[FLBStackCache.sharedInstance remove:self.uniqueIDString];
}
#pragma mark - Life circle methods #pragma mark - Life circle methods
- (void)viewDidLayoutSubviews - (void)viewDidLayoutSubviews
{ {
[super viewDidLayoutSubviews]; [super viewDidLayoutSubviews];
[[FLBFlutterApplication sharedApplication] resume]; [FLUTTER_APP resume];
} }
- (void)viewWillAppear:(BOOL)animated - (void)viewWillAppear:(BOOL)animated
{ {
[[FLBFlutterApplication sharedApplication] resume]; if([FLUTTER_APP contains:self]){
//For new page we should attach flutter view in view will appear [self detatchFlutterEngine];
//for better performance. }else{
if(![[FLBFlutterApplication sharedApplication] contains:self]){ [self attatchFlutterEngine];
[self attatchFlutterView];
} }
[self showSnapShotVew]; [FLUTTER_APP resume];
//For new page we should attach flutter view in view will appear
//for better performance.
[Service_NavigationService willShowPageContainer:^(NSNumber *result) {} [BoostMessageChannel willShowPageContainer:^(NSNumber *result) {}
pageName:_name pageName:_name
params:_params params:_params
uniqueId:self.uniqueIDString]; uniqueId:self.uniqueIDString];
//Save some first time page info. //Save some first time page info.
if(![FlutterBoostConfig sharedInstance].fPagename){ if(![FlutterBoostPlugin sharedInstance].fPagename){
[FlutterBoostConfig sharedInstance].fPagename = _name; [FlutterBoostPlugin sharedInstance].fPagename = _name;
[FlutterBoostConfig sharedInstance].fPageId = self.uniqueIDString; [FlutterBoostPlugin sharedInstance].fPageId = self.uniqueIDString;
[FlutterBoostConfig sharedInstance].fParams = _params; [FlutterBoostPlugin sharedInstance].fParams = _params;
} }
[super viewWillAppear:animated]; [super viewWillAppear:animated];
...@@ -301,70 +183,50 @@ static NSUInteger kInstanceCounter = 0; ...@@ -301,70 +183,50 @@ static NSUInteger kInstanceCounter = 0;
- (void)viewDidAppear:(BOOL)animated - (void)viewDidAppear:(BOOL)animated
{ {
[[FLBFlutterApplication sharedApplication] resume]; [FLUTTER_APP addUniqueViewController:self];
//Ensure flutter view is attached. //Ensure flutter view is attached.
[self attatchFlutterView]; [self attatchFlutterEngine];
[FLUTTER_APP resume];
[Service_NavigationService didShowPageContainer:^(NSNumber *result) {} [BoostMessageChannel didShowPageContainer:^(NSNumber *result) {}
pageName:_name pageName:_name
params:_params params:_params
uniqueId:self.uniqueIDString]; uniqueId:self.uniqueIDString];
[[FLBFlutterApplication sharedApplication] addUniqueViewController:self];
[super viewDidAppear:animated]; [super viewDidAppear:animated];
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(2 * NSEC_PER_SEC)),
dispatch_get_main_queue(),^{
[weakSelf showFlutterView];
});
} }
- (void)viewWillDisappear:(BOOL)animated - (void)viewWillDisappear:(BOOL)animated
{ {
//is top. [BoostMessageChannel willDisappearPageContainer:^(NSNumber *result) {}
if([FLUTTER_APP isTop:self.uniqueIDString]
&& self.navigationController.interactivePopGestureRecognizer.state != UIGestureRecognizerStateBegan){
[self saveScreenShot];
}
self.screenShotView.image = [self getSavedScreenShot];
if(self.screenShotView.image){
[self.view bringSubviewToFront:self.screenShotView];
}
[Service_NavigationService willDisappearPageContainer:^(NSNumber *result) {}
pageName:_name pageName:_name
params:_params params:_params
uniqueId:self.uniqueIDString]; uniqueId:self.uniqueIDString];
[[[UIApplication sharedApplication] keyWindow] endEditing:YES];
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
} }
- (void)viewDidDisappear:(BOOL)animated - (void)viewDidDisappear:(BOOL)animated
{ {
[Service_NavigationService didDisappearPageContainer:^(NSNumber *result) {} [super viewDidDisappear:animated];
[FLUTTER_APP resume];
[BoostMessageChannel didDisappearPageContainer:^(NSNumber *result) {}
pageName:_name pageName:_name
params:_params params:_params
uniqueId:self.uniqueIDString]; uniqueId:self.uniqueIDString];
}
[self clearCurrentScreenShotImage]; - (void)installSplashScreenViewIfNecessary {
[super viewDidDisappear:animated]; //Do nothing.
[FLUTTER_APP inactive];
} }
#pragma mark - FLBViewControllerResultHandler - (BOOL)loadDefaultSplashScreenView
- (void)onRecievedResult:(NSDictionary *)resultData forKey:(NSString *)key
{ {
[Service_NavigationService onNativePageResult:^(NSNumber *finished) {} return NO;
uniqueId:self.uniqueIDString
key:key
resultData:resultData
params:@{}];
} }
......
...@@ -3,27 +3,33 @@ ...@@ -3,27 +3,33 @@
# #
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'flutter_boost' 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.summary = 'A new Flutter plugin make flutter better to use!'
s.description = <<-DESC s.description = <<-DESC
A new Flutter plugin make flutter better to use! A new Flutter plugin make flutter better to use!
DESC DESC
s.homepage = 'http://example.com' s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' } s.license = { :file => '../LICENSE.md' }
s.author = { 'Alibaba Xianyu' => 'email@example.com' } s.author = { 'Alibaba Xianyu' => 'email@example.com' }
s.source = { :path => '.' } s.source = { :path => '.' }
s.source_files = 'Classes/**/*.{h,m,mm}' s.source_files = 'Classes/**/*.{h,m,mm}'
s.public_header_files = s.public_header_files =
'Classes/Boost/FlutterBoost.h',
'Classes/Boost/FlutterBoostPlugin.h', 'Classes/Boost/FlutterBoostPlugin.h',
'Classes/Boost/FLBPlatform.h', 'Classes/Boost/FLBPlatform.h',
'Classes/Boost/FLBFlutterContainer.h',
'Classes/Boost/FLBFlutterAppDelegate.h', 'Classes/Boost/FLBFlutterAppDelegate.h',
'Classes/Container/FLBFlutterViewContainer.h', 'Classes/Boost/FLBTypes.h',
'Classes/Container/FLBViewControllerResultHandler.h' 'Classes/Boost/FlutterBoost.h',
'Classes/Boost/BoostChannel.h',
'Classes/container/FLBFlutterViewContainer.h'
s.dependency 'Flutter' s.dependency 'Flutter'
s.dependency 'xservice_kit' s.libraries = 'c++'
s.xcconfig = {
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11',
'CLANG_CXX_LIBRARY' => 'libc++'
}
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
end 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);
}
}
/*
* 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 'dart:async';
import 'package:xservice_kit/foundation_ext/foundation_ext.dart';
import 'package:xservice_kit/ServiceTemplate.dart';
import 'package:xservice_kit/ServiceGateway.dart';
class NavigationService {
static final ServiceTemplate _service =
new ServiceTemplate("NavigationService");
static void regsiter() {
ServiceGateway.sharedInstance().registerService(_service);
}
//List event from event channel.
static int listenEvent(void onData(dynamic event)) {
return _service.listenEvent(onData);
}
//Cancel event for subscription with ID.
static void cancelEventForSubscription(int subID) {
_service.cancelEventForSubscription(subID);
}
static Future<bool> onShownContainerChanged(String newName,String oldName,Map params) {
Map<String,dynamic> properties = new Map<String,dynamic>();
properties["newName"]=newName;
properties["oldName"]=oldName;
properties["params"]=params;
return _service.methodChannel().invokeMethod('onShownContainerChanged',properties).then<bool>((value){
return BOOL(value);
});
}
static Future<bool> onFlutterPageResult(String uniqueId,String key,Map resultData,Map params) {
Map<String,dynamic> properties = new Map<String,dynamic>();
properties["uniqueId"]=uniqueId;
properties["key"]=key;
properties["resultData"]=resultData;
properties["params"]=params;
return _service.methodChannel().invokeMethod('onFlutterPageResult',properties).then<bool>((value){
return BOOL(value);
});
}
static Future<Map> pageOnStart(Map params) async {
Map<String,dynamic> properties = new Map<String,dynamic>();
properties["params"]=params;
try {
return await _service.methodChannel().invokeMethod('pageOnStart',properties).then<Map>((value){
return value as Map;
});
} catch (e) {
print('Page on start exception');
return Future<Map>.value({});
}
}
static Future<bool> openPage(String pageName,Map params,bool animated) {
Map<String,dynamic> properties = new Map<String,dynamic>();
properties["pageName"]=pageName;
properties["params"]=params;
properties["animated"]=animated;
return _service.methodChannel().invokeMethod('openPage',properties).then<bool>((value){
return BOOL(value);
});
}
static Future<bool> closePage(String uniqueId,String pageName,Map params,bool animated) {
Map<String,dynamic> properties = new Map<String,dynamic>();
properties["uniqueId"]=uniqueId;
properties["pageName"]=pageName;
properties["params"]=params;
properties["animated"]=animated;
return _service.methodChannel().invokeMethod('closePage',properties).then<bool>((value){
return BOOL(value);
});
}
}
\ 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 'NavigationService.dart';
import '../handlers/NavigationService_onNativePageResult.dart';
import '../handlers/NavigationService_didShowPageContainer.dart';
import '../handlers/NavigationService_willShowPageContainer.dart';
import '../handlers/NavigationService_willDisappearPageContainer.dart';
import '../handlers/NavigationService_didDisappearPageContainer.dart';
import '../handlers/NavigationService_didInitPageContainer.dart';
import '../handlers/NavigationService_willDeallocPageContainer.dart';
class NavigationServiceRegister{
static register(){
NavigationService.regsiter();
NavigationService_onNativePageResult.regsiter();
NavigationService_didShowPageContainer.regsiter();
NavigationService_willShowPageContainer.regsiter();
NavigationService_willDisappearPageContainer.regsiter();
NavigationService_didDisappearPageContainer.regsiter();
NavigationService_didInitPageContainer.regsiter();
NavigationService_willDeallocPageContainer.regsiter();
}
}
\ 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 '../NavigationService/service/NavigationServiceRegister.dart';
class ServiceLoader{
static load(){
NavigationServiceRegister.register();
}
}
\ No newline at end of file
...@@ -21,39 +21,97 @@ ...@@ -21,39 +21,97 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
import 'dart:async';
import 'dart:async';
import 'dart:ui';
import 'package:flutter/services.dart'; 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 { typedef Future<dynamic> EventListener(String name, Map arguments);
static void regsiter() { typedef Future<dynamic> MethodHandler(MethodCall call);
ServiceGateway.sharedInstance()
.registerHandler(new NavigationService_willDisappearPageContainer()); 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();
} }
@override Map msg = Map();
String name() { msg["name"] = name;
return "willDisappearPageContainer"; msg["arguments"] = arguments;
_methodChannel.invokeMethod("__event__", msg);
} }
@override Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async {
String service() { assert(method != "__event__");
return "NavigationService";
return _methodChannel.invokeMethod<T>(method,arguments);
} }
@override Future<List<T>> invokeListMethod<T>(String method, [ dynamic arguments ]) async {
Future<bool> onMethodCall(MethodCall call) { assert(method != "__event__");
return onCall(call.arguments["pageName"], call.arguments["params"],
call.arguments["uniqueId"]); return _methodChannel.invokeListMethod<T>(method,arguments);
} }
//==============================================Do not edit code above! Future<Map<K, V>> invokeMapMethod<K, V>(String method, [ dynamic arguments ]) async {
assert(method != "__event__");
return _methodChannel.invokeMapMethod<K, V>(method,arguments);
}
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);
};
}
VoidCallback addMethodHandler(MethodHandler handler) {
assert(handler != null);
_methodHandlers.add(handler);
Future<bool> onCall(String pageName, Map params, String uniqueId) async { return (){
return ContainerCoordinator.singleton _methodHandlers.remove(handler);
.nativeContainerWillDisappear(pageName, params, uniqueId); };
} }
} }
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_boost/container/container_manager.dart'; import 'container_manager.dart';
import 'package:flutter_boost/flutter_boost.dart'; import '../flutter_boost.dart';
import 'package:flutter_boost/router/boost_page_route.dart'; import 'boost_page_route.dart';
import 'package:flutter_boost/support/logger.dart'; import '../support/logger.dart';
enum ContainerLifeCycle { enum ContainerLifeCycle {
Init, Init,
...@@ -39,9 +39,6 @@ enum ContainerLifeCycle { ...@@ -39,9 +39,6 @@ enum ContainerLifeCycle {
typedef void BoostContainerLifeCycleObserver( typedef void BoostContainerLifeCycleObserver(
ContainerLifeCycle state, BoostContainerSettings settings); ContainerLifeCycle state, BoostContainerSettings settings);
typedef void ResultObserver(
int requestCode, int responseCode, Map<dynamic, dynamic> result);
class BoostContainer extends Navigator { class BoostContainer extends Navigator {
final BoostContainerSettings settings; final BoostContainerSettings settings;
...@@ -116,14 +113,14 @@ class BoostContainer extends Navigator { ...@@ -116,14 +113,14 @@ class BoostContainer extends Navigator {
} }
class BoostContainerState extends NavigatorState { class BoostContainerState extends NavigatorState {
final Set<VoidCallback> _backPressedListeners = Set<VoidCallback>(); VoidCallback backPressedHandler;
final Set<ResultObserver> _resultObservers = Set<ResultObserver>();
String get uniqueId => widget.settings.uniqueId; String get uniqueId => widget.settings.uniqueId;
String get name => widget.settings.name; String get name => widget.settings.name;
Map get params => widget.settings.params;
BoostContainerSettings get settings => widget.settings; BoostContainerSettings get settings => widget.settings;
bool get onstage => bool get onstage =>
...@@ -135,6 +132,8 @@ class BoostContainerState extends NavigatorState { ...@@ -135,6 +132,8 @@ class BoostContainerState extends NavigatorState {
@override @override
BoostContainer get widget => super.widget as BoostContainer; BoostContainer get widget => super.widget as BoostContainer;
final List<Route<dynamic>> routerHistory = <Route<dynamic>>[];
ContainerNavigatorObserver findContainerNavigatorObserver( ContainerNavigatorObserver findContainerNavigatorObserver(
Navigator navigator) { Navigator navigator) {
for (NavigatorObserver observer in navigator.observers) { for (NavigatorObserver observer in navigator.observers) {
...@@ -146,6 +145,12 @@ class BoostContainerState extends NavigatorState { ...@@ -146,6 +145,12 @@ class BoostContainerState extends NavigatorState {
return null; return null;
} }
@override
void initState() {
super.initState();
backPressedHandler = () => maybePop();
}
@override @override
void didUpdateWidget(Navigator oldWidget) { void didUpdateWidget(Navigator oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
...@@ -157,58 +162,74 @@ class BoostContainerState extends NavigatorState { ...@@ -157,58 +162,74 @@ class BoostContainerState extends NavigatorState {
void dispose() { void dispose() {
findContainerNavigatorObserver(widget)?.removeBoostNavigatorObserver( findContainerNavigatorObserver(widget)?.removeBoostNavigatorObserver(
FlutterBoost.containerManager.navigatorObserver); FlutterBoost.containerManager.navigatorObserver);
routerHistory.clear();
super.dispose(); super.dispose();
} }
void performBackPressed() { void performBackPressed() {
Logger.log('performBackPressed'); Logger.log('performBackPressed');
if (_backPressedListeners.isEmpty) { backPressedHandler?.call();
FlutterBoost.singleton
.closePage(name, uniqueId, settings.params, animated: false);
} else {
for (VoidCallback cb in _backPressedListeners) {
cb();
}
} }
}
void performOnResult(Map<dynamic, dynamic> data) {
Logger.log('performOnResult ${data.toString()}');
final int requestCode = data['requestCode']; @override
final int responseCode = data['responseCode']; Future<bool> maybePop<T extends Object>([T result]) async {
final Map result = data['result']; final Route<T> route = routerHistory.last;
final RoutePopDisposition disposition = await route.willPop();
for (ResultObserver observer in _resultObservers) { if (mounted) {
observer(requestCode, responseCode, result); 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 @override
bool pop<T extends Object>([T result]) { bool pop<T extends Object>([T result]) {
if (routerHistory.length > 1) {
routerHistory.removeLast();
}
if (canPop()) { if (canPop()) {
return super.pop(result); return super.pop(result);
} else { } else {
if (BoostContainerManager.of(context).canPop()) { if (T is Map<String, dynamic>) {
BoostContainerManager.of(context).pop(); FlutterBoost.singleton
return true; .close(uniqueId, result: result as Map<String, dynamic>);
} else {
FlutterBoost.singleton.close(uniqueId);
} }
} }
return false; return false;
} }
VoidCallback addBackPressedListener(VoidCallback listener) { @override
_backPressedListeners.add(listener); Future<T> push<T extends Object>(Route<T> route) {
Route<T> newRoute;
return () => _backPressedListeners.remove(listener); if (FlutterBoost.containerManager.prePushRoute != null) {
newRoute = FlutterBoost.containerManager
.prePushRoute(name, uniqueId, params, route);
} }
VoidCallback addResultObserver(ResultObserver observer) { Future<T> future = super.push<T>(newRoute ?? route);
_resultObservers.add(observer);
routerHistory.add(route);
if (FlutterBoost.containerManager.postPushRoute != null) {
FlutterBoost.containerManager
.postPushRoute(name, uniqueId, params, newRoute ?? route, future);
}
return () => _resultObservers.remove(observer); return future;
} }
VoidCallback addLifeCycleObserver(BoostContainerLifeCycleObserver observer) { VoidCallback addLifeCycleObserver(BoostContainerLifeCycleObserver observer) {
......
...@@ -22,22 +22,33 @@ ...@@ -22,22 +22,33 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_boost/AIOService/NavigationService/service/NavigationService.dart'; import 'package:flutter/semantics.dart';
import 'package:flutter_boost/container/boost_container.dart'; import 'package:flutter/services.dart';
import 'package:flutter_boost/flutter_boost.dart'; import '../channel/boost_channel.dart';
import 'package:flutter_boost/messaging/native_page_container_event_handler.dart'; import 'boost_container.dart';
import 'package:flutter_boost/support/logger.dart'; import '../flutter_boost.dart';
import '../support/logger.dart';
class ContainerCoordinator implements NativePageContainerEventHandler { class ContainerCoordinator {
static final ContainerCoordinator singleton = ContainerCoordinator(); static ContainerCoordinator get singleton => _instance;
static ContainerCoordinator _instance;
final Map<String, PageBuilder> _pageBuilders = <String, PageBuilder>{}; final Map<String, PageBuilder> _pageBuilders = <String, PageBuilder>{};
PageBuilder _defaultPageBuilder; PageBuilder _defaultPageBuilder;
ContainerCoordinator() { ContainerCoordinator(BoostChannel channel) {
NavigationService.listenEvent(onChannelEvent); assert(_instance == null);
_instance = this;
channel.addEventListener("lifecycle",
(String name, Map arguments) => _onChannelEvent(arguments));
channel.addMethodHandler((MethodCall call) => _onMethodCall(call));
} }
BoostContainerSettings _createContainerSettings( BoostContainerSettings _createContainerSettings(
...@@ -86,11 +97,13 @@ class ContainerCoordinator implements NativePageContainerEventHandler { ...@@ -86,11 +97,13 @@ class ContainerCoordinator implements NativePageContainerEventHandler {
} }
} }
void onChannelEvent(dynamic event) { Future<dynamic> _onChannelEvent(dynamic event) {
if (event is Map) { if (event is Map) {
Map map = event as Map; Map map = event;
final String type = map['type']; final String type = map['type'];
Logger.log("onEvent $type");
switch (type) { switch (type) {
//Handler back key pressed event. //Handler back key pressed event.
case 'backPressedCallback': case 'backPressedCallback':
...@@ -123,11 +136,71 @@ class ContainerCoordinator implements NativePageContainerEventHandler { ...@@ -123,11 +136,71 @@ class ContainerCoordinator implements NativePageContainerEventHandler {
break; break;
} }
} }
return Future<dynamic>(() {});
} }
//Messaging Future<dynamic> _onMethodCall(MethodCall call) {
@override Logger.log("onMetohdCall ${call.method}");
bool nativeContainerWillShow(String name, Map params, String pageId) {
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>(() {});
}
bool _nativeContainerWillShow(String name, Map params, String pageId) {
if (FlutterBoost.containerManager?.containsContainer(pageId) != true) { if (FlutterBoost.containerManager?.containsContainer(pageId) != true) {
FlutterBoost.containerManager FlutterBoost.containerManager
?.pushContainer(_createContainerSettings(name, params, pageId)); ?.pushContainer(_createContainerSettings(name, params, pageId));
...@@ -136,11 +209,23 @@ class ContainerCoordinator implements NativePageContainerEventHandler { ...@@ -136,11 +209,23 @@ class ContainerCoordinator implements NativePageContainerEventHandler {
return true; return true;
} }
@override
bool nativeContainerDidShow(String name, Map params, String pageId) { bool nativeContainerDidShow(String name, Map params, String pageId) {
FlutterBoost.containerManager FlutterBoost.containerManager
?.showContainer(_createContainerSettings(name, params, pageId)); ?.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), performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Appear); ContainerLifeCycle.Appear);
...@@ -150,34 +235,30 @@ class ContainerCoordinator implements NativePageContainerEventHandler { ...@@ -150,34 +235,30 @@ class ContainerCoordinator implements NativePageContainerEventHandler {
return true; return true;
} }
@override bool _nativeContainerWillDisappear(String name, Map params, String pageId) {
bool nativeContainerWillDisappear(String name, Map params, String pageId) {
return true; return true;
} }
@override bool _nativeContainerDidDisappear(String name, Map params, String pageId) {
bool nativeContainerDidDisappear(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId), performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Disappear); ContainerLifeCycle.Disappear);
return true; return true;
} }
@override bool _nativeContainerDidInit(String name, Map params, String pageId) {
bool nativeContainerDidInit(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId), performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Init); ContainerLifeCycle.Init);
return true; return true;
} }
@override bool _nativeContainerWillDealloc(String name, Map params, String pageId) {
bool nativeContainerWillDealloc(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId), performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Destroy); ContainerLifeCycle.Destroy);
FlutterBoost.containerManager?.remove(pageId); FlutterBoost.containerManager?.remove(pageId);
Logger.log( Logger.log(
'native containner dealloc, \nmanager dump:\n${FlutterBoost.containerManager?.dump()}'); 'native containner dealloc, \n manager dump:\n${FlutterBoost.containerManager?.dump()}');
return true; return true;
} }
......
...@@ -23,11 +23,10 @@ ...@@ -23,11 +23,10 @@
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter_boost/AIOService/NavigationService/service/NavigationService.dart'; import 'boost_container.dart';
import 'package:flutter_boost/container/boost_container.dart'; import 'container_coordinator.dart';
import 'package:flutter_boost/container/container_coordinator.dart'; import '../flutter_boost.dart';
import 'package:flutter_boost/flutter_boost.dart'; import '../support/logger.dart';
import 'package:flutter_boost/support/logger.dart';
enum ContainerOperation { Push, Onstage, Pop, Remove } enum ContainerOperation { Push, Onstage, Pop, Remove }
...@@ -37,7 +36,11 @@ typedef BoostContainerObserver = void Function( ...@@ -37,7 +36,11 @@ typedef BoostContainerObserver = void Function(
@immutable @immutable
class BoostContainerManager extends StatefulWidget { class BoostContainerManager extends StatefulWidget {
final Navigator initNavigator; final Navigator initNavigator;
const BoostContainerManager({Key key, this.initNavigator}) : super(key: key); final PrePushRoute prePushRoute;
final PostPushRoute postPushRoute;
const BoostContainerManager(
{Key key, this.initNavigator, this.prePushRoute, this.postPushRoute})
: super(key: key);
@override @override
ContainerManagerState createState() => ContainerManagerState(); ContainerManagerState createState() => ContainerManagerState();
...@@ -69,6 +72,10 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -69,6 +72,10 @@ class ContainerManagerState extends State<BoostContainerManager> {
String _lastShownContainer; String _lastShownContainer;
PrePushRoute get prePushRoute => widget.prePushRoute;
PostPushRoute get postPushRoute => widget.postPushRoute;
bool get foreground => _foreground; bool get foreground => _foreground;
ManagerNavigatorObserver get navigatorObserver => _navigatorObserver; ManagerNavigatorObserver get navigatorObserver => _navigatorObserver;
...@@ -127,7 +134,12 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -127,7 +134,12 @@ class ContainerManagerState extends State<BoostContainerManager> {
void _onShownContainerChanged(String old, String now) { void _onShownContainerChanged(String old, String now) {
Logger.log('onShownContainerChanged old:$old now:$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() { void _refreshOverlayEntries() {
...@@ -215,7 +227,7 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -215,7 +227,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
.observersOf<BoostContainerObserver>()) { .observersOf<BoostContainerObserver>()) {
observer(ContainerOperation.Onstage, _onstage.settings); observer(ContainerOperation.Onstage, _onstage.settings);
} }
Logger.log('ContainerObserver didOnstage'); Logger.log('ContainerObserver#2 didOnstage');
} else { } else {
pushContainer(settings); pushContainer(settings);
} }
...@@ -257,7 +269,7 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -257,7 +269,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
.observersOf<BoostContainerObserver>()) { .observersOf<BoostContainerObserver>()) {
observer(ContainerOperation.Push, _onstage.settings); observer(ContainerOperation.Push, _onstage.settings);
} }
Logger.log('ContainerObserver didPush'); Logger.log('ContainerObserver#2 didPush');
} }
void pop() { void pop() {
...@@ -273,7 +285,7 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -273,7 +285,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
observer(ContainerOperation.Pop, old.settings); observer(ContainerOperation.Pop, old.settings);
} }
Logger.log('ContainerObserver didPop'); Logger.log('ContainerObserver#2 didPop');
} }
void remove(String uniqueId) { void remove(String uniqueId) {
...@@ -294,7 +306,7 @@ class ContainerManagerState extends State<BoostContainerManager> { ...@@ -294,7 +306,7 @@ class ContainerManagerState extends State<BoostContainerManager> {
observer(ContainerOperation.Remove, container.settings); observer(ContainerOperation.Remove, container.settings);
} }
Logger.log('ContainerObserver didRemove'); Logger.log('ContainerObserver#2 didRemove');
} }
} }
......
...@@ -24,14 +24,10 @@ ...@@ -24,14 +24,10 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'container/boost_container.dart';
import 'package:flutter_boost/AIOService/NavigationService/service/NavigationService.dart'; import 'container/container_manager.dart';
import 'package:flutter_boost/container/boost_container.dart';
import 'package:flutter_boost/container/container_manager.dart'; import 'channel/boost_channel.dart';
import 'package:flutter_boost/messaging/page_result_mediator.dart';
import 'package:flutter_boost/router/router.dart';
import 'AIOService/loader/ServiceLoader.dart';
import 'container/container_coordinator.dart'; import 'container/container_coordinator.dart';
import 'observers_holders.dart'; import 'observers_holders.dart';
...@@ -40,32 +36,38 @@ export 'container/container_manager.dart'; ...@@ -40,32 +36,38 @@ export 'container/container_manager.dart';
typedef Widget PageBuilder(String pageName, Map params, String uniqueId); typedef Widget PageBuilder(String pageName, Map params, String uniqueId);
typedef Route PrePushRoute(
String url, String uniqueId, Map params, Route route);
typedef void PostPushRoute(
String url, String uniqueId, Map params, Route route, Future result);
class FlutterBoost { class FlutterBoost {
static final FlutterBoost _instance = FlutterBoost(); static final FlutterBoost _instance = FlutterBoost();
final GlobalKey<ContainerManagerState> containerManagerKey = final GlobalKey<ContainerManagerState> containerManagerKey =
GlobalKey<ContainerManagerState>(); GlobalKey<ContainerManagerState>();
final Router _router = Router();
final ObserversHolder _observersHolder = ObserversHolder(); final ObserversHolder _observersHolder = ObserversHolder();
final PageResultMediator _resultMediator = PageResultMediator(); final BoostChannel _boostChannel = BoostChannel();
FlutterBoost() {
ServiceLoader.load();
}
static FlutterBoost get singleton => _instance; static FlutterBoost get singleton => _instance;
static ContainerManagerState get containerManager => static ContainerManagerState get containerManager =>
_instance.containerManagerKey.currentState; _instance.containerManagerKey.currentState;
static TransitionBuilder init([TransitionBuilder builder]) { static TransitionBuilder init(
{TransitionBuilder builder,
PrePushRoute prePush,
PostPushRoute postPush}) {
return (BuildContext context, Widget child) { return (BuildContext context, Widget child) {
assert(child is Navigator, 'child must be Navigator, what is wrong?'); assert(child is Navigator, 'child must be Navigator, what is wrong?');
//Logger.log('Running flutter boost opt!');
final BoostContainerManager manager = BoostContainerManager( final BoostContainerManager manager = BoostContainerManager(
key: _instance.containerManagerKey, initNavigator: child); key: _instance.containerManagerKey,
initNavigator: child,
prePushRoute: prePush,
postPushRoute: postPush);
if (builder != null) { if (builder != null) {
return builder(context, manager); return builder(context, manager);
...@@ -77,65 +79,83 @@ class FlutterBoost { ...@@ -77,65 +79,83 @@ class FlutterBoost {
ObserversHolder get observersHolder => _observersHolder; ObserversHolder get observersHolder => _observersHolder;
BoostChannel get channel => _boostChannel;
FlutterBoost(){
ContainerCoordinator(_boostChannel);
}
///Register a default page builder. ///Register a default page builder.
void registerDefaultPageBuilder(PageBuilder builder) { void registerDefaultPageBuilder(PageBuilder builder) {
ContainerCoordinator.singleton.registerDefaultPageBuilder(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 ///Register a map builders
void registerPageBuilders(Map<String, PageBuilder> builders) { void registerPageBuilders(Map<String, PageBuilder> builders) {
ContainerCoordinator.singleton.registerPageBuilders(builders); ContainerCoordinator.singleton.registerPageBuilders(builders);
} }
Future<bool> openPage(String url, Map params, Future<Map<String,dynamic>> open(String url,{Map<String,dynamic> urlParams,Map<String,dynamic> exts}){
{bool animated, PageResultHandler resultHandler}) {
return _router.openPage(url, params,
animated: animated, resultHandler: resultHandler);
}
Future<bool> closePage(String url, String pageId, Map params, Map<String, dynamic> properties = new Map<String, dynamic>();
{bool animated}) { properties["url"] = url;
return _router.closePage(url, pageId, params, animated: animated); properties["urlParams"] = urlParams;
properties["exts"] = exts;
return channel.invokeMethod<Map<String,dynamic>>(
'openPage', properties);
} }
//Close currentPage page. Future<bool> close(String id,{Map<String,dynamic> result,Map<String,dynamic> exts}){
Future<bool> closeCurPage(Map params) {
return _router.closeCurPage(params); assert(id != null);
BoostContainerSettings settings = containerManager?.onstageSettings;
Map<String, dynamic> properties = new Map<String, dynamic>();
if(exts == null){
exts = Map<String,dynamic>();
} }
Future<bool> closePageForContext(BuildContext context) { exts["params"] = settings.params;
BoostContainerSettings settings = BoostContainer.of(context).settings;
return closePage(settings.name, settings.uniqueId, settings.params, if(!exts.containsKey("animated")){
animated: true); exts["animated"] = true;
} }
///query current top page and show it properties["uniqueId"] = id;
static void handleOnStartPage() async {
final Map<dynamic, dynamic> pageInfo = if (result != null) {
await NavigationService.pageOnStart(<dynamic, dynamic>{}); properties["result"] = result;
if (pageInfo == null || pageInfo.isEmpty) return; }
if (pageInfo.containsKey("name") && if (exts != null) {
pageInfo.containsKey("params") && properties["exts"] = exts;
pageInfo.containsKey("uniqueId")) {
ContainerCoordinator.singleton.nativeContainerDidShow(
pageInfo["name"], pageInfo["params"], pageInfo["uniqueId"]);
} }
return channel.invokeMethod<bool>('closePage', properties);
} }
bool onPageResult(String key, Map<String, dynamic> resultData) { Future<bool> closeCurrent({Map<String,dynamic> result,Map<String,dynamic> exts}) {
containerManager?.containerStateOf(key)?.performOnResult(resultData); BoostContainerSettings settings = containerManager?.onstageSettings;
_resultMediator.onPageResult(key, resultData); if(exts == null){
return true; 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) { Future<bool> closeByContext(BuildContext context,{Map<String,dynamic> result,Map<String,dynamic> exts}) {
return _resultMediator.setPageResultHandler(key, handler); 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 ///register for Container changed callbacks
...@@ -150,4 +170,6 @@ class FlutterBoost { ...@@ -150,4 +170,6 @@ class FlutterBoost {
///register callbacks for Navigators push & pop ///register callbacks for Navigators push & pop
VoidCallback addBoostNavigatorObserver(BoostNavigatorObserver observer) => VoidCallback addBoostNavigatorObserver(BoostNavigatorObserver observer) =>
_observersHolder.addObserver<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 'package:flutter_boost/support/logger.dart';
typedef void PageResultHandler(String key , Map<dynamic,dynamic> result);
typedef VoidCallback = void Function();
class PageResultMediator{
Map<String,PageResultHandler> _handlers = Map();
void onPageResult(String key , Map<dynamic,dynamic> resultData){
if(key == null) return;
Logger.log("did receive page result $resultData for page key $key");
if(_handlers.containsKey(key)){
_handlers[key](key,resultData);
_handlers.remove(key);
}
}
VoidCallback setPageResultHandler(String key, PageResultHandler handler){
if(key == null || handler == null) return (){};
_handlers[key] = handler;
return (){
_handlers.remove(key);
};
}
}
\ 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();
void setMessageProxy(MessageProxy prx) {
if (prx != null) {
_msgProxy = prx;
}
}
Future<bool> openPage(String url, Map params,
{bool animated = true, PageResultHandler resultHandler}) {
if (resultHandler != null) {
params["needResult"] = true;
FlutterBoost.singleton.setPageResultHandler(url,
(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);
}
name: flutter_boost name: flutter_boost
description: A next-generation Flutter-Native hybrid solution. FlutterBoost is a Flutter plugin which enables hybrid integration of Flutter for your existing native apps with minimum efforts. description: A next-generation Flutter-Native hybrid solution. FlutterBoost is a Flutter plugin which enables hybrid integration of Flutter for your existing native apps with minimum efforts.
version: 0.0.39 version: 0.1.50
author: Alibaba Xianyu author: Alibaba Xianyu
homepage: https://github.com/alibaba/flutter_boost homepage: https://github.com/alibaba/flutter_boost
...@@ -11,8 +11,6 @@ dependencies: ...@@ -11,8 +11,6 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
xservice_kit: ^0.0.26
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec # following page: https://www.dartlang.org/tools/pub/pubspec
...@@ -20,8 +18,8 @@ dependencies: ...@@ -20,8 +18,8 @@ dependencies:
# The following section is specific to Flutter. # The following section is specific to Flutter.
flutter: flutter:
plugin: plugin:
androidPackage: com.taobao.idlefish.flutterboost androidPackage: com.idlefish.flutterboost
pluginClass: FlutterBoostPlugin pluginClass: BoostChannel
# To add assets to your plugin package, add an assets section, like this: # To add assets to your plugin package, add an assets section, like this:
# assets: # assets:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment