diff --git a/CHANGELOG.md b/CHANGELOG.md index 30c6eb9..9877510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,20 @@ -## 0.0.1 +## 1.0.0 -* Flutter A/B Testing 插件,支持 Android 和 iOS 中使用 +* 单个接口和全局设置,支持自定义属性请求分流 +* 支持自定义主体请求分流 + +## 0.0.4 +* 修复 Android MethodChannel 偶现回调多次异常 + +## 0.0.3 + +* SDK 依赖由 jcenter 调整为 mavenCentral ## 0.0.2 * 新增初始化接口 -## 0.0.3 +## 0.0.1 -* SDK 依赖由 jcenter 调整为 mavenCentral +* Flutter A/B Testing 插件,支持 Android 和 iOS 中使用 diff --git a/LICENSE b/LICENSE index 7170a36..1e2760d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,28 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2015-2022 Sensors Data Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +软件名称:神策 ABTesting SDK +版本号:所有版本 +许可协议版本:1.0 + +1. 商业许可协议(用于商业用途需购买许可) +任何商业用途必须获得商业许可。 + +商业许可协议条款: + +- 商业用途:任何直接或间接产生收入的用途都需要购买商业许可。 +- 付款条款:在使用本软件用于商业用途之前,您必须支付全额许可费用。具体的付款方式将在双方联系后提供。 +- 商业支持:购买商业许可后,您将获得一年的技术支持和软件更新服务。 +- 禁止再许可:商业用户不得再许可、转售或转让本软件。每份商业许可仅适用于单一实体或公司。 +- 源代码访问:购买商业许可的用户将获得本软件的代码访问权限,并可根据业务需求进行内部修改。但不得公开发布或再分发修改后的版本。 +- 使用范围限制:商业许可仅限于购买者的内部使用,不得与第三方共享或用于为第三方提供服务。任何超出许可范围的使用行为均需额外授权,并可能产生额外费用。 +- 联系信息:如需购买商业许可,请联系 dv@sensorsdata.com。 +- 知识产权声明:本软件的版权归神策网络科技(北京)有限公司所有。购买商业许可仅授予您使用权,所有权仍归属本公司。 +- 终止条款: 如果您未支付相关费用或违反本协议的任何条款,商业许可将自动终止。您必须立即停止所有商业用途,并销毁或删除所有软件副本。 + +2. 附加授权规则条款 +授权规则条款: + +- 功能限制:未经本软件作者的明确书面许可,您不得移除、绕过或规避本软件中的任何功能限制或试用限制。 +- 商标使用:未经授权,您不得在宣传、市场推广或销售产品时使用本软件的名称、商标或品牌标识。任何商标使用必须得到明确的书面许可。 +- 修改条款:本协议的条款可能会不时更新,用户有责任定期检查最新版本。任何重大更改将通过项目主页或电子邮件通知用户。 + +3. 联系方式 +如需更多信息或申请商业许可,请联系 dv@sensorsdata.com。 diff --git a/README.md b/README.md index 21405eb..9cd7986 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ -![logo](https://opensource.sensorsdata.cn/wp-content/uploads/logo.png) -

+ ## 神策简介 @@ -13,12 +12,21 @@ 神策 abtesting_sdk_flutter 插件,封装了神策 iOS & Android A/B Testing 常用 API ,使用此插件,可以使用 A/B Testing 功能。 +## 神策埋点 SDK 官网 +如需了解神策埋点 SDK 的更多商业授权信息,请访问[神策埋点 SDK 官网](https://jssdk.debugbox.sensorsdata.cn/)获取更多详细信息。 + +## 联系我们 +若您有商业合作或产品集成需求,请通过下面的渠道联系我们获取专业服务与支持。 + +| 加微信号:skycode008,或扫码添加联系人 | 扫码关注「神策埋点 SDK」公众号 ![gzh](https://github.com/sensorsdata/sa-sdk-android/blob/master/gzh.jpeg) | +| ------ | ------ | + ## 使用方式 在 Flutter 项目的 `pubspec.yaml` 文件中添加 `abtesting_sdk_flutter` 依赖 ```yml dependencies: # 添加神策 flutter plugin - abtesting_sdk_flutter: ^0.0.3 + abtesting_sdk_flutter: ^1.0.0 ``` 执行 flutter packages get 命令安装插件 @@ -31,47 +39,11 @@ dependencies: 请参考神策官网 [Flutter 插件集成文档](https://manual.sensorsdata.cn/abtesting/latest/flutter-a-b-testing-50987212.html)。 -## 贡献 - -* 1. 在您的 GitHub 账户下 fork abtesting_sdk_flutter 开源项目; -* 2. 根据您的需求在本地 clone 一份 abtesting_sdk_flutter 源码; -* 3. 您修改或者新增功能后,push 到您 fork 的远程分支; -* 4. 创建 pull request,向 abtesting_sdk_flutter 官方开发分支提交合入请求; -* 5. 神策 SDK 研发团队会及时 review 代码,测试通过后合入。 - - -## 讨论 - -| 扫码加入神策数据开源社区 QQ 群
群号:785122381 | 扫码加入神策数据开源社区微信群 | -| ------ | ------ | -|![ QQ 讨论群](https://opensource.sensorsdata.cn/wp-content/uploads/ContentCommonPic_1.png) | ![ 微信讨论群 ](https://opensource.sensorsdata.cn/wp-content/uploads/ContentCommonPic_2.png) | - -## 公众号 - -| 扫码关注
神策数据开源社区 | 扫码关注
神策数据开源社区服务号 | -| ------ | ------ | -|![ 微信订阅号 ](https://opensource.sensorsdata.cn/wp-content/uploads/ContentCommonPic_3.png) | ![ 微信服务号 ](https://opensource.sensorsdata.cn/wp-content/uploads/ContentCommonPic_4.png) | - - ## 新书推荐 -| 《数据驱动:从方法到实践》 | 《Android 全埋点解决方案》 | 《iOS 全埋点解决方案》 +| [《数据驱动:从方法到实践》](https://item.jd.com/12322322.html) | [《Android 全埋点解决方案》](https://item.jd.com/12574672.html) | [《iOS 全埋点解决方案》](https://item.jd.com/12867068.html) | ------ | ------ | ------ | -| [![《数据驱动:从方法到实践》](https://opensource.sensorsdata.cn/wp-content/uploads/data_driven_book_1.jpg)](https://item.jd.com/12322322.html) | [![《Android 全埋点解决方案》](https://opensource.sensorsdata.cn/wp-content/uploads/Android-全埋点thumbnail_1.png)](https://item.jd.com/12574672.html) | [![《iOS 全埋点解决方案》](https://opensource.sensorsdata.cn/wp-content/uploads/iOS-全埋点thumbnail_1.png)](https://item.jd.com/12867068.html) ## License - -Copyright 2015-2022 Sensors Data Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file +[License 协议](https://github.com/sensorsdata/abtesting_sdk_flutter/blob/main/LICENSE) diff --git a/android/build.gradle b/android/build.gradle index 12a77ab..8590476 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,9 +20,17 @@ rootProject.allprojects { } apply plugin: 'com.android.library' +import com.android.Version +def agpVersion = Version.ANDROID_GRADLE_PLUGIN_VERSION +def needNamespace = agpVersion.tokenize('.')[0].toInteger() >= 7 && + agpVersion.tokenize('.')[1].toInteger() >= 3 +println ">>> Current AGP Version: $agpVersion" android { compileSdkVersion 30 + if (needNamespace) { + namespace 'com.sensorsdata.analytics.abtesting.abtesting_sdk_flutter' + } defaultConfig { minSdkVersion 16 diff --git a/android/src/main/java/com/sensorsdata/analytics/abtesting/abtesting_sdk_flutter/AbtestingSdkFlutterPlugin.java b/android/src/main/java/com/sensorsdata/analytics/abtesting/abtesting_sdk_flutter/AbtestingSdkFlutterPlugin.java index 25c94d7..075efe4 100644 --- a/android/src/main/java/com/sensorsdata/analytics/abtesting/abtesting_sdk_flutter/AbtestingSdkFlutterPlugin.java +++ b/android/src/main/java/com/sensorsdata/analytics/abtesting/abtesting_sdk_flutter/AbtestingSdkFlutterPlugin.java @@ -9,6 +9,7 @@ import com.sensorsdata.abtest.OnABTestReceivedData; import com.sensorsdata.abtest.SensorsABTest; import com.sensorsdata.abtest.SensorsABTestConfigOptions; +import com.sensorsdata.abtest.SensorsABTestExperiment; import org.json.JSONObject; @@ -27,9 +28,11 @@ /** AbtestingSdkFlutterPlugin */ public class AbtestingSdkFlutterPlugin implements FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android + /// The MethodChannel that will the communication between Flutter and native + /// Android /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// This local reference serves to register the plugin with the Flutter Engine + /// and unregister it /// when the Flutter Engine is detached from the Activity private MethodChannel channel; private Context applicationContext; @@ -61,6 +64,18 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { case "fastFetchABTest": fastFetchABTest(call, result); break; + case "setCustomIDs": + setCustomIDs(call, result); + break; + case "setCustomProperties": + setCustomProperties(call, result); + break; + case "fastFetchABTestWithExperiment": + fastFetchABTestWithExperiment(call, result); + break; + case "asyncFetchABTestWithExperiment": + asyncFetchABTestWithExperiment(call, result); + break; default: result.notImplemented(); break; @@ -68,8 +83,19 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { } private void startWithConfigOptions(MethodCall call, Result result) { - String url = (String) call.arguments; - SensorsABTestConfigOptions abTestConfigOptions = new SensorsABTestConfigOptions(url); + Map args = call.arguments(); + + String urlString = (String) args.get("urlString"); + Object customObj = args.get("customProperties"); + Map customProperties = null; + if (customObj instanceof Map) { + customProperties = (Map) customObj; + } + + SensorsABTestConfigOptions abTestConfigOptions = new SensorsABTestConfigOptions(urlString); + if (customProperties != null) { + abTestConfigOptions.setCustomProperties(assertMapToJSON(customProperties)); + } SensorsABTest.startWithConfigOptions(applicationContext, abTestConfigOptions); result.success(null); } @@ -127,26 +153,139 @@ public void run() { }); } - private void checkResult(Object abResult, Result result) { - if (abResult instanceof JSONObject) { - JSONObject jsonObject = (JSONObject) abResult; - result.success(jsonObject.toString()); + private void fastFetchABTestWithExperiment(MethodCall call, final Result result) { + List list = (List) call.arguments; + if (list == null || list.size() == 0) { + result.success(null); return; } - result.success(abResult); + + Map experimenMap = (Map) list.get(0); + // 构建最终的 Experiment + SensorsABTestExperiment experiment = buildExperimentFromMap(experimenMap); + + SensorsABTest.shareInstance().fastFetchABTest(experiment, new OnABTestReceivedData() { + @Override + public void onResult(final Object value) { + uiThreadHandler.post(new Runnable() { + @Override + public void run() { + checkResult(value, result); + } + }); + } + }); } - private static Map toMap(JSONObject obj) { - if (obj == null) { - return null; + private void asyncFetchABTestWithExperiment(MethodCall call, final Result result) { + List list = (List) call.arguments; + if (list == null || list.size() == 0) { + result.success(null); + return; } - Map data = new HashMap<>(); - Iterator it = obj.keys(); - while (it.hasNext()) { - String key = it.next(); - data.put(key, obj.opt(key)); + + Map experimenMap = (Map) list.get(0); + + // 构建最终的 Experiment + SensorsABTestExperiment experiment = buildExperimentFromMap(experimenMap); + + SensorsABTest.shareInstance().asyncFetchABTest(experiment, new OnABTestReceivedData() { + @Override + public void onResult(final Object value) { + uiThreadHandler.post(new Runnable() { + @Override + public void run() { + checkResult(value, result); + } + }); + } + }); + } + + private void setCustomIDs(MethodCall call, Result result) { + Map customIDs = call.arguments(); + SensorsABTest.shareInstance().setCustomIDs(customIDs); + result.success(null); + } + + private void setCustomProperties(MethodCall call, Result result) { + Map customProperties = call.arguments(); + SensorsABTest.shareInstance().setCustomProperties(assertMapToJSON(customProperties)); + result.success(null); + } + + // 从 Map 构建 Experiment 对象 + private SensorsABTestExperiment buildExperimentFromMap(Map experimentDic) { + String paramName = (String) experimentDic.get("paramName"); + Object defaultValue = experimentDic.get("defaultValue"); + if (defaultValue instanceof Map) { + defaultValue = new JSONObject((Map) defaultValue); + } + + SensorsABTestExperiment.ExperimentBuilder builder = SensorsABTestExperiment.newBuilder(paramName, + defaultValue); + Object timeoutObj = experimentDic.get("timeoutInterval"); + if (timeoutObj instanceof Number) { + int timeout = ((Number) timeoutObj).intValue(); // 毫秒 + builder.setTimeoutMillSeconds(timeout); + } + + Map properties = (Map) experimentDic.get("properties"); + if (properties != null) { + for (String key : properties.keySet()) { + Object value = properties.get(key); + + if (value == null) { + builder.addProperty(key, (String) null); + } else if (value instanceof String) { + builder.addProperty(key, (String) value); + } else if (value instanceof Boolean) { + builder.addProperty(key, (Boolean) value); + } else if (value instanceof Integer) { + builder.addProperty(key, (Integer) value); + } else if (value instanceof Long) { + builder.addProperty(key, (Long) value); + } else if (value instanceof Double) { + builder.addProperty(key, (Double) value); + } else if (value instanceof Float) { + builder.addProperty(key, ((Float) value).doubleValue()); + } else if (value instanceof List) { + try { + builder.addProperty(key, (List) value); + } catch (Exception e) { + } + } else { + // 兜底处理,直接转字符串 + builder.addProperty(key, String.valueOf(value)); + } + } + } + + // 构建最终的 Experiment + SensorsABTestExperiment experiment = builder.create(); + return experiment; + } + + private void checkResult(Object abResult, Result result) { + try { + if (abResult instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) abResult; + result.success(jsonObject.toString()); + return; + } + result.success(abResult); + } catch (java.lang.Exception e) { + // ignored + } + } + + private static JSONObject assertMapToJSON(Map map) { + if (map != null) { + return new JSONObject(map); + } else { + // SALog.d(TAG, "传入的属性为空"); + return null; } - return data; } @Override diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index c13becf..476e08d 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,9 +26,9 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 34 ndkVersion flutter.ndkVersion - + namespace 'com.sensorsdata.analytics.abtesting.example' compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 @@ -47,7 +47,7 @@ android { applicationId "com.sensorsdata.analytics.abtesting.example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 21 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -55,9 +55,9 @@ android { buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + minifyEnabled false // 禁用 R8 + shrinkResources false // 可选 + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } diff --git a/example/android/build.gradle b/example/android/build.gradle index 83ae220..029d299 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' + classpath 'com.android.tools.build:gradle:8.3.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a..7c0de0c 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,10 @@ -org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true -android.enableJetifier=true +# 必须开启,否则构建性能差 +org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 + +# 使用 Android Studio 内置 JDK(Java 21) +org.gradle.java.home=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home + +# 减少构建错误的推荐配置 +android.useAndroidX=true +android.enableJetifier=true \ No newline at end of file diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index cb24abd..d5ce57c 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip \ No newline at end of file diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9367d48..7c56964 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 12.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index f7d6a5e..0656068 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' + platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 38382ed..5e765bf 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,23 +1,16 @@ PODS: - - abtesting_sdk_flutter (0.0.1): + - abtesting_sdk_flutter (1.0.0): - Flutter - SensorsABTesting - Flutter (1.0.0) - - sensors_analytics_flutter_plugin (2.0.0): + - sensors_analytics_flutter_plugin (4.2.1): - Flutter - - SensorsAnalyticsSDK (>= 2.1.17) - - SensorsABTesting (0.1.0): - - SensorsAnalyticsSDK (>= 3.0.0) - - SensorsAnalyticsSDK (3.0.2): - - SensorsAnalyticsSDK/Core (= 3.0.2) - - SensorsAnalyticsSDK/AutoTrack (3.0.2): - - SensorsAnalyticsSDK/Common - - SensorsAnalyticsSDK/Common (3.0.2) - - SensorsAnalyticsSDK/Core (3.0.2): - - SensorsAnalyticsSDK/Common - - SensorsAnalyticsSDK/Visualized - - SensorsAnalyticsSDK/Visualized (3.0.2): - - SensorsAnalyticsSDK/AutoTrack + - SensorsAnalyticsSDK (>= 4.9.0) + - SensorsABTesting (1.1.0): + - SensorsAnalyticsSDK (>= 4.5.6) + - SensorsAnalyticsSDK (5.0.5): + - SensorsAnalyticsSDK/Core (= 5.0.5) + - SensorsAnalyticsSDK/Core (5.0.5) DEPENDENCIES: - abtesting_sdk_flutter (from `.symlinks/plugins/abtesting_sdk_flutter/ios`) @@ -38,12 +31,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sensors_analytics_flutter_plugin/ios" SPEC CHECKSUMS: - abtesting_sdk_flutter: e43611513a82c30a8b3e070d3a535b944c354d2d - Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c - sensors_analytics_flutter_plugin: 09080518746dbc455182989f2839245f8c9c1fd9 - SensorsABTesting: f441708cde9dd0a764e0c1b3d59bbbeb79a89d8e - SensorsAnalyticsSDK: 0d3ec178f0405eeea64d8a9aafc9b99b8ac812f9 + abtesting_sdk_flutter: e7f445e6b79b41ffb0a4e6aab4098ad4f7f3b702 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + sensors_analytics_flutter_plugin: 15a81c8bfdbfa44b1f8cdb51dc8fe0427980f3c5 + SensorsABTesting: 9d512e44b6426242fe85b26307c755de4edb393f + SensorsAnalyticsSDK: 9b7967f54889d2c0aa734a10caffc0e5a617773a -PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d +PODFILE CHECKSUM: fb408a289158b00660fcdf4ee69e15b9fae7e08f -COCOAPODS: 1.10.2 +COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index a2e7060..a0c4a17 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -150,6 +150,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, DAE13CFB62FE20364467C1D4 /* [CP] Copy Pods Resources */, + 2250FBA2C25D67DEE4D1ACEC /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -166,7 +167,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -207,12 +208,31 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 2250FBA2C25D67DEE4D1ACEC /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -223,6 +243,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -350,7 +371,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -364,15 +385,20 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 75FYWDWHL5; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.sensorsdata.analytics.abtesting.abtestingSdkFlutterExample; + PRODUCT_BUNDLE_IDENTIFIER = cn.sensorsdata.abtesting.Example; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Dev_All_Sensorsdata; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -424,7 +450,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -473,7 +499,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -487,15 +513,20 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 75FYWDWHL5; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.sensorsdata.analytics.abtesting.abtestingSdkFlutterExample; + PRODUCT_BUNDLE_IDENTIFIER = cn.sensorsdata.abtesting.Example; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Dev_All_Sensorsdata; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -505,15 +536,20 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 75FYWDWHL5; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.sensorsdata.analytics.abtesting.abtestingSdkFlutterExample; + PRODUCT_BUNDLE_IDENTIFIER = cn.sensorsdata.abtesting.Example; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Dev_All_Sensorsdata; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..e67b280 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ +#if __has_include() +#import +#else +#import "SensorsAnalyticsSDK.h" +#endif + #import @implementation AppDelegate @@ -9,14 +14,19 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [GeneratedPluginRegistrant registerWithRegistry:self]; // Override point for customization after application launch. + +// [self startSensorsAnalyticsSDKWithOptions: launchOptions]; +// +// [self startSensorsABTest]; + +// - // 测试环境,获取试验地址 - NSString* kSABResultsTestURL = @"http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E"; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} +- (void)startSensorsAnalyticsSDKWithOptions:(NSDictionary *)launchOptions { // 测试环境,数据接收地址 NSString* kSABTestServerURL = @"http://10.130.6.4:8106/sa?project=default"; - - SAConfigOptions *options = [[SAConfigOptions alloc] initWithServerURL:kSABTestServerURL launchOptions:launchOptions]; // options.autoTrackEventType = SensorsAnalyticsEventTypeAppStart | SensorsAnalyticsEventTypeAppEnd | SensorsAnalyticsEventTypeAppClick | SensorsAnalyticsEventTypeAppViewScreen; @@ -27,16 +37,23 @@ - (BOOL)application:(UIApplication *)application options.enableVisualizedAutoTrack = YES; options.enableJavaScriptBridge = YES; options.enableLog = YES; + options.flushNetworkPolicy = SensorsAnalyticsNetworkTypeALL; [SensorsAnalyticsSDK startWithConfigOptions:options]; + + [SensorsAnalyticsSDK.sharedInstance track:@"test_track" withProperties:@{@"device_type": @"iPhone"}]; + // [[SensorsAnalyticsSDK sharedInstance] setFlushNetworkPolicy:SensorsAnalyticsNetworkTypeALL]; -// - //SensorsABTestConfigOptions *abtestConfigOptions = [[SensorsABTestConfigOptions alloc] initWithURL:kSABResultsTestURL]; - //[SensorsABTest startWithConfigOptions:abtestConfigOptions]; -// +} + +- (void)startSensorsABTest { + // 测试环境,获取试验地址 + NSString* kSABResultsTestURL = @"http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E"; + + SensorsABTestConfigOptions *abtestConfigOptions = [[SensorsABTestConfigOptions alloc] initWithURL:kSABResultsTestURL]; + [SensorsABTest startWithConfigOptions:abtestConfigOptions]; - return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @end diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 1dd1d13..9594ccd 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -27,6 +29,8 @@ NSAllowsArbitraryLoads + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/example/lib/main.dart b/example/lib/main.dart index b9da8d5..8ea080d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -18,8 +18,7 @@ class _MyAppState extends State { void initState() { super.initState(); SensorsAnalyticsFlutterPlugin.init( - serverUrl: - "http://10.130.6.4:8106/sa?project=default", + serverUrl: "http://10.130.6.4:8106/sa?project=default", autoTrackTypes: { SAAutoTrackType.APP_START, SAAutoTrackType.APP_VIEW_SCREEN, @@ -37,16 +36,20 @@ class _MyAppState extends State { flushBulkSize: 150, enableLog: true, javaScriptBridge: true, - encrypt: true, - heatMap: true, - visualized: VisualizedConfig(autoTrack: true, properties: true), + // encrypt: true, + // heatMap: true, + // visualized: VisualizedConfig(autoTrack: true, properties: true), android: AndroidConfig( maxCacheSize: 48 * 1024 * 1024, jellybean: true, subProcessFlush: true), ios: IOSConfig(maxCacheSize: 10000), - globalProperties: {'aaa': 'aaa-value', 'bbb': 'bbb-value'}); + globalProperties: {'aaa': 'flutter 全局属性', 'bbb': 'flutter 全局属性-2'}); +// 注册公共属性 + // final anonymousId = SensorsAnalyticsFlutterPlugin.getAnonymousId(); + // SensorsAnalyticsFlutterPlugin.registerSuperProperties( + // {'anonymous_id': anonymousId}); } @override @@ -61,21 +64,39 @@ class _MyAppState extends State { Text('Running on: $_platformVersion\n'), TextButton( onPressed: () async { - int? result = await SensorsABTest.fetchCacheABTest("int", 666); + // 旧测试分流地址:http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E + SensorsABTest.startWithConfigOptions( + "http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E"); + print("startWithConfigOptions====="); + }, + child: Text("startWithConfigOptions")), + TextButton( + onPressed: () async { + var map = {"address": "beijing"}; + SensorsABTest.startWithConfigOptions( + "http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E", + map); + print("startWithConfigOptions customProperties====="); + }, + child: Text("startWithConfigOptions-customProperties")), + TextButton( + onPressed: () async { + int? result = + await SensorsABTest.fetchCacheABTest("index_cqs", 666); print("fetchCacheABTest result is===$result"); }, child: Text("fetchCacheABTest")), TextButton( onPressed: () async { Map? result = - await SensorsABTest.fetchABTest("int", {}, 3111); - print("fetchABTest result is===$result"); + await SensorsABTest.asyncFetchABTest("json_cqs", {}); + print("asyncFetchABTest result is===$result"); }, - child: Text("fetchABTest")), + child: Text("asyncFetchABTest")), TextButton( onPressed: () async { - String? result = - await SensorsABTest.fastFetchABTest("param_cat", "should_be_a_cat"); + String? result = await SensorsABTest.fastFetchABTest( + "color_cqs", "字符串默认值"); print("fastFetchABTest result is===$result"); }, child: Text("fastFetchABTest")), @@ -87,10 +108,44 @@ class _MyAppState extends State { child: Text("login")), TextButton( onPressed: () async { - SensorsABTest.startWithConfigOptions("http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E"); - print("startWithConfigOptions====="); + var experiment = + SensorsABTestExperiment.experimentWithParamName( + "cqs_city1", "cqs_city 默认值"); + // 设置自定义属性 + experiment.properties = {"\$city": "合肥"}; + experiment.timeoutInterval = 20 * 1000; + SensorsABTest.fastFetchABTestWithExperiment(experiment) + .then((value) { + print("fastFetchABTestWithExperiment result is===$value"); + }); }, - child: Text("startWithConfigOptions")), + child: Text("fastFetchABTestWithExperiment")), + TextButton( + onPressed: () async { + // 不设置自定义属性 + var experiment = + SensorsABTestExperiment.experimentWithParamName( + "cqs_city", "cqs_city 默认值"); + experiment.timeoutInterval = 20 * 1000; + SensorsABTest.asyncFetchABTestWithExperiment(experiment) + .then((value) { + print("asyncFetchABTestWithExperiment result is===$value"); + }); + }, + child: Text("asyncFetchABTestWithExperiment")), + TextButton( + onPressed: () async { + // 设置自定义主体 + SensorsABTest.setCustomIDs({"custom_id_1": "custom_value_1"}); + }, + child: Text("setCustomIDs")), + TextButton( + onPressed: () async { + // 设置自定义主体 + SensorsABTest.setCustomProperties( + {"custom_properties_1": "custom_value_1"}); + }, + child: Text("setCustomProperties")), ])), ), ); diff --git a/example/pubspec.lock b/example/pubspec.lock index 676d549..256b612 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,32 +7,36 @@ packages: path: ".." relative: true source: path - version: "0.0.3" + version: "1.0.0" async: dependency: transitive description: name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.flutter-io.cn" source: hosted - version: "2.9.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" @@ -40,13 +44,15 @@ packages: dependency: transitive description: name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.flutter-io.cn" source: hosted - version: "1.16.0" + version: "1.18.0" cupertino_icons: dependency: "direct main" description: name: cupertino_icons + sha256: "486b7bc707424572cdf7bd7e812a0c146de3fd47ecadf070254cc60383f21dd8" url: "https://pub.flutter-io.cn" source: hosted version: "1.0.3" @@ -54,6 +60,7 @@ packages: dependency: transitive description: name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.flutter-io.cn" source: hosted version: "1.3.1" @@ -67,41 +74,83 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" matcher: dependency: transitive description: name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.12" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.5" + version: "0.8.0" meta: dependency: transitive description: name: meta + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0" + version: "1.12.0" path: dependency: transitive description: name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.2" + version: "1.9.0" sensors_analytics_flutter_plugin: dependency: "direct main" description: name: sensors_analytics_flutter_plugin + sha256: "56b82c2532546cf2c4445f4f29d342f543637de3aa11e0a8a5b59e746eadc7b2" url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "4.2.1" sky_engine: dependency: transitive description: flutter @@ -111,34 +160,39 @@ packages: dependency: transitive description: name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.0" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.flutter-io.cn" source: hosted version: "1.2.1" @@ -146,16 +200,26 @@ packages: dependency: transitive description: name: test_api + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.flutter-io.cn" source: hosted - version: "0.4.12" + version: "0.7.0" vector_math: dependency: transitive description: name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.2" + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.flutter-io.cn" + source: hosted + version: "14.2.1" sdks: - dart: ">=2.17.0-0 <3.0.0" - flutter: ">=1.20.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 3e510f5..3b24061 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter: sdk: flutter - sensors_analytics_flutter_plugin: ^2.2.1 + sensors_analytics_flutter_plugin: ^4.2.1 abtesting_sdk_flutter: # When depending on this package from a real application you should use: # abtesting_sdk_flutter: ^x.y.z diff --git a/ios/Classes/AbtestingSdkFlutterPlugin.m b/ios/Classes/AbtestingSdkFlutterPlugin.m index c1ce60f..f85f08d 100644 --- a/ios/Classes/AbtestingSdkFlutterPlugin.m +++ b/ios/Classes/AbtestingSdkFlutterPlugin.m @@ -19,19 +19,33 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self fastFetchABTest:call result:result]; } else if ([@"startWithConfigOptions" isEqualToString:call.method]) { [self startWithConfigOptions:call result:result]; + } else if ([@"setCustomIDs" isEqualToString:call.method]) { + [self setCustomIDs:call result:result]; + } else if ([@"setCustomProperties" isEqualToString:call.method]) { + [self setCustomProperties:call result:result]; + } else if ([@"fastFetchABTestWithExperiment" isEqualToString:call.method]) { + [self fastFetchABTestWithExperiment:call result:result]; + } else if ([@"asyncFetchABTestWithExperiment" isEqualToString:call.method]) { + [self asyncFetchABTestWithExperiment:call result:result]; } else { result(FlutterMethodNotImplemented); } } --(void)startWithConfigOptions:(FlutterMethodCall*)call result:(FlutterResult)result{ - NSString* url = (NSString*)call.arguments; - SensorsABTestConfigOptions *abtestConfigOptions = [[SensorsABTestConfigOptions alloc] initWithURL:url]; +- (void)startWithConfigOptions:(FlutterMethodCall*)call result:(FlutterResult)result{ + NSDictionary *args = call.arguments; + NSString *urlString = args[@"urlString"]; + NSDictionary *customProperties = args[@"customProperties"]; + + SensorsABTestConfigOptions *abtestConfigOptions = [[SensorsABTestConfigOptions alloc] initWithURL:urlString]; + if (customProperties != nil && [customProperties isKindOfClass:[NSDictionary class]]) { + abtestConfigOptions.customProperties = customProperties; + } [SensorsABTest startWithConfigOptions:abtestConfigOptions]; result(nil); } --(void)fetchCacheABTest:(FlutterMethodCall*)call result:(FlutterResult)result{ +- (void)fetchCacheABTest:(FlutterMethodCall*)call result:(FlutterResult)result{ NSArray* arguments = (NSArray *)call.arguments; if (arguments.count < 2) { result(nil); @@ -46,7 +60,7 @@ -(void)fetchCacheABTest:(FlutterMethodCall*)call result:(FlutterResult)result{ result(finalresult); } --(void)asyncFetchABTest:(FlutterMethodCall*)call result:(FlutterResult)flutterResult { +- (void)asyncFetchABTest:(FlutterMethodCall*)call result:(FlutterResult)flutterResult { NSArray* arguments = (NSArray *)call.arguments; if (arguments.count < 3) { flutterResult(nil); @@ -64,17 +78,58 @@ -(void)asyncFetchABTest:(FlutterMethodCall*)call result:(FlutterResult)flutterRe }]; } --(void)fastFetchABTest:(FlutterMethodCall*)call result:(FlutterResult)flutterResult { +- (void)fastFetchABTest:(FlutterMethodCall*)call result:(FlutterResult)flutterResult { NSArray* arguments = (NSArray *)call.arguments; - if (arguments.count < 3) { + NSString* paramName = arguments[0]; + double second = [arguments[2] doubleValue] / 1000; + + [[SensorsABTest sharedInstance] fastFetchABTestWithParamName:paramName defaultValue:arguments[1] timeoutInterval:second completionHandler:^(id _Nullable finalresult) { + if([finalresult isKindOfClass:[NSDictionary class]]) { + NSDictionary* dic = finalresult; + finalresult = [self convertToJsonData:dic]; + } + flutterResult(finalresult); + }]; +} + +- (void)fastFetchABTestWithExperiment:(FlutterMethodCall*)call result:(FlutterResult)flutterResult { + NSArray* arguments = (NSArray *)call.arguments; + if (arguments.count == 0) { flutterResult(nil); return; } - NSString* paramName = arguments[0]; - double second = [arguments[2] doubleValue] / 1000; + NSDictionary *experimentDic = arguments[0]; + SensorsABTestExperiment *experiment = [SensorsABTestExperiment experimentWithParamName:experimentDic[@"paramName"] defaultValue:experimentDic[@"defaultValue"]]; + if (experimentDic[@"timeoutInterval"]) { + experiment.timeoutInterval = [experimentDic[@"timeoutInterval"] doubleValue] / 1000; + } + experiment.properties = experimentDic[@"properties"]; - [[SensorsABTest sharedInstance] fastFetchABTestWithParamName:paramName defaultValue:arguments[1] timeoutInterval:second completionHandler:^(id _Nullable finalresult) { + [[SensorsABTest sharedInstance] fastFetchABTestWithExperiment:experiment completionHandler:^(id _Nullable finalresult) { + if([finalresult isKindOfClass:[NSDictionary class]]) { + NSDictionary* dic = finalresult; + finalresult = [self convertToJsonData:dic]; + } + flutterResult(finalresult); + }]; +} + +- (void)asyncFetchABTestWithExperiment:(FlutterMethodCall*)call result:(FlutterResult)flutterResult { + NSArray* arguments = (NSArray *)call.arguments; + if (arguments.count == 0) { + flutterResult(nil); + return; + } + + NSDictionary *experimentDic = arguments[0]; + SensorsABTestExperiment *experiment = [SensorsABTestExperiment experimentWithParamName:experimentDic[@"paramName"] defaultValue:experimentDic[@"defaultValue"]]; + if (experimentDic[@"timeoutInterval"]) { + experiment.timeoutInterval = [experimentDic[@"timeoutInterval"] doubleValue] / 1000; + } + experiment.properties = experimentDic[@"properties"]; + + [[SensorsABTest sharedInstance] asyncFetchABTestWithExperiment:experiment completionHandler:^(id _Nullable finalresult) { if([finalresult isKindOfClass:[NSDictionary class]]) { NSDictionary* dic = finalresult; finalresult = [self convertToJsonData:dic]; @@ -83,6 +138,20 @@ -(void)fastFetchABTest:(FlutterMethodCall*)call result:(FlutterResult)flutterRes }]; } + +- (void)setCustomIDs:(FlutterMethodCall*)call result:(FlutterResult)result { + NSDictionary *customIDs = call.arguments; + [[SensorsABTest sharedInstance] setCustomIDs:customIDs]; + result(nil); +} + +- (void)setCustomProperties:(FlutterMethodCall*)call result:(FlutterResult)result { + NSDictionary *customProperties = call.arguments; + [[SensorsABTest sharedInstance] setCustomProperties:customProperties]; + result(nil); +} + +/// NSDictionary 转 JSON 字符串 - (NSString *)convertToJsonData:(NSDictionary *)dict { if (![NSJSONSerialization isValidJSONObject:dict]) { NSLog(@"obj is not valid JSON: %@",dict); diff --git a/ios/abtesting_sdk_flutter.podspec b/ios/abtesting_sdk_flutter.podspec index fff82de..1eea9b5 100644 --- a/ios/abtesting_sdk_flutter.podspec +++ b/ios/abtesting_sdk_flutter.podspec @@ -4,19 +4,19 @@ # Pod::Spec.new do |s| s.name = 'abtesting_sdk_flutter' - s.version = '0.0.3' + s.version = '1.0.0' s.summary = 'A/B Testing Flutter Plugin' s.description = <<-DESC A/B Testing Flutter Plugin DESC s.homepage = 'http://github.com/sensorsdata/abtesting_sdk_flutter' s.license = { :file => '../LICENSE' } - s.author = { 'SensorsData' => 'zhangwei@sensorsdata.cn' } + s.author = { 'SensorsData' => 'chuqiangsheng@sensorsdata.cn' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.platform = :ios, '8.0' + s.platform = :ios, '9.0' s.dependency 'SensorsABTesting' # Flutter.framework does not contain a i386 slice. diff --git a/lib/abtesting_sdk_flutter.dart b/lib/abtesting_sdk_flutter.dart index 972a0f8..a14161a 100644 --- a/lib/abtesting_sdk_flutter.dart +++ b/lib/abtesting_sdk_flutter.dart @@ -3,6 +3,69 @@ import 'dart:convert'; import 'package:flutter/services.dart'; +/// A/B Testing 试验参数配置 +class SensorsABTestExperiment { + /// --- 只读字段 --- + final String paramName; + final dynamic defaultValue; + + /// --- 可读写字段 --- + Map? properties; + double timeoutInterval; + + /// 默认超时时间(Flutter 中,毫秒单位) + static const double defaultTimeoutInterval = 30 * 1000; + + /// 禁止使用默认构造方法 + SensorsABTestExperiment._({ + required this.paramName, + required this.defaultValue, + this.properties, + double? timeoutInterval, + }) : timeoutInterval = timeoutInterval ?? defaultTimeoutInterval; + + /// 指定构造方法 —— 对应 initWithParamName:defaultValue: + factory SensorsABTestExperiment({ + required String paramName, + required dynamic defaultValue, + Map? properties, + double? timeoutInterval, + }) { + return SensorsABTestExperiment._( + paramName: paramName, + defaultValue: defaultValue, + properties: properties, + timeoutInterval: timeoutInterval, + ); + } + + /// 工厂方法 —— 对应 +experimentWithParamName:defaultValue: + factory SensorsABTestExperiment.experimentWithParamName( + String paramName, + dynamic defaultValue, + ) { + return SensorsABTestExperiment._( + paramName: paramName, + defaultValue: defaultValue, + ); + } + + /// 转成 Map + Map toMap() { + final map = { + "paramName": paramName, + "defaultValue": defaultValue, + "timeoutInterval": timeoutInterval, + }; + + if (properties != null) { + map["properties"] = properties; + } + + return map; + } +} + class SensorsABTest { static const MethodChannel _channel = const MethodChannel('sa_abtesting_sdk'); static const int TIMEOUT_REQUEST = 30 * 1000; @@ -10,9 +73,17 @@ class SensorsABTest { ///初始化 A/B Testing,在此之前请确保先初始化神策分析 SDK: ///[Android 参考文档](https://manual.sensorsdata.cn/sa/latest/tech_sdk_client_android_basic-17563982.html), ///[iOS 参考文档](https://manual.sensorsdata.cn/sa/latest/tech_sdk_client_ios_use-27724338.html)。 - ///[url] 为服务器地址 - static void startWithConfigOptions(String url){ - _channel.invokeMethod("startWithConfigOptions", url); + ///[urlString] 分流地址 url + ///[customProperties] 自定义属性,可选 + static void startWithConfigOptions( + String urlString, [ + Map? customProperties, + ]) { + final params = { + "urlString": urlString, + "customProperties": customProperties, + }; + _channel.invokeMethod("startWithConfigOptions", params); } /// 从缓存中获取试验结果。[paramName] 是参数名称,[defaultValue] 是对应参数的默认值, @@ -28,31 +99,101 @@ class SensorsABTest { return _channel.invokeMethod("fetchCacheABTest", [paramName, defaultValue]); } + /// 如果本地有缓存,则返回缓存数据;否则从网络请求最新的试验数据,默认 30s 超时时间。[paramName] 是参数名称,[defaultValue] 是对应参数的默认值, + /// 泛型 [T] 的支持类型包括:[int]、[String]、[bool] 和代表 JSON 的 [Map] 类型, + /// [timeoutMillSeconds] 的值默认是 30 * 1000,单位是 ms。 + static Future fastFetchABTest(String paramName, T defaultValue, + [int timeoutMillSeconds = TIMEOUT_REQUEST]) async { + if (defaultValue is Map) { + dynamic result = await _channel.invokeMethod( + "fastFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); + dynamic finalResult = jsonDecode(result); + return Future.value(finalResult); + } + return _channel.invokeMethod( + "fastFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); + } + /// 始终从网络请求试验结果,默认 30s 超时时间。[paramName] 是参数名称,[defaultValue] 是对应参数的默认值, /// 泛型 [T] 的支持类型包括:[int]、[String]、[bool] 和代表 JSON 的 [Map] 类型, /// [timeoutMillSeconds] 的值默认是 30 * 1000,单位是 ms。 - static Future fetchABTest(String paramName, T defaultValue, + static Future asyncFetchABTest(String paramName, T defaultValue, [int timeoutMillSeconds = TIMEOUT_REQUEST]) async { if (defaultValue is Map) { - dynamic result = await _channel - .invokeMethod("asyncFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); + dynamic result = await _channel.invokeMethod( + "asyncFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); dynamic finalResult = jsonDecode(result); return Future.value(finalResult); } - return _channel.invokeMethod("asyncFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); + return _channel.invokeMethod( + "asyncFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); } - /// 如果本地有缓存,则返回缓存数据;否则从网络请求最新的试验数据,默认 30s 超时时间。[paramName] 是参数名称,[defaultValue] 是对应参数的默认值, + /// 如果本地有缓存,则返回缓存数据;否则从网络请求最新的试验数据,默认 30s 超时时间。 + /// [experiment] 是试验参数配置对象, + /// 泛型 [T] 的支持类型包括:[int]、[String]、[bool] 和代表 JSON 的 [Map] 类型。 + static Future fastFetchABTestWithExperiment( + SensorsABTestExperiment experiment) async { + dynamic map = experiment.toMap(); + + if (experiment.defaultValue is Map) { + dynamic result = + await _channel.invokeMethod("fastFetchABTestWithExperiment", [map]); + dynamic finalResult = jsonDecode(result); + return Future.value(finalResult); + } + return _channel.invokeMethod("fastFetchABTestWithExperiment", [map]); + } + + /// 始终从网络请求试验结果,默认 30s 超时时间。 + /// [experiment] 是试验参数配置对象, + /// 泛型 [T] 的支持类型包括:[int]、[String]、[bool] 和代表 JSON 的 [Map] 类型。 + static Future asyncFetchABTestWithExperiment( + SensorsABTestExperiment experiment) async { + dynamic map = experiment.toMap(); + + if (experiment.defaultValue is Map) { + dynamic result = + await _channel.invokeMethod("asyncFetchABTestWithExperiment", [map]); + dynamic finalResult = jsonDecode(result); + return Future.value(finalResult); + } + return _channel.invokeMethod("asyncFetchABTestWithExperiment", [map]); + } + +/** + * 设置获取试验时的自定义主体 ID,全局有效 + * [customIDs] 自定义主体 ID + */ + static void setCustomIDs(Map customIDs) { + _channel.invokeMethod("setCustomIDs", customIDs); + } + + /** + * 设置自定义属性 + * 设置多次时,以最后设置为准,会直接覆盖前次设置内容 + * 设置自定义属性,下次 SDK 初始化后重置 + * [customProperties] 设置的自定义属性内容 + */ + static void setCustomProperties(Map customProperties) { + _channel.invokeMethod("setCustomProperties", customProperties); + } + + /// --- 已废弃方法,请使用 asyncFetchABTest --- + /// 始终从网络请求试验结果,默认 30s 超时时间。[paramName] 是参数名称,[defaultValue] 是对应参数的默认值, /// 泛型 [T] 的支持类型包括:[int]、[String]、[bool] 和代表 JSON 的 [Map] 类型, /// [timeoutMillSeconds] 的值默认是 30 * 1000,单位是 ms。 - static Future fastFetchABTest(String paramName, T defaultValue, + @Deprecated( + 'Please use the asyncFetchABTest interface, It has the same functionality.') + static Future fetchABTest(String paramName, T defaultValue, [int timeoutMillSeconds = TIMEOUT_REQUEST]) async { if (defaultValue is Map) { - dynamic result = await _channel - .invokeMethod("fastFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); + dynamic result = await _channel.invokeMethod( + "asyncFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); dynamic finalResult = jsonDecode(result); return Future.value(finalResult); } - return _channel.invokeMethod("fastFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); + return _channel.invokeMethod( + "asyncFetchABTest", [paramName, defaultValue, timeoutMillSeconds]); } } diff --git a/pubspec.lock b/pubspec.lock index 44e6d01..5128f32 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,27 +5,31 @@ packages: dependency: transitive description: name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.flutter-io.cn" source: hosted - version: "2.9.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" @@ -33,13 +37,15 @@ packages: dependency: transitive description: name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.flutter-io.cn" source: hosted - version: "1.16.0" + version: "1.18.0" fake_async: dependency: transitive description: name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.flutter-io.cn" source: hosted version: "1.3.1" @@ -53,34 +59,62 @@ packages: description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" matcher: dependency: transitive description: name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.12" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.5" + version: "0.8.0" meta: dependency: transitive description: name: meta + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0" + version: "1.12.0" path: dependency: transitive description: name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.2" + version: "1.9.0" sky_engine: dependency: transitive description: flutter @@ -90,34 +124,39 @@ packages: dependency: transitive description: name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.0" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.flutter-io.cn" source: hosted version: "1.2.1" @@ -125,16 +164,26 @@ packages: dependency: transitive description: name: test_api + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.flutter-io.cn" source: hosted - version: "0.4.12" + version: "0.7.0" vector_math: dependency: transitive description: name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.2" + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.flutter-io.cn" + source: hosted + version: "14.2.1" sdks: - dart: ">=2.17.0-0 <3.0.0" - flutter: ">=1.20.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index bc14717..37f7f9a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,10 @@ name: abtesting_sdk_flutter description: A/B Testing Flutter Plugin -version: 0.0.3 +version: 1.0.0 homepage: http://github.com/sensorsdata/abtesting_sdk_flutter environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" flutter: ">=1.20.0" dependencies: