Compare commits

..

51 Commits

Author SHA1 Message Date
Sean f36a3fa05c xj 2023-12-06 13:52:27 +08:00
Sean 5ef957afa3 2.42.7 2023-12-06 13:47:43 +08:00
Sean 731d616d16 log 日志 2023-11-21 17:41:30 +08:00
Sean 1b566ef884 线程xj 2023-11-20 13:56:58 +08:00
Stewen 8574fc53c0 恢复 2023-10-19 14:16:15 +08:00
Stewen cedb23eec5 蓝牙2.42.2 2023-10-19 14:09:04 +08:00
Sean 31f68432f9 xj 2023-10-18 15:08:53 +08:00
Sean 08a5a23fde test 2023-10-18 14:52:28 +08:00
Sean 4999057d25 getCustomMenus 2023-10-18 14:00:38 +08:00
Stewen 5b29167042 Merge branch 'phiz_2.42.1' of https://git-dev.xyue.zip:8443/phiz/mop-flutter-sdk into phiz_2.42.1.1
# Conflicts:
#	ios/mop.podspec
2023-10-16 10:53:34 +08:00
Stewen b7b59390c9 纯finclip 2.42.1代码 2023-10-16 10:51:24 +08:00
Stewen 275ca3d4a1 fix error 2023-09-26 19:56:20 +08:00
Stewen efcabfe7c8 add shareToMoment 2023-09-26 19:46:56 +08:00
Stewen c291d64199 不写版本号试试 2023-09-26 18:10:57 +08:00
Stewen ebc5edcbf5 恢复2.41.13 2023-09-26 17:28:46 +08:00
Stewen 892035a0c8 蓝牙弄2.41.5 2023-09-26 17:21:31 +08:00
Stewen c9c7f00cb4 解决冲突 2023-09-26 15:38:06 +08:00
Stewen 5606f294e5 Merge branch 'phiz_2.41.5.10' of https://git-dev.xyue.zip:8443/phiz/mop-flutter-sdk into phiz_2.41.13.1
# Conflicts:
#	ios/Classes/Api/MOP_initialize.m
#	ios/mop.podspec
#	ios/mop.podspec.tpl
2023-09-26 15:37:08 +08:00
Stewen 52e29a405e 加入iOS蓝牙SDK 2023-09-26 14:15:08 +08:00
Stewen ac757dbfb5 注释FATClipBoardComponent 2023-09-23 15:54:53 +08:00
Stewen 2da5b8ead3 注释FinAppletClipBoard 2023-09-23 15:43:58 +08:00
Stewen 7a95d27791 注释FinAppletClipBoard 2023-09-23 15:43:02 +08:00
Stewen 685ff6ff1b 改下 2023-09-23 15:41:50 +08:00
Stewen 8e85abcb15 移除FinAppletClipBoard 2023-09-23 15:11:24 +08:00
Stewen b3c70cd5f2 .11 2023-09-23 15:04:46 +08:00
Stewen a5de8826c8 FinAppletClipBoard 2023-09-23 14:58:11 +08:00
Stewen 9c4c772c70 版本号要一致 2023-09-23 14:46:49 +08:00
Stewen fa41022f41 调整格式 2023-09-23 14:41:54 +08:00
Stewen 03eedd4991 Merge branch 'phiz_2.41.5.9' of https://git-dev.xyue.zip:8443/phiz/mop-flutter-sdk into phiz_2.41.13.1
# Conflicts:
#	ios/mop.podspec
2023-09-23 14:37:59 +08:00
Stewen 70f910a76c 纯凡泰2.41.13版本 2023-09-23 14:35:26 +08:00
Stewen 40e6eeda59 log 2023-09-18 10:48:49 +08:00
Stewen 99d074d023 set googleMapApi timeout 2023-08-23 17:33:29 +08:00
Stewen c030e31a5a 去掉小程序支付测试 2023-08-23 15:51:39 +08:00
Stewen 0ac5a988dc sync 2023-08-22 18:02:39 +08:00
stewen 98168c167e fix map sdk 2023-08-14 18:51:27 +08:00
stewen f2077784db add log 2023-08-14 11:55:29 +08:00
stewen 2ed4c891c5 试下修改menus 2023-08-14 09:24:21 +08:00
stewen f9ee987c5a 添加加载中 2023-08-12 15:44:12 +08:00
stewen 2b3bd6a226 debug 2023-08-12 15:25:17 +08:00
stewen 957f14dde5 获取按钮改成同步 2023-08-08 16:08:21 +08:00
stewen 6749f2fc82 fix countrycode 2023-08-04 17:56:51 +08:00
stewen a6bd02e789 fix error 2023-08-04 17:33:45 +08:00
stewen 366f8ab9ac fix error 2023-08-04 17:30:34 +08:00
stewen 58a8e5b804 ios同步方法更改 2023-08-04 17:25:36 +08:00
stewen 9951bebc56 config googlemapkey 2023-08-03 14:33:57 +08:00
stewen 8ef8d78b2a 提交测试 2023-08-03 10:54:49 +08:00
jayce b800cb3786 import FinAppletExt 2023-08-02 19:59:34 +08:00
jayce f097d86cd1 clipdata 2023-08-02 19:41:51 +08:00
jayce 195b2d11a9 add google map 2023-08-02 19:36:40 +08:00
simpleman1984@126.com 4df164b35f sync method and clipdata 2023-08-02 17:48:03 +08:00
simpleman1984@126.com 4cfdbafcbf sync method and clipdata 2023-08-02 17:31:33 +08:00
12 changed files with 181 additions and 27 deletions

View File

@ -91,6 +91,6 @@ kapt {
} }
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.finogeeks.lib:finapplet:2.41.11' implementation 'com.finogeeks.lib:finapplet:2.42.7'
implementation 'com.finogeeks.mop:plugins:2.41.11' implementation 'com.finogeeks.mop:plugins:2.42.7'
} }

View File

@ -133,6 +133,7 @@ public class InitSDKModule extends BaseApi {
configBuilder.setSchemes(schemes); configBuilder.setSchemes(schemes);
} }
configBuilder.setDebugMode((Boolean) configMap.get("debug")); configBuilder.setDebugMode((Boolean) configMap.get("debug"));
configBuilder.setEnableLog((Boolean) configMap.get("debug"));
Integer maxRunningApplet = (Integer) configMap.get("maxRunningApplet"); Integer maxRunningApplet = (Integer) configMap.get("maxRunningApplet");
if (maxRunningApplet != null) { if (maxRunningApplet != null) {
configBuilder.setMaxRunningApplet(maxRunningApplet); configBuilder.setMaxRunningApplet(maxRunningApplet);
@ -194,11 +195,23 @@ public class InitSDKModule extends BaseApi {
if (appletText != null) { if (appletText != null) {
configBuilder.setAppletText(appletText); configBuilder.setAppletText(appletText);
} }
Integer languageInteger = (Integer) configMap.get("language");
if (languageInteger == 1) { Object localeLanguage = configMap.get("localeLanguage");
configBuilder.setLocale(Locale.ENGLISH); if (localeLanguage != null) {
String language = (String) localeLanguage;
if (language.contains("_")) {
String[] locales = language.split("_");
configBuilder.setLocale(new Locale(locales[0], locales[1]));
} else {
configBuilder.setLocale(new Locale(language));
}
} else { } else {
configBuilder.setLocale(Locale.SIMPLIFIED_CHINESE); Integer languageInteger = (Integer) configMap.get("language");
if (languageInteger == 1) {
configBuilder.setLocale(Locale.ENGLISH);
} else {
configBuilder.setLocale(Locale.SIMPLIFIED_CHINESE);
}
} }
// uiConfig // uiConfig

View File

@ -2,7 +2,7 @@ package com.finogeeks.mop.api.mop;
import android.content.Context; import android.content.Context;
import com.finogeeks.finochat.sdkcore.client.FinoChatSDKCoreClient; import com.finogeeks.finclip.sdkcore.manager.FinClipSDKCoreManager;
import com.finogeeks.lib.applet.client.FinAppClient; import com.finogeeks.lib.applet.client.FinAppClient;
import com.finogeeks.lib.applet.db.entity.FinApplet; import com.finogeeks.lib.applet.db.entity.FinApplet;
import com.finogeeks.mop.api.BaseApi; import com.finogeeks.mop.api.BaseApi;
@ -25,7 +25,7 @@ public class SmSignModule extends BaseApi {
@Override @Override
public void invoke(String event, Map param, ICallback callback) { public void invoke(String event, Map param, ICallback callback) {
String text = (String) param.get("plainText"); String text = (String) param.get("plainText");
String result = FinoChatSDKCoreClient.getInstance().finoLicenseService().messageDigest(text); String result = new FinClipSDKCoreManager.Builder().build().messageDigestBySM(text);
Map<String, Object> res = new HashMap<>(); Map<String, Object> res = new HashMap<>();
res.put("data", result); res.put("data", result);
callback.onSuccess(res); callback.onSuccess(res);

68
bundle.sh 100755
View File

@ -0,0 +1,68 @@
export LANG=en_US.UTF-8
export FASTLANE_DISABLE_COLORS=1
export version="$1"
export iosVersion="$2"
export androidVersion="$3"
#version=`git describe --abbrev=0 --tags | tr -d '\\n' | tr -d '\\t'`
echo "当前版本号:${version}"
echo "依赖的iOS${iosVersion}"
echo "依赖的Android:${androidVersion}"
#git reset --hard
#git checkout ${version}
# pubspec.yaml
cp -r pubspec.tpl.yaml pubspec.yaml
sed -i "" "s/__mop_flutter_sdk_version__/${version}/g" pubspec.yaml
# iOS podspec
if [ -n "$iosVersion" ]
then
echo "更新mop.podspec====>"
cp -r ios/mop.podspec.tpl ios/mop.podspec
sed -i "" "s/__finapplet_version__/${iosVersion}/g" ios/mop.podspec
fi
# android gradle
if [ -n "$androidVersion" ]
then
echo "更新build.gradle====>"
cp -r android/build.gradle.tpl android/build.gradle
sed -i "" "s/__finapplet_version__/${androidVersion}/g" android/build.gradle
fi
cat pubspec.yaml
git remote add ssh-origin ssh://git@gitlab.finogeeks.club:2233/finclipsdk/finclip-flutter-sdk.git
git add .
git commit -m "release: version:$version"
git tag -d ${version}
git push ssh-origin --delete tag ${version}
git tag -a ${version} -m 'FinClip-Flutter-SDK发版'
git push ssh-origin HEAD:refs/heads/master --tags -f
#export http_proxy=http://127.0.0.1:1087
#export https_proxy=http://127.0.0.1:1087
flutter packages pub publish --dry-run --server=https://pub.dartlang.org
flutter packages pub publish --server=https://pub.dartlang.org --force
#unset http_proxy
#unset https_proxy
git remote add github ssh://git@github.com/finogeeks/mop-flutter-sdk.git
#git push github HEAD:refs/heads/master --tags
git push github HEAD:refs/heads/master

View File

@ -83,9 +83,26 @@ class _MyAppState extends State<MyApp> {
Mop.instance.registerExtensionApi('getUserProfile', getUserProfile); Mop.instance.registerExtensionApi('getUserProfile', getUserProfile);
Mop.instance.registerExtensionApi('pushNativePage', pushNativePage);
if (!mounted) return; if (!mounted) return;
} }
Future<Map<String, dynamic>> pushNativePage(dynamic params) async {
print(params);
Map<String, dynamic> result = {
"userInfo":{
"nickName" : "haley",
"avatarUrl" : "https://www.finclip.com",
"gender" : 1,
"country" : "China",
"province" : "Guangdong",
"city" : "shenzhen",
}
};
return Future.value(result);
}
Future<Map<String, dynamic>> getUserProfile(dynamic params) async { Future<Map<String, dynamic>> getUserProfile(dynamic params) async {
Map<String, dynamic> result = { Map<String, dynamic> result = {
"userInfo":{ "userInfo":{
@ -262,6 +279,12 @@ class MyAppletHandler extends AppletHandler {
return Future.value(result); return Future.value(result);
} }
// @override
// Future<Map<String, dynamic>> getUserInfo() {
// // TODO: implement getUserInfo
// throw UnimplementedError();
// }
@override @override
Future<void> onCustomMenuClick(String appId, String path, String menuId, String appInfo) { Future<void> onCustomMenuClick(String appId, String path, String menuId, String appInfo) {
// TODO: implement onCustomMenuClick // TODO: implement onCustomMenuClick

View File

@ -47,41 +47,59 @@
- (NSDictionary *)getUserInfoWithAppletInfo:(FATAppletInfo *)appletInfo { - (NSDictionary *)getUserInfoWithAppletInfo:(FATAppletInfo *)appletInfo {
NSLog(@"getUserInfoWithAppletInfo"); NSLog(@"getUserInfoWithAppletInfo");
__block NSDictionary *userInfo; __block NSDictionary *userInfo;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel]; FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel];
[channel invokeMethod:@"extensionApi:getUserInfo" arguments:nil result:^(id _Nullable result) { [channel invokeMethod:@"extensionApi:getUserInfo" arguments:nil result:^(id _Nullable result) {
CFRunLoopStop(CFRunLoopGetMain()); CFRunLoopStop(runLoop);
userInfo = result; userInfo = result;
}]; }];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
CFRunLoopStop(runLoop);
});
CFRunLoopRun(); CFRunLoopRun();
return userInfo; return userInfo;
} }
- (BOOL)appletInfo:(FATAppletInfo *)appletInfo didClickMoreBtnAtPath:(NSString *)path { - (BOOL)appletInfo:(FATAppletInfo *)appletInfo didClickMoreBtnAtPath:(NSString *)path {
NSLog(@"appletInfo:didClickMoreBtnAtPath");
__block BOOL flag; __block BOOL flag;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel]; FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel];
NSLog(@"appletInfo:didClickMoreBtnAtPath,appId=%@,path=%@,channel=%@",appletInfo.appId,path,channel);
[channel invokeMethod:@"extensionApi:customCapsuleMoreButtonClick" arguments:@{@"appId": appletInfo.appId} result:^(id _Nullable result) { [channel invokeMethod:@"extensionApi:customCapsuleMoreButtonClick" arguments:@{@"appId": appletInfo.appId} result:^(id _Nullable result) {
CFRunLoopStop(CFRunLoopGetMain()); CFRunLoopStop(runLoop);
if ([result isKindOfClass:[NSNumber class]]) { if ([result isKindOfClass:[NSNumber class]]) {
flag = [result boolValue]; flag = [result boolValue];
} }
}]; }];
CFRunLoopRun(); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
CFRunLoopStop(runLoop);
});
CFRunLoopRun();
return flag; return flag;
} }
- (NSArray<id<FATAppletMenuProtocol>> *)customMenusInApplet:(FATAppletInfo *)appletInfo atPath:(NSString *)path { - (NSArray<id<FATAppletMenuProtocol>> *)customMenusInApplet:(FATAppletInfo *)appletInfo atPath:(NSString *)path {
NSLog(@"customMenusInApplet"); NSLog(@"customMenusInApplet:%@,appletInfo=%@",path,appletInfo);
__block NSArray *list; __block NSArray *list;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel]; FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel];
[channel invokeMethod:@"extensionApi:getCustomMenus" arguments:@{@"appId": appletInfo.appId} result:^(id _Nullable result) { [channel invokeMethod:@"extensionApi:getCustomMenus" arguments:@{@"appId": appletInfo.appId} result:^(id _Nullable result) {
CFRunLoopStop(CFRunLoopGetMain()); CFRunLoopStop(runLoop);
if ([result isKindOfClass:[NSArray class]]) { if ([result isKindOfClass:[NSArray class]]) {
list = result; list = result;
NSLog(@"customMenusInApplet2222:list=%@",list);
} }
}]; }];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
CFRunLoopStop(runLoop);
});
CFRunLoopRun(); CFRunLoopRun();
NSLog(@"customMenusInApplet:%@,list=%@",path,list);
NSMutableArray *models = [NSMutableArray array]; NSMutableArray *models = [NSMutableArray array];
for (NSDictionary<NSString *, NSString *> *data in list) { for (NSDictionary<NSString *, NSString *> *data in list) {
@ -112,7 +130,9 @@
return models; return models;
} }
- (void)clickCustomItemMenuWithInfo:(NSDictionary *)contentInfo inApplet:(FATAppletInfo *)appletInfo completion:(void (^)(FATExtensionCode code, NSDictionary *result))completion { - (void)clickCustomItemMenuWithInfo:(NSDictionary *)contentInfo inApplet:(FATAppletInfo *)appletInfo completion:(void (^)(FATExtensionCode code, NSDictionary *result))completion {
NSLog(@"HJH1,clickCustomItemMenuWithInfo");
NSError *parseError = nil; NSError *parseError = nil;
NSMutableDictionary *shareDic = [[NSMutableDictionary alloc] initWithDictionary:[self dictionaryRepresentation:appletInfo]]; NSMutableDictionary *shareDic = [[NSMutableDictionary alloc] initWithDictionary:[self dictionaryRepresentation:appletInfo]];
[shareDic setValue:@{@"desc" : shareDic[@"originalInfo"][@"customData"][@"detailDescription"]} forKey:@"params"]; [shareDic setValue:@{@"desc" : shareDic[@"originalInfo"][@"customData"][@"detailDescription"]} forKey:@"params"];
@ -133,6 +153,7 @@
if ([@"Desktop" isEqualToString:contentInfo[@"menuId"]]) { if ([@"Desktop" isEqualToString:contentInfo[@"menuId"]]) {
[self addToDesktopItemClick:appletInfo path:contentInfo[@"path"]]; [self addToDesktopItemClick:appletInfo path:contentInfo[@"path"]];
} }
NSLog(@"HJH2,clickCustomItemMenuWithInfo");
} }
- (NSDictionary *)dictionaryRepresentation:(FATAppletInfo *)object { - (NSDictionary *)dictionaryRepresentation:(FATAppletInfo *)object {

View File

@ -23,6 +23,22 @@
return _instance; return _instance;
} }
- (BOOL)getUserInfoWithAppletInfo:(FATAppletInfo *)appletInfo bindGetUserInfo:(void (^)(NSDictionary *result))bindGetUserInfo
{
FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel];
NSLog(@"getUserInfoWithAppletInfo:%@", channel);
[channel invokeMethod:@"extensionApi:getUserInfo" arguments:nil result:^(id _Nullable result) {
NSDictionary *userInfo;
if (![result isKindOfClass:[NSDictionary class]]) {
userInfo = @{@"errMsg":@"getUserInfo:fail return value format invalid"};
} else {
userInfo = result;
}
bindGetUserInfo(userInfo);
}];
return YES;
}
- (BOOL)getUserProfileWithAppletInfo:(FATAppletInfo *)appletInfo - (BOOL)getUserProfileWithAppletInfo:(FATAppletInfo *)appletInfo
bindGetUserProfile:(void (^)(NSDictionary *result))bindGetUserProfile bindGetUserProfile:(void (^)(NSDictionary *result))bindGetUserProfile
{ {

View File

@ -69,7 +69,7 @@
} else { } else {
config.language = FATPreferredLanguageSimplifiedChinese; config.language = FATPreferredLanguageSimplifiedChinese;
} }
config.customLanguagePath = self.config[@"customLanguagePath"];
NSError* error = nil; NSError* error = nil;
FATUIConfig *uiconfig = [[FATUIConfig alloc]init]; FATUIConfig *uiconfig = [[FATUIConfig alloc]init];

View File

@ -15,15 +15,13 @@
{ {
NSLog(@"MOP_registerExtensionApi"); NSLog(@"MOP_registerExtensionApi");
FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel]; FlutterMethodChannel *channel = [[MopPlugin instance] methodChannel];
[[FATClient sharedClient] registerExtensionApi:self.name handle:^(id param, FATExtensionApiCallback callback) { NSString *name = self.name;
NSLog(@"invoke ExtensionApi:"); [[FATClient sharedClient] registerExtensionApi:name handler:^(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback) {
NSLog(@"%@",self.name); NSLog(@"channel:%@---invoke ExtensionApi:%@, param:%@", channel, name, param);
NSLog(@"%@",param); NSString *api = [@"extensionApi:" stringByAppendingString:name];
NSString* api = [@"extensionApi:" stringByAppendingString:self.name];
[channel invokeMethod:api arguments:param result:^(id _Nullable result) { [channel invokeMethod:api arguments:param result:^(id _Nullable result) {
NSLog(@"extensionApi reslut:%@",result); NSLog(@"extensionApi [%@] reslut:%@", name, result);
// flutter // flutter
// BOOL isFlutterError = [result isKindOfClass:[FlutterError class]] || result == FlutterMethodNotImplemented;
BOOL isValid = [result isKindOfClass:[NSDictionary class]]; BOOL isValid = [result isKindOfClass:[NSDictionary class]];
if (!isValid) { if (!isValid) {
NSLog(@"extensionApi reslut is not NSDictionary"); NSLog(@"extensionApi reslut is not NSDictionary");
@ -34,7 +32,7 @@
BOOL hasError = [[result allKeys] containsObject:@"errMsg"]; BOOL hasError = [[result allKeys] containsObject:@"errMsg"];
if (hasError) { if (hasError) {
NSString *errMsg = result[@"errMsg"]; NSString *errMsg = result[@"errMsg"];
NSString *errPrefix = [NSString stringWithFormat:@"%@:fail", self.name]; NSString *errPrefix = [NSString stringWithFormat:@"%@:fail", name];
BOOL isFail = [errMsg hasPrefix:errPrefix]; BOOL isFail = [errMsg hasPrefix:errPrefix];
if (isFail) { if (isFail) {
NSLog(@"extensionApi reslut:fail"); NSLog(@"extensionApi reslut:fail");

View File

@ -17,7 +17,7 @@ A finclip miniprogram flutter sdk.
s.dependency 'Flutter' s.dependency 'Flutter'
s.ios.deployment_target = '9.0' s.ios.deployment_target = '9.0'
s.dependency 'FinApplet' , '2.41.11' s.dependency 'FinApplet' , '2.42.7'
s.dependency 'FinAppletExt' , '2.41.11' s.dependency 'FinAppletExt' , '2.42.7'
end end

View File

@ -210,6 +210,19 @@ class Config {
/// SDK /// SDK
LanguageType language = LanguageType.Chinese; LanguageType language = LanguageType.Chinese;
/// iOS
/// SDK language
///
/// mainBundle @"abc.lproj"
/// Bundle @"bundleName.bundle/abc.lproj"
String? customLanguagePath;
/// Android
/// SDK language
/// https://uutool.cn/info-i18n/ Java java.util.Locale
/// zh_CNzh_TWen
String? localeLanguage;
/// Android /// Android
/// 使tbs /// 使tbs
bool useLocalTbsCore = false; bool useLocalTbsCore = false;
@ -267,6 +280,8 @@ class Config {
"useLocalTbsCore": useLocalTbsCore, "useLocalTbsCore": useLocalTbsCore,
"tbsCoreUrl": tbsCoreUrl, "tbsCoreUrl": tbsCoreUrl,
"enableJ2V8": enableJ2V8, "enableJ2V8": enableJ2V8,
"customLanguagePath": customLanguagePath,
"localeLanguage": localeLanguage,
}; };
} }
} }

View File

@ -1,6 +1,6 @@
name: mop name: mop
description: A Finogeeks MiniProgram Flutter SDK. description: A Finogeeks MiniProgram Flutter SDK.
version: '2.41.11' version: '2.42.7'
homepage: https://github.com/finogeeks/mop-flutter-sdk homepage: https://github.com/finogeeks/mop-flutter-sdk
environment: environment: