提交 437ecd40 authored 作者: JarvanMo's avatar JarvanMo

Merge remote-tracking branch 'origin/v4'

...@@ -16,14 +16,14 @@ RUN sudo apt-get update && sudo apt-get install -y google-cloud-sdk && \ ...@@ -16,14 +16,14 @@ RUN sudo apt-get update && sudo apt-get install -y google-cloud-sdk && \
gcloud config set component_manager/disable_update_check true gcloud config set component_manager/disable_update_check true
RUN yes | sdkmanager \ RUN yes | sdkmanager \
"platforms;android-29" \ "platforms;android-33" \
"build-tools;29.0.2" \ "build-tools;33.0.2" \
"extras;google;m2repository" \ "extras;google;m2repository" \
"extras;android;m2repository" "extras;android;m2repository"
RUN yes | sdkmanager \ RUN yes | sdkmanager \
"platforms;android-30" \ "platforms;android-32" \
"build-tools;28.0.3" \ "build-tools;33.0.2" \
"extras;google;m2repository" \ "extras;google;m2repository" \
"extras;android;m2repository" "extras;android;m2repository"
......
...@@ -37,3 +37,4 @@ example/.pub-cache/ ...@@ -37,3 +37,4 @@ example/.pub-cache/
example/.pub/ example/.pub/
example/build/ example/build/
example example
example/android/app/debug.keystore
# 4.0.0
* 重构Flutter端,现在需要`Fluwx fluwx = Fluwx();`调用fluwx实例
* 支持取消回传值的监听
* 枚举例按照dart语言规范进行了重命名
* 一些包含`WeChat`的方法删除了`WeChat`
* 部分类改为sealed class
* No_pay现已合并入Fluwx
* 将一些设置移到pubspec.yaml,具体可以参看`example/pubspec.yaml`
* 删除了log相关操作,因为现在可以通过yaml配置
* 新增一些open功能
# 4.0.0-pre.3
* `Fluwx`接口优化。合并了一些函数以优化使用体验。
* 修复Logging在iOS端不好的问题。
# 4.0.0-pre.2
* No_pay现已合并入Fluwx
* 将一些设置移到pubspec.yaml,具体可以参看`example/pubspec.yaml`
* 删除了log相关操作,因为现在可以通过yaml配置
* 增加了open()方法并删除了openWeChatApp
# 4.0.0-pre.1
* 重构Flutter端,现在需要`Fluwx fluwx = Fluwx();`调用fluwx实例
* 支持取消回传值的监听
* 枚举例按照dart语言规范进行了重命名
* 一些包含`WeChat`的方法删除了`WeChat`
* 部分类改为sealed class
* 最低dart版本>=3.1.0-26.0.dev
# 3.13.1 # 3.13.1
* 分享到小程序的thumbnail为必填 * 分享到小程序的thumbnail为必填
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
## Preparation ## Preparation
[Migrate to V4 now](./doc/MIGRATE_TO_V4_CN.md)
`Fluwx` is good but not God. You'd better read [official documents](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1) before `Fluwx` is good but not God. You'd better read [official documents](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1) before
integrating `Fluwx`. Then you'll understand how to generate Android signature, what's universal link for iOS, how to add URL schema for iOS and so on. integrating `Fluwx`. Then you'll understand how to generate Android signature, what's universal link for iOS, how to add URL schema for iOS and so on.
...@@ -49,23 +51,34 @@ dependencies: ...@@ -49,23 +51,34 @@ dependencies:
`Fluwx` without pay: `Fluwx` without pay:
```yaml > Developers who need to exclude payment for iOS can enable `no_pay` in [pubspec.yaml](./example/pubspec.yaml).
dependencies:
fluwx_no_pay: ^${latestVersion}
```
![pub package](https://img.shields.io/pub/v/fluwx_no_pay.svg) ![pub package](https://img.shields.io/pub/v/fluwx_no_pay.svg)
> NOTE: Never forget to replace ^${latestVersion} with actual version. > NOTE: Never forget to replace ^${latestVersion} with actual version.
## Configurations
`Fluwx` enables multiple configurations in the section `fluwx` of `pubspec.yaml` from v4, you can reference [pubspec.yaml](./example/pubspec.yaml)
for more details.
> For iOS, some configurations, such as url_scheme,universal_link, LSApplicationQueriesSchemes, can be configured by `fluwx`,
> what you need to do is to fill configurations in `pubspec.yaml`
- app_id. Required. It'll be used to generate scheme on iOS and register WxApi on Android side if app is cold boot.
- debug_logging. Optional. Enable logs by setting it `true`.
- flutter_activity. Optional. This is usually used by cold boot from WeChat on Android. `Fluwx` will try to launch launcher activity if not set.
- universal_link. Required for iOS. It'll be used to generate universal link on your projects.
## Register WxAPI ## Register WxAPI
Register your app via `fluwx` if necessary. Register your app via `fluwx` if necessary.
```dart ```dart
registerWxApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/"); Fluwx fluwx = Fluwx();
fluwx.registerApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/");
``` ```
The param `universalLink` only works with iOS. You can read [this document](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html) to learn The param `universalLink` only works with iOS. You can read [this document](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html) to learn
how to create universalLink. You can also learn how to add URL schema, how to add `LSApplicationQueriesSchemes` in your iOS project. This is essential. how to create universalLink. You can also learn how to add URL schema, how to add `LSApplicationQueriesSchemes` in your iOS project. This is essential.
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
## 准备 ## 准备
[迁移到V4指南](./doc/MIGRATE_TO_V4_CN.md)
`Fluwx` 可以做很多工作但不是所有. 在集成之前,最好读一下[官方文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1). `Fluwx` 可以做很多工作但不是所有. 在集成之前,最好读一下[官方文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1).
然后你才知道怎么生成签名,怎么使用universal link以及怎么添加URL schema等. 然后你才知道怎么生成签名,怎么使用universal link以及怎么添加URL schema等.
...@@ -46,23 +48,32 @@ dependencies: ...@@ -46,23 +48,32 @@ dependencies:
``` ```
![pub package](https://img.shields.io/pub/v/fluwx.svg) ![pub package](https://img.shields.io/pub/v/fluwx.svg)
`Fluwx`,不带支付: 不带支付的`Fluwx`:
```yaml
dependencies:
fluwx_no_pay: ^${latestVersion}
```
![pub package](https://img.shields.io/pub/v/fluwx_no_pay.svg) > 一些开发者并不需要在iOS端使用支付能力,此时您可以通过在[pubspec.yaml](./example/pubspec.yaml).
![pub package](https://img.shields.io/pub/v/fluwx_no_pay.svg)中开启`no_pay`
> NOTE: 别忘记替换 ^${latestVersion} !!!! > NOTE: 别忘记替换 ^${latestVersion} !!!!
## 配置
`Fluwx` 从v4开始可以在`pubspec.yaml``fluwx`进行一些配置。具体可以参考[pubspec.yaml](./example/pubspec.yaml)
> V4开始,iOS中的url_scheme,universal_link, LSApplicationQueriesSchemes可以不必开发者手动配动。只需在`pubspec.yaml`
> 中填写即可。
- app_id. 必填. 它将用于生成iOS的url_scheme以及在Android端冷启动时,重新初始化WxApi。
- debug_logging. 可选. 把它设置成`true`可以开启日志。
- flutter_activity. 可选. 这个通常是用于Android的冷启动。如果不设置任何值,`Fluwx`将尝试启动launcher activity.
- universal_link. iOS 必填. 它将用自动配置universal_link。
## 注册 WxAPI ## 注册 WxAPI
通过 `fluwx` 注册WxApi. 通过 `fluwx` 注册WxApi.
```dart ```dart
registerWxApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/"); Fluwx fluwx = Fluwx();
fluwx.registerApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/");
``` ```
参数 `universalLink` 只在iOS上有用. 查看[文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html) 以便了解如何生成通用链接. 参数 `universalLink` 只在iOS上有用. 查看[文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html) 以便了解如何生成通用链接.
......
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
...@@ -6,3 +6,4 @@ ...@@ -6,3 +6,4 @@
.DS_Store .DS_Store
/build /build
/captures /captures
.cxx
import org.yaml.snakeyaml.Yaml
group 'com.jarvan.fluwx' group 'com.jarvan.fluwx'
version '1.0-SNAPSHOT' version '1.0-SNAPSHOT'
...@@ -9,12 +11,13 @@ buildscript { ...@@ -9,12 +11,13 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.1.2' classpath 'com.android.tools.build:gradle:7.3.1'
classpath "org.yaml:snakeyaml:2.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
rootProject.allprojects { allprojects {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
...@@ -23,28 +26,77 @@ rootProject.allprojects { ...@@ -23,28 +26,77 @@ rootProject.allprojects {
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
android { android {
compileSdkVersion 31 namespace "com.jarvan.fluwx"
compileSdk 31
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
} }
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
consumerProguardFiles 'consumer-proguard-rules.txt' consumerProguardFiles 'consumer-proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders = loadManifestPlaceholder()
} }
lintOptions {
disable 'InvalidPackage'
}
}
dependencies { dependencies {
api 'com.tencent.mm.opensdk:wechat-sdk-android:6.8.24' api 'com.tencent.mm.opensdk:wechat-sdk-android:6.8.24'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'id.zelory:compressor:3.0.1' implementation 'id.zelory:compressor:3.0.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.1' implementation 'com.squareup.okhttp3:okhttp:4.10.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
}
testOptions {
unitTests.all {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen { false }
showStandardStreams = true
}
}
}
}
Map loadManifestPlaceholder() {
def path = rootProject.projectDir.parent + File.separator + "pubspec.yaml"
InputStream input = new FileInputStream(new File(path))
Yaml yaml = new Yaml()
Map projectConfig = yaml.load(input)
String appId = ""
String interruptWxRequest = "true"
String flutterActivity = ""
String debugLogging = "disabled"
Map fluwx = (Map) projectConfig.get("fluwx")
if (fluwx) {
Map android = (Map) fluwx.get("android")
if (android) {
def iwr = android.get("interrupt_wx_request")
if (iwr) {
interruptWxRequest = (String) iwr
}
def activity = android.get("flutter_activity")
if (activity) {
flutterActivity = (String) activity
}
}
def logging = fluwx.get("debug_logging")
if (logging && logging == "true") {
debugLogging = "true"
}
}
return ["WeChatAppId": appId,
"InterruptWeChatRequestByFluwx": interruptWxRequest,
"FluwxFlutterActivity": flutterActivity,
"WeChatDebugLogging": debugLogging]
} }
group = "com.jarvan.fluwx"
version = "1.0-SNAPSHOT"
plugins {
id("com.android.library")
kotlin("android")
}
allprojects {
repositories {
google()
mavenCentral()
}
}
android {
namespace = "com.jarvan.fluwx"
compileSdk = 31
sourceSets {
val main by getting
main.java.srcDirs("src/main/kotlin")
val test by getting
test.java.srcDirs("src/test/kotlin")
}
defaultConfig {
minSdk = 16
consumerProguardFile("consumer-proguard-rules.txt")
}
testOptions {
unitTests.all {
it.useJUnitPlatform()
it.testLogging {
events("passed", "skipped", "failed", "standardOut", "standardError")
showStandardStreams = true
it.outputs.upToDateWhen {
false
}
}
}
}
}
dependencies {
api("com.tencent.mm.opensdk:wechat-sdk-android:6.8.24")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation("id.zelory:compressor:3.0.1")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.mockito:mockito-core:5.0.0")
}
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
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
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.jarvan.fluwx">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
...@@ -12,10 +11,26 @@ ...@@ -12,10 +11,26 @@
</queries> </queries>
<application> <application>
<meta-data
android:name="WeChatAppId"
android:value="${WeChatAppId}" />
<meta-data
android:name="InterruptWeChatRequestByFluwx"
android:value="${InterruptWeChatRequestByFluwx}" />
<meta-data
android:name="WeChatDebugLogging"
android:value="${WeChatDebugLogging}" />
<meta-data
android:name="FluwxFlutterActivity"
android:value="${FluwxFlutterActivity}" />
<activity <activity
android:name=".wxapi.FluwxWXEntryActivity" android:name=".wxapi.FluwxWXEntryActivity"
android:launchMode="singleTask"
android:exported="false" android:exported="false"
android:launchMode="singleTask"
android:taskAffinity="${applicationId}" android:taskAffinity="${applicationId}"
android:theme="@style/DisablePreviewTheme" /> android:theme="@style/DisablePreviewTheme" />
...@@ -25,13 +40,14 @@ ...@@ -25,13 +40,14 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity" android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity"
android:taskAffinity="${applicationId}" android:taskAffinity="${applicationId}"
android:theme="@style/DisablePreviewTheme"/> android:theme="@style/DisablePreviewTheme" />
<activity-alias <activity-alias
android:name="${applicationId}.wxapi.WXPayEntryActivity" android:name="${applicationId}.wxapi.WXPayEntryActivity"
android:exported="true" android:exported="true"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity" android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity"
android:theme="@style/DisablePreviewTheme"/> android:theme="@style/DisablePreviewTheme" />
<provider <provider
android:name="com.jarvan.fluwx.FluwxFileProvider" android:name="com.jarvan.fluwx.FluwxFileProvider"
......
...@@ -2,12 +2,25 @@ package com.jarvan.fluwx ...@@ -2,12 +2,25 @@ package com.jarvan.fluwx
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.Log
import androidx.annotation.NonNull import androidx.annotation.NonNull
import com.jarvan.fluwx.handlers.* import com.jarvan.fluwx.handlers.FluwxAuthHandler
import com.jarvan.fluwx.handlers.FluwxRequestHandler
import com.jarvan.fluwx.handlers.FluwxShareHandler
import com.jarvan.fluwx.handlers.FluwxShareHandlerEmbedding
import com.jarvan.fluwx.handlers.PermissionHandler
import com.jarvan.fluwx.handlers.WXAPiHandler
import com.jarvan.fluwx.utils.KEY_FLUWX_REQUEST_INFO_EXT_MSG
import com.jarvan.fluwx.utils.WXApiUtils import com.jarvan.fluwx.utils.WXApiUtils
import com.tencent.mm.opensdk.modelbiz.* import com.tencent.mm.opensdk.modelbiz.ChooseCardFromWXCardPackage
import com.tencent.mm.opensdk.modelbiz.OpenRankList
import com.tencent.mm.opensdk.modelbiz.OpenWebview
import com.tencent.mm.opensdk.modelbiz.SubscribeMessage
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessView
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessWebview
import com.tencent.mm.opensdk.modelbiz.WXOpenCustomerServiceChat
import com.tencent.mm.opensdk.modelpay.PayReq import com.tencent.mm.opensdk.modelpay.PayReq
import com.tencent.mm.opensdk.openapi.SendReqCallback
import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
...@@ -36,31 +49,27 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, ...@@ -36,31 +49,27 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
private var context: Context? = null private var context: Context? = null
private fun handelIntent(intent: Intent) { private fun handelIntent(intent: Intent) {
val action = intent.action intent.getStringExtra(KEY_FLUWX_REQUEST_INFO_EXT_MSG)?.let {
val dataString = intent.dataString extMsg = it
if (Intent.ACTION_VIEW == action) {
extMsg = dataString
} }
} }
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.jarvanmo/fluwx") val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.jarvanmo/fluwx")
channel.setMethodCallHandler(this) channel.setMethodCallHandler(this)
val applicationContext = flutterPluginBinding.applicationContext
fluwxChannel = channel fluwxChannel = channel
context = flutterPluginBinding.applicationContext context = flutterPluginBinding.applicationContext
authHandler = FluwxAuthHandler(channel) authHandler = FluwxAuthHandler(channel)
shareHandler = FluwxShareHandlerEmbedding( shareHandler = FluwxShareHandlerEmbedding(
flutterPluginBinding.flutterAssets, flutterPluginBinding.flutterAssets, flutterPluginBinding.applicationContext
flutterPluginBinding.applicationContext
) )
} }
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { override fun onMethodCall(call: MethodCall, result: Result) {
callingChannel = fluwxChannel callingChannel = fluwxChannel
when { when {
call.method == "registerApp" -> WXAPiHandler.registerApp(call, result, context) call.method == "registerApp" -> WXAPiHandler.registerApp(call, result, context)
call.method == "startLog" -> WXAPiHandler.startLog(call, result)
call.method == "stopLog" -> WXAPiHandler.stopLog(call, result)
call.method == "sendAuth" -> authHandler?.sendAuth(call, result) call.method == "sendAuth" -> authHandler?.sendAuth(call, result)
call.method == "authByQRCode" -> authHandler?.authByQRCode(call, result) call.method == "authByQRCode" -> authHandler?.authByQRCode(call, result)
call.method == "stopAuthByQRCode" -> authHandler?.stopAuthByQRCode(result) call.method == "stopAuthByQRCode" -> authHandler?.stopAuthByQRCode(result)
...@@ -75,15 +84,18 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, ...@@ -75,15 +84,18 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
call.method == "isWeChatInstalled" -> WXAPiHandler.checkWeChatInstallation(result) call.method == "isWeChatInstalled" -> WXAPiHandler.checkWeChatInstallation(result)
call.method == "getExtMsg" -> getExtMsg(result) call.method == "getExtMsg" -> getExtMsg(result)
call.method == "openWeChatCustomerServiceChat" -> openWeChatCustomerServiceChat( call.method == "openWeChatCustomerServiceChat" -> openWeChatCustomerServiceChat(
call, call, result
result
) )
call.method == "checkSupportOpenBusinessView" -> WXAPiHandler.checkSupportOpenBusinessView( call.method == "checkSupportOpenBusinessView" -> WXAPiHandler.checkSupportOpenBusinessView(
result result
) )
call.method == "openBusinessView" -> openBusinessView(call, result) call.method == "openBusinessView" -> openBusinessView(call, result)
call.method == "openWeChatInvoice" -> openWeChatInvoice(call, result); call.method == "openWeChatInvoice" -> openWeChatInvoice(call, result)
call.method == "openUrl" -> openUrl(call, result)
call.method == "openRankList" -> openRankList(result)
else -> result.notImplemented() else -> result.notImplemented()
} }
} }
...@@ -104,7 +116,9 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, ...@@ -104,7 +116,9 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
request.timeStamp = System.currentTimeMillis().toString() request.timeStamp = System.currentTimeMillis().toString()
request.nonceStr = System.currentTimeMillis().toString() request.nonceStr = System.currentTimeMillis().toString()
request.signType = "SHA1" request.signType = "SHA1"
request.cardSign = WXApiUtils.createSign(request.appId, request.nonceStr, request.timeStamp, request.cardType) request.cardSign = WXApiUtils.createSign(
request.appId, request.nonceStr, request.timeStamp, request.cardType
)
val done = WXAPiHandler.wxApi?.sendReq(request) val done = WXAPiHandler.wxApi?.sendReq(request)
result.success(done) result.success(done)
} }
...@@ -122,11 +136,13 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, ...@@ -122,11 +136,13 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
shareHandler?.permissionHandler = PermissionHandler(binding.activity) shareHandler?.permissionHandler = PermissionHandler(binding.activity)
handelIntent(binding.activity.intent) handelIntent(binding.activity.intent)
FluwxRequestHandler.handleRequestInfoFromIntent(binding.activity.intent)
} }
override fun onAttachedToActivity(binding: ActivityPluginBinding) { override fun onAttachedToActivity(binding: ActivityPluginBinding) {
// WXAPiHandler.setContext(binding.activity.applicationContext) // WXAPiHandler.setContext(binding.activity.applicationContext)
handelIntent(binding.activity.intent) handelIntent(binding.activity.intent)
FluwxRequestHandler.handleRequestInfoFromIntent(binding.activity.intent)
shareHandler?.permissionHandler = PermissionHandler(binding.activity) shareHandler?.permissionHandler = PermissionHandler(binding.activity)
} }
...@@ -262,8 +278,27 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, ...@@ -262,8 +278,27 @@ class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
private fun openWXApp(result: Result) = result.success(WXAPiHandler.wxApi?.openWXApp()) private fun openWXApp(result: Result) = result.success(WXAPiHandler.wxApi?.openWXApp())
private fun openUrl(call: MethodCall, result: Result) {
val req = OpenWebview.Req()
req.url = call.argument("url")
WXAPiHandler.wxApi?.sendReq(req, SendReqCallback {
result.success(it)
}) ?: kotlin.run {
result.success(false)
}
}
private fun openRankList(result: Result) {
val req = OpenRankList.Req()
WXAPiHandler.wxApi?.sendReq(req, SendReqCallback {
result.success(it)
}) ?: kotlin.run {
result.success(false)
}
}
override fun onNewIntent(intent: Intent): Boolean { override fun onNewIntent(intent: Intent): Boolean {
handelIntent(intent) handelIntent(intent)
return false return false
} }
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.jarvan.fluwx.handlers package com.jarvan.fluwx.handlers
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
...@@ -24,13 +25,15 @@ import android.os.Bundle ...@@ -24,13 +25,15 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import com.jarvan.fluwx.FluwxPlugin import com.jarvan.fluwx.FluwxPlugin
import com.jarvan.fluwx.utils.KEY_FLUWX_REQUEST_INFO_BUNDLE
import com.jarvan.fluwx.utils.KEY_FLUWX_REQUEST_INFO_EXT_MSG
import com.jarvan.fluwx.utils.startFlutterActivity
import com.tencent.mm.opensdk.modelbase.BaseReq import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelmsg.ShowMessageFromWX import com.tencent.mm.opensdk.modelmsg.ShowMessageFromWX
import java.security.cert.Extension import java.security.cert.Extension
object FluwxRequestHandler { object FluwxRequestHandler {
private const val KEY_FLUWX_REQUEST_INFO_BUNDLE = "KEY_FLUWX_REQUEST_INFO_BUNDLE"
var customOnReqDelegate: ((baseReq: BaseReq, activity: Activity) -> Unit)? = null var customOnReqDelegate: ((baseReq: BaseReq, activity: Activity) -> Unit)? = null
...@@ -43,7 +46,8 @@ object FluwxRequestHandler { ...@@ -43,7 +46,8 @@ object FluwxRequestHandler {
} }
} }
private fun handleShowMessageFromWXBundle(bundle: Bundle) = handleWXShowMessageFromWX(ShowMessageFromWX.Req(bundle)) private fun handleShowMessageFromWXBundle(bundle: Bundle) =
handleWXShowMessageFromWX(ShowMessageFromWX.Req(bundle))
private fun handleRequest(req: BaseReq) { private fun handleRequest(req: BaseReq) {
when (req) { when (req) {
...@@ -66,22 +70,21 @@ object FluwxRequestHandler { ...@@ -66,22 +70,21 @@ object FluwxRequestHandler {
// com.tencent.mm.opensdk.constants.ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX = 4 // com.tencent.mm.opensdk.constants.ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX = 4
if (!WXAPiHandler.coolBoot) { if (!WXAPiHandler.coolBoot) {
handleRequest(baseReq) handleRequest(baseReq)
startSpecifiedActivity(defaultFlutterActivityAction(activity), activity = activity) activity.startFlutterActivity()
} else { } else {
when (baseReq) { when (baseReq) {
is ShowMessageFromWX.Req -> { is ShowMessageFromWX.Req -> {
try { activity.startFlutterActivity(
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("wechatextmsg://${activity.packageName}/?extmsg=${baseReq.message.messageExt}")) wxRequestBundle = Bundle().apply {
val bundle = Bundle() baseReq.toBundle(this)
baseReq.toBundle(bundle) },
intent.putExtra(KEY_FLUWX_REQUEST_INFO_BUNDLE, bundle) bundle = Bundle().apply {
activity.startActivity(intent) putString(
activity.finish() KEY_FLUWX_REQUEST_INFO_EXT_MSG,
baseReq.message.messageExt
)
})
WXAPiHandler.coolBoot = false WXAPiHandler.coolBoot = false
}catch (e:Exception) {
Log.i("fluwx","call scheme error:${e.toString()}")
}
} }
} }
} }
...@@ -91,32 +94,18 @@ object FluwxRequestHandler { ...@@ -91,32 +94,18 @@ object FluwxRequestHandler {
fun onReq(baseReq: BaseReq, activity: Activity) { fun onReq(baseReq: BaseReq, activity: Activity) {
try { try {
val packageManager = activity.packageManager val packageManager = activity.packageManager
val appInfo = packageManager.getApplicationInfo(activity.packageName, PackageManager.GET_META_DATA) val appInfo = packageManager.getApplicationInfo(
val defaultHandle = appInfo.metaData.getBoolean("handleWeChatRequestByFluwx", true) activity.packageName,
PackageManager.GET_META_DATA
)
val defaultHandle = appInfo.metaData.getBoolean("InterruptWeChatRequestByFluwx", true)
if (defaultHandle) { if (defaultHandle) {
defaultOnReqDelegate(baseReq, activity) defaultOnReqDelegate(baseReq, activity)
} else { } else {
customOnReqDelegate?.invoke(baseReq, activity) customOnReqDelegate?.invoke(baseReq, activity)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.i("Fluwx", "can't load meta-data handleWeChatRequestByFluwx") Log.i("Fluwx", "can't load meta-data InterruptWeChatRequestByFluwx")
}
}
private fun startSpecifiedActivity(action: String, activity: Activity, bundle: Bundle? = null, bundleKey: String? = null) {
Intent(action).run {
bundleKey?.let {
putExtra(bundleKey, bundle)
}
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
activity.packageManager?.let {
resolveActivity(it)?.also {
activity.startActivity(this)
activity.finish()
} }
} }
}
}
private fun defaultFlutterActivityAction(context: Context): String = "${context.packageName}.FlutterActivity"
} }
\ No newline at end of file
...@@ -19,7 +19,10 @@ ...@@ -19,7 +19,10 @@
package com.jarvan.fluwx.handlers package com.jarvan.fluwx.handlers
import android.content.Context import android.content.Context
import android.content.pm.PackageManager
import android.util.Log import android.util.Log
import com.jarvan.fluwx.BuildConfig
import com.jarvan.fluwx.FluwxPlugin
import com.tencent.mm.opensdk.constants.Build import com.tencent.mm.opensdk.constants.Build
import com.tencent.mm.opensdk.openapi.IWXAPI import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory import com.tencent.mm.opensdk.openapi.WXAPIFactory
...@@ -54,6 +57,16 @@ object WXAPiHandler : ILog { ...@@ -54,6 +57,16 @@ object WXAPiHandler : ILog {
fun registerApp(call: MethodCall, result: MethodChannel.Result, context: Context?) { fun registerApp(call: MethodCall, result: MethodChannel.Result, context: Context?) {
context?.let {
with(it) {
val appInfo =
packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val enableLogging = appInfo.metaData.getString("WeChatDebugLogging", "")
if (enableLogging == "true" && BuildConfig.DEBUG) {
startLog()
}
}
}
if (call.argument<Boolean?>("android") == false) { if (call.argument<Boolean?>("android") == false) {
return return
} }
...@@ -89,12 +102,15 @@ object WXAPiHandler : ILog { ...@@ -89,12 +102,15 @@ object WXAPiHandler : ILog {
wxApi == null -> { wxApi == null -> {
result.error("Unassigned WxApi", "please config wxapi first", null) result.error("Unassigned WxApi", "please config wxapi first", null)
} }
wxApi?.isWXAppInstalled != true -> { wxApi?.isWXAppInstalled != true -> {
result.error("WeChat Not Installed", "Please install the WeChat first", null) result.error("WeChat Not Installed", "Please install the WeChat first", null)
} }
wxApi?.wxAppSupportAPI ?: 0 < Build.OPEN_BUSINESS_VIEW_SDK_INT -> {
(wxApi?.wxAppSupportAPI ?: 0) < Build.OPEN_BUSINESS_VIEW_SDK_INT -> {
result.error("WeChat Not Supported", "Please upgrade the WeChat version", null) result.error("WeChat Not Supported", "Please upgrade the WeChat version", null)
} }
else -> { else -> {
result.success(true) result.success(true)
} }
...@@ -102,60 +118,48 @@ object WXAPiHandler : ILog { ...@@ -102,60 +118,48 @@ object WXAPiHandler : ILog {
} }
private fun registerWxAPIInternal(appId: String, context: Context) { private fun registerWxAPIInternal(appId: String, context: Context) {
with(context) {
val appInfo =
packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val enableLogging = appInfo.metaData.getString("WeChatDebugLogging", "")
if (enableLogging == "true" && BuildConfig.DEBUG) {
startLog()
}
}
val api = WXAPIFactory.createWXAPI(context.applicationContext, appId) val api = WXAPIFactory.createWXAPI(context.applicationContext, appId)
registered = api.registerApp(appId) registered = api.registerApp(appId)
wxApi = api wxApi = api
} }
fun startLog(call: MethodCall, result: MethodChannel.Result) { fun startLog() {
wxApi?.setLogImpl(this); wxApi?.setLogImpl(this)
result.success(true);
}
fun stopLog(call: MethodCall, result: MethodChannel.Result) {
wxApi?.setLogImpl(null);
result.success(true);
} }
override fun d(p0: String?, p1: String?) { override fun d(p0: String?, p1: String?) {
when { logToFlutter(p0,p1)
p1 != null -> {
Log.d(p0, p1);
}
}
} }
override fun i(p0: String?, p1: String?) { override fun i(p0: String?, p1: String?) {
when { logToFlutter(p0,p1)
p1 != null -> {
Log.d(p0, p1);
}
}
} }
override fun e(p0: String?, p1: String?) { override fun e(p0: String?, p1: String?) {
when { logToFlutter(p0,p1)
p1 != null -> {
Log.d(p0, p1);
}
}
} }
override fun v(p0: String?, p1: String?) { override fun v(p0: String?, p1: String?) {
when { logToFlutter(p0,p1)
p1 != null -> {
Log.d(p0, p1);
}
}
} }
override fun w(p0: String?, p1: String?) { override fun w(p0: String?, p1: String?) {
when { logToFlutter(p0,p1)
p1 != null -> {
Log.d(p0, p1);
}
} }
private fun logToFlutter(tag:String?,message:String?){
FluwxPlugin.callingChannel?.invokeMethod("wechatLog", mapOf(
"detail" to "$tag : $message"
))
} }
} }
......
package com.jarvan.fluwx.utils
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
internal const val KEY_FLUWX_REQUEST_INFO_EXT_MSG = "KEY_FLUWX_REQUEST_INFO_EXT_MSG"
internal const val KEY_FLUWX_REQUEST_INFO_BUNDLE = "KEY_FLUWX_REQUEST_INFO_BUNDLE"
internal fun Activity.startFlutterActivity(
wxRequestBundle: Bundle? = null,
bundle: Bundle? = null,
) {
flutterActivityIntent()?.also { intent ->
intent.addFluwxExtras()
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
bundle?.let {
intent.putExtras(it)
}
wxRequestBundle?.let {
intent.putExtra(KEY_FLUWX_REQUEST_INFO_BUNDLE, it)
}
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Log.w("fluwx", "Can not start activity for Intent: $intent")
} finally {
finish()
}
}
}
internal fun Context.flutterActivityIntent(): Intent? {
val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val flutterActivity = appInfo.metaData.getString("FluwxFlutterActivity", "")
return if (flutterActivity.isBlank()) {
packageManager.getLaunchIntentForPackage(packageName)
} else {
Intent().also {
it.setClassName(this, "${packageName}.$flutterActivity")
}
}
}
internal fun Intent.addFluwxExtras() {
putExtra("fluwx_payload_from_fluwx", true)
}
\ No newline at end of file
...@@ -22,6 +22,8 @@ import android.os.Bundle ...@@ -22,6 +22,8 @@ import android.os.Bundle
import com.jarvan.fluwx.handlers.FluwxResponseHandler import com.jarvan.fluwx.handlers.FluwxResponseHandler
import com.jarvan.fluwx.handlers.FluwxRequestHandler import com.jarvan.fluwx.handlers.FluwxRequestHandler
import com.jarvan.fluwx.handlers.WXAPiHandler import com.jarvan.fluwx.handlers.WXAPiHandler
import com.jarvan.fluwx.utils.flutterActivityIntent
import com.jarvan.fluwx.utils.startFlutterActivity
import com.tencent.mm.opensdk.modelbase.BaseReq import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelbase.BaseResp import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
...@@ -38,19 +40,18 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler { ...@@ -38,19 +40,18 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
try { try {
if (!WXAPiHandler.wxApiRegistered) { if (!WXAPiHandler.wxApiRegistered) {
val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA) val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val wechatAppId = appInfo.metaData.getString("weChatAppId") val wechatAppId = appInfo.metaData.getString("WeChatAppId")
if (wechatAppId != null) { if (wechatAppId != null) {
WXAPiHandler.setupWxApi(wechatAppId,this) WXAPiHandler.setupWxApi(wechatAppId,this)
WXAPiHandler.coolBoot = true WXAPiHandler.coolBoot = true
} else { } else {
Log.e("fluwx","can't load meta-data weChatAppId") Log.w("fluwx","can't load meta-data weChatAppId")
} }
} }
WXAPiHandler.wxApi?.handleIntent(intent, this) WXAPiHandler.wxApi?.handleIntent(intent, this)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
startSpecifiedActivity(defaultFlutterActivityAction()) this.startFlutterActivity()
finish()
} }
} }
...@@ -63,8 +64,7 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler { ...@@ -63,8 +64,7 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
WXAPiHandler.wxApi?.handleIntent(intent, this) WXAPiHandler.wxApi?.handleIntent(intent, this)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
startSpecifiedActivity(defaultFlutterActivityAction()) this.startFlutterActivity()
finish()
} }
} }
...@@ -80,21 +80,4 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler { ...@@ -80,21 +80,4 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
FluwxResponseHandler.handleResponse(resp) FluwxResponseHandler.handleResponse(resp)
finish() finish()
} }
private fun startSpecifiedActivity(action: String, bundle: Bundle? = null, bundleKey: String? = null) {
Intent(action).run {
bundleKey?.let {
putExtra(bundleKey, bundle)
}
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
packageManager?.let {
resolveActivity(packageManager)?.also {
startActivity(this)
finish()
}
}
}
}
private fun defaultFlutterActivityAction(): String = "$packageName.FlutterActivity"
} }
\ No newline at end of file
package com.jarvan.fluwx
class FluwxTest {
fun test(){}
}
\ No newline at end of file
package com.jarvan.fluwx
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlin.test.Test
import org.mockito.Mockito
/*
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
*
* Once you have built the plugin's example app, you can run these tests from the command
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
* you can run them directly from IDEs that support JUnit such as Android Studio.
*/
internal class FluwxPluginTest {
@Test
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
val plugin = FluwxPlugin()
val call = MethodCall("getPlatformVersion", null)
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
plugin.onMethodCall(call, mockResult)
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
}
}
...@@ -4,8 +4,9 @@ The purpose of `sendWeChatAuth` is to get auth code and then get information for ...@@ -4,8 +4,9 @@ The purpose of `sendWeChatAuth` is to get auth code and then get information for
Getting `access_token` is not supported in `fluwx`. For `access_token`, please visit [official documents](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html). Getting `access_token` is not supported in `fluwx`. For `access_token`, please visit [official documents](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html).
```dart ```dart
sendWeChatAuth(scope: "snsapi_userinfo", state: "wechat_sdk_demo_test"); Fluwx fluwx = Fluwx();
``` fluwx.authBy(which: NormalAuth(scope: 'snsapi_userinfo', state: 'wechat_sdk_demo_test'));
```
> WHY? I think we shall fetch access_token or user info at backend. > WHY? I think we shall fetch access_token or user info at backend.
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
`sendWeChatAuth`的目的是为了获取code,拿到了code才能进行微信登录,可以通过[官方文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html)查看具体流程。 `sendWeChatAuth`的目的是为了获取code,拿到了code才能进行微信登录,可以通过[官方文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html)查看具体流程。
```dart ```dart
sendWeChatAuth(scope: "snsapi_userinfo", state: "wechat_sdk_demo_test"); Fluwx fluwx = Fluwx();
fluwx.authBy(which: NormalAuth(scope: 'snsapi_userinfo', state: 'wechat_sdk_demo_test'));
``` ```
> 为什么不支持获取用户信息? 我认为获取用户信息应该后端来做,即使没有后端,你也可以在dart层自己实现. > 为什么不支持获取用户信息? 我认为获取用户信息应该后端来做,即使没有后端,你也可以在dart层自己实现.
......
## Basic knowledge ## Basic knowledge
### Response from WeChat ### Response from WeChat
Actually, almost every result from functions like `shareToWeChat` or `payWithWeChat` which call `sendRequest` in native doesn't makes sense. The `bool` value is the result of `sendRequest`. Actually, almost every result from functions like `share` or `pay` which call `sendRequest` in native doesn't makes sense. The `bool` value is the result of `sendRequest`.
So if you want get the real result you shall do like this: So if you want get the real result you shall do like this:
```dart ```dart
fluwx.weChatResponseEventHandler.listen((res) { fluwx.subscribeResponse((response) {
if (res is fluwx.WeChatPaymentResponse) { if (response is WeChatAuthResponse) {
// do something here
} }
}); });
``` ```
Take a look at subclasses of `BaseWeChatResponse` for help. Take a look at subclasses of `WeChatResponse` for help.
> NOTE: If you get `errCode = -1`, please read the WeChatSDK document for help. There are to many cases lead to that. > NOTE: If you get `errCode = -1`, please read the WeChatSDK document for help. There are to many cases lead to that.
You can also unsubscribe response by calling `fluwx.subscribeResponse`.
### Images in WeChat ### Images in WeChat
The are four built-in types of `WeChatImage` in `fluwx`: The are four built-in types of `WeChatImage` in `fluwx`:
......
...@@ -6,15 +6,17 @@ ...@@ -6,15 +6,17 @@
为了获取真实的回调,你应该这样做: 为了获取真实的回调,你应该这样做:
```dart ```dart
fluwx.weChatResponseEventHandler.listen((res) { fluwx.subscribeResponse((response) {
if (res is fluwx.WeChatPaymentResponse) { if (response is WeChatAuthResponse) {
// do something here
} }
}); });
``` ```
> 笔记: 如果你的 `errCode = -1`, 那请阅读微信官方文档,因为-1的原因数不胜数. > 笔记: 如果你的 `errCode = -1`, 那请阅读微信官方文档,因为-1的原因数不胜数.
你也可以通过`fluwx.unsubscribeResponse`取消订阅消息。
### 图片 ### 图片
有四种内置 `WeChatImage`: 有四种内置 `WeChatImage`:
......
## Launch App from H5
Fluwx supports launch app from `<wx-open-launch-app>`, and pass `extInfo` to your app.
For Android side, you need add the following action for your FlutterActivity in `AndroidManifest.xml`:
```
<intent-filter>
<action android:name="${applicationId}.FlutterActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent-filter>
```
At the same time, you also need to add `<meta-data>` in application which used to store your WeChat AppId:
```xml
<meta-data
android:name="weChatAppId"
android:value="12345678" />
```
If you want to pass `extInfo` to Flutter, you need to add the following code in `MainActivity.kt`:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//If you didn't configure WxAPI, add the following code
WXAPiHandler.setupWxApi("wxd930ea5d5a258f4f",this)
//Get Ext-Info from Intent.
FluwxRequestHandler.handleRequestInfoFromIntent(intent)
}
```
If you want to custom your request logic, you need add the `<meta-data>` in application:
```xml
<meta-data
android:name="handleWeChatRequestByFluwx"
android:value="false" />
```
And then, set `FluwxRequestHandler.customOnReqDelegate` on your own.
## on Android 11
Please add the following queries in your app's `AndroidManifest.xml`:
```xml
<queries>
<intent>
<action android:name="${applicationId}.FlutterActivity" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent>
</queries>
```
## IOS ## IOS
Please register your WXApi in your `AppDelegate`: Please register your WXApi in your `AppDelegate`:
```oc ```oc
......
## 从H5启动app
Fluwx 支持从`<wx-open-launch-app>`启动你的app, 并且支持传递`extInfo`给你的app.
对于Android来说,你要在`AndroidManifest.xml`中给你的`Activity`加上一个标签:
```
<intent-filter>
<action android:name="${applicationId}.FlutterActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent-filter>
```
与此同时,你还需要在需要在application中加上`<meta-data>`,把你的appId放进去:
```xml
<meta-data
android:name="weChatAppId"
android:value="12345678" />
```
如果你想把`extInfo`传给Flutter, 你要在`MainActivity`加上如下代码:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//If you didn't configure WxAPI, add the following code
WXAPiHandler.setupWxApi("wxd930ea5d5a258f4f",this)
//Get Ext-Info from Intent.
FluwxRequestHandler.handleRequestInfoFromIntent(intent)
}
```
如果你想自定义你的调用逻辑, 你需要在application中加上`<meta-data>`:
```xml
<meta-data
android:name="handleWeChatRequestByFluwx"
android:value="false" />
```
然后, 自己实现 `FluwxRequestHandler.customOnReqDelegate`.
## 兼容Android 11
请在你的应用的`AndroidManifest.xml`中添加以下queries:
```xml
<queries>
<intent>
<action android:name="${applicationId}.FlutterActivity" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent>
</queries>
```
## IOS ## IOS
请在你的`AppDelegate`中主动注册`WXApi` 请在你的`AppDelegate`中主动注册`WXApi`
......
## Upgrade to V4
`Fluwx v4` not only brings a lot exciting functionalities but also breaking changes。
- Now we need to initialize the instance using `Fluwx fluwx = Fluwx()`
- Listening response from WeChat changed to `subscribeResponse` and also adding `unsubscribeResponse` to support
cancel listening.
- Keyword `wechat` in some functions is removed.
- Some functions are extracted to a single function,and now you can pass different params instead.
- Some configurations are moved to[pubspec.yaml](../example/pubspec.yaml),for example, you can enable/disable log in `pubspec.yaml`.
- `no_pay` can be enabled by [pubspec.yaml](../example/pubspec.yaml), reference example for more details.
\ No newline at end of file
## 升级到V4
`Fluwx v4`带来了很多令人兴奋的功能,但也带来了少破坏性更新。
- 现在我们需要使用`Fluwx fluwx = Fluwx()`初始化实例
- 监听微信回调变成了`subscribeResponse`并且增加了`unsubscribeResponse`以支持取消监听
- 很多带有`wechat`关键字的函数已经把`wechat`关键字删除了
- 很多方法被整到了一个函数中,现在你可以传递不同的对象实现对应的业务
- 一些配置被移动到了[pubspec.yaml](../example/pubspec.yaml),可以通过`pubspec.yaml`配置是否开启日志等等
- `no_pay`现在也通过[pubspec.yaml](../example/pubspec.yaml)配置,具体可以参加example.
\ No newline at end of file
...@@ -3,15 +3,16 @@ ...@@ -3,15 +3,16 @@
Calling payment is easy but to make it work isn't not so easy: Calling payment is easy but to make it work isn't not so easy:
```dart ```dart
payWithWeChat( fluwx.pay(
appId: result['appid'], which: Payment(
partnerId: result['partnerid'], appId: result['appid'].toString(),
prepayId: result['prepayid'], partnerId: result['partnerid'].toString(),
packageValue: result['package'], prepayId: result['prepayid'].toString(),
nonceStr: result['noncestr'], packageValue: result['package'].toString(),
timeStamp: result['timestamp'], nonceStr: result['noncestr'].toString(),
sign: result['sign'], timestamp: result['timestamp'],
); sign: result['sign'].toString(),
));
``` ```
Take a look at [payment document](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1#) for help. Take a look at [payment document](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1#) for help.
...@@ -3,31 +3,16 @@ ...@@ -3,31 +3,16 @@
调用支付方法很简单,但想成功并不简单: 调用支付方法很简单,但想成功并不简单:
```dart ```dart
payWithWeChat( fluwx.pay(
appId: result['appid'], which: Payment(
partnerId: result['partnerid'], appId: result['appid'].toString(),
prepayId: result['prepayid'], partnerId: result['partnerid'].toString(),
packageValue: result['package'], prepayId: result['prepayid'].toString(),
nonceStr: result['noncestr'], packageValue: result['package'].toString(),
timeStamp: result['timestamp'], nonceStr: result['noncestr'].toString(),
sign: result['sign'], timestamp: result['timestamp'],
); sign: result['sign'].toString(),
``` ));
## iOS 支付
* 配置`URL Schemes` ,内容为应用的`AppID`, 可以登录微信开放平台查看。编辑`ios/Runner/Info.plist`
```xml
<key>CFBundleURLSchemes</key>
<array>
<string>wx84cxxxxxx</string>
</array>
```
* 配置`LSApplicationQueriesSchemes`
![image-20210523140138835](https://gitee.com/inkkk0516/typora/raw/master/image-20210523140138835.png)
* 使用 * 使用
......
...@@ -2,18 +2,18 @@ ...@@ -2,18 +2,18 @@
Simple and easy: Simple and easy:
```dart ```dart
shareToWeChat(WeChatShareTextModel("source text", scene: WeChatScene.SESSION)); fluwx.share(WeChatShareTextModel("source text", scene: WeChatScene.SESSION));
``` ```
The destination of sharing can be SESSION(default),TIMELINE or FAVORITE.However,mini-program only support SESSION. The destination of sharing can be SESSION(default),TIMELINE or FAVORITE.However,mini-program only support SESSION.
```dart ```dart
///[WeChatScene.SESSION]会话 ///[WeChatScene.session]会话
///[WeChatScene.TIMELINE]朋友圈 ///[WeChatScene.timeline]朋友圈
///[WeChatScene.FAVORITE]收藏 ///[WeChatScene.favorite]收藏
enum WeChatScene { enum WeChatScene {
SESSION, session,
TIMELINE, timeline,
FAVORITE favorite
} }
``` ```
......
...@@ -2,21 +2,22 @@ ...@@ -2,21 +2,22 @@
简单: 简单:
```dart ```dart
shareToWeChat(WeChatShareTextModel("source text", scene: WeChatScene.SESSION)); fluwx.share(WeChatShareTextModel("source text", scene: WeChatScene.SESSION));
``` ```
绝大部分分享可以分享到会话,朋友圈,收藏(小程序目前只能分享到会话)。默认分享到会话。 绝大部分分享可以分享到会话,朋友圈,收藏(小程序目前只能分享到会话)。默认分享到会话。
```dart ```dart
///[WeChatScene.SESSION]会话 ///[WeChatScene.session]会话
///[WeChatScene.TIMELINE]朋友圈 ///[WeChatScene.timeline]朋友圈
///[WeChatScene.FAVORITE]收藏 ///[WeChatScene.favorite]收藏
enum WeChatScene { enum WeChatScene {
SESSION, session,
TIMELINE, timeline,
FAVORITE favorite
} }
``` ```
支持的分享各类: 支持的分享各类:
- WeChatShareTextModel - WeChatShareTextModel
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
**/Podfile.lock migrate_working_dir/
# IntelliJ related # IntelliJ related
*.iml *.iml
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
# Flutter/Dart/Pub related # Flutter/Dart/Pub related
**/doc/api/ **/doc/api/
**/ios/Flutter/.last_build_id **/ios/Flutter/.last_build_id
**/ios/Flutter/flutter_export_environment.sh
.dart_tool/ .dart_tool/
.flutter-plugins .flutter-plugins
.flutter-plugins-dependencies .flutter-plugins-dependencies
...@@ -33,11 +32,13 @@ ...@@ -33,11 +32,13 @@
.pub/ .pub/
/build/ /build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related # Symbolication related
app.*.symbols app.*.symbols
# Obfuscation related # Obfuscation related
app.*.map.json app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 0b8abb4724aa590dd0f429683339b1e045a1594d
channel: stable
project_type: app
...@@ -8,9 +8,9 @@ This project is a starting point for a Flutter application. ...@@ -8,9 +8,9 @@ This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project: A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter, view our For help getting started with Flutter development, view the
[online documentation](https://flutter.dev/docs), which offers tutorials, [online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference. samples, guidance on mobile development, and a full API reference.
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
gradle-wrapper.jar gradle-wrapper.jar
**.classpath
**.project
**.settings
/.gradle /.gradle
/captures/ /captures/
/gradlew /gradlew
/gradlew.bat /gradlew.bat
/local.properties /local.properties
GeneratedPluginRegistrant.java GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
...@@ -8,7 +8,6 @@ if (localPropertiesFile.exists()) { ...@@ -8,7 +8,6 @@ if (localPropertiesFile.exists()) {
def flutterRoot = localProperties.getProperty('flutter.sdk') def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) { if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
} }
...@@ -27,27 +26,26 @@ apply plugin: 'kotlin-android' ...@@ -27,27 +26,26 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 31 namespace "com.jarvan.fluwx_example"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
lintOptions {
disable 'InvalidPackage'
}
defaultConfig { defaultConfig {
// 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 "net.sourceforge.simcpux" applicationId "net.sourceforge.simcpux"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 31 targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
flavorDimensions "branch"
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
...@@ -73,7 +71,4 @@ flutter { ...@@ -73,7 +71,4 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
} }
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.jarvan.fluwx_example">
<!-- 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
...@@ -28,27 +27,15 @@ ...@@ -28,27 +27,15 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:exported="true"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="${applicationId}.FlutterActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="${applicationId}"
android:path="/"
android:scheme="wechatextmsg" />
</intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
......
...@@ -5,7 +5,6 @@ import android.os.Bundle ...@@ -5,7 +5,6 @@ import android.os.Bundle
import com.jarvan.fluwx.handlers.FluwxRequestHandler import com.jarvan.fluwx.handlers.FluwxRequestHandler
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() { class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources> </resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
...@@ -6,7 +6,7 @@ buildscript { ...@@ -6,7 +6,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.1.2' classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
...@@ -26,6 +26,6 @@ subprojects { ...@@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(':app')
} }
task clean(type: Delete) { tasks.register("clean", Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }
...@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME ...@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
include ':app' pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
def plugins = new Properties() plugins {
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
if (pluginsFile.exists()) { }
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
} }
plugins.each { name, path -> include ":app"
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name" apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
project(":$name").projectDir = pluginDirectory
}
// This is a basic Flutter integration test.
//
// Since integration tests run in a full Flutter application, they can interact
// with the host side of a plugin implementation, unlike Dart unit tests.
//
// For more information about Flutter integration tests, please see
// https://docs.flutter.dev/cookbook/testing/integration/introduction
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:fluwx/fluwx.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
});
}
**/dgph
*.mode1v3 *.mode1v3
*.mode2v3 *.mode2v3
*.moved-aside *.moved-aside
...@@ -11,7 +12,6 @@ ...@@ -11,7 +12,6 @@
Icon? Icon?
**/Pods/ **/Pods/
**/.symlinks/ **/.symlinks/
Podfile.lock
profile profile
xcuserdata xcuserdata
**/.generated/ **/.generated/
...@@ -19,6 +19,7 @@ Flutter/App.framework ...@@ -19,6 +19,7 @@ Flutter/App.framework
Flutter/Flutter.framework Flutter/Flutter.framework
Flutter/Flutter.podspec Flutter/Flutter.podspec
Flutter/Generated.xcconfig Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx Flutter/app.flx
Flutter/app.zip Flutter/app.zip
Flutter/flutter_assets/ Flutter/flutter_assets/
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>App</string> <string>App</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
......
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"
...@@ -29,6 +29,9 @@ flutter_ios_podfile_setup ...@@ -29,6 +29,9 @@ flutter_ios_podfile_setup
target 'Runner' do target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end end
post_install do |installer| post_install do |installer|
......
PODS:
- Flutter (1.0.0)
- fluwx (0.0.1):
- Flutter
- fluwx/no_pay (= 0.0.1)
- fluwx/no_pay (0.0.1):
- Flutter
- "OpenWeChatSDKNoPay (~> 2.0.2+1)"
- integration_test (0.0.1):
- Flutter
- OpenWeChatSDKNoPay (2.0.2)
DEPENDENCIES:
- Flutter (from `Flutter`)
- fluwx (from `.symlinks/plugins/fluwx/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
SPEC REPOS:
trunk:
- OpenWeChatSDKNoPay
EXTERNAL SOURCES:
Flutter:
:path: Flutter
fluwx:
:path: ".symlinks/plugins/fluwx/ios"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
fluwx: f608fff0e3a8c30199b626fed590d83cdd820a52
integration_test: 13825b8a9334a850581300559b8839134b124670
OpenWeChatSDKNoPay: 59a9628a746352bb400329cb7f12e6dc7096bf6e
PODFILE CHECKSUM: beab77b38961de946f08660e554f80ac174dc842
COCOAPODS: 1.12.1
...@@ -8,15 +8,27 @@ ...@@ -8,15 +8,27 @@
/* 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 */; };
331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 331C80F3294D02FB00263BE5 /* RunnerTests.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
62B88C18565D88C02906C10C /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27C08EA6C421C7E89069DE6B /* libPods-Runner.a */; }; 5255F9316848A30ED1F84FF5 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 61BB2832514DC5AC842E583C /* libPods-RunnerTests.a */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
F3F2D014A64FDD073F05B4CA /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F1790AB3CDE0ED1C0E115C2 /* libPods-Runner.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = { 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
...@@ -31,11 +43,18 @@ ...@@ -31,11 +43,18 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
06B1F1CBB0F5E70CD45DBD70 /* Runner.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
1201E93D5A4AF06F8B2154F7 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
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>"; };
27C08EA6C421C7E89069DE6B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 1F1790AB3CDE0ED1C0E115C2 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
32AE9497AA826921991B27BF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80F3294D02FB00263BE5 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = "<group>"; };
3796AEEE99D23C2985F27594 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
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>"; };
3CDE7FB3ADAC9EC781FA9BD4 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
517551098246B7C71A7D63D8 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
61BB2832514DC5AC842E583C /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
...@@ -47,38 +66,36 @@ ...@@ -47,38 +66,36 @@
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>"; };
9E479F6222F46DD1BF9F2E3E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; A9DDFA5760AD388C7AE67D68 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
D9FC4FD7820D62136E2C7EAB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; F071110615569824B74AF901 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
331C80EE294D02FB00263BE5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5255F9316848A30ED1F84FF5 /* libPods-RunnerTests.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = { 97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
62B88C18565D88C02906C10C /* libPods-Runner.a in Frameworks */, F3F2D014A64FDD073F05B4CA /* libPods-Runner.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
3510C167085BA3D3F8540F37 /* Pods */ = { 331C80F2294D02FB00263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
9E479F6222F46DD1BF9F2E3E /* Pods-Runner.debug.xcconfig */,
32AE9497AA826921991B27BF /* Pods-Runner.release.xcconfig */,
D9FC4FD7820D62136E2C7EAB /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
8513A54653CE3C4FB2C564F0 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
27C08EA6C421C7E89069DE6B /* libPods-Runner.a */, 331C80F3294D02FB00263BE5 /* RunnerTests.m */,
); );
name = Frameworks; path = RunnerTests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
...@@ -97,9 +114,10 @@ ...@@ -97,9 +114,10 @@
children = ( children = (
9740EEB11CF90186004384FC /* Flutter */, 9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
331C80F2294D02FB00263BE5 /* RunnerTests */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
3510C167085BA3D3F8540F37 /* Pods */, B7024FD9E7130F00288DF469 /* Pods */,
8513A54653CE3C4FB2C564F0 /* Frameworks */, C05958523A4B26DE07C628DD /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
...@@ -107,6 +125,7 @@ ...@@ -107,6 +125,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
97C146EE1CF9000F007C117D /* Runner.app */, 97C146EE1CF9000F007C117D /* Runner.app */,
331C80F1294D02FB00263BE5 /* RunnerTests.xctest */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -123,6 +142,7 @@ ...@@ -123,6 +142,7 @@
97C146F11CF9000F007C117D /* Supporting Files */, 97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
06B1F1CBB0F5E70CD45DBD70 /* Runner.entitlements */,
); );
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -135,14 +155,55 @@ ...@@ -135,14 +155,55 @@
name = "Supporting Files"; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
B7024FD9E7130F00288DF469 /* Pods */ = {
isa = PBXGroup;
children = (
F071110615569824B74AF901 /* Pods-Runner.debug.xcconfig */,
3796AEEE99D23C2985F27594 /* Pods-Runner.release.xcconfig */,
517551098246B7C71A7D63D8 /* Pods-Runner.profile.xcconfig */,
1201E93D5A4AF06F8B2154F7 /* Pods-RunnerTests.debug.xcconfig */,
A9DDFA5760AD388C7AE67D68 /* Pods-RunnerTests.release.xcconfig */,
3CDE7FB3ADAC9EC781FA9BD4 /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
C05958523A4B26DE07C628DD /* Frameworks */ = {
isa = PBXGroup;
children = (
1F1790AB3CDE0ED1C0E115C2 /* libPods-Runner.a */,
61BB2832514DC5AC842E583C /* libPods-RunnerTests.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
331C80F0294D02FB00263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
A4A7D39886C9A1DA97227C83 /* [CP] Check Pods Manifest.lock */,
331C80ED294D02FB00263BE5 /* Sources */,
331C80EE294D02FB00263BE5 /* Frameworks */,
331C80EF294D02FB00263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C80F6294D02FB00263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = { 97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
E2E8F9B5C8F1E9545B8C092B /* [CP] Check Pods Manifest.lock */, 6284E94FC7A5514593210205 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */, 9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
...@@ -165,16 +226,23 @@ ...@@ -165,16 +226,23 @@
97C146E61CF9000F007C117D /* Project object */ = { 97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
KnownAssetTags = (
New,
);
LastUpgradeCheck = 1300; LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "The Chromium Authors"; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
331C80F0294D02FB00263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
}; };
}; };
}; };
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 9.3";
developmentRegion = en; developmentRegion = en;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
...@@ -187,11 +255,19 @@ ...@@ -187,11 +255,19 @@
projectRoot = ""; projectRoot = "";
targets = ( targets = (
97C146ED1CF9000F007C117D /* Runner */, 97C146ED1CF9000F007C117D /* Runner */,
331C80F0294D02FB00263BE5 /* RunnerTests */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
331C80EF294D02FB00263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = { 97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
...@@ -213,6 +289,7 @@ ...@@ -213,6 +289,7 @@
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
); );
name = "Thin Binary"; name = "Thin Binary";
outputPaths = ( outputPaths = (
...@@ -221,6 +298,28 @@ ...@@ -221,6 +298,28 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
6284E94FC7A5514593210205 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
...@@ -236,7 +335,7 @@ ...@@ -236,7 +335,7 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
}; };
E2E8F9B5C8F1E9545B8C092B /* [CP] Check Pods Manifest.lock */ = { A4A7D39886C9A1DA97227C83 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -251,7 +350,7 @@ ...@@ -251,7 +350,7 @@
outputFileListPaths = ( outputFileListPaths = (
); );
outputPaths = ( outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
...@@ -261,6 +360,14 @@ ...@@ -261,6 +360,14 @@
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
331C80ED294D02FB00263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = { 97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
...@@ -273,6 +380,14 @@ ...@@ -273,6 +380,14 @@
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C80F6294D02FB00263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = { 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
...@@ -316,7 +431,6 @@ ...@@ -316,7 +431,6 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
...@@ -335,7 +449,7 @@ ...@@ -335,7 +449,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
...@@ -349,24 +463,63 @@ ...@@ -349,24 +463,63 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 8JJXUFV6F7;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample1; PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Profile; name = Profile;
}; };
331C80F8294D02FB00263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 1201E93D5A4AF06F8B2154F7 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C80F9294D02FB00263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A9DDFA5760AD388C7AE67D68 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C80FA294D02FB00263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3CDE7FB3ADAC9EC781FA9BD4 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = { 97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
...@@ -390,7 +543,6 @@ ...@@ -390,7 +543,6 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
...@@ -415,7 +567,7 @@ ...@@ -415,7 +567,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
...@@ -446,7 +598,6 @@ ...@@ -446,7 +598,6 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
...@@ -465,7 +616,7 @@ ...@@ -465,7 +616,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
...@@ -479,19 +630,16 @@ ...@@ -479,19 +630,16 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 8JJXUFV6F7;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample1; PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
...@@ -502,19 +650,16 @@ ...@@ -502,19 +650,16 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 8JJXUFV6F7;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample1; PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
...@@ -523,6 +668,16 @@ ...@@ -523,6 +668,16 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C80F8294D02FB00263BE5 /* Debug */,
331C80F9294D02FB00263BE5 /* Release */,
331C80FA294D02FB00263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
......
<?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"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
...@@ -37,6 +37,17 @@ ...@@ -37,6 +37,17 @@
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<Testables> <Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C80F0294D02FB00263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
......
<?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>PreviewsEnabled</key>
<false/>
</dict>
</plist>
...@@ -2,10 +2,16 @@ ...@@ -2,10 +2,16 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Fluwx</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleGetInfoString</key>
<string/>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
...@@ -20,22 +26,39 @@ ...@@ -20,22 +26,39 @@
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
<array> <array>
<dict/>
<dict> <dict>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Editor</string> <string>Editor</string>
<key>CFBundleURLName</key> <key>CFBundleURLName</key>
<string>wxd930ea5d5a258f4f</string> <string>weixin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>123456</string>
</array>
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSApplicationCategoryType</key>
<string/>
<key>LSApplicationQueriesSchemes</key> <key>LSApplicationQueriesSchemes</key>
<array> <array>
<string>weixin</string> <string>weixin</string>
<string>weixinULAPI</string> <string>weixinULAPI</string>
<string>weixinURLParamsAPI</string>
</array> </array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
...@@ -55,9 +78,5 @@ ...@@ -55,9 +78,5 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict> </dict>
</plist> </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>com.apple.developer.associated-domains</key>
<array>
<string>applinks:testdomain.com</string>
</array>
</dict>
</plist>
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
@import fluwx;
// This demonstrates a simple unit test of the Objective-C portion of this plugin's implementation.
//
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
@interface RunnerTests : XCTestCase
@end
@implementation RunnerTests
- (void)testExample {
FluwxPlugin *plugin = [[FluwxPlugin alloc] init];
FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getPlatformVersion"
arguments:nil];
XCTestExpectation *expectation = [self expectationWithDescription:@"result block must be called"];
[plugin handleMethodCall:call
result:^(id result) {
NSString *expected = [NSString
stringWithFormat:@"iOS %@", UIDevice.currentDevice.systemVersion];
XCTAssertEqualObjects(result, expected);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:1 handler:nil];
}
@end
...@@ -26,6 +26,8 @@ class MyApp extends StatefulWidget { ...@@ -26,6 +26,8 @@ class MyApp extends StatefulWidget {
} }
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
Fluwx fluwx = Fluwx();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
...@@ -33,14 +35,14 @@ class _MyAppState extends State<MyApp> { ...@@ -33,14 +35,14 @@ class _MyAppState extends State<MyApp> {
} }
_initFluwx() async { _initFluwx() async {
await registerWxApi( await fluwx.registerApi(
appId: 'wxd930ea5d5a258f4f', appId: 'wxd930ea5d5a258f4f',
doOnAndroid: true, doOnAndroid: true,
doOnIOS: true, doOnIOS: true,
universalLink: 'https://your.univerallink.com/link/', universalLink: 'https://your.univerallink.com/link/',
); );
var result = await isWeChatInstalled; var result = await fluwx.isWeChatInstalled;
print('is installed $result'); debugPrint('is installed $result');
} }
// Platform messages are asynchronous, so we initialize in an async method. // Platform messages are asynchronous, so we initialize in an async method.
...@@ -51,20 +53,20 @@ class _MyAppState extends State<MyApp> { ...@@ -51,20 +53,20 @@ class _MyAppState extends State<MyApp> {
return MaterialApp( return MaterialApp(
routes: <String, WidgetBuilder>{ routes: <String, WidgetBuilder>{
'shareText': (context) => ShareTextPage(), 'shareText': (context) => ShareTextPage(),
'shareImage': (context) => ShareImagePage(), 'shareImage': (context) => const ShareImagePage(),
'shareWebPage': (context) => ShareWebPagePage(), 'shareWebPage': (context) => const ShareWebPagePage(),
'shareMusic': (context) => ShareMusicPage(), 'shareMusic': (context) => const ShareMusicPage(),
'shareVideo': (context) => ShareVideoPage(), 'shareVideo': (context) => const ShareVideoPage(),
'sendAuth': (context) => SendAuthPage(), 'sendAuth': (context) => const SendAuthPage(),
'shareMiniProgram': (context) => ShareMiniProgramPage(), 'shareMiniProgram': (context) => ShareMiniProgramPage(),
'pay': (context) => PayPage(), 'pay': (context) => const PayPage(),
'launchMiniProgram': (context) => LaunchMiniProgramPage(), 'launchMiniProgram': (context) => const LaunchMiniProgramPage(),
'subscribeMessage': (ctx) => SubscribeMessagePage(), 'subscribeMessage': (ctx) => const SubscribeMessagePage(),
'AuthByQRCode': (ctx) => AuthByQRCodePage(), 'AuthByQRCode': (ctx) => const AuthByQRCodePage(),
'AutoDeduct': (ctx) => SignAutoDeductPage(), 'AutoDeduct': (ctx) => const SignAutoDeductPage(),
}, },
home: Scaffold( home: Scaffold(
appBar: AppBar(title: const Text('Plugin example app')), appBar: AppBar(title: const Text('Fluwx sample')),
body: ShareSelectorPage(), body: ShareSelectorPage(),
), ),
); );
...@@ -72,6 +74,10 @@ class _MyAppState extends State<MyApp> { ...@@ -72,6 +74,10 @@ class _MyAppState extends State<MyApp> {
} }
class ShareSelectorPage extends StatelessWidget { class ShareSelectorPage extends StatelessWidget {
final Fluwx fluwx = Fluwx();
ShareSelectorPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return Center(
...@@ -81,32 +87,12 @@ class ShareSelectorPage extends StatelessWidget { ...@@ -81,32 +87,12 @@ class ShareSelectorPage extends StatelessWidget {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: OutlinedButton( child: OutlinedButton(
onPressed: () async { onPressed: () async {
String? extMsg = await getExtMsg(); String? extMsg = await fluwx.getExtMsg();
print('extMsg:$extMsg\n'); debugPrint('extMsg:$extMsg\n');
}, },
child: const Text('Get ExtMessage'), child: const Text('Get ExtMessage'),
), ),
), ),
Padding(
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
onPressed: () async {
bool? success = await startLog(logLevel: WXLogLevel.NORMAL);
print('startLog:$success\n');
},
child: const Text('start log'),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
onPressed: () async {
dynamic success = await stopLog();
print('stopLog:$success\n');
},
child: const Text('stop log'),
),
),
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: OutlinedButton( child: OutlinedButton(
...@@ -219,7 +205,7 @@ class ShareSelectorPage extends StatelessWidget { ...@@ -219,7 +205,7 @@ class ShareSelectorPage extends StatelessWidget {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: OutlinedButton( child: OutlinedButton(
onPressed: () { onPressed: () {
openWeChatApp(); fluwx.open(target: WeChatApp());
}, },
child: const Text('Open WeChat App'), child: const Text('Open WeChat App'),
), ),
......
...@@ -6,11 +6,11 @@ class AuthByQRCodePage extends StatefulWidget { ...@@ -6,11 +6,11 @@ class AuthByQRCodePage extends StatefulWidget {
const AuthByQRCodePage({Key? key}) : super(key: key); const AuthByQRCodePage({Key? key}) : super(key: key);
@override @override
_AuthByQRCodePageState createState() => _AuthByQRCodePageState(); State<AuthByQRCodePage> createState() => _AuthByQRCodePageState();
} }
class _AuthByQRCodePageState extends State<AuthByQRCodePage> { class _AuthByQRCodePageState extends State<AuthByQRCodePage> {
String _status = 'status'; final String _status = 'status';
Uint8List? _image; Uint8List? _image;
@override @override
......
...@@ -5,30 +5,35 @@ class LaunchMiniProgramPage extends StatefulWidget { ...@@ -5,30 +5,35 @@ class LaunchMiniProgramPage extends StatefulWidget {
const LaunchMiniProgramPage({Key? key}) : super(key: key); const LaunchMiniProgramPage({Key? key}) : super(key: key);
@override @override
_LaunchMiniProgramPageState createState() => _LaunchMiniProgramPageState(); State<LaunchMiniProgramPage> createState() => _LaunchMiniProgramPageState();
} }
class _LaunchMiniProgramPageState extends State<LaunchMiniProgramPage> { class _LaunchMiniProgramPageState extends State<LaunchMiniProgramPage> {
String? _result = '无'; String? _result = '无';
final Fluwx fluwx = Fluwx();
late Function(WeChatResponse) responseListener;
@override
void dispose() {
super.dispose();
_result = null;
fluwx.unsubscribeResponse(responseListener);
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
weChatResponseEventHandler.listen((res) { responseListener = (response) {
if (res is WeChatLaunchMiniProgramResponse) { if (response is WeChatLaunchMiniProgramResponse) {
if (mounted) { if (mounted) {
setState(() { setState(() {
_result = 'isSuccessful:${res.isSuccessful}'; _result = 'isSuccessful:${response.isSuccessful}';
}); });
} }
} }
}); };
}
@override fluwx.subscribeResponse(responseListener);
void dispose() {
super.dispose();
_result = null;
} }
@override @override
...@@ -41,7 +46,7 @@ class _LaunchMiniProgramPageState extends State<LaunchMiniProgramPage> { ...@@ -41,7 +46,7 @@ class _LaunchMiniProgramPageState extends State<LaunchMiniProgramPage> {
children: <Widget>[ children: <Widget>[
OutlinedButton( OutlinedButton(
onPressed: () { onPressed: () {
launchWeChatMiniProgram(username: 'gh_d43f693ca31f'); fluwx.open(target: MiniProgram(username: 'gh_d43f693ca31f'));
}, },
child: const Text('Launch MiniProgrom'), child: const Text('Launch MiniProgrom'),
), ),
......
...@@ -2,34 +2,38 @@ import 'dart:convert'; ...@@ -2,34 +2,38 @@ import 'dart:convert';
import 'dart:io' as H; import 'dart:io' as H;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart' as fluwx; import 'package:fluwx/fluwx.dart';
class PayPage extends StatefulWidget { class PayPage extends StatefulWidget {
const PayPage({Key? key}) : super(key: key); const PayPage({Key? key}) : super(key: key);
@override @override
_PayPageState createState() => _PayPageState(); State<PayPage> createState() => _PayPageState();
} }
class _PayPageState extends State<PayPage> { class _PayPageState extends State<PayPage> {
String _url = 'https://wxpay.wxutil.com/pub_v2/app/app_pay.php'; final Fluwx fluwx = Fluwx();
final String _url = 'https://wxpay.wxutil.com/pub_v2/app/app_pay.php';
String _result = '无'; String _result = '无';
late Function(WeChatResponse) responseListener;
@override
void dispose() {
super.dispose();
fluwx.unsubscribeResponse(responseListener);
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
fluwx.weChatResponseEventHandler.listen((res) { responseListener = (response) {
if (res is fluwx.WeChatPaymentResponse) { if (response is WeChatPaymentResponse) {
setState(() { setState(() {
_result = 'pay :${res.isSuccessful}'; _result = 'pay :${response.isSuccessful}';
}); });
} }
}); };
// fluwx.responseFromPayment.listen((data) { fluwx.subscribeResponse(responseListener);
// setState(() {
// _result = '${data.errCode}';
// });
// });
} }
@override @override
...@@ -46,23 +50,21 @@ class _PayPageState extends State<PayPage> { ...@@ -46,23 +50,21 @@ class _PayPageState extends State<PayPage> {
}; };
var request = await h.getUrl(Uri.parse(_url)); var request = await h.getUrl(Uri.parse(_url));
var response = await request.close(); var response = await request.close();
var data = await Utf8Decoder().bind(response).join(); var data = await const Utf8Decoder().bind(response).join();
Map<String, dynamic> result = json.decode(data); Map<String, dynamic> result = json.decode(data);
print(result['appid']); debugPrint(result['appid']);
print(result['timestamp']); debugPrint(result['timestamp']);
fluwx fluwx
.payWithWeChat( .pay(
which: Payment(
appId: result['appid'].toString(), appId: result['appid'].toString(),
partnerId: result['partnerid'].toString(), partnerId: result['partnerid'].toString(),
prepayId: result['prepayid'].toString(), prepayId: result['prepayid'].toString(),
packageValue: result['package'].toString(), packageValue: result['package'].toString(),
nonceStr: result['noncestr'].toString(), nonceStr: result['noncestr'].toString(),
timeStamp: result['timestamp'], timestamp: result['timestamp'],
sign: result['sign'].toString(), sign: result['sign'].toString(),
) ));
.then((data) {
print('---》$data');
});
}, },
child: const Text('pay'), child: const Text('pay'),
), ),
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart' as fluwx; import 'package:fluwx/fluwx.dart';
class SendAuthPage extends StatefulWidget { class SendAuthPage extends StatefulWidget {
const SendAuthPage({Key? key}) : super(key: key); const SendAuthPage({Key? key}) : super(key: key);
@override @override
_SendAuthPageState createState() => _SendAuthPageState(); State<SendAuthPage> createState() => _SendAuthPageState();
} }
class _SendAuthPageState extends State<SendAuthPage> { class _SendAuthPageState extends State<SendAuthPage> {
String? _result = '无'; String? _result = '无';
Fluwx fluwx = Fluwx();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) { fluwx.subscribeResponse((response) {
if (res is fluwx.WeChatAuthResponse) { if (response is WeChatAuthResponse) {
setState(() { setState(() {
_result = 'state :${res.state} \n code:${res.code}'; _result = 'state :${response.state} \n code:${response.code}';
}); });
} }
}); });
...@@ -38,10 +39,11 @@ class _SendAuthPageState extends State<SendAuthPage> { ...@@ -38,10 +39,11 @@ class _SendAuthPageState extends State<SendAuthPage> {
OutlinedButton( OutlinedButton(
onPressed: () { onPressed: () {
fluwx fluwx
.sendWeChatAuth( .authBy(
which: NormalAuth(
scope: 'snsapi_userinfo', scope: 'snsapi_userinfo',
state: 'wechat_sdk_demo_test', state: 'wechat_sdk_demo_test',
) ))
.then((data) {}); .then((data) {});
}, },
child: const Text('send auth'), child: const Text('send auth'),
......
...@@ -5,20 +5,21 @@ class ShareImagePage extends StatefulWidget { ...@@ -5,20 +5,21 @@ class ShareImagePage extends StatefulWidget {
const ShareImagePage({Key? key}) : super(key: key); const ShareImagePage({Key? key}) : super(key: key);
@override @override
_ShareImagePageState createState() => _ShareImagePageState(); State<ShareImagePage> createState() => _ShareImagePageState();
} }
class _ShareImagePageState extends State<ShareImagePage> { class _ShareImagePageState extends State<ShareImagePage> {
WeChatScene scene = WeChatScene.SESSION; WeChatScene scene = WeChatScene.session;
String _response = ''; String _response = '';
WeChatImage? source; WeChatImage? source;
WeChatImage? thumbnail; WeChatImage? thumbnail;
Fluwx fluwx = Fluwx();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
weChatResponseEventHandler.listen((res) { fluwx.subscribeResponse((res) {
if (res is WeChatShareResponse) { if (res is WeChatShareResponse) {
setState(() { setState(() {
_response = 'state :${res.isSuccessful}'; _response = 'state :${res.isSuccessful}';
...@@ -34,7 +35,7 @@ class _ShareImagePageState extends State<ShareImagePage> { ...@@ -34,7 +35,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
title: const Text('shareImage'), title: const Text('shareImage'),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
icon: Icon(Icons.share, color: Colors.white), icon: const Icon(Icons.share, color: Colors.white),
onPressed: _shareImage, onPressed: _shareImage,
), ),
], ],
...@@ -44,7 +45,7 @@ class _ShareImagePageState extends State<ShareImagePage> { ...@@ -44,7 +45,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
TextField( TextField(
decoration: InputDecoration(labelText: '图片地址(仅限网络)'), decoration: const InputDecoration(labelText: '图片地址(仅限网络)'),
controller: TextEditingController( controller: TextEditingController(
text: 'https://timgsa.baidu.com/timg' text: 'https://timgsa.baidu.com/timg'
'?image' '?image'
...@@ -73,7 +74,7 @@ class _ShareImagePageState extends State<ShareImagePage> { ...@@ -73,7 +74,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.SESSION, value: WeChatScene.session,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -85,7 +86,7 @@ class _ShareImagePageState extends State<ShareImagePage> { ...@@ -85,7 +86,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.TIMELINE, value: WeChatScene.timeline,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -97,7 +98,7 @@ class _ShareImagePageState extends State<ShareImagePage> { ...@@ -97,7 +98,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.FAVORITE, value: WeChatScene.favorite,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -116,7 +117,7 @@ class _ShareImagePageState extends State<ShareImagePage> { ...@@ -116,7 +117,7 @@ class _ShareImagePageState extends State<ShareImagePage> {
} }
void _shareImage() { void _shareImage() {
shareToWeChat(WeChatShareImageModel(source!, thumbnail: thumbnail)); fluwx.share(WeChatShareImageModel(source!, thumbnail: thumbnail));
} }
void handleRadioValueChanged(WeChatScene scene) { void handleRadioValueChanged(WeChatScene scene) {
......
...@@ -2,12 +2,14 @@ import 'package:flutter/material.dart'; ...@@ -2,12 +2,14 @@ import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart'; import 'package:fluwx/fluwx.dart';
class ShareMiniProgramPage extends StatefulWidget { class ShareMiniProgramPage extends StatefulWidget {
const ShareMiniProgramPage({Key? key}) : super(key: key);
@override @override
_ShareMiniProgramPageState createState() => _ShareMiniProgramPageState(); State<ShareMiniProgramPage> createState() => _ShareMiniProgramPageState();
} }
class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> { class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
WeChatScene scene = WeChatScene.SESSION; WeChatScene scene = WeChatScene.session;
String _webPageUrl = 'http://www.qq.com'; String _webPageUrl = 'http://www.qq.com';
String _thumbnail = 'https://timgsa.baidu.com/timg' String _thumbnail = 'https://timgsa.baidu.com/timg'
'?image' '?image'
...@@ -22,6 +24,8 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> { ...@@ -22,6 +24,8 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
String _path = '/pages/media'; String _path = '/pages/media';
String _description = 'Fluwx'; String _description = 'Fluwx';
Fluwx fluwx = Fluwx();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
...@@ -29,7 +33,7 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> { ...@@ -29,7 +33,7 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
title: const Text('ShareMiniProgram'), title: const Text('ShareMiniProgram'),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
icon: Icon(Icons.share, color: Colors.white), icon: const Icon(Icons.share, color: Colors.white),
onPressed: _share, onPressed: _share,
), ),
], ],
...@@ -95,6 +99,6 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> { ...@@ -95,6 +99,6 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
description: _description, description: _description,
thumbnail: WeChatImage.network(_thumbnail), thumbnail: WeChatImage.network(_thumbnail),
); );
shareToWeChat(model); fluwx.share(model);
} }
} }
...@@ -5,7 +5,7 @@ class ShareMusicPage extends StatefulWidget { ...@@ -5,7 +5,7 @@ class ShareMusicPage extends StatefulWidget {
const ShareMusicPage({Key? key}) : super(key: key); const ShareMusicPage({Key? key}) : super(key: key);
@override @override
_ShareMusicPageState createState() => _ShareMusicPageState(); State<ShareMusicPage> createState() => _ShareMusicPageState();
} }
class _ShareMusicPageState extends State<ShareMusicPage> { class _ShareMusicPageState extends State<ShareMusicPage> {
...@@ -15,7 +15,8 @@ class _ShareMusicPageState extends State<ShareMusicPage> { ...@@ -15,7 +15,8 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
String _title = 'Beyond'; String _title = 'Beyond';
String _description = 'A Popular Rock Band From China'; String _description = 'A Popular Rock Band From China';
String _thumnail = 'images/logo.png'; String _thumnail = 'images/logo.png';
WeChatScene scene = WeChatScene.SESSION; WeChatScene scene = WeChatScene.session;
Fluwx _fluwx = Fluwx();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -24,7 +25,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> { ...@@ -24,7 +25,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
title: const Text('ShareMusicPage'), title: const Text('ShareMusicPage'),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
icon: Icon(Icons.share, color: Colors.white), icon: const Icon(Icons.share, color: Colors.white),
onPressed: _share, onPressed: _share,
), ),
], ],
...@@ -41,21 +42,22 @@ class _ShareMusicPageState extends State<ShareMusicPage> { ...@@ -41,21 +42,22 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
onChanged: (str) { onChanged: (str) {
_musicUrl = str; _musicUrl = str;
}, },
decoration: InputDecoration(labelText: 'music url'), decoration: const InputDecoration(labelText: 'music url'),
), ),
TextField( TextField(
controller: TextEditingController(text: 'http://www.qq.com'), controller: TextEditingController(text: 'http://www.qq.com'),
onChanged: (str) { onChanged: (str) {
_musicLowBandUrl = str; _musicLowBandUrl = str;
}, },
decoration: InputDecoration(labelText: 'music low band url'), decoration:
const InputDecoration(labelText: 'music low band url'),
), ),
TextField( TextField(
controller: TextEditingController(text: 'Beyond'), controller: TextEditingController(text: 'Beyond'),
onChanged: (str) { onChanged: (str) {
_title = str; _title = str;
}, },
decoration: InputDecoration(labelText: 'title'), decoration: const InputDecoration(labelText: 'title'),
), ),
TextField( TextField(
controller: TextEditingController( controller: TextEditingController(
...@@ -64,14 +66,14 @@ class _ShareMusicPageState extends State<ShareMusicPage> { ...@@ -64,14 +66,14 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
onChanged: (str) { onChanged: (str) {
_description = str; _description = str;
}, },
decoration: InputDecoration(labelText: 'description'), decoration: const InputDecoration(labelText: 'description'),
), ),
TextField( TextField(
controller: TextEditingController(text: 'images/logo.png'), controller: TextEditingController(text: 'images/logo.png'),
onChanged: (str) { onChanged: (str) {
_thumnail = str; _thumnail = str;
}, },
decoration: InputDecoration(labelText: 'thumbnail'), decoration: const InputDecoration(labelText: 'thumbnail'),
), ),
Row( Row(
children: <Widget>[ children: <Widget>[
...@@ -79,7 +81,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> { ...@@ -79,7 +81,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.SESSION, value: WeChatScene.session,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -91,7 +93,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> { ...@@ -91,7 +93,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.TIMELINE, value: WeChatScene.timeline,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -103,7 +105,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> { ...@@ -103,7 +105,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.FAVORITE, value: WeChatScene.favorite,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -130,7 +132,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> { ...@@ -130,7 +132,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
thumbnail: WeChatImage.network(_thumnail), thumbnail: WeChatImage.network(_thumnail),
); );
shareToWeChat(model); _fluwx.share(model);
} }
void handleRadioValueChanged(WeChatScene scene) { void handleRadioValueChanged(WeChatScene scene) {
......
...@@ -2,13 +2,16 @@ import 'package:flutter/material.dart'; ...@@ -2,13 +2,16 @@ import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart'; import 'package:fluwx/fluwx.dart';
class ShareTextPage extends StatefulWidget { class ShareTextPage extends StatefulWidget {
const ShareTextPage({Key? key}) : super(key: key);
@override @override
_ShareTextPageState createState() => _ShareTextPageState(); State<ShareTextPage> createState() => _ShareTextPageState();
} }
class _ShareTextPageState extends State<ShareTextPage> { class _ShareTextPageState extends State<ShareTextPage> {
String _text = 'share text from fluwx'; String _text = 'share text from fluwx';
WeChatScene scene = WeChatScene.SESSION; WeChatScene scene = WeChatScene.session;
Fluwx fluwx = Fluwx();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -31,7 +34,7 @@ class _ShareTextPageState extends State<ShareTextPage> { ...@@ -31,7 +34,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
onChanged: (str) { onChanged: (str) {
_text = str; _text = str;
}, },
decoration: InputDecoration(labelText: 'TextToShare'), decoration: const InputDecoration(labelText: 'TextToShare'),
), ),
Row( Row(
children: <Widget>[ children: <Widget>[
...@@ -39,7 +42,7 @@ class _ShareTextPageState extends State<ShareTextPage> { ...@@ -39,7 +42,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.SESSION, value: WeChatScene.session,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -51,7 +54,7 @@ class _ShareTextPageState extends State<ShareTextPage> { ...@@ -51,7 +54,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.TIMELINE, value: WeChatScene.timeline,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -63,7 +66,7 @@ class _ShareTextPageState extends State<ShareTextPage> { ...@@ -63,7 +66,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.FAVORITE, value: WeChatScene.favorite,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -81,9 +84,7 @@ class _ShareTextPageState extends State<ShareTextPage> { ...@@ -81,9 +84,7 @@ class _ShareTextPageState extends State<ShareTextPage> {
} }
void _shareText() { void _shareText() {
shareToWeChat(WeChatShareTextModel(_text, scene: scene)).then((data) { fluwx.share(WeChatShareTextModel(_text, scene: scene));
print('-->$data');
});
} }
void handleRadioValueChanged(WeChatScene scene) { void handleRadioValueChanged(WeChatScene scene) {
......
...@@ -5,16 +5,18 @@ class ShareVideoPage extends StatefulWidget { ...@@ -5,16 +5,18 @@ class ShareVideoPage extends StatefulWidget {
const ShareVideoPage({Key? key}) : super(key: key); const ShareVideoPage({Key? key}) : super(key: key);
@override @override
_ShareMusicPageState createState() => _ShareMusicPageState(); State<ShareVideoPage> createState() => _ShareVideoPageState();
} }
class _ShareMusicPageState extends State<ShareVideoPage> { class _ShareVideoPageState extends State<ShareVideoPage> {
String _videoUrl = 'http://www.qq.com'; String _videoUrl = 'http://www.qq.com';
String _videoLowBandUrl = 'http://www.qq.com'; String _videoLowBandUrl = 'http://www.qq.com';
String _title = 'Beyond'; String _title = 'Beyond';
String _description = 'A Popular Rock Band From China'; String _description = 'A Popular Rock Band From China';
String _thumnail = 'images/logo.png'; String _thumnail = 'images/logo.png';
WeChatScene scene = WeChatScene.SESSION; WeChatScene scene = WeChatScene.session;
Fluwx fluwx = Fluwx();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -23,7 +25,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> { ...@@ -23,7 +25,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
title: const Text('ShareVideoPage'), title: const Text('ShareVideoPage'),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
icon: Icon(Icons.share, color: Colors.white), icon: const Icon(Icons.share, color: Colors.white),
onPressed: _share, onPressed: _share,
), ),
], ],
...@@ -40,21 +42,22 @@ class _ShareMusicPageState extends State<ShareVideoPage> { ...@@ -40,21 +42,22 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
onChanged: (str) { onChanged: (str) {
_videoUrl = str; _videoUrl = str;
}, },
decoration: InputDecoration(labelText: 'video url'), decoration: const InputDecoration(labelText: 'video url'),
), ),
TextField( TextField(
controller: TextEditingController(text: 'http://www.qq.com'), controller: TextEditingController(text: 'http://www.qq.com'),
onChanged: (str) { onChanged: (str) {
_videoLowBandUrl = str; _videoLowBandUrl = str;
}, },
decoration: InputDecoration(labelText: 'video low band url'), decoration:
const InputDecoration(labelText: 'video low band url'),
), ),
TextField( TextField(
controller: TextEditingController(text: 'Beyond'), controller: TextEditingController(text: 'Beyond'),
onChanged: (str) { onChanged: (str) {
_title = str; _title = str;
}, },
decoration: InputDecoration(labelText: 'title'), decoration: const InputDecoration(labelText: 'title'),
), ),
TextField( TextField(
controller: controller:
...@@ -62,14 +65,14 @@ class _ShareMusicPageState extends State<ShareVideoPage> { ...@@ -62,14 +65,14 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
onChanged: (str) { onChanged: (str) {
_description = str; _description = str;
}, },
decoration: InputDecoration(labelText: 'description'), decoration: const InputDecoration(labelText: 'description'),
), ),
TextField( TextField(
controller: TextEditingController(text: 'images/logo.png'), controller: TextEditingController(text: 'images/logo.png'),
onChanged: (str) { onChanged: (str) {
_thumnail = str; _thumnail = str;
}, },
decoration: InputDecoration(labelText: 'thumbnail'), decoration: const InputDecoration(labelText: 'thumbnail'),
), ),
Row( Row(
children: <Widget>[ children: <Widget>[
...@@ -77,7 +80,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> { ...@@ -77,7 +80,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.SESSION, value: WeChatScene.session,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -89,7 +92,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> { ...@@ -89,7 +92,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.TIMELINE, value: WeChatScene.timeline,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -101,7 +104,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> { ...@@ -101,7 +104,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.FAVORITE, value: WeChatScene.favorite,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -124,10 +127,10 @@ class _ShareMusicPageState extends State<ShareVideoPage> { ...@@ -124,10 +127,10 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
videoLowBandUrl: _videoLowBandUrl, videoLowBandUrl: _videoLowBandUrl,
thumbnail: WeChatImage.network(_thumnail), thumbnail: WeChatImage.network(_thumnail),
description: _description, description: _description,
scene: this.scene, scene: scene,
title: _title, title: _title,
); );
shareToWeChat(model); fluwx.share(model);
} }
void handleRadioValueChanged(WeChatScene scene) { void handleRadioValueChanged(WeChatScene scene) {
......
...@@ -12,7 +12,9 @@ class ShareWebPagePageState extends State<ShareWebPagePage> { ...@@ -12,7 +12,9 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
String _url = 'share text from fluwx'; String _url = 'share text from fluwx';
String _title = 'Fluwx'; String _title = 'Fluwx';
String _thumnail = 'images/logo.png'; String _thumnail = 'images/logo.png';
WeChatScene scene = WeChatScene.SESSION; WeChatScene scene = WeChatScene.session;
Fluwx fluwx = Fluwx();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -21,7 +23,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> { ...@@ -21,7 +23,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
title: const Text('ShareWebPage'), title: const Text('ShareWebPage'),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
icon: Icon(Icons.share, color: Colors.white), icon: const Icon(Icons.share, color: Colors.white),
onPressed: _share, onPressed: _share,
), ),
], ],
...@@ -37,21 +39,21 @@ class ShareWebPagePageState extends State<ShareWebPagePage> { ...@@ -37,21 +39,21 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
onChanged: (str) { onChanged: (str) {
_url = str; _url = str;
}, },
decoration: InputDecoration(labelText: 'web page'), decoration: const InputDecoration(labelText: 'web page'),
), ),
TextField( TextField(
controller: TextEditingController(text: 'Fluwx'), controller: TextEditingController(text: 'Fluwx'),
onChanged: (str) { onChanged: (str) {
_title = str; _title = str;
}, },
decoration: InputDecoration(labelText: 'thumbnail'), decoration: const InputDecoration(labelText: 'thumbnail'),
), ),
TextField( TextField(
controller: TextEditingController(text: 'images/logo.png'), controller: TextEditingController(text: 'images/logo.png'),
onChanged: (str) { onChanged: (str) {
_thumnail = str; _thumnail = str;
}, },
decoration: InputDecoration(labelText: 'thumbnail'), decoration: const InputDecoration(labelText: 'thumbnail'),
), ),
Row( Row(
children: <Widget>[ children: <Widget>[
...@@ -59,7 +61,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> { ...@@ -59,7 +61,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.SESSION, value: WeChatScene.session,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -71,7 +73,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> { ...@@ -71,7 +73,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.TIMELINE, value: WeChatScene.timeline,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -83,7 +85,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> { ...@@ -83,7 +85,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
Row( Row(
children: <Widget>[ children: <Widget>[
Radio<WeChatScene>( Radio<WeChatScene>(
value: WeChatScene.FAVORITE, value: WeChatScene.favorite,
groupValue: scene, groupValue: scene,
onChanged: (v) { onChanged: (v) {
if (v != null) handleRadioValueChanged(v); if (v != null) handleRadioValueChanged(v);
...@@ -107,7 +109,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> { ...@@ -107,7 +109,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
thumbnail: WeChatImage.network(_thumnail), thumbnail: WeChatImage.network(_thumnail),
scene: scene, scene: scene,
); );
shareToWeChat(model); fluwx.share(model);
} }
void handleRadioValueChanged(WeChatScene scene) { void handleRadioValueChanged(WeChatScene scene) {
......
...@@ -6,10 +6,9 @@ class SignAutoDeductPage extends StatefulWidget { ...@@ -6,10 +6,9 @@ class SignAutoDeductPage extends StatefulWidget {
const SignAutoDeductPage({Key? key}) : super(key: key); const SignAutoDeductPage({Key? key}) : super(key: key);
@override @override
_SignAutoDeductPageState createState() => _SignAutoDeductPageState(); State<SignAutoDeductPage> createState() => _SignAutoDeductPageState();
} }
/// see wechat [document](https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_5&index=2)
class _SignAutoDeductPageState extends State<SignAutoDeductPage> { class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
TextEditingController appId = TextEditingController appId =
TextEditingController(text: 'wx316f9c82e99ac105'); TextEditingController(text: 'wx316f9c82e99ac105');
...@@ -25,10 +24,12 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> { ...@@ -25,10 +24,12 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
TextEditingController timestamp = TextEditingController(text: ''); TextEditingController timestamp = TextEditingController(text: '');
TextEditingController returnApp = TextEditingController(text: '3'); TextEditingController returnApp = TextEditingController(text: '3');
Fluwx fluwx = Fluwx();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
weChatResponseEventHandler.listen((resp) { fluwx.subscribeResponse((resp) {
print('resp = ${resp.isSuccessful}'); print('resp = ${resp.isSuccessful}');
}); });
} }
...@@ -54,10 +55,9 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> { ...@@ -54,10 +55,9 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('SubscribeMessagePage'), title: const Text('SubscribeMessagePage'),
), ),
body: Container( body: ListView(
child: ListView(
children: <Widget>[ children: <Widget>[
_buildTextField( _buildTextField(
title: 'appId', title: 'appId',
...@@ -109,7 +109,6 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> { ...@@ -109,7 +109,6 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
), ),
], ],
), ),
),
); );
} }
...@@ -124,7 +123,8 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> { ...@@ -124,7 +123,8 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
} }
void _signAutoDeduct() { void _signAutoDeduct() {
autoDeDuctWeChat( fluwx.autoDeduct(
data: AutoDeduct.detail(
appId: appId.text, appId: appId.text,
mchId: mchId.text, mchId: mchId.text,
planId: planId.text, planId: planId.text,
...@@ -136,6 +136,6 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> { ...@@ -136,6 +136,6 @@ class _SignAutoDeductPageState extends State<SignAutoDeductPage> {
sign: sign.text, sign: sign.text,
timestamp: timestamp.text, timestamp: timestamp.text,
returnApp: '3', returnApp: '3',
); ));
} }
} }
...@@ -5,7 +5,7 @@ class SubscribeMessagePage extends StatefulWidget { ...@@ -5,7 +5,7 @@ class SubscribeMessagePage extends StatefulWidget {
const SubscribeMessagePage({Key? key}) : super(key: key); const SubscribeMessagePage({Key? key}) : super(key: key);
@override @override
_SubscribeMessagePageState createState() => _SubscribeMessagePageState(); State<SubscribeMessagePage> createState() => _SubscribeMessagePageState();
} }
/// see wechat [document](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1500434436_aWfqW&token=&lang=zh_CN) /// see wechat [document](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1500434436_aWfqW&token=&lang=zh_CN)
...@@ -16,12 +16,13 @@ class _SubscribeMessagePageState extends State<SubscribeMessagePage> { ...@@ -16,12 +16,13 @@ class _SubscribeMessagePageState extends State<SubscribeMessagePage> {
TextEditingController templateId = TextEditingController( TextEditingController templateId = TextEditingController(
text: 'cm_vM2k3IjHcYbkGUeAfL6Fja_7Pgv4Hx_q4tA253Ss'); text: 'cm_vM2k3IjHcYbkGUeAfL6Fja_7Pgv4Hx_q4tA253Ss');
TextEditingController reserved = TextEditingController(text: '123'); TextEditingController reserved = TextEditingController(text: '123');
Fluwx fluwx = Fluwx();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
weChatResponseEventHandler.listen((resp) { fluwx.subscribeResponse((resp) {
print('resp = $resp'); debugPrint('resp = $resp');
}); });
} }
...@@ -80,11 +81,12 @@ class _SubscribeMessagePageState extends State<SubscribeMessagePage> { ...@@ -80,11 +81,12 @@ class _SubscribeMessagePageState extends State<SubscribeMessagePage> {
} }
void _requestSubMsg() { void _requestSubMsg() {
subscribeWeChatMsg( fluwx.open(
target: SubscribeMessage(
appId: appId.text, appId: appId.text,
scene: int.tryParse(scene.text) ?? 1, scene: int.tryParse(scene.text) ?? 1,
templateId: templateId.text, templateId: templateId.text,
reserved: reserved.text, reserved: reserved.text,
); ));
} }
} }
name: fluwx_example name: fluwx_example
description: Demonstrates how to use the fluwx plugin. description: Demonstrates how to use the fluwx plugin.
publish_to: 'none' # The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment: environment:
sdk: ">=2.12.4 <3.0.0" sdk: '>=3.0.0 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
characters: ^1.2.0
fluwx:
# When depending on this package from a real application you should use:
# fluwx: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.1+1 cupertino_icons: ^1.0.2
dev_dependencies: dev_dependencies:
integration_test:
sdk: flutter
flutter_test: flutter_test:
sdk: flutter sdk: flutter
fluwx: # The "flutter_lints" package below contains a set of recommended lints to
path: ../ # encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
# 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://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter. # The following section is specific to Flutter packages.
flutter: flutter:
# The following line ensures that the Material Icons font is # The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in # included with your application, so that you can use the icons in
# the material Icons class. # the material Icons class.
uses-material-design: true uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets: assets:
- images/logo.png - images/logo.png
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware. # https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see # For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages # https://flutter.dev/assets-and-images/#from-packages
...@@ -63,3 +82,15 @@ flutter: ...@@ -63,3 +82,15 @@ flutter:
# #
# For details regarding fonts from package dependencies, # For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages # see https://flutter.dev/custom-fonts/#from-packages
fluwx:
app_id: 123456
# only debug in debug mode
debug_logging: true
android:
# interrupt_wx_request: true # default is true
# flutter_activity: MainActivity #Default to launch app's launcher
ios:
universal_link: https://testdomain.com
# payment is enabled by default
# no_pay: true
// This is a basic Flutter widget test. // This is a basic Flutter widget test.
// //
// To perform an interaction with a widget in your test, use the WidgetTester // To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll // utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget // gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct. // tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx_example/main.dart'; import 'package:fluwx_example/main.dart';
...@@ -12,15 +13,15 @@ import 'package:fluwx_example/main.dart'; ...@@ -12,15 +13,15 @@ import 'package:fluwx_example/main.dart';
void main() { void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async { testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame. // Build our app and trigger a frame.
await tester.pumpWidget(MyApp()); await tester.pumpWidget(const MyApp());
// Verify that platform version is retrieved. // Verify that platform version is retrieved.
// expect( expect(
// find.byWidgetPredicate( find.byWidgetPredicate(
// (Widget widget) => widget is Text && (Widget widget) => widget is Text &&
// widget.data.startsWith('Running on:'), widget.data!.startsWith('Running on:'),
// ), ),
// findsOneWidget, findsOneWidget,
// ); );
}); });
} }
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="Demonstrates how to use the fluwx plugin.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="fluwx_example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>fluwx_example</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
const serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
</body>
</html>
{
"name": "fluwx_example",
"short_name": "fluwx_example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "Demonstrates how to use the fluwx plugin.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
...@@ -34,6 +34,5 @@ Icon? ...@@ -34,6 +34,5 @@ Icon?
.tags* .tags*
/Flutter/Generated.xcconfig /Flutter/Generated.xcconfig
/Flutter/ephemeral/
/Flutter/flutter_export_environment.sh /Flutter/flutter_export_environment.sh
\ No newline at end of file
.gradle
\ No newline at end of file
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#import "FluwxPlugin.h" #import "FluwxPlugin.h"
#import "WXApiRequestHandler.h" #import "WXApiRequestHandler.h"
#import "WechatAuthSDK.h" #import "WechatAuthSDK.h"
#import <WXApi.h>
@class FluwxStringUtil; @class FluwxStringUtil;
......
...@@ -26,7 +26,7 @@ FlutterMethodChannel *_fluwxMethodChannel = nil; ...@@ -26,7 +26,7 @@ FlutterMethodChannel *_fluwxMethodChannel = nil;
SendAuthReq *authReq = [[SendAuthReq alloc] init]; SendAuthReq *authReq = [[SendAuthReq alloc] init];
authReq.scope = call.arguments[@"scope"]; authReq.scope = call.arguments[@"scope"];
authReq.state = (call.arguments[@"state"] == (id) [NSNull null]) ? nil : call.arguments[@"state"]; authReq.state = (call.arguments[@"state"] == (id) [NSNull null]) ? nil : call.arguments[@"state"];
[WXApi sendAuthReq:authReq viewController:vc delegate:[FluwxResponseHandler defaultManager] completion:^(BOOL success) { [WXApi sendAuthReq:authReq viewController:vc delegate:self completion:^(BOOL success) {
result(@(success)); result(@(success));
}]; }];
} }
......
#import "FluwxPlugin.h" #import "FluwxPlugin.h"
#import "FluwxResponseHandler.h"
#import "FluwxStringUtil.h" #import "FluwxStringUtil.h"
#import "FluwxAuthHandler.h" #import "FluwxAuthHandler.h"
#import "FluwxShareHandler.h" #import "FluwxShareHandler.h"
#import "FluwxDelegate.h" #import "FluwxDelegate.h"
#import <WXApi.h>
@interface FluwxPlugin()<WXApiManagerDelegate> #import <WXApiObject.h>
@interface FluwxPlugin()<WXApiDelegate>
@property (strong,nonatomic)NSString *extMsg; @property (strong,nonatomic)NSString *extMsg;
@end @end
typedef void(^FluwxWXReqRunnable)(void); typedef void(^FluwxWXReqRunnable)(void);
@implementation FluwxPlugin @implementation FluwxPlugin
const NSString *errStr = @"errStr";
const NSString *errCode = @"errCode";
const NSString *openId = @"openId";
const NSString *type = @"type";
const NSString *lang = @"lang";
const NSString *country = @"country";
const NSString *description = @"description";
FluwxAuthHandler *_fluwxAuthHandler; FluwxAuthHandler *_fluwxAuthHandler;
FluwxShareHandler *_fluwxShareHandler; FluwxShareHandler *_fluwxShareHandler;
BOOL _isRunning; BOOL _isRunning;
...@@ -32,7 +42,6 @@ FlutterMethodChannel *channel = nil; ...@@ -32,7 +42,6 @@ FlutterMethodChannel *channel = nil;
binaryMessenger:[registrar messenger]]; binaryMessenger:[registrar messenger]];
FluwxPlugin *instance = [[FluwxPlugin alloc] initWithRegistrar:registrar methodChannel:channel]; FluwxPlugin *instance = [[FluwxPlugin alloc] initWithRegistrar:registrar methodChannel:channel];
[registrar addMethodCallDelegate:instance channel:channel]; [registrar addMethodCallDelegate:instance channel:channel];
[[FluwxResponseHandler defaultManager] setMethodChannel:channel];
[registrar addApplicationDelegate:instance]; [registrar addApplicationDelegate:instance];
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
...@@ -48,8 +57,12 @@ FlutterMethodChannel *channel = nil; ...@@ -48,8 +57,12 @@ FlutterMethodChannel *channel = nil;
_fluwxShareHandler = [[FluwxShareHandler alloc] initWithRegistrar:registrar]; _fluwxShareHandler = [[FluwxShareHandler alloc] initWithRegistrar:registrar];
_isRunning = NO; _isRunning = NO;
channel = flutterMethodChannel; channel = flutterMethodChannel;
[FluwxResponseHandler defaultManager].delegate = self;
#if WECHAT_LOGGING
[WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString *log) {
[self logToFlutterWithDetail:log];
}];
#endif
} }
return self; return self;
} }
...@@ -59,10 +72,6 @@ FlutterMethodChannel *channel = nil; ...@@ -59,10 +72,6 @@ FlutterMethodChannel *channel = nil;
if ([@"registerApp" isEqualToString:call.method]) { if ([@"registerApp" isEqualToString:call.method]) {
[self registerApp:call result:result]; [self registerApp:call result:result];
} else if ([@"startLog" isEqualToString:call.method]) {
[self startLog:call result:result];
} else if ([@"stopLog" isEqualToString:call.method]) {
[self stopLog:call result:result];
} else if ([@"isWeChatInstalled" isEqualToString:call.method]) { } else if ([@"isWeChatInstalled" isEqualToString:call.method]) {
[self checkWeChatInstallation:call result:result]; [self checkWeChatInstallation:call result:result];
} else if ([@"sendAuth" isEqualToString:call.method]) { } else if ([@"sendAuth" isEqualToString:call.method]) {
...@@ -73,10 +82,6 @@ FlutterMethodChannel *channel = nil; ...@@ -73,10 +82,6 @@ FlutterMethodChannel *channel = nil;
[_fluwxAuthHandler stopAuthByQRCode:call result:result]; [_fluwxAuthHandler stopAuthByQRCode:call result:result];
} else if ([@"openWXApp" isEqualToString:call.method]) { } else if ([@"openWXApp" isEqualToString:call.method]) {
result(@([WXApi openWXApp])); result(@([WXApi openWXApp]));
} else if ([@"payWithFluwx" isEqualToString:call.method]) {
[self handlePayment:call result:result];
} else if ([@"payWithHongKongWallet" isEqualToString:call.method]) {
[self handleHongKongWalletPayment:call result:result];
} else if ([@"launchMiniProgram" isEqualToString:call.method]) { } else if ([@"launchMiniProgram" isEqualToString:call.method]) {
[self handleLaunchMiniProgram:call result:result]; [self handleLaunchMiniProgram:call result:result];
} else if ([@"subscribeMsg" isEqualToString:call.method]) { } else if ([@"subscribeMsg" isEqualToString:call.method]) {
...@@ -97,9 +102,27 @@ FlutterMethodChannel *channel = nil; ...@@ -97,9 +102,27 @@ FlutterMethodChannel *channel = nil;
[self openWeChatCustomerServiceChat:call result:result]; [self openWeChatCustomerServiceChat:call result:result];
} else if ([@"checkSupportOpenBusinessView" isEqualToString:call.method]) { } else if ([@"checkSupportOpenBusinessView" isEqualToString:call.method]) {
[self checkSupportOpenBusinessView:call result:result]; [self checkSupportOpenBusinessView:call result:result];
} else if ([@"openRankList" isEqualToString:call.method]) {
[self handleOpenRankListCall:call result:result];
} else if ([@"openUrl" isEqualToString:call.method]) {
[self handleOpenUrlCall:call result:result];
} else if([@"openWeChatInvoice" isEqualToString:call.method]) { } else if([@"openWeChatInvoice" isEqualToString:call.method]) {
[self openWeChatInvoice:call result:result]; [self openWeChatInvoice:call result:result];
} else { }
else if ([@"payWithFluwx" isEqualToString:call.method]) {
#ifndef NO_PAY
[self handlePayment:call result:result];
#else
result(@NO);
#endif
} else if ([@"payWithHongKongWallet" isEqualToString:call.method]) {
#ifndef NO_PAY
[self handleHongKongWalletPayment:call result:result];
#else
result(@NO);
#endif
}
else {
result(FlutterMethodNotImplemented); result(FlutterMethodNotImplemented);
} }
} }
...@@ -143,28 +166,17 @@ FlutterMethodChannel *channel = nil; ...@@ -143,28 +166,17 @@ FlutterMethodChannel *channel = nil;
BOOL isWeChatRegistered = [WXApi registerApp:appId universalLink:universalLink]; BOOL isWeChatRegistered = [WXApi registerApp:appId universalLink:universalLink];
result(@(isWeChatRegistered)); #if WECHAT_LOGGING
} if(isWeChatRegistered) {
[WXApi checkUniversalLinkReady:^(WXULCheckStep step, WXCheckULStepResult* result) {
- (void)startLog:(FlutterMethodCall *)call result:(FlutterResult)result { NSString *log = [NSString stringWithFormat:@"%@, %u, %@, %@", @(step), result.success, result.errorInfo, result.suggestion];
NSNumber *typeInt = call.arguments[@"logLevel"]; [self logToFlutterWithDetail:log];
WXLogLevel logLevel = WXLogLevelDetail;
if ([typeInt isEqualToNumber:@1]) {
logLevel = WXLogLevelDetail;
} else if ([typeInt isEqualToNumber:@0]) {
logLevel = WXLogLevelNormal;
}
NSLog(@"%@",call.arguments);
[WXApi startLogByLevel:logLevel logBlock:^(NSString * _Nonnull log) {
NSLog(@"%@",log);
}]; }];
result([NSNumber numberWithBool:true]); }
} #endif
- (void)stopLog:(FlutterMethodCall *)call result:(FlutterResult)result { result(@(isWeChatRegistered));
[WXApi stopLog];
result([NSNumber numberWithBool:true]);
} }
- (void)checkWeChatInstallation:(FlutterMethodCall *)call result:(FlutterResult)result { - (void)checkWeChatInstallation:(FlutterMethodCall *)call result:(FlutterResult)result {
...@@ -192,6 +204,7 @@ FlutterMethodChannel *channel = nil; ...@@ -192,6 +204,7 @@ FlutterMethodChannel *channel = nil;
} }
} }
#ifndef NO_PAY
- (void)handlePayment:(FlutterMethodCall *)call result:(FlutterResult)result { - (void)handlePayment:(FlutterMethodCall *)call result:(FlutterResult)result {
...@@ -227,6 +240,7 @@ FlutterMethodChannel *channel = nil; ...@@ -227,6 +240,7 @@ FlutterMethodChannel *channel = nil;
result(@(done)); result(@(done));
}]; }];
} }
#endif
- (void)handleLaunchMiniProgram:(FlutterMethodCall *)call result:(FlutterResult)result { - (void)handleLaunchMiniProgram:(FlutterMethodCall *)call result:(FlutterResult)result {
NSString *userName = call.arguments[@"userName"]; NSString *userName = call.arguments[@"userName"];
...@@ -312,31 +326,59 @@ FlutterMethodChannel *channel = nil; ...@@ -312,31 +326,59 @@ FlutterMethodChannel *channel = nil;
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [WXApi handleOpenURL:url delegate:[FluwxResponseHandler defaultManager]]; return [WXApi handleOpenURL:url delegate:self];
} }
// NOTE: 9.0以后使用新API接口 // NOTE: 9.0以后使用新API接口
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options { - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options {
return [WXApi handleOpenURL:url delegate:[FluwxResponseHandler defaultManager]]; return [WXApi handleOpenURL:url delegate:self];
} }
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nonnull))restorationHandler{ - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nonnull))restorationHandler{
return [WXApi handleOpenUniversalLink:userActivity delegate:[FluwxResponseHandler defaultManager]]; return [WXApi handleOpenUniversalLink:userActivity delegate:self];
} }
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity API_AVAILABLE(ios(13.0)){ - (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity API_AVAILABLE(ios(13.0)){
[WXApi handleOpenUniversalLink:userActivity delegate:[FluwxResponseHandler defaultManager]]; [WXApi handleOpenUniversalLink:userActivity delegate:self];
}
- (void)handleOpenUrlCall:(FlutterMethodCall *)call
result:(FlutterResult)result {
OpenWebviewReq *req = [[OpenWebviewReq alloc] init];
req.url = call.arguments[@"url"];
[WXApi sendReq:req
completion:^(BOOL success){
result(@(success));
}];
}
- (void)handleOpenRankListCall:(FlutterMethodCall *)call
result:(FlutterResult)result {
OpenRankListReq *req = [[OpenRankListReq alloc] init];
[WXApi sendReq:req
completion:^(BOOL success){
result(@(success));
}];
} }
- (BOOL)handleOpenURL:(NSNotification *)aNotification { - (BOOL)handleOpenURL:(NSNotification *)aNotification {
if (handleOpenURLByFluwx) { if (handleOpenURLByFluwx) {
NSString *aURLString = [aNotification userInfo][@"url"]; NSString *aURLString = [aNotification userInfo][@"url"];
NSURL *aURL = [NSURL URLWithString:aURLString]; NSURL *aURL = [NSURL URLWithString:aURLString];
return [WXApi handleOpenURL:aURL delegate:[FluwxResponseHandler defaultManager]]; return [WXApi handleOpenURL:aURL delegate:self];
} else { } else {
return NO; return NO;
} }
} }
- (void)logToFlutterWithDetail:(NSString *) detail {
if(channel != nil){
NSDictionary *result = @{
@"detail":detail
};
[channel invokeMethod:@"wechatLog" arguments:result];
}
}
- (void)managerDidRecvLaunchFromWXReq:(LaunchFromWXReq *)request { - (void)managerDidRecvLaunchFromWXReq:(LaunchFromWXReq *)request {
[FluwxDelegate defaultManager].extMsg = request.message.messageExt; [FluwxDelegate defaultManager].extMsg = request.message.messageExt;
// LaunchFromWXReq *launchFromWXReq = (LaunchFromWXReq *)request; // LaunchFromWXReq *launchFromWXReq = (LaunchFromWXReq *)request;
...@@ -352,4 +394,247 @@ FlutterMethodChannel *channel = nil; ...@@ -352,4 +394,247 @@ FlutterMethodChannel *channel = nil;
// } // }
} }
- (void)onResp:(BaseResp *)resp {
if ([resp isKindOfClass:[SendMessageToWXResp class]]) {
SendMessageToWXResp *messageResp = (SendMessageToWXResp *) resp;
NSDictionary *result = @{
description: messageResp.description == nil ? @"" : messageResp.description,
errStr: messageResp.errStr == nil ? @"" : messageResp.errStr,
errCode: @(messageResp.errCode),
type: @(messageResp.type),
country: messageResp.country == nil ? @"" : messageResp.country,
lang: messageResp.lang == nil ? @"" : messageResp.lang};
if(channel != nil){
[channel invokeMethod:@"onShareResponse" arguments:result];
}
} else if ([resp isKindOfClass:[SendAuthResp class]]) {
SendAuthResp *authResp = (SendAuthResp *) resp;
NSDictionary *result = @{
description: authResp.description == nil ? @"" : authResp.description,
errStr: authResp.errStr == nil ? @"" : authResp.errStr,
errCode: @(authResp.errCode),
type: @(authResp.type),
country: authResp.country == nil ? @"" : authResp.country,
lang: authResp.lang == nil ? @"" : authResp.lang,
@"code": [FluwxStringUtil nilToEmpty:authResp.code],
@"state": [FluwxStringUtil nilToEmpty:authResp.state]
};
if(channel != nil){
[channel invokeMethod:@"onAuthResponse" arguments:result];
}
} else if ([resp isKindOfClass:[AddCardToWXCardPackageResp class]]) {
} else if ([resp isKindOfClass:[WXChooseCardResp class]]) {
} else if ([resp isKindOfClass:[WXChooseInvoiceResp class]]) {
//TODO 处理发票返回,并回调Dart
WXChooseInvoiceResp *chooseInvoiceResp = (WXChooseInvoiceResp *) resp;
NSArray *array = chooseInvoiceResp.cardAry;
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:array.count];
for (int i = 0; i< array.count; i++) {
WXInvoiceItem *item = array[i];
NSDictionary *dict = @{@"app_id":item.appID, @"encrypt_code":item.encryptCode, @"card_id":item.cardId};
[mutableArray addObject:dict];
}
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:mutableArray options:NSJSONWritingPrettyPrinted error: &error];
NSString *cardItemList = @"";
if ([jsonData length] && error == nil) {
cardItemList = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
NSDictionary *result = @{
description: chooseInvoiceResp.description == nil ? @"" : chooseInvoiceResp.description,
errStr: chooseInvoiceResp.errStr == nil ? @"" : chooseInvoiceResp.errStr,
errCode: @(chooseInvoiceResp.errCode),
type: @(chooseInvoiceResp.type),
@"cardItemList":cardItemList
};
if(channel != nil){
[channel invokeMethod:@"onOpenWechatInvoiceResponse" arguments:result];
}
} else if ([resp isKindOfClass:[WXSubscribeMsgResp class]]) {
WXSubscribeMsgResp *subscribeMsgResp = (WXSubscribeMsgResp *) resp;
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSString *openid = subscribeMsgResp.openId;
if(openid != nil && openid != NULL && ![openid isKindOfClass:[NSNull class]]){
result[@"openid"] = openid;
}
NSString *templateId = subscribeMsgResp.templateId;
if(templateId != nil && templateId != NULL && ![templateId isKindOfClass:[NSNull class]]){
result[@"templateId"] = templateId;
}
NSString *action = subscribeMsgResp.action;
if(action != nil && action != NULL && ![action isKindOfClass:[NSNull class]]){
result[@"action"] = action;
}
NSString *reserved = subscribeMsgResp.action;
if(reserved != nil && reserved != NULL && ![reserved isKindOfClass:[NSNull class]]){
result[@"reserved"] = reserved;
}
UInt32 scene = subscribeMsgResp.scene;
result[@"scene"] = @(scene);
if(channel != nil){
[channel invokeMethod:@"onSubscribeMsgResp" arguments:result];
}
} else if ([resp isKindOfClass:[WXLaunchMiniProgramResp class]]) {
WXLaunchMiniProgramResp *miniProgramResp = (WXLaunchMiniProgramResp *) resp;
NSDictionary *commonResult = @{
description: miniProgramResp.description == nil ? @"" : miniProgramResp.description,
errStr: miniProgramResp.errStr == nil ? @"" : miniProgramResp.errStr,
errCode: @(miniProgramResp.errCode),
type: @(miniProgramResp.type),
};
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:commonResult];
if (miniProgramResp.extMsg != nil) {
result[@"extMsg"] = miniProgramResp.extMsg;
}
// @"extMsg":miniProgramResp.extMsg == nil?@"":miniProgramResp.extMsg
if(channel != nil){
[channel invokeMethod:@"onLaunchMiniProgramResponse" arguments:result];
}
} else if ([resp isKindOfClass:[WXInvoiceAuthInsertResp class]]) {
} else if ([resp isKindOfClass:[WXOpenBusinessWebViewResp class]]) {
WXOpenBusinessWebViewResp *businessResp = (WXOpenBusinessWebViewResp *) resp;
NSDictionary *result = @{
description: [FluwxStringUtil nilToEmpty:businessResp.description],
errStr: [FluwxStringUtil nilToEmpty:resp.errStr],
errCode: @(businessResp.errCode),
type: @(businessResp.type),
@"resultInfo": [FluwxStringUtil nilToEmpty:businessResp.result],
@"businessType": @(businessResp.businessType),
};
if(channel != nil){
[channel invokeMethod:@"onWXOpenBusinessWebviewResponse" arguments:result];
}
} else if ([resp isKindOfClass:[WXOpenCustomerServiceResp class]])
{
WXOpenCustomerServiceResp *customerResp = (WXOpenCustomerServiceResp *) resp;
NSDictionary *result = @{
description: [FluwxStringUtil nilToEmpty:customerResp.description],
errStr: [FluwxStringUtil nilToEmpty:resp.errStr],
errCode: @(customerResp.errCode),
type: @(customerResp.type),
@"extMsg":[FluwxStringUtil nilToEmpty:customerResp.extMsg]
};
if(channel != nil){
[channel invokeMethod:@"onWXOpenBusinessWebviewResponse" arguments:result];
}
// 相关错误信息
}else if ([resp isKindOfClass:[WXOpenBusinessViewResp class]])
{
WXOpenBusinessViewResp *openBusinessViewResp = (WXOpenBusinessViewResp *) resp;
NSDictionary *result = @{
description: [FluwxStringUtil nilToEmpty:openBusinessViewResp.description],
errStr: [FluwxStringUtil nilToEmpty:resp.errStr],
errCode: @(openBusinessViewResp.errCode),
@"businessType":openBusinessViewResp.businessType,
type: @(openBusinessViewResp.type),
@"extMsg":[FluwxStringUtil nilToEmpty:openBusinessViewResp.extMsg]
};
if(channel != nil){
[channel invokeMethod:@"onOpenBusinessViewResponse" arguments:result];
}
// 相关错误信息
}
#ifndef NO_PAY
else if ([resp isKindOfClass:[WXPayInsuranceResp class]]) {
if ([_delegate respondsToSelector:@selector(managerDidRecvPayInsuranceResponse:)]) {
[_delegate managerDidRecvPayInsuranceResponse:(WXPayInsuranceResp *) resp];
}
} else if ([resp isKindOfClass:[PayResp class]]) {
PayResp *payResp = (PayResp *) resp;
NSDictionary *result = @{
description: [FluwxStringUtil nilToEmpty:payResp.description],
errStr: [FluwxStringUtil nilToEmpty:resp.errStr],
errCode: @(payResp.errCode),
type: @(payResp.type),
@"extData": [FluwxStringUtil nilToEmpty:[FluwxDelegate defaultManager].extData],
@"returnKey": [FluwxStringUtil nilToEmpty:payResp.returnKey],
};
[FluwxDelegate defaultManager].extData = nil;
[fluwxMethodChannel invokeMethod:@"onPayResponse" arguments:result];
} else if ([resp isKindOfClass:[WXNontaxPayResp class]]) {
}
#endif
}
- (void)onReq:(BaseReq *)req {
if ([req isKindOfClass:[GetMessageFromWXReq class]]) {
} else if ([req isKindOfClass:[ShowMessageFromWXReq class]]) {
} else if ([req isKindOfClass:[LaunchFromWXReq class]]) {
LaunchFromWXReq *launchFromWXReq = (LaunchFromWXReq *) req;
WXMediaMessage *wmm = launchFromWXReq.message;
NSString *msg = @"";
if (wmm == nil || wmm == NULL || [wmm isKindOfClass:[NSNull class]]) {
msg = @"";
}else {
msg = wmm.messageExt;
if (msg == nil || msg == NULL || [msg isKindOfClass:[NSNull class]]) {
msg = @"";
}
}
NSDictionary *result = @{
@"extMsg": msg
};
if(channel != nil){
[channel invokeMethod:@"onWXShowMessageFromWX" arguments:result];
}
}
}
@end @end
//
// Created by mo on 2020/3/7.
//
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
#import <Flutter/Flutter.h>
#import <WXApi.h>
#import <WXApiObject.h>
@protocol WXApiManagerDelegate <NSObject>
@optional
- (void)managerDidRecvGetMessageReq:(GetMessageFromWXReq *)request;
- (void)managerDidRecvShowMessageReq:(ShowMessageFromWXReq *)request;
- (void)managerDidRecvLaunchFromWXReq:(LaunchFromWXReq *)request;
- (void)managerDidRecvMessageResponse:(SendMessageToWXResp *)response;
- (void)managerDidRecvAuthResponse:(SendAuthResp *)response;
- (void)managerDidRecvAddCardResponse:(AddCardToWXCardPackageResp *)response;
- (void)managerDidRecvChooseCardResponse:(WXChooseCardResp *)response;
- (void)managerDidRecvChooseInvoiceResponse:(WXChooseInvoiceResp *)response;
- (void)managerDidRecvSubscribeMsgResponse:(WXSubscribeMsgResp *)response;
- (void)managerDidRecvLaunchMiniProgram:(WXLaunchMiniProgramResp *)response;
- (void)managerDidRecvInvoiceAuthInsertResponse:(WXInvoiceAuthInsertResp *)response;
- (void)managerDidRecvNonTaxpayResponse:(WXNontaxPayResp *)response;
- (void)managerDidRecvPayInsuranceResponse:(WXPayInsuranceResp *)response;
- (void)managerDidRecvPaymentResponse:(PayResp *)response;
@end
@interface FluwxResponseHandler : NSObject <WXApiDelegate>
@property(nonatomic, assign) id <WXApiManagerDelegate> delegate;
+ (instancetype)defaultManager;
- (void)setMethodChannel:(FlutterMethodChannel *)flutterMethodChannel;
@end
//
// Created by mo on 2020/3/7.
//
#import <Flutter/Flutter.h>
#import "FluwxStringUtil.h"
#import <WXApiObject.h>
#import "FluwxResponseHandler.h"
#import "FluwxDelegate.h"
@implementation FluwxResponseHandler
const NSString *errStr = @"errStr";
const NSString *errCode = @"errCode";
const NSString *openId = @"openId";
const NSString *type = @"type";
const NSString *lang = @"lang";
const NSString *country = @"country";
const NSString *description = @"description";
#pragma mark - LifeCycle
+ (instancetype)defaultManager {
static dispatch_once_t onceToken;
static FluwxResponseHandler *instance;
dispatch_once(&onceToken, ^{
instance = [[FluwxResponseHandler alloc] init];
});
return instance;
}
FlutterMethodChannel *fluwxMethodChannel = nil;
- (void)setMethodChannel:(FlutterMethodChannel *)flutterMethodChannel {
fluwxMethodChannel = flutterMethodChannel;
}
- (void)onNeedGrantReadPasteBoardPermissionWithURL:(nonnull NSURL *)openURL completion:(nonnull WXGrantReadPasteBoardPermissionCompletion)completion {
/// allow wechat OpenSDK to accessing the clipboard and paste
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
#pragma mark - WXApiDelegate
- (void)onResp:(BaseResp *)resp {
if ([resp isKindOfClass:[SendMessageToWXResp class]]) {
if (_delegate
&& [_delegate respondsToSelector:@selector(managerDidRecvMessageResponse:)]) {
SendMessageToWXResp *messageResp = (SendMessageToWXResp *) resp;
[_delegate managerDidRecvMessageResponse:messageResp];
}
SendMessageToWXResp *messageResp = (SendMessageToWXResp *) resp;
NSDictionary *result = @{
description: messageResp.description == nil ? @"" : messageResp.description,
errStr: messageResp.errStr == nil ? @"" : messageResp.errStr,
errCode: @(messageResp.errCode),
type: @(messageResp.type),
country: messageResp.country == nil ? @"" : messageResp.country,
lang: messageResp.lang == nil ? @"" : messageResp.lang};
[fluwxMethodChannel invokeMethod:@"onShareResponse" arguments:result];
} else if ([resp isKindOfClass:[SendAuthResp class]]) {
if (_delegate
&& [_delegate respondsToSelector:@selector(managerDidRecvAuthResponse:)]) {
SendAuthResp *authResp = (SendAuthResp *) resp;
[_delegate managerDidRecvAuthResponse:authResp];
}
SendAuthResp *authResp = (SendAuthResp *) resp;
NSDictionary *result = @{
description: authResp.description == nil ? @"" : authResp.description,
errStr: authResp.errStr == nil ? @"" : authResp.errStr,
errCode: @(authResp.errCode),
type: @(authResp.type),
country: authResp.country == nil ? @"" : authResp.country,
lang: authResp.lang == nil ? @"" : authResp.lang,
@"code": [FluwxStringUtil nilToEmpty:authResp.code],
@"state": [FluwxStringUtil nilToEmpty:authResp.state]
};
[fluwxMethodChannel invokeMethod:@"onAuthResponse" arguments:result];
} else if ([resp isKindOfClass:[AddCardToWXCardPackageResp class]]) {
if (_delegate
&& [_delegate respondsToSelector:@selector(managerDidRecvAddCardResponse:)]) {
AddCardToWXCardPackageResp *addCardResp = (AddCardToWXCardPackageResp *) resp;
[_delegate managerDidRecvAddCardResponse:addCardResp];
}
} else if ([resp isKindOfClass:[WXChooseCardResp class]]) {
if (_delegate
&& [_delegate respondsToSelector:@selector(managerDidRecvChooseCardResponse:)]) {
WXChooseCardResp *chooseCardResp = (WXChooseCardResp *) resp;
[_delegate managerDidRecvChooseCardResponse:chooseCardResp];
}
} else if ([resp isKindOfClass:[WXChooseInvoiceResp class]]) {
//TODO 处理发票返回,并回调Dart
WXChooseInvoiceResp *chooseInvoiceResp = (WXChooseInvoiceResp *) resp;
NSArray *array = chooseInvoiceResp.cardAry;
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:array.count];
for (int i = 0; i< array.count; i++) {
WXInvoiceItem *item = array[i];
NSDictionary *dict = @{@"app_id":item.appID, @"encrypt_code":item.encryptCode, @"card_id":item.cardId};
[mutableArray addObject:dict];
}
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:mutableArray options:NSJSONWritingPrettyPrinted error: &error];
NSString *cardItemList = @"";
if ([jsonData length] && error == nil) {
cardItemList = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
NSDictionary *result = @{
description: chooseInvoiceResp.description == nil ? @"" : chooseInvoiceResp.description,
errStr: chooseInvoiceResp.errStr == nil ? @"" : chooseInvoiceResp.errStr,
errCode: @(chooseInvoiceResp.errCode),
type: @(chooseInvoiceResp.type),
@"cardItemList":cardItemList
};
[fluwxMethodChannel invokeMethod:@"onOpenWechatInvoiceResponse" arguments:result];
} else if ([resp isKindOfClass:[WXSubscribeMsgResp class]]) {
if ([_delegate respondsToSelector:@selector(managerDidRecvSubscribeMsgResponse:)]) {
[_delegate managerDidRecvSubscribeMsgResponse:(WXSubscribeMsgResp *) resp];
}
WXSubscribeMsgResp *subscribeMsgResp = (WXSubscribeMsgResp *) resp;
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSString *openid = subscribeMsgResp.openId;
if(openid != nil && openid != NULL && ![openid isKindOfClass:[NSNull class]]){
result[@"openid"] = openid;
}
NSString *templateId = subscribeMsgResp.templateId;
if(templateId != nil && templateId != NULL && ![templateId isKindOfClass:[NSNull class]]){
result[@"templateId"] = templateId;
}
NSString *action = subscribeMsgResp.action;
if(action != nil && action != NULL && ![action isKindOfClass:[NSNull class]]){
result[@"action"] = action;
}
NSString *reserved = subscribeMsgResp.action;
if(reserved != nil && reserved != NULL && ![reserved isKindOfClass:[NSNull class]]){
result[@"reserved"] = reserved;
}
UInt32 scene = subscribeMsgResp.scene;
result[@"scene"] = @(scene);
[fluwxMethodChannel invokeMethod:@"onSubscribeMsgResp" arguments:result];
} else if ([resp isKindOfClass:[WXLaunchMiniProgramResp class]]) {
if ([_delegate respondsToSelector:@selector(managerDidRecvLaunchMiniProgram:)]) {
[_delegate managerDidRecvLaunchMiniProgram:(WXLaunchMiniProgramResp *) resp];
}
WXLaunchMiniProgramResp *miniProgramResp = (WXLaunchMiniProgramResp *) resp;
NSDictionary *commonResult = @{
description: miniProgramResp.description == nil ? @"" : miniProgramResp.description,
errStr: miniProgramResp.errStr == nil ? @"" : miniProgramResp.errStr,
errCode: @(miniProgramResp.errCode),
type: @(miniProgramResp.type),
};
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:commonResult];
if (miniProgramResp.extMsg != nil) {
result[@"extMsg"] = miniProgramResp.extMsg;
}
// @"extMsg":miniProgramResp.extMsg == nil?@"":miniProgramResp.extMsg
[fluwxMethodChannel invokeMethod:@"onLaunchMiniProgramResponse" arguments:result];
} else if ([resp isKindOfClass:[WXInvoiceAuthInsertResp class]]) {
if ([_delegate respondsToSelector:@selector(managerDidRecvInvoiceAuthInsertResponse:)]) {
[_delegate managerDidRecvInvoiceAuthInsertResponse:(WXInvoiceAuthInsertResp *) resp];
}
} else if ([resp isKindOfClass:[WXNontaxPayResp class]]) {
if ([_delegate respondsToSelector:@selector(managerDidRecvNonTaxpayResponse:)]) {
[_delegate managerDidRecvNonTaxpayResponse:(WXNontaxPayResp *) resp];
}
} else if ([resp isKindOfClass:[WXPayInsuranceResp class]]) {
if ([_delegate respondsToSelector:@selector(managerDidRecvPayInsuranceResponse:)]) {
[_delegate managerDidRecvPayInsuranceResponse:(WXPayInsuranceResp *) resp];
}
} else if ([resp isKindOfClass:[PayResp class]]) {
if ([_delegate respondsToSelector:@selector(managerDidRecvPaymentResponse:)]) {
[_delegate managerDidRecvPaymentResponse:(PayResp *) resp];
}
PayResp *payResp = (PayResp *) resp;
NSDictionary *result = @{
description: [FluwxStringUtil nilToEmpty:payResp.description],
errStr: [FluwxStringUtil nilToEmpty:resp.errStr],
errCode: @(payResp.errCode),
type: @(payResp.type),
@"extData": [FluwxStringUtil nilToEmpty:[FluwxDelegate defaultManager].extData],
@"returnKey": [FluwxStringUtil nilToEmpty:payResp.returnKey],
};
[FluwxDelegate defaultManager].extData = nil;
[fluwxMethodChannel invokeMethod:@"onPayResponse" arguments:result];
} else if ([resp isKindOfClass:[WXOpenBusinessWebViewResp class]]) {
WXOpenBusinessWebViewResp *businessResp = (WXOpenBusinessWebViewResp *) resp;
NSDictionary *result = @{
description: [FluwxStringUtil nilToEmpty:businessResp.description],
errStr: [FluwxStringUtil nilToEmpty:resp.errStr],
errCode: @(businessResp.errCode),
type: @(businessResp.type),
@"resultInfo": [FluwxStringUtil nilToEmpty:businessResp.result],
@"businessType": @(businessResp.businessType),
};
[fluwxMethodChannel invokeMethod:@"onWXOpenBusinessWebviewResponse" arguments:result];
} else if ([resp isKindOfClass:[WXOpenCustomerServiceResp class]])
{
WXOpenCustomerServiceResp *customerResp = (WXOpenCustomerServiceResp *) resp;
NSDictionary *result = @{
description: [FluwxStringUtil nilToEmpty:customerResp.description],
errStr: [FluwxStringUtil nilToEmpty:resp.errStr],
errCode: @(customerResp.errCode),
type: @(customerResp.type),
@"extMsg":[FluwxStringUtil nilToEmpty:customerResp.extMsg]
};
[fluwxMethodChannel invokeMethod:@"onWXOpenBusinessWebviewResponse" arguments:result];
// 相关错误信息
}else if ([resp isKindOfClass:[WXOpenBusinessViewResp class]])
{
WXOpenBusinessViewResp *openBusinessViewResp = (WXOpenBusinessViewResp *) resp;
NSDictionary *result = @{
description: [FluwxStringUtil nilToEmpty:openBusinessViewResp.description],
errStr: [FluwxStringUtil nilToEmpty:resp.errStr],
errCode: @(openBusinessViewResp.errCode),
@"businessType":openBusinessViewResp.businessType,
type: @(openBusinessViewResp.type),
@"extMsg":[FluwxStringUtil nilToEmpty:openBusinessViewResp.extMsg]
};
[fluwxMethodChannel invokeMethod:@"onOpenBusinessViewResponse" arguments:result];
// 相关错误信息
}
}
- (void)onReq:(BaseReq *)req {
if ([req isKindOfClass:[GetMessageFromWXReq class]]) {
if (_delegate
&& [_delegate respondsToSelector:@selector(managerDidRecvGetMessageReq:)]) {
GetMessageFromWXReq *getMessageReq = (GetMessageFromWXReq *) req;
[_delegate managerDidRecvGetMessageReq:getMessageReq];
}
} else if ([req isKindOfClass:[ShowMessageFromWXReq class]]) {
if (_delegate
&& [_delegate respondsToSelector:@selector(managerDidRecvShowMessageReq:)]) {
ShowMessageFromWXReq *showMessageReq = (ShowMessageFromWXReq *) req;
[_delegate managerDidRecvShowMessageReq:showMessageReq];
}
} else if ([req isKindOfClass:[LaunchFromWXReq class]]) {
if (_delegate
&& [_delegate respondsToSelector:@selector(managerDidRecvLaunchFromWXReq:)]) {
LaunchFromWXReq *launchReq = (LaunchFromWXReq *) req;
[_delegate managerDidRecvLaunchFromWXReq:launchReq];
}
LaunchFromWXReq *launchFromWXReq = (LaunchFromWXReq *) req;
WXMediaMessage *wmm = launchFromWXReq.message;
NSString *msg = @"";
if (wmm == nil || wmm == NULL || [wmm isKindOfClass:[NSNull class]]) {
msg = @"";
}else {
msg = wmm.messageExt;
if (msg == nil || msg == NULL || [msg isKindOfClass:[NSNull class]]) {
msg = @"";
}
}
NSDictionary *result = @{
@"extMsg": msg
};
[fluwxMethodChannel invokeMethod:@"onWXShowMessageFromWX" arguments:result];
}
}
@end
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <WXApiObject.h> #import <WXApiObject.h>
#import "FluwxResponseHandler.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
...@@ -142,6 +141,7 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -142,6 +141,7 @@ NS_ASSUME_NONNULL_BEGIN
completion:(void (^ __nullable)(BOOL success))completion; completion:(void (^ __nullable)(BOOL success))completion;
#ifndef NO_PAY
+ (void)sendPayment:(NSString *)appId + (void)sendPayment:(NSString *)appId
PartnerId:(NSString *)partnerId PartnerId:(NSString *)partnerId
PrepayId:(NSString *)prepayId PrepayId:(NSString *)prepayId
...@@ -150,6 +150,7 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -150,6 +150,7 @@ NS_ASSUME_NONNULL_BEGIN
Package:(NSString *)package Package:(NSString *)package
Sign:(NSString *)sign Sign:(NSString *)sign
completion:(void (^ __nullable)(BOOL success))completion; completion:(void (^ __nullable)(BOOL success))completion;
#endif
+ (void)openCustomerService:(NSString *)url + (void)openCustomerService:(NSString *)url
CorpId:(NSString *)corpId CorpId:(NSString *)corpId
......
...@@ -381,7 +381,7 @@ ...@@ -381,7 +381,7 @@
return [WXApi sendAuthReq:req return [WXApi sendAuthReq:req
viewController:viewController viewController:viewController
delegate:[FluwxResponseHandler defaultManager] delegate:self
completion:completion]; completion:completion];
} }
...@@ -426,7 +426,7 @@ ...@@ -426,7 +426,7 @@
[WXApi sendReq:chooseInvoiceReq completion:completion]; [WXApi sendReq:chooseInvoiceReq completion:completion];
} }
#ifndef NO_PAY
+ (void)sendPayment:(NSString *)appId PartnerId:(NSString *)partnerId PrepayId:(NSString *)prepayId NonceStr:(NSString *)nonceStr Timestamp:(UInt32)timestamp Package:(NSString *)package Sign:(NSString *)sign + (void)sendPayment:(NSString *)appId PartnerId:(NSString *)partnerId PrepayId:(NSString *)prepayId NonceStr:(NSString *)nonceStr Timestamp:(UInt32)timestamp Package:(NSString *)package Sign:(NSString *)sign
completion:(void (^ __nullable)(BOOL success))completion { completion:(void (^ __nullable)(BOOL success))completion {
...@@ -442,6 +442,7 @@ ...@@ -442,6 +442,7 @@
[WXApi sendReq:req completion:completion]; [WXApi sendReq:req completion:completion];
} }
#endif
+ (void)openCustomerService:(NSString *)url CorpId:(NSString *)corpId completion:(void (^)(BOOL))completion { + (void)openCustomerService:(NSString *)url CorpId:(NSString *)corpId completion:(void (^)(BOOL))completion {
WXOpenCustomerServiceReq *req = [[WXOpenCustomerServiceReq alloc] init]; WXOpenCustomerServiceReq *req = [[WXOpenCustomerServiceReq alloc] init];
......
# #
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint fluwx.podspec' to validate before publishing. # Run `pod lib lint fluwx.podspec` to validate before publishing.
# #
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint wechat_kit.podspec` to validate before publishing.
#
pubspec = YAML.load_file(File.join('..', 'pubspec.yaml'))
library_version = pubspec['version'].gsub('+', '-')
current_dir = Dir.pwd
calling_dir = File.dirname(__FILE__)
project_dir = calling_dir.slice(0..(calling_dir.index('/.symlinks')))
flutter_project_dir = calling_dir.slice(0..(calling_dir.index('/ios/.symlinks')))
cfg = YAML.load_file(File.join(flutter_project_dir, 'pubspec.yaml'))
debug_logging = false
if cfg['fluwx'] && cfg['fluwx']['debug_logging'] == true
debug_logging = true
end
if cfg['fluwx'] && cfg['fluwx']['ios'] && cfg['fluwx']['ios']['no_pay'] == true
fluwx_subspec = 'no_pay'
else
fluwx_subspec = 'pay'
end
Pod::UI.puts "using sdk with #{fluwx_subspec}"
if cfg['fluwx'] && (cfg['fluwx']['app_id'] && cfg['fluwx']['ios'] && cfg['fluwx']['ios']['universal_link'])
app_id = cfg['fluwx']['app_id']
universal_link = cfg['fluwx']['ios']['universal_link']
system("ruby #{current_dir}/wechat_setup.rb -a #{app_id} -u #{universal_link} -p #{project_dir} -n Runner.xcodeproj")
else
abort("required values:[app_id, universal_link] are missing. Please add them in pubspec.yaml:\nfluwx:\n app_id: ${app id}\n \nios:\nuniversal_link: https://${applinks domain}/universal_link/${example_app}/wechat/\n")
end
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'fluwx' s.name = 'fluwx'
s.version = '0.0.1' s.version = '0.0.1'
s.summary = 'A new Flutter plugin for Wechat SDK.' s.summary = 'The capability of implementing WeChat SDKs in Flutter. With Fluwx, developers can use WeChatSDK easily, such as sharing, payment, lanuch mini program and etc.'
s.description = <<-DESC s.description = <<-DESC
A new Flutter plugin for Wechat SDK. The capability of implementing WeChat SDKs in Flutter. With Fluwx, developers can use WeChatSDK easily, such as sharing, payment, lanuch mini program and etc.
DESC DESC
s.homepage = 'https://github.com/OpenFlutter/fluwx' s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' } s.license = { :file => '../LICENSE' }
s.author = { 'JarvanMo' => 'jarvan.mo@gmail.com' } s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' } s.source = { :path => '.' }
s.source_files = 'Classes/**/*' s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/public/*.h' s.public_header_files = 'Classes/**/*.h'
s.static_framework = true
s.dependency 'Flutter' s.dependency 'Flutter'
# s.dependency 'WechatOpenSDK', '1.8.7.1' s.platform = :ios, '12.0'
# s.dependency 'OpenWeChatSDK','~> 1.9.9' s.default_subspec = fluwx_subspec
s.dependency 'WechatOpenSDK-XCFramework','~> 2.0.2'
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/Headers/Public/#{s.name}" } pod_target_xcconfig = {
s.frameworks = ["SystemConfiguration", "CoreTelephony","WebKit"] 'OTHER_LDFLAGS' => '$(inherited) -ObjC -all_load'
s.libraries = ["z", "sqlite3.0", "c++"] }
s.preserve_paths = 'Lib/*.a'
s.vendored_libraries = "**/*.a" s.subspec 'pay' do |sp|
s.ios.deployment_target = '12.0' sp.dependency 'WechatOpenSDK-XCFramework','~> 2.0.2'
# Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. if debug_logging
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } pod_target_xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = '$(inherited) WECHAT_LOGGING=1'
else
pod_target_xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = '$(inherited) WECHAT_LOGGING=0'
end
sp.pod_target_xcconfig = pod_target_xcconfig
end
s.subspec 'no_pay' do |sp|
sp.dependency 'OpenWeChatSDKNoPay','~> 2.0.2+1'
sp.frameworks = 'CoreGraphics', 'Security', 'WebKit'
sp.libraries = 'c++', 'z', 'sqlite3.0'
if debug_logging
pod_target_xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = '$(inherited) NO_PAY=1 WECHAT_LOGGING=1'
else
pod_target_xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = '$(inherited) NO_PAY=1 WECHAT_LOGGING=0'
end
sp.pod_target_xcconfig = pod_target_xcconfig
end
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
end end
#
# Reference documentations
# https://github.com/firebase/flutterfire/blob/master/packages/firebase_crashlytics/firebase_crashlytics/ios/crashlytics_add_upload_symbols
# https://github.com/MagicalWater/Base-APP-Env/blob/master/fastlane/actions/xcode_parse.rb
#
require 'xcodeproj'
require 'plist'
require 'optparse'
require 'uri'
# Dictionary to hold command line arguments
options_dict = {}
# Parse command line arguments into options_dict
OptionParser.new do |options|
options.banner = "Setup the Wechat to an Xcode target."
options.on("-p", "--projectDirectory=DIRECTORY", String, "Directory of the Xcode project") do |dir|
options_dict[:project_dir] = dir
end
options.on("-n", "--projectName=NAME", String, "Name of the Xcode project (ex: Runner.xcodeproj)") do |name|
options_dict[:project_name] = name
end
options.on("-a", "--appId=APPID", String, "App ID for Wechat") do |opts|
options_dict[:app_id] = opts
end
options.on("-u", "--universalLink=UNIVERSALLINK", String, "Universal Link for Wechat") do |opts|
options_dict[:universal_link] = opts
end
end.parse!
# Minimum required arguments are a project directory and project name
unless (options_dict[:project_dir] and options_dict[:project_name])
abort("Must provide a project directory and project name.\n")
end
# Path to the Xcode project to modify
project_path = File.join(options_dict[:project_dir], options_dict[:project_name])
unless (File.exist?(project_path))
abort("Project at #{project_path} does not exist. Please check paths manually.\n");
end
# Actually open and modify the project
project = Xcodeproj::Project.open(project_path)
project.targets.each do |target|
if target.name == "Runner"
app_id = options_dict[:app_id]
universal_link = options_dict[:universal_link]
applinks = "applinks:#{URI.parse(universal_link).host}"
sectionObject = {}
project.objects.each do |object|
if object.uuid == target.uuid
sectionObject = object
break
end
end
sectionObject.build_configurations.each do |config|
infoplist = config.build_settings["INFOPLIST_FILE"]
if !infoplist
abort("INFOPLIST_FILE is not exist\n")
end
infoplistFile = File.join(options_dict[:project_dir], infoplist)
if !File.exist?(infoplistFile)
abort("#{infoplist} is not exist\n")
end
result = Plist.parse_xml(infoplistFile, marshal: false)
if !result
result = {}
end
urlTypes = result["CFBundleURLTypes"]
if !urlTypes
urlTypes = []
result["CFBundleURLTypes"] = urlTypes
end
isUrlTypeExist = urlTypes.any? { |urlType| urlType["CFBundleURLSchemes"] && (urlType["CFBundleURLSchemes"].include? app_id) }
if !isUrlTypeExist
urlTypes << {
"CFBundleTypeRole": "Editor",
"CFBundleURLName": "weixin",
"CFBundleURLSchemes": [ app_id ]
}
File.write(infoplistFile, Plist::Emit.dump(result))
end
queriesSchemes = result["LSApplicationQueriesSchemes"]
if !queriesSchemes
queriesSchemes = []
result["LSApplicationQueriesSchemes"] = queriesSchemes
end
wechatQueriesSchemes = ["weixin", "weixinULAPI", "weixinURLParamsAPI"]
if wechatQueriesSchemes.any? { |queriesScheme| !(queriesSchemes.include? queriesScheme) }
wechatQueriesSchemes.each do |queriesScheme|
if !(queriesSchemes.include? queriesScheme)
queriesSchemes << queriesScheme
end
end
File.write(infoplistFile, Plist::Emit.dump(result))
end
security = result["NSAppTransportSecurity"]
if !security
security = {}
result["NSAppTransportSecurity"] = security
end
if security["NSAllowsArbitraryLoads"] != true
security["NSAllowsArbitraryLoads"] = true
File.write(infoplistFile, Plist::Emit.dump(result))
end
if security["NSAllowsArbitraryLoadsInWebContent"] != true
security["NSAllowsArbitraryLoadsInWebContent"] = true
File.write(infoplistFile, Plist::Emit.dump(result))
end
end
sectionObject.build_configurations.each do |config|
codeSignEntitlements = config.build_settings["CODE_SIGN_ENTITLEMENTS"]
if !codeSignEntitlements
codeSignEntitlements = "Runner/Runner.entitlements"
config.build_settings["CODE_SIGN_ENTITLEMENTS"] = codeSignEntitlements
project.save()
end
codeSignEntitlementsFile = File.join(options_dict[:project_dir], codeSignEntitlements)
if !File.exist?(codeSignEntitlementsFile)
content = Plist::Emit.dump({})
File.write(codeSignEntitlementsFile, content)
end
runnerTargetMainGroup = project.main_group.find_subpath('Runner', false)
isRefExist = runnerTargetMainGroup.files.any? { |file| file.path.include? File.basename(codeSignEntitlementsFile) }
if !isRefExist
runnerTargetMainGroup.new_reference(File.basename(codeSignEntitlementsFile))
project.save()
end
result = Plist.parse_xml(codeSignEntitlementsFile, marshal: false)
if !result
result = {}
end
domains = result["com.apple.developer.associated-domains"]
if !domains
domains = []
result["com.apple.developer.associated-domains"] = domains
end
isApplinksExist = domains.include? applinks
if !isApplinksExist
domains << applinks
File.write(codeSignEntitlementsFile, Plist::Emit.dump(result))
end
end
end
end
/*
* Copyright (c) 2023. OpenFlutter Project
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this
* file to you 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.
*/
/// Fluwx is a powerful plugin for WeChatSDK. /// Fluwx is a powerful plugin for WeChatSDK.
/// easy to use. /// easy to use.
/// ///
/// A open sou;rce project authorized by [OpenFlutter](https://github.com/OpenFlutter). /// A open sou;rce project authorized by [OpenFlutter](https://github.com/OpenFlutter).
library fluwx; library fluwx;
export 'src/fluwx_iml.dart'; export 'src/fluwx.dart';
export 'src/foundation/arguments.dart';
export 'src/response/wechat_response.dart'; export 'src/response/wechat_response.dart';
export 'src/share/share_models.dart';
export 'src/wechat_enums.dart'; export 'src/wechat_enums.dart';
export 'src/wechat_file.dart' hide FileSchema; export 'src/wechat_file.dart' hide FileSchema;
/*
* Copyright (c) 2023. OpenFlutter Project
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this
* file to you 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.
*/
import 'dart:async';
import 'foundation/arguments.dart';
import 'method_channel/fluwx_platform_interface.dart';
import 'response/wechat_response.dart';
class Fluwx {
late final WeakReference<void Function(WeChatResponse event)>
responseListener;
final List<Function(WeChatResponse response)> _responseListeners = [];
Fluwx() {
responseListener = WeakReference((event) {
for (var listener in _responseListeners) {
listener(event);
}
});
final target = responseListener.target;
if (target != null) {
FluwxPlatform.instance.responseEventHandler.listen(target);
}
}
Future<bool> get isWeChatInstalled =>
FluwxPlatform.instance.isWeChatInstalled;
/// Open given target. See [OpenType] for more details
Future<bool> open({required OpenType target}) {
return FluwxPlatform.instance.open(target);
}
/// It's ok if you register multi times.
/// [appId] is not necessary.
/// if [doOnIOS] is true ,fluwx will register WXApi on iOS.
/// if [doOnAndroid] is true, fluwx will register WXApi on Android.
/// [universalLink] is required if you want to register on iOS.
Future<bool> registerApi({
required String appId,
bool doOnIOS = true,
bool doOnAndroid = true,
String? universalLink,
}) async {
return FluwxPlatform.instance.registerApi(
appId: appId,
doOnAndroid: doOnAndroid,
doOnIOS: doOnIOS,
universalLink: universalLink);
}
/// Share your requests to WeChat.
/// This depends on the actual type of [what].
Future<bool> share(WeChatShareModel what) async {
return FluwxPlatform.instance.share(what);
}
/// Login by WeChat.See [AuthType] for more details.
Future<bool> authBy({required AuthType which}) async {
return FluwxPlatform.instance.authBy(which);
}
/// please read * [official docs](https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_2.shtml).
Future<bool> autoDeduct({required AutoDeduct data}) async {
return FluwxPlatform.instance.autoDeduct(data);
}
Future<bool> get isSupportOpenBusinessView async =>
await FluwxPlatform.instance.isSupportOpenBusinessView;
Future<String?> getExtMsg() async {
return FluwxPlatform.instance.getExtMsg();
}
Future<bool> pay({required PayType which}) async {
return FluwxPlatform.instance.pay(which);
}
/// Subscribe responses from WeChat
subscribeResponse(Function(WeChatResponse response) listener) {
_responseListeners.add(listener);
}
/// Unsubscribe responses from WeChat
unsubscribeResponse(Function(WeChatResponse response) listener) {
_responseListeners.remove(listener);
}
}
/*
* Copyright (c) 2020. OpenFlutter Project
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this
* file to you 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.
*/
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:fluwx/fluwx.dart';
const Map<Type, String> _shareModelMethodMapper = {
WeChatShareTextModel: 'shareText',
WeChatShareImageModel: 'shareImage',
WeChatShareMusicModel: 'shareMusic',
WeChatShareVideoModel: 'shareVideo',
WeChatShareWebPageModel: 'shareWebPage',
WeChatShareMiniProgramModel: 'shareMiniProgram',
WeChatShareFileModel: 'shareFile',
};
MethodChannel _channel = MethodChannel('com.jarvanmo/fluwx')
..setMethodCallHandler(_methodHandler);
StreamController<BaseWeChatResponse> _weChatResponseEventHandlerController =
StreamController.broadcast();
/// Response answers from WeChat after sharing, payment etc.
Stream<BaseWeChatResponse> get weChatResponseEventHandler =>
_weChatResponseEventHandlerController.stream;
/// [true] if WeChat installed, otherwise [false].
/// Please add WeChat to the white list in order use this method on IOS.
Future<bool> get isWeChatInstalled async {
return await _channel.invokeMethod('isWeChatInstalled');
}
///just open WeChat, noting to do.
Future<bool> openWeChatApp() async {
return await _channel.invokeMethod('openWXApp');
}
/// It's ok if you register multi times.
/// [appId] is not necessary.
/// if [doOnIOS] is true ,fluwx will register WXApi on iOS.
/// if [doOnAndroid] is true, fluwx will register WXApi on Android.
/// [universalLink] is required if you want to register on iOS.
Future<bool> registerWxApi({
required String appId,
bool doOnIOS = true,
bool doOnAndroid = true,
String? universalLink,
}) async {
if (doOnIOS && Platform.isIOS) {
if (universalLink == null ||
universalLink.trim().isEmpty ||
!universalLink.startsWith('https')) {
throw ArgumentError.value(
universalLink,
"You're trying to use illegal universal link, see "
'https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html '
'for more detail',
);
}
}
return await _channel.invokeMethod('registerApp', {
'appId': appId,
'iOS': doOnIOS,
'android': doOnAndroid,
'universalLink': universalLink
});
}
// Get ext Message
Future<String?> getExtMsg() {
return _channel.invokeMethod('getExtMsg');
}
/// start Log
/// defalult [WXLogLevel.DETAIL]
Future<bool?> startLog({WXLogLevel logLevel = WXLogLevel.DETAIL}) async {
return await _channel
.invokeMethod('startLog', {'logLevel': logLevel.toNativeInt()});
}
/// stop log
Future<bool?> stopLog() async {
return await _channel.invokeMethod('stopLog');
}
/// Share your requests to WeChat.
/// This depends on the actual type of [model].
/// see [_shareModelMethodMapper] for detail.
Future<bool> shareToWeChat(WeChatShareBaseModel model) async {
if (_shareModelMethodMapper.containsKey(model.runtimeType)) {
final methodChannel = _shareModelMethodMapper[model.runtimeType];
if (methodChannel == null) {
throw ArgumentError.value(
'${model.runtimeType} method channel not found',
);
}
return await _channel.invokeMethod(methodChannel, model.toMap());
}
return Future.error('no method mapper found[${model.runtimeType}]');
}
/// The WeChat-Login is under Auth-2.0
/// This method login with native WeChat app.
/// For users without WeChat app, please use [authByQRCode] instead
/// This method only supports getting AuthCode,this is first step to login with WeChat
/// Once AuthCode got, you need to request Access_Token
/// For more information please visit:
/// * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317851&token=
Future<bool> sendWeChatAuth(
{required String scope,
String state = 'state',
bool nonAutomatic = false}) async {
assert(scope.trim().isNotEmpty);
return await _channel.invokeMethod(
'sendAuth',
{'scope': scope, 'state': state, 'nonAutomatic': nonAutomatic},
);
}
/// open mini-program
/// see [WXMiniProgramType]
Future<bool> launchWeChatMiniProgram({
required String username,
String? path,
WXMiniProgramType miniProgramType = WXMiniProgramType.RELEASE,
}) async {
assert(username.trim().isNotEmpty);
return await _channel.invokeMethod('launchMiniProgram', {
'userName': username,
'path': path,
'miniProgramType': miniProgramType.toNativeInt()
});
}
/// request payment with WeChat.
/// Read the official document for more detail.
/// [timeStamp] is int because [timeStamp] will be mapped to Unit32.
Future<bool> payWithWeChat({
required String appId,
required String partnerId,
required String prepayId,
required String packageValue,
required String nonceStr,
required int timeStamp,
required String sign,
String? signType,
String? extData,
}) async {
return await _channel.invokeMethod('payWithFluwx', {
'appId': appId,
'partnerId': partnerId,
'prepayId': prepayId,
'packageValue': packageValue,
'nonceStr': nonceStr,
'timeStamp': timeStamp,
'sign': sign,
'signType': signType,
'extData': extData,
});
}
/// request Hong Kong Wallet payment with WeChat.
/// Read the official document for more detail.
Future<bool> payWithWeChatHongKongWallet({required String prepayId}) async {
return await _channel.invokeMethod('payWithHongKongWallet', {
'prepayId': prepayId,
});
}
/// subscribe WeChat message
Future<bool> subscribeWeChatMsg({
required String appId,
required int scene,
required String templateId,
String? reserved,
}) async {
return await _channel.invokeMethod(
'subscribeMsg',
{
'appId': appId,
'scene': scene,
'templateId': templateId,
'reserved': reserved,
},
);
}
/// please read official docs.
Future<bool> autoDeDuctWeChat({
required String appId,
required String mchId,
required String planId,
required String contractCode,
required String requestSerial,
required String contractDisplayAccount,
required String notifyUrl,
required String version,
required String sign,
required String timestamp,
String returnApp = '3',
int businessType = 12,
}) async {
return await _channel.invokeMethod('autoDeduct', {
'appid': appId,
'mch_id': mchId,
'plan_id': planId,
'contract_code': contractCode,
'request_serial': requestSerial,
'contract_display_account': contractDisplayAccount,
'notify_url': notifyUrl,
'version': version,
'sign': sign,
'timestamp': timestamp,
'return_app': returnApp,
'businessType': businessType
});
}
/// please read * [official docs](https://pay.weixin.qq.com/wiki/doc/api/wxpay_v2/papay/chapter3_2.shtml).
Future<bool> autoDeductWeChatV2(
Map<String, String> queryInfo, {
int businessType = 12,
}) async {
return await _channel.invokeMethod(
'autoDeductV2', {'queryInfo': queryInfo, 'businessType': businessType});
}
/// Sometimes WeChat is not installed on users's devices.However we can
/// request a QRCode so that we can get AuthCode by scanning the QRCode
/// All required params must not be null or empty
/// [schemeData] only works on iOS
/// see * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=215238808828h4XN&token=&lang=zh_CN
Future<bool> authWeChatByQRCode({
required String appId,
required String scope,
required String nonceStr,
required String timeStamp,
required String signature,
String? schemeData,
}) async {
assert(appId.isNotEmpty);
assert(scope.isNotEmpty);
assert(nonceStr.isNotEmpty);
assert(timeStamp.isNotEmpty);
assert(signature.isNotEmpty);
return await _channel.invokeMethod('authByQRCode', {
'appId': appId,
'scope': scope,
'nonceStr': nonceStr,
'timeStamp': timeStamp,
'signature': signature,
'schemeData': schemeData
});
}
/// stop [authWeChatByQRCode]
Future<bool> stopWeChatAuthByQRCode() async {
return await _channel.invokeMethod('stopAuthByQRCode');
}
Future _methodHandler(MethodCall methodCall) {
final response = BaseWeChatResponse.create(
methodCall.method,
methodCall.arguments,
);
_weChatResponseEventHandlerController.add(response);
return Future.value();
}
/// IOS only
Future<bool> authWeChatByPhoneLogin({
required String scope,
String state = 'state',
}) async {
return await _channel.invokeMethod(
'authByPhoneLogin',
{'scope': scope, 'state': state},
);
}
Future<bool> openWeChatCustomerServiceChat(
{required String url, required String corpId}) async {
return await _channel.invokeMethod(
"openWeChatCustomerServiceChat", {"corpId": corpId, "url": url});
}
/// see * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter6_2_1.shtml
Future<bool> openWeChatBusinessView(
{required String businessType, required String query}) async {
return await _channel.invokeMethod(
"openBusinessView", {"businessType": businessType, "query": query});
}
Future<bool> checkSupportOpenBusinessView() async {
return await _channel.invokeMethod("checkSupportOpenBusinessView");
}
Future<bool> openWeChatInvoice(
{required String appId,
required String cardType,
String locationId = "",
String cardId = "",
String canMultiSelect = "1"}) async {
return await _channel.invokeMethod("openWeChatInvoice", {
"appId": appId,
"cardType": cardType,
"locationId": locationId,
"cardId": cardId,
"canMultiSelect": canMultiSelect
});
}
/* /*
* Copyright (c) 2020. OpenFlutter Project * Copyright (c) 2023. OpenFlutter Project
* *
* Licensed to the Apache Software Foundation (ASF) under one or more contributor * Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for * license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this * additional information regarding copyright ownership. The ASF licenses this
* file to you under the Apache License, Version 2.0 (the "License"); you may not * file to you 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 * use this file except in compliance with the License. You may obtain a copy of
* the License at * the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/src/wechat_file.dart';
void main() { // In order to *not* need this ignore, consider extracting the "web" version
test('test WeChatImage.fromNetwork', () { // of your plugin as a separate package, instead of inlining it in the same
var withSuffixImage = WeChatImage.network( // package as the core of your plugin.
'http://image.openflutter.dev/fluwx.png', // ignore: avoid_web_libraries_in_flutter
); import 'package:flutter_web_plugins/flutter_web_plugins.dart';
expect(withSuffixImage.source, 'http://image.openflutter.dev/fluwx.png');
expect(withSuffixImage.suffix, '.png');
expect(FileSchema.NETWORK, withSuffixImage.schema);
var withNoSuffixNoUrlSuffixImage = WeChatImage.network( import 'method_channel/fluwx_platform_interface.dart';
'http://image.openflutter.dev/fluwx',
);
expect(
'http://image.openflutter.dev/fluwx',
withNoSuffixNoUrlSuffixImage.source,
);
expect(withNoSuffixNoUrlSuffixImage.suffix, '.jpeg');
expect(FileSchema.NETWORK, withSuffixImage.schema);
var withSpecifiedSuffixImage = WeChatImage.network( /// A web implementation of the FluwxPlatform of the Fluwx plugin.
'http://image.openflutter.dev/fluwx.jpeg', class FluwxWeb extends FluwxPlatform {
suffix: '.png', /// Constructs a FluwxWeb
); FluwxWeb();
expect(
withSpecifiedSuffixImage.source, static void registerWith(Registrar registrar) {
'http://image.openflutter.dev/fluwx.jpeg', FluwxPlatform.instance = FluwxWeb();
); }
expect(withSpecifiedSuffixImage.suffix, '.png');
expect(withSpecifiedSuffixImage.schema, FileSchema.NETWORK);
});
} }
/* /*
* Copyright (c) 2020. OpenFlutter Project * Copyright (c) 2023. OpenFlutter Project
* *
* Licensed to the Apache Software Foundation (ASF) under one or more contributor * Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for * license agreements. See the NOTICE file distributed with this work for
...@@ -16,20 +16,16 @@ ...@@ -16,20 +16,16 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/src/share/share_models.dart';
import 'package:fluwx/src/wechat_enums.dart';
void main() { import '../wechat_enums.dart';
test('create WeChatTextModel', () { import '../wechat_file.dart';
var model = WeChatShareTextModel('text', scene: WeChatScene.FAVORITE);
expect(model.source, 'text');
expect(model.scene, WeChatScene.FAVORITE);
});
test('WeChatTextModel toMap', () { part 'auth_type.dart';
var map = WeChatShareTextModel('text', scene: WeChatScene.FAVORITE).toMap(); part 'auto_deduct.dart';
expect(map['source'], 'text'); part 'open_type.dart';
expect(map['scene'], 2); part 'pay_type.dart';
}); part 'share_models.dart';
mixin _Argument {
Map<String, dynamic> get arguments => {};
} }
part of 'arguments.dart';
sealed class AuthType with _Argument {}
/// The WeChat-Login is under Auth-2.0
/// This method login with native WeChat app.
/// For users without WeChat app, please use [QRCode] instead
/// This method only supports getting AuthCode,this is first step to login with WeChat
/// Once AuthCode got, you need to request Access_Token
/// For more information please visit:
/// * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317851&token=
class NormalAuth extends AuthType {
final String scope;
final String state;
final bool nonAutomatic;
NormalAuth(
{required this.scope, this.state = 'state', this.nonAutomatic = false})
: assert(scope.trim().isNotEmpty);
@override
Map<String, dynamic> get arguments =>
{'scope': scope, 'state': state, 'nonAutomatic': nonAutomatic};
}
/// Sometimes WeChat is not installed on users's devices.However we can
/// request a QRCode so that we can get AuthCode by scanning the QRCode
/// All required params must not be null or empty
/// [schemeData] only works on iOS
/// see * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=215238808828h4XN&token=&lang=zh_CN
class QRCode extends AuthType {
final String appId;
final String scope;
final String nonceStr;
final String timestamp;
final String signature;
final String? schemeData;
QRCode({
required this.appId,
required this.scope,
required this.nonceStr,
required this.timestamp,
required this.signature,
this.schemeData,
}) : assert(appId.isNotEmpty),
assert(scope.isNotEmpty),
assert(nonceStr.isNotEmpty),
assert(timestamp.isNotEmpty),
assert(signature.isNotEmpty);
@override
Map<String, dynamic> get arguments => {
'appId': appId,
'scope': scope,
'nonceStr': nonceStr,
'timeStamp': timestamp,
'signature': signature,
'schemeData': schemeData
};
}
/// Currently only support iOS
class PhoneLogin extends AuthType {
final String scope;
final String state;
PhoneLogin({
required this.scope,
this.state = 'state',
});
@override
Map<String, dynamic> get arguments => {'scope': scope, 'state': state};
}
part of 'arguments.dart';
class AutoDeduct with _Argument {
final Map<String, String> queryInfo;
final int businessType;
final Map<String, dynamic> _detailData;
bool get isV2 => _detailData.isEmpty;
AutoDeduct.detail({
required String appId,
required String mchId,
required String planId,
required String contractCode,
required String requestSerial,
required String contractDisplayAccount,
required String notifyUrl,
required String version,
required String sign,
required String timestamp,
String returnApp = '3',
this.businessType = 12,
}) : queryInfo = {},
_detailData = {
'appid': appId,
'mch_id': mchId,
'plan_id': planId,
'contract_code': contractCode,
'request_serial': requestSerial,
'contract_display_account': contractDisplayAccount,
'notify_url': notifyUrl,
'version': version,
'sign': sign,
'timestamp': timestamp,
'return_app': returnApp,
'businessType': businessType
};
AutoDeduct.custom({required this.queryInfo, this.businessType = 12})
: _detailData = {};
@override
Map<String, dynamic> get arguments => isV2
? {'queryInfo': queryInfo, 'businessType': businessType}
: _detailData;
}
part of 'arguments.dart';
sealed class OpenType with _Argument {}
/// Just open WeChat app.
class WeChatApp extends OpenType {}
/// Open WeChat browser with given url.
class Browser extends OpenType {
final String url;
Browser(this.url);
@override
Map<String, dynamic> get arguments => {'url': url};
}
class RankList extends OpenType {}
/// see * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter6_2_1.shtml
class BusinessView extends OpenType {
final String businessType;
final String query;
BusinessView({required this.businessType, required this.query});
@override
Map<String, dynamic> get arguments =>
{"businessType": businessType, "query": query};
}
class Invoice extends OpenType {
final String appId;
final String cardType;
final String locationId;
final String cardId;
final String canMultiSelect;
Invoice(
{required this.appId,
required this.cardType,
this.locationId = "",
this.cardId = "",
this.canMultiSelect = "1"});
@override
Map<String, dynamic> get arguments => {
"appId": appId,
"cardType": cardType,
"locationId": locationId,
"cardId": cardId,
"canMultiSelect": canMultiSelect
};
}
class CustomerServiceChat extends OpenType {
final String corpId;
final String url;
CustomerServiceChat({required this.corpId, required this.url});
@override
Map<String, dynamic> get arguments => {"corpId": corpId, "url": url};
}
/// open mini-program
/// see [WXMiniProgramType]
class MiniProgram extends OpenType {
final String username;
final String? path;
final WXMiniProgramType miniProgramType;
MiniProgram({
required this.username,
this.path,
this.miniProgramType = WXMiniProgramType.release,
}) : assert(username.trim().isNotEmpty);
@override
Map<String, dynamic> get arguments => {
'userName': username,
'path': path,
'miniProgramType': miniProgramType.value
};
}
/// See *https://developers.weixin.qq.com/doc/oplatform/Mobile_App/One-time_subscription_info.html for more detail
class SubscribeMessage extends OpenType {
final String appId;
final int scene;
final String templateId;
final String? reserved;
SubscribeMessage({
required this.appId,
required this.scene,
required this.templateId,
this.reserved,
});
@override
Map<String, dynamic> get arguments => {
'appId': appId,
'scene': scene,
'templateId': templateId,
'reserved': reserved,
};
}
part of 'arguments.dart';
sealed class PayType with _Argument {}
/// request payment with WeChat.
/// Read the official document for more detail.
/// [timestamp] is int because [timestamp] will be mapped to Unit32.
class Payment extends PayType {
final String appId;
final String partnerId;
final String prepayId;
final String packageValue;
final String nonceStr;
final int timestamp;
final String sign;
final String? signType;
final String? extData;
Payment({
required this.appId,
required this.partnerId,
required this.prepayId,
required this.packageValue,
required this.nonceStr,
required this.timestamp,
required this.sign,
this.signType,
this.extData,
});
@override
Map<String, dynamic> get arguments => {
'appId': appId,
'partnerId': partnerId,
'prepayId': prepayId,
'packageValue': packageValue,
'nonceStr': nonceStr,
'timeStamp': timestamp,
'sign': sign,
'signType': signType,
'extData': extData,
};
}
/// request Hong Kong Wallet payment with WeChat.
/// Read the official document for more detail.
class HongKongWallet extends PayType {
final String prepayId;
HongKongWallet({required this.prepayId});
@override
Map<String, dynamic> get arguments => {
'prepayId': prepayId,
};
}
/* /*
* Copyright (c) 2020. OpenFlutter Project * Copyright (c) 2023. OpenFlutter Project
* *
* Licensed to the Apache Software Foundation (ASF) under one or more contributor * Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for * license agreements. See the NOTICE file distributed with this work for
...@@ -16,7 +16,8 @@ ...@@ -16,7 +16,8 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
import 'package:fluwx/fluwx.dart';
part of 'arguments.dart';
const String _scene = "scene"; const String _scene = "scene";
const String _source = "source"; const String _source = "source";
...@@ -29,24 +30,22 @@ const String _messageAction = "messageAction"; ...@@ -29,24 +30,22 @@ const String _messageAction = "messageAction";
const String _compressThumbnail = "compressThumbnail"; const String _compressThumbnail = "compressThumbnail";
const String _msgSignature = "msgSignature"; const String _msgSignature = "msgSignature";
mixin WeChatShareBaseModel { sealed class WeChatShareModel with _Argument {}
Map toMap();
}
/// [source] the text you want to send to WeChat /// [source] the text you want to send to WeChat
/// [scene] the target you want to send /// [scene] the target you want to send
class WeChatShareTextModel implements WeChatShareBaseModel { class WeChatShareTextModel extends WeChatShareModel {
WeChatShareTextModel( WeChatShareTextModel(
this.source, { this.source, {
this.scene = WeChatScene.SESSION, this.scene = WeChatScene.session,
this.mediaTagName, this.mediaTagName,
this.messageAction, this.messageAction,
this.messageExt, this.messageExt,
this.msgSignature, this.msgSignature,
String? description, String? description,
String? title, String? title,
}) : this.title = title ?? source, }) : title = title ?? source,
this.description = description ?? source; description = description ?? source;
final String source; final String source;
final WeChatScene scene; final WeChatScene scene;
...@@ -58,8 +57,7 @@ class WeChatShareTextModel implements WeChatShareBaseModel { ...@@ -58,8 +57,7 @@ class WeChatShareTextModel implements WeChatShareBaseModel {
final String? msgSignature; final String? msgSignature;
@override @override
Map toMap() { Map<String, dynamic> get arguments => {
return {
_scene: scene.index, _scene: scene.index,
_source: source, _source: source,
_messageExt: messageExt, _messageExt: messageExt,
...@@ -69,16 +67,15 @@ class WeChatShareTextModel implements WeChatShareBaseModel { ...@@ -69,16 +67,15 @@ class WeChatShareTextModel implements WeChatShareBaseModel {
_description: description, _description: description,
_msgSignature: msgSignature _msgSignature: msgSignature
}; };
}
} }
/// the default value is [MINI_PROGRAM_TYPE_RELEASE] /// the default value is [MINI_PROGRAM_TYPE_RELEASE]
/// [hdImagePath] only works on iOS, not sure the relationship /// [hdImagePath] only works on iOS, not sure the relationship
/// between [thumbnail] and [hdImagePath]. /// between [thumbnail] and [hdImagePath].
class WeChatShareMiniProgramModel implements WeChatShareBaseModel { class WeChatShareMiniProgramModel extends WeChatShareModel {
WeChatShareMiniProgramModel( WeChatShareMiniProgramModel(
{required this.webPageUrl, {required this.webPageUrl,
this.miniProgramType = WXMiniProgramType.RELEASE, this.miniProgramType = WXMiniProgramType.release,
required this.userName, required this.userName,
this.path = "/", this.path = "/",
this.title, this.title,
...@@ -111,10 +108,9 @@ class WeChatShareMiniProgramModel implements WeChatShareBaseModel { ...@@ -111,10 +108,9 @@ class WeChatShareMiniProgramModel implements WeChatShareBaseModel {
final String? msgSignature; final String? msgSignature;
@override @override
Map toMap() { Map<String, dynamic> get arguments => {
return {
'webPageUrl': webPageUrl, 'webPageUrl': webPageUrl,
"miniProgramType": miniProgramType.toNativeInt(), "miniProgramType": miniProgramType.value,
"userName": userName, "userName": userName,
"path": path, "path": path,
"title": title, "title": title,
...@@ -127,24 +123,23 @@ class WeChatShareMiniProgramModel implements WeChatShareBaseModel { ...@@ -127,24 +123,23 @@ class WeChatShareMiniProgramModel implements WeChatShareBaseModel {
_compressThumbnail: compressThumbnail, _compressThumbnail: compressThumbnail,
_msgSignature: msgSignature _msgSignature: msgSignature
}; };
}
} }
/// [source] the image you want to send to WeChat /// [source] the image you want to send to WeChat
/// [scene] the target you want to send /// [scene] the target you want to send
/// [thumbnail] the preview of your image, will be created from [scene] if null. /// [thumbnail] the preview of your image, will be created from [scene] if null.
class WeChatShareImageModel implements WeChatShareBaseModel { class WeChatShareImageModel extends WeChatShareModel {
WeChatShareImageModel(this.source, WeChatShareImageModel(this.source,
{WeChatImage? thumbnail, {WeChatImage? thumbnail,
this.title, this.title,
this.scene = WeChatScene.SESSION, this.scene = WeChatScene.session,
this.description, this.description,
this.mediaTagName, this.mediaTagName,
this.messageAction, this.messageAction,
this.messageExt, this.messageExt,
this.compressThumbnail = true, this.compressThumbnail = true,
this.msgSignature}) this.msgSignature})
: this.thumbnail = thumbnail ?? source; : thumbnail = thumbnail ?? source;
final WeChatImage source; final WeChatImage source;
final WeChatImage thumbnail; final WeChatImage thumbnail;
...@@ -158,8 +153,7 @@ class WeChatShareImageModel implements WeChatShareBaseModel { ...@@ -158,8 +153,7 @@ class WeChatShareImageModel implements WeChatShareBaseModel {
final String? msgSignature; final String? msgSignature;
@override @override
Map toMap() { Map<String, dynamic> get arguments => {
return {
_scene: scene.index, _scene: scene.index,
_source: source.toMap(), _source: source.toMap(),
_thumbnail: thumbnail.toMap(), _thumbnail: thumbnail.toMap(),
...@@ -170,12 +164,11 @@ class WeChatShareImageModel implements WeChatShareBaseModel { ...@@ -170,12 +164,11 @@ class WeChatShareImageModel implements WeChatShareBaseModel {
_compressThumbnail: compressThumbnail, _compressThumbnail: compressThumbnail,
_msgSignature: msgSignature _msgSignature: msgSignature
}; };
}
} }
/// if [musicUrl] and [musicLowBandUrl] are both provided, /// if [musicUrl] and [musicLowBandUrl] are both provided,
/// only [musicUrl] will be used. /// only [musicUrl] will be used.
class WeChatShareMusicModel implements WeChatShareBaseModel { class WeChatShareMusicModel extends WeChatShareModel {
WeChatShareMusicModel( WeChatShareMusicModel(
{this.musicUrl, {this.musicUrl,
this.musicLowBandUrl, this.musicLowBandUrl,
...@@ -187,7 +180,7 @@ class WeChatShareMusicModel implements WeChatShareBaseModel { ...@@ -187,7 +180,7 @@ class WeChatShareMusicModel implements WeChatShareBaseModel {
this.mediaTagName, this.mediaTagName,
this.messageAction, this.messageAction,
this.messageExt, this.messageExt,
this.scene = WeChatScene.SESSION, this.scene = WeChatScene.session,
this.compressThumbnail = true, this.compressThumbnail = true,
this.msgSignature}) this.msgSignature})
: assert(musicUrl != null || musicLowBandUrl != null); : assert(musicUrl != null || musicLowBandUrl != null);
...@@ -207,8 +200,7 @@ class WeChatShareMusicModel implements WeChatShareBaseModel { ...@@ -207,8 +200,7 @@ class WeChatShareMusicModel implements WeChatShareBaseModel {
final String? msgSignature; final String? msgSignature;
@override @override
Map toMap() { Map<String, dynamic> get arguments => {
return {
_scene: scene.index, _scene: scene.index,
"musicUrl": musicUrl, "musicUrl": musicUrl,
"musicDataUrl": musicDataUrl, "musicDataUrl": musicDataUrl,
...@@ -222,14 +214,13 @@ class WeChatShareMusicModel implements WeChatShareBaseModel { ...@@ -222,14 +214,13 @@ class WeChatShareMusicModel implements WeChatShareBaseModel {
_compressThumbnail: compressThumbnail, _compressThumbnail: compressThumbnail,
_msgSignature: msgSignature _msgSignature: msgSignature
}; };
}
} }
/// if [videoUrl] and [videoLowBandUrl] are both provided, /// if [videoUrl] and [videoLowBandUrl] are both provided,
/// only [videoUrl] will be used. /// only [videoUrl] will be used.
class WeChatShareVideoModel implements WeChatShareBaseModel { class WeChatShareVideoModel extends WeChatShareModel {
WeChatShareVideoModel( WeChatShareVideoModel(
{this.scene = WeChatScene.SESSION, {this.scene = WeChatScene.session,
this.videoUrl, this.videoUrl,
this.videoLowBandUrl, this.videoLowBandUrl,
this.title = "", this.title = "",
...@@ -256,8 +247,7 @@ class WeChatShareVideoModel implements WeChatShareBaseModel { ...@@ -256,8 +247,7 @@ class WeChatShareVideoModel implements WeChatShareBaseModel {
final String? msgSignature; final String? msgSignature;
@override @override
Map toMap() { Map<String, dynamic> get arguments => {
return {
_scene: scene.index, _scene: scene.index,
"videoUrl": videoUrl, "videoUrl": videoUrl,
"videoLowBandUrl": videoLowBandUrl, "videoLowBandUrl": videoLowBandUrl,
...@@ -269,25 +259,24 @@ class WeChatShareVideoModel implements WeChatShareBaseModel { ...@@ -269,25 +259,24 @@ class WeChatShareVideoModel implements WeChatShareBaseModel {
_compressThumbnail: compressThumbnail, _compressThumbnail: compressThumbnail,
_msgSignature: msgSignature _msgSignature: msgSignature
}; };
}
} }
/// [webPage] url you want to send to wechat /// [webPage] url you want to send to wechat
/// [thumbnail] logo of your website /// [thumbnail] logo of your website
class WeChatShareWebPageModel implements WeChatShareBaseModel { class WeChatShareWebPageModel extends WeChatShareModel {
WeChatShareWebPageModel( WeChatShareWebPageModel(
this.webPage, { this.webPage, {
this.title = "", this.title = "",
String? description, String? description,
this.thumbnail, this.thumbnail,
this.scene = WeChatScene.SESSION, this.scene = WeChatScene.session,
this.mediaTagName, this.mediaTagName,
this.messageAction, this.messageAction,
this.messageExt, this.messageExt,
this.msgSignature, this.msgSignature,
this.compressThumbnail = true, this.compressThumbnail = true,
}) : assert(webPage.isNotEmpty), }) : assert(webPage.isNotEmpty),
this.description = description ?? webPage; description = description ?? webPage;
final String webPage; final String webPage;
final WeChatImage? thumbnail; final WeChatImage? thumbnail;
...@@ -301,8 +290,7 @@ class WeChatShareWebPageModel implements WeChatShareBaseModel { ...@@ -301,8 +290,7 @@ class WeChatShareWebPageModel implements WeChatShareBaseModel {
final String? msgSignature; final String? msgSignature;
@override @override
Map toMap() { Map<String, dynamic> get arguments => {
return {
_scene: scene.index, _scene: scene.index,
"webPage": webPage, "webPage": webPage,
_thumbnail: thumbnail?.toMap(), _thumbnail: thumbnail?.toMap(),
...@@ -313,19 +301,18 @@ class WeChatShareWebPageModel implements WeChatShareBaseModel { ...@@ -313,19 +301,18 @@ class WeChatShareWebPageModel implements WeChatShareBaseModel {
_compressThumbnail: compressThumbnail, _compressThumbnail: compressThumbnail,
_msgSignature: msgSignature _msgSignature: msgSignature
}; };
}
} }
/// [source] the file you want to share, [source.suffix] is necessary on iOS. /// [source] the file you want to share, [source.suffix] is necessary on iOS.
/// [scene] can't be [WeChatScene.TIMELINE], otherwise, sharing nothing. /// [scene] can't be [WeChatScene.TIMELINE], otherwise, sharing nothing.
/// send files to WeChat /// send files to WeChat
class WeChatShareFileModel implements WeChatShareBaseModel { class WeChatShareFileModel extends WeChatShareModel {
WeChatShareFileModel( WeChatShareFileModel(
this.source, { this.source, {
this.title = "", this.title = "",
this.description = "", this.description = "",
this.thumbnail, this.thumbnail,
this.scene = WeChatScene.SESSION, this.scene = WeChatScene.session,
this.mediaTagName, this.mediaTagName,
this.messageAction, this.messageAction,
this.messageExt, this.messageExt,
...@@ -345,8 +332,7 @@ class WeChatShareFileModel implements WeChatShareBaseModel { ...@@ -345,8 +332,7 @@ class WeChatShareFileModel implements WeChatShareBaseModel {
final String? msgSignature; final String? msgSignature;
@override @override
Map toMap() { Map<String, dynamic> get arguments => {
return {
_scene: scene.index, _scene: scene.index,
_source: source.toMap(), _source: source.toMap(),
_thumbnail: thumbnail?.toMap(), _thumbnail: thumbnail?.toMap(),
...@@ -357,5 +343,4 @@ class WeChatShareFileModel implements WeChatShareBaseModel { ...@@ -357,5 +343,4 @@ class WeChatShareFileModel implements WeChatShareBaseModel {
_compressThumbnail: compressThumbnail, _compressThumbnail: compressThumbnail,
_msgSignature: msgSignature _msgSignature: msgSignature
}; };
}
} }
/*
* Copyright (c) 2023. OpenFlutter Project
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this
* file to you 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.
*/
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../foundation/arguments.dart';
import '../response/wechat_response.dart';
import 'fluwx_platform_interface.dart';
/// An implementation of [FluwxPlatform] that uses method channels.
class MethodChannelFluwx extends FluwxPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodChannel = const MethodChannel('com.jarvanmo/fluwx');
MethodChannelFluwx() {
methodChannel.setMethodCallHandler(_methodHandler);
}
final Map<Type, String> _shareModelMethodMapper = {
WeChatShareTextModel: 'shareText',
WeChatShareImageModel: 'shareImage',
WeChatShareMusicModel: 'shareMusic',
WeChatShareVideoModel: 'shareVideo',
WeChatShareWebPageModel: 'shareWebPage',
WeChatShareMiniProgramModel: 'shareMiniProgram',
WeChatShareFileModel: 'shareFile',
};
final StreamController<WeChatResponse> _responseEventHandler =
StreamController.broadcast();
/// Response answers from WeChat after sharing, payment etc.
@override
Stream<WeChatResponse> get responseEventHandler =>
_responseEventHandler.stream;
Future _methodHandler(MethodCall methodCall) {
if (methodCall.method == "wechatLog") {
_printLog(methodCall.arguments);
} else {
final response = WeChatResponse.create(
methodCall.method,
methodCall.arguments,
);
_responseEventHandler.add(response);
}
return Future.value();
}
_printLog(Map data) {
debugPrint("FluwxLog: ${data["detail"]}");
}
/// [true] if WeChat installed, otherwise [false].
/// Please add WeChat to the white list in order use this method on IOS.
@override
Future<bool> get isWeChatInstalled async {
return await methodChannel.invokeMethod('isWeChatInstalled');
}
@override
Future<bool> open(OpenType target) async {
switch (target) {
case WeChatApp():
return await methodChannel.invokeMethod('openWXApp') ?? false;
case Browser():
return await methodChannel.invokeMethod('openUrl') ?? false;
case RankList():
return await methodChannel.invokeMethod("openRankList") ?? false;
case BusinessView():
return await methodChannel.invokeMethod(
"openBusinessView", target.arguments) ??
false;
case Invoice():
return await methodChannel.invokeMethod(
"openWeChatInvoice", target.arguments) ??
false;
case CustomerServiceChat():
return await methodChannel.invokeMethod(
"openWeChatCustomerServiceChat", target.arguments) ??
false;
case MiniProgram():
return await methodChannel.invokeMethod(
'launchMiniProgram', target.arguments) ??
false;
case SubscribeMessage():
return await methodChannel.invokeMethod(
'subscribeMsg', target.arguments);
}
}
@override
Future<bool> registerApi({
required String appId,
bool doOnIOS = true,
bool doOnAndroid = true,
String? universalLink,
}) async {
if (doOnIOS && Platform.isIOS) {
if (universalLink == null ||
universalLink.trim().isEmpty ||
!universalLink.startsWith('https')) {
throw ArgumentError.value(
universalLink,
"You're trying to use illegal universal link, see "
'https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html '
'for more detail',
);
}
}
return await methodChannel.invokeMethod('registerApp', {
'appId': appId,
'iOS': doOnIOS,
'android': doOnAndroid,
'universalLink': universalLink
});
}
/// Get ext Message
@override
Future<String?> getExtMsg() {
return methodChannel.invokeMethod('getExtMsg');
}
/// see [_shareModelMethodMapper] for detail.
@override
Future<bool> share(WeChatShareModel what) async {
if (_shareModelMethodMapper.containsKey(what.runtimeType)) {
final channelName = _shareModelMethodMapper[what.runtimeType];
if (channelName == null) {
throw ArgumentError.value(
'${what.runtimeType} method channel not found',
);
}
return await methodChannel.invokeMethod(channelName, what.arguments);
}
return Future.error('no method mapper found[${what.runtimeType}]');
}
@override
Future<bool> authBy(AuthType which) async {
switch (which) {
case NormalAuth():
return await methodChannel.invokeMethod(
'sendAuth',
which.arguments,
);
case QRCode():
return await methodChannel.invokeMethod(
'authByQRCode', which.arguments) ??
false;
case PhoneLogin():
return await methodChannel.invokeMethod(
'authByPhoneLogin', which.arguments);
}
}
@override
Future<bool> pay(PayType which) async {
switch (which) {
case Payment():
return await methodChannel.invokeMethod(
'payWithFluwx',
);
case HongKongWallet():
return await methodChannel.invokeMethod(
'payWithHongKongWallet', which.arguments);
}
}
@override
Future<bool> autoDeduct(AutoDeduct data) async {
return await methodChannel.invokeMethod(
data.isV2 ? "autoDeductV2" : 'autoDeduct', data.arguments) ??
false;
}
/// stop [authWeChatByQRCode]
@override
Future<bool> stopAuthByQRCode() async {
return await methodChannel.invokeMethod('stopAuthByQRCode');
}
@override
Future<bool> get isSupportOpenBusinessView async =>
await methodChannel.invokeMethod("checkSupportOpenBusinessView");
}
/*
* Copyright (c) 2023. OpenFlutter Project
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this
* file to you 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.
*/
import 'package:fluwx/fluwx.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'fluwx_method_channel.dart';
abstract class FluwxPlatform extends PlatformInterface {
/// Constructs a FluwxPlatform.
FluwxPlatform() : super(token: _token);
static final Object _token = Object();
static FluwxPlatform _instance = MethodChannelFluwx();
/// The default instance of [FluwxPlatform] to use.
///
/// Defaults to [MethodChannelFluwx].
static FluwxPlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [FluwxPlatform] when
/// they register themselves.
static set instance(FluwxPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Stream<WeChatResponse> get responseEventHandler {
throw UnimplementedError('responseEventHandler has not been implemented.');
}
Future<bool> get isWeChatInstalled {
throw UnimplementedError('isWeChatInstalled has not been implemented.');
}
Future<bool> open(OpenType target) {
throw UnimplementedError('open() has not been implemented.');
}
Future<bool> registerApi({
required String appId,
bool doOnIOS = true,
bool doOnAndroid = true,
String? universalLink,
}) {
throw UnimplementedError('registerWxApi() has not been implemented.');
}
Future<String?> getExtMsg() {
throw UnimplementedError('getExtMsg() has not been implemented.');
}
Future<bool> share(WeChatShareModel what) {
throw UnimplementedError('share() has not been implemented.');
}
Future<bool> sendAuth(
{required String scope,
String state = 'state',
bool nonAutomatic = false}) {
throw UnimplementedError('sendAuth() has not been implemented.');
}
Future<bool> authByPhoneLogin({
required String scope,
String state = 'state',
}) async {
throw UnimplementedError('authByPhoneLogin() has not been implemented.');
}
Future<bool> authByQRCode({
required String appId,
required String scope,
required String nonceStr,
required String timestamp,
required String signature,
String? schemeData,
}) async {
throw UnimplementedError('authByQRCode() has not been implemented.');
}
Future<bool> stopAuthByQRCode() async {
throw UnimplementedError(
'stopWeChatAuthByQRCode() has not been implemented.');
}
Future<bool> pay(PayType which) {
throw UnimplementedError('pay() has not been implemented.');
}
Future<bool> autoDeduct(AutoDeduct data) {
throw UnimplementedError('autoDeduct() has not been implemented.');
}
Future<bool> authBy(AuthType which) {
throw UnimplementedError('authBy() has not been implemented.');
}
Future<bool> get isSupportOpenBusinessView async {
throw UnimplementedError(
'isSupportOpenBusinessView() has not been implemented.');
}
}
...@@ -22,7 +22,7 @@ import 'dart:typed_data'; ...@@ -22,7 +22,7 @@ import 'dart:typed_data';
const String _errCode = 'errCode'; const String _errCode = 'errCode';
const String _errStr = 'errStr'; const String _errStr = 'errStr';
typedef BaseWeChatResponse _WeChatResponseInvoker(Map argument); typedef _WeChatResponseInvoker = WeChatResponse Function(Map argument);
Map<String, _WeChatResponseInvoker> _nameAndResponseMapper = { Map<String, _WeChatResponseInvoker> _nameAndResponseMapper = {
'onShareResponse': (Map argument) => WeChatShareResponse.fromMap(argument), 'onShareResponse': (Map argument) => WeChatShareResponse.fromMap(argument),
...@@ -50,11 +50,11 @@ Map<String, _WeChatResponseInvoker> _nameAndResponseMapper = { ...@@ -50,11 +50,11 @@ Map<String, _WeChatResponseInvoker> _nameAndResponseMapper = {
WeChatOpenInvoiceResponse.fromMap(argument), WeChatOpenInvoiceResponse.fromMap(argument),
}; };
class BaseWeChatResponse { sealed class WeChatResponse {
BaseWeChatResponse._(this.errCode, this.errStr); WeChatResponse._(this.errCode, this.errStr);
/// Create response from the response pool. /// Create response from the response pool.
factory BaseWeChatResponse.create(String name, Map argument) { factory WeChatResponse.create(String name, Map argument) {
var result = _nameAndResponseMapper[name]; var result = _nameAndResponseMapper[name];
if (result == null) { if (result == null) {
throw ArgumentError("Can't found instance of $name"); throw ArgumentError("Can't found instance of $name");
...@@ -68,14 +68,15 @@ class BaseWeChatResponse { ...@@ -68,14 +68,15 @@ class BaseWeChatResponse {
bool get isSuccessful => errCode == 0; bool get isSuccessful => errCode == 0;
} }
class WeChatOpenInvoiceResponse extends BaseWeChatResponse { class WeChatOpenInvoiceResponse extends WeChatResponse {
String? cardItemList; String? cardItemList;
WeChatOpenInvoiceResponse.fromMap(Map map) WeChatOpenInvoiceResponse.fromMap(Map map)
: cardItemList = map["cardItemList"], : cardItemList = map["cardItemList"],
super._(map[_errCode], map[_errStr]); super._(map[_errCode], map[_errStr]);
} }
class WeChatShareResponse extends BaseWeChatResponse { class WeChatShareResponse extends WeChatResponse {
WeChatShareResponse.fromMap(Map map) WeChatShareResponse.fromMap(Map map)
: type = map['type'], : type = map['type'],
super._(map[_errCode], map[_errStr]); super._(map[_errCode], map[_errStr]);
...@@ -83,7 +84,7 @@ class WeChatShareResponse extends BaseWeChatResponse { ...@@ -83,7 +84,7 @@ class WeChatShareResponse extends BaseWeChatResponse {
final int type; final int type;
} }
class WeChatAuthResponse extends BaseWeChatResponse { class WeChatAuthResponse extends WeChatResponse {
WeChatAuthResponse.fromMap(Map map) WeChatAuthResponse.fromMap(Map map)
: type = map['type'], : type = map['type'],
country = map['country'], country = map['country'],
...@@ -115,7 +116,7 @@ class WeChatAuthResponse extends BaseWeChatResponse { ...@@ -115,7 +116,7 @@ class WeChatAuthResponse extends BaseWeChatResponse {
1432; 1432;
} }
class WeChatLaunchMiniProgramResponse extends BaseWeChatResponse { class WeChatLaunchMiniProgramResponse extends WeChatResponse {
WeChatLaunchMiniProgramResponse.fromMap(Map map) WeChatLaunchMiniProgramResponse.fromMap(Map map)
: type = map['type'], : type = map['type'],
extMsg = map['extMsg'], extMsg = map['extMsg'],
...@@ -125,7 +126,7 @@ class WeChatLaunchMiniProgramResponse extends BaseWeChatResponse { ...@@ -125,7 +126,7 @@ class WeChatLaunchMiniProgramResponse extends BaseWeChatResponse {
final String? extMsg; final String? extMsg;
} }
class WeChatPaymentResponse extends BaseWeChatResponse { class WeChatPaymentResponse extends WeChatResponse {
WeChatPaymentResponse.fromMap(Map map) WeChatPaymentResponse.fromMap(Map map)
: type = map['type'], : type = map['type'],
extData = map['extData'], extData = map['extData'],
...@@ -135,7 +136,7 @@ class WeChatPaymentResponse extends BaseWeChatResponse { ...@@ -135,7 +136,7 @@ class WeChatPaymentResponse extends BaseWeChatResponse {
final String? extData; final String? extData;
} }
class WeChatOpenCustomerServiceChatResponse extends BaseWeChatResponse { class WeChatOpenCustomerServiceChatResponse extends WeChatResponse {
WeChatOpenCustomerServiceChatResponse.fromMap(Map map) WeChatOpenCustomerServiceChatResponse.fromMap(Map map)
: extMsg = map['extMsg'], : extMsg = map['extMsg'],
super._(map[_errCode], map[_errStr]); super._(map[_errCode], map[_errStr]);
...@@ -143,7 +144,7 @@ class WeChatOpenCustomerServiceChatResponse extends BaseWeChatResponse { ...@@ -143,7 +144,7 @@ class WeChatOpenCustomerServiceChatResponse extends BaseWeChatResponse {
final String? extMsg; final String? extMsg;
} }
class WeChatOpenBusinessViewResponse extends BaseWeChatResponse { class WeChatOpenBusinessViewResponse extends WeChatResponse {
final String? extMsg; final String? extMsg;
final String? openid; final String? openid;
final String? businessType; final String? businessType;
...@@ -157,7 +158,7 @@ class WeChatOpenBusinessViewResponse extends BaseWeChatResponse { ...@@ -157,7 +158,7 @@ class WeChatOpenBusinessViewResponse extends BaseWeChatResponse {
super._(map[_errCode], map[_errStr]); super._(map[_errCode], map[_errStr]);
} }
class WeChatSubscribeMsgResponse extends BaseWeChatResponse { class WeChatSubscribeMsgResponse extends WeChatResponse {
WeChatSubscribeMsgResponse.fromMap(Map map) WeChatSubscribeMsgResponse.fromMap(Map map)
: openid = map['openid'], : openid = map['openid'],
templateId = map['templateId'], templateId = map['templateId'],
...@@ -173,25 +174,23 @@ class WeChatSubscribeMsgResponse extends BaseWeChatResponse { ...@@ -173,25 +174,23 @@ class WeChatSubscribeMsgResponse extends BaseWeChatResponse {
final int scene; final int scene;
} }
class WeChatOpenBusinessWebviewResponse extends BaseWeChatResponse { class WeChatOpenBusinessWebviewResponse extends WeChatResponse {
WeChatOpenBusinessWebviewResponse.fromMap(Map map) WeChatOpenBusinessWebviewResponse.fromMap(Map map)
: type = map['type'], : type = map['type'],
errCode = map[_errCode],
businessType = map['businessType'], businessType = map['businessType'],
resultInfo = map['resultInfo'], resultInfo = map['resultInfo'],
super._(map[_errCode], map[_errStr]); super._(map[_errCode], map[_errStr]);
final int? type; final int? type;
final int errCode;
final int? businessType; final int? businessType;
final String resultInfo; final String resultInfo;
} }
class WeChatAuthByQRCodeFinishedResponse extends BaseWeChatResponse { class WeChatAuthByQRCodeFinishedResponse extends WeChatResponse {
WeChatAuthByQRCodeFinishedResponse.fromMap(Map map) WeChatAuthByQRCodeFinishedResponse.fromMap(Map map)
: authCode = map['authCode'], : authCode = map['authCode'],
qrCodeErrorCode = (_authByQRCodeErrorCodes[_errCode] ?? qrCodeErrorCode = (_authByQRCodeErrorCodes[_errCode] ??
AuthByQRCodeErrorCode.UNKNOWN), AuthByQRCodeErrorCode.unknown),
super._(map[_errCode], map[_errStr]); super._(map[_errCode], map[_errStr]);
final String? authCode; final String? authCode;
...@@ -199,7 +198,7 @@ class WeChatAuthByQRCodeFinishedResponse extends BaseWeChatResponse { ...@@ -199,7 +198,7 @@ class WeChatAuthByQRCodeFinishedResponse extends BaseWeChatResponse {
} }
///[qrCode] in memory. ///[qrCode] in memory.
class WeChatAuthGotQRCodeResponse extends BaseWeChatResponse { class WeChatAuthGotQRCodeResponse extends WeChatResponse {
WeChatAuthGotQRCodeResponse.fromMap(Map map) WeChatAuthGotQRCodeResponse.fromMap(Map map)
: qrCode = map['qrCode'], : qrCode = map['qrCode'],
super._(map[_errCode], map[_errStr]); super._(map[_errCode], map[_errStr]);
...@@ -207,13 +206,13 @@ class WeChatAuthGotQRCodeResponse extends BaseWeChatResponse { ...@@ -207,13 +206,13 @@ class WeChatAuthGotQRCodeResponse extends BaseWeChatResponse {
final Uint8List? qrCode; final Uint8List? qrCode;
} }
class WeChatQRCodeScannedResponse extends BaseWeChatResponse { class WeChatQRCodeScannedResponse extends WeChatResponse {
WeChatQRCodeScannedResponse.fromMap(Map map) WeChatQRCodeScannedResponse.fromMap(Map map)
: super._(map[_errCode], map[_errStr]); : super._(map[_errCode], map[_errStr]);
} }
// 获取微信打开App时携带的参数 // 获取微信打开App时携带的参数
class WeChatShowMessageFromWXRequest extends BaseWeChatResponse { class WeChatShowMessageFromWXRequest extends WeChatResponse {
WeChatShowMessageFromWXRequest.fromMap(Map map) WeChatShowMessageFromWXRequest.fromMap(Map map)
: extMsg = map['extMsg'], : extMsg = map['extMsg'],
super._(0, ''); super._(0, '');
...@@ -222,21 +221,21 @@ class WeChatShowMessageFromWXRequest extends BaseWeChatResponse { ...@@ -222,21 +221,21 @@ class WeChatShowMessageFromWXRequest extends BaseWeChatResponse {
} }
enum AuthByQRCodeErrorCode { enum AuthByQRCodeErrorCode {
OK, // WechatAuth_Err_OK(0) ok, // WechatAuth_Err_OK(0)
NORMAL_ERR, // WechatAuth_Err_NormalErr(-1) normalErr, // WechatAuth_Err_NormalErr(-1)
NETWORK_ERR, // WechatAuth_Err_NetworkErr(-2) networkErr, // WechatAuth_Err_NetworkErr(-2)
JSON_DECODE_ERR, // WechatAuth_Err_JsonDecodeErr(-3), WechatAuth_Err_GetQrcodeFailed on iOS jsonDecodeErr, // WechatAuth_Err_JsonDecodeErr(-3), WechatAuth_Err_GetQrcodeFailed on iOS
CANCEL, // WechatAuth_Err_Cancel(-4) cancel, // WechatAuth_Err_Cancel(-4)
TIMEOUT, // WechatAuth_Err_Timeout(-5) timeout, // WechatAuth_Err_Timeout(-5)
AUTH_STOPPED, // WechatAuth_Err_Auth_Stopped(-6), Android only authStopped, // WechatAuth_Err_Auth_Stopped(-6), Android only
UNKNOWN unknown
} }
const Map<int, AuthByQRCodeErrorCode> _authByQRCodeErrorCodes = { const Map<int, AuthByQRCodeErrorCode> _authByQRCodeErrorCodes = {
0: AuthByQRCodeErrorCode.OK, 0: AuthByQRCodeErrorCode.ok,
-1: AuthByQRCodeErrorCode.NORMAL_ERR, -1: AuthByQRCodeErrorCode.normalErr,
-2: AuthByQRCodeErrorCode.NETWORK_ERR, -2: AuthByQRCodeErrorCode.networkErr,
-3: AuthByQRCodeErrorCode.JSON_DECODE_ERR, -3: AuthByQRCodeErrorCode.jsonDecodeErr,
-4: AuthByQRCodeErrorCode.CANCEL, -4: AuthByQRCodeErrorCode.cancel,
-5: AuthByQRCodeErrorCode.AUTH_STOPPED -5: AuthByQRCodeErrorCode.authStopped
}; };
/* /*
* Copyright (c) 2020. OpenFlutter Project * Copyright (c) 2023. OpenFlutter Project
* *
* Licensed to the Apache Software Foundation (ASF) under one or more contributor * Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for * license agreements. See the NOTICE file distributed with this work for
...@@ -17,44 +17,19 @@ ...@@ -17,44 +17,19 @@
* the License. * the License.
*/ */
/// [WXMiniProgramType.RELEASE]正式版 /// [WXMiniProgramType.release]正式版
/// [WXMiniProgramType.TEST]测试版 /// [WXMiniProgramType.test]测试版
/// [WXMiniProgramType.PREVIEW]预览版 /// [WXMiniProgramType.preview]预览版
enum WXMiniProgramType { RELEASE, TEST, PREVIEW } enum WXMiniProgramType {
release(0),
test(1),
preview(2);
/// [WeChatScene.SESSION]会话 final int value;
/// [WeChatScene.TIMELINE]朋友圈 const WXMiniProgramType(this.value);
/// [WeChatScene.FAVORITE]收藏
enum WeChatScene { SESSION, TIMELINE, FAVORITE }
extension MiniProgramTypeExtensions on WXMiniProgramType {
int toNativeInt() {
switch (this) {
case WXMiniProgramType.PREVIEW:
return 2;
case WXMiniProgramType.TEST:
return 1;
case WXMiniProgramType.RELEASE:
return 0;
default:
return 0;
}
}
} }
/// 打印日常的日志 /// [WeChatScene.session]会话
/// 打印详细的日志 /// [WeChatScene.timeline]朋友圈
enum WXLogLevel { NORMAL, DETAIL } /// [WeChatScene.favorite]收藏
enum WeChatScene { session, timeline, favorite }
extension LogLevelExtensions on WXLogLevel {
int toNativeInt() {
switch (this) {
case WXLogLevel.DETAIL:
return 1;
case WXLogLevel.NORMAL:
return 0;
default:
return 0;
}
}
}
/* /*
* Copyright (c) 2020. OpenFlutter Project * Copyright (c) 2023. OpenFlutter Project
* *
* Licensed to the Apache Software Foundation (ASF) under one or more contributor * Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for * license agreements. See the NOTICE file distributed with this work for
...@@ -47,37 +47,33 @@ class WeChatImage extends WeChatFile { ...@@ -47,37 +47,33 @@ class WeChatImage extends WeChatFile {
class WeChatFile { class WeChatFile {
/// [source] must begin with http or https /// [source] must begin with http or https
WeChatFile.network( WeChatFile.network(
String source, { String this.source, {
String? suffix, String? suffix,
}) : assert(source.startsWith('http')), }) : assert(source.startsWith('http')),
source = source, schema = FileSchema.network,
schema = FileSchema.NETWORK,
suffix = source.readSuffix(suffix, defaultSuffixTxt); suffix = source.readSuffix(suffix, defaultSuffixTxt);
///[source] path of the image, like '/asset/image.pdf?package=flutter', ///[source] path of the image, like '/asset/image.pdf?package=flutter',
///the query param package in [source] only available when you want to specify the package of image ///the query param package in [source] only available when you want to specify the package of image
WeChatFile.asset( WeChatFile.asset(
String source, { String this.source, {
String? suffix, String? suffix,
}) : assert(source.trim().isNotEmpty), }) : assert(source.trim().isNotEmpty),
this.source = source, schema = FileSchema.asset,
this.schema = FileSchema.ASSET, suffix = source.readSuffix(suffix, defaultSuffixTxt);
this.suffix = source.readSuffix(suffix, defaultSuffixTxt);
WeChatFile.file( WeChatFile.file(
File source, { File source, {
String suffix = defaultSuffixTxt, String suffix = defaultSuffixTxt,
}) : source = source.path, }) : source = source.path,
schema = FileSchema.FILE, schema = FileSchema.file,
suffix = source.path.readSuffix(suffix, defaultSuffixTxt); suffix = source.path.readSuffix(suffix, defaultSuffixTxt);
WeChatFile.binary( WeChatFile.binary(
Uint8List source, { Uint8List this.source, {
String suffix = defaultSuffixTxt, this.suffix = defaultSuffixTxt,
}) : assert(suffix.trim().isNotEmpty), }) : assert(suffix.trim().isNotEmpty),
source = source, schema = FileSchema.binary;
schema = FileSchema.BINARY,
suffix = suffix;
final dynamic source; final dynamic source;
final FileSchema schema; final FileSchema schema;
...@@ -88,11 +84,11 @@ class WeChatFile { ...@@ -88,11 +84,11 @@ class WeChatFile {
/// Types of image, usually there are for types listed below. /// Types of image, usually there are for types listed below.
/// ///
/// [NETWORK] is online images. /// [network] is online images.
/// [ASSET] is flutter asset image. /// [asset] is flutter asset image.
/// [BINARY] is binary image, shall be be [Uint8List] /// [binary] is binary image, shall be be [Uint8List]
/// [FILE] is local file, usually not comes from flutter asset. /// [file] is local file, usually not comes from flutter asset.
enum FileSchema { NETWORK, ASSET, FILE, BINARY } enum FileSchema { network, asset, file, binary }
extension _FileSuffix on String { extension _FileSuffix on String {
/// returns [suffix] if [suffix] not blank. /// returns [suffix] if [suffix] not blank.
......
name: fluwx name: fluwx
description: The capability of implementing WeChat SDKs in Flutter. With Fluwx, developers can use WeChatSDK easily, such as sharing, payment, lanuch mini program and etc. description: The capability of implementing WeChat SDKs in Flutter. With Fluwx, developers can use WeChatSDK easily, such as sharing, payment, lanuch mini program and etc.
version: 3.13.1 version: 4.0.0
homepage: https://github.com/JarvanMo/fluwx homepage: https://github.com/OpenFlutter/fluwx
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: '>=3.0.0 <4.0.0'
flutter: ">=1.12.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_web_plugins:
sdk: flutter
plugin_platform_interface: ^2.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
# 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://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter. # The following section is specific to Flutter packages.
flutter: flutter:
# This section identifies this Flutter project as a plugin project. # This section identifies this Flutter project as a plugin project.
# The androidPackage and pluginClass identifiers should not ordinarily # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
# be modified. They are used by the tooling to maintain consistency when # which should be registered in the plugin registry. This is required for
# using method channels.
# The Android 'package' specifies package in which the registered class is.
# This is required for using method channels on Android.
# The 'ffiPlugin' specifies that native code should be built and bundled.
# This is required for using `dart:ffi`.
# All these are used by the tooling to maintain consistency when
# adding or updating assets for this project. # adding or updating assets for this project.
plugin: plugin:
platforms: platforms:
...@@ -31,6 +40,9 @@ flutter: ...@@ -31,6 +40,9 @@ flutter:
pluginClass: FluwxPlugin pluginClass: FluwxPlugin
ios: ios:
pluginClass: FluwxPlugin pluginClass: FluwxPlugin
web:
pluginClass: FluwxWeb
fileName: fluwx_web.dart
# 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:
...@@ -41,7 +53,7 @@ flutter: ...@@ -41,7 +53,7 @@ flutter:
# https://flutter.dev/assets-and-images/#from-packages # https://flutter.dev/assets-and-images/#from-packages
# #
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware. # https://flutter.dev/assets-and-images/#resolution-aware
# To add custom fonts to your plugin package, add a fonts section here, # To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a # in this "flutter" section. Each entry in this list should have a
......
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/fluwx.dart' as fluwx;
void main() {
const MethodChannel channel = MethodChannel('com.jarvanmo/fluwx');
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
channel.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'registerApp') {
if (methodCall.arguments['appId'] == 'wx13124324324') {
return Future.value(true);
} else {
return Future.value(false);
}
} else if (methodCall.method == 'shareText') {
channel.invokeMethod(
'onShareResponse',
{'type': 1, 'errCode': 1, 'errStr': 'hehe'},
);
return Future.value(true);
} else if (methodCall.method == 'shareImage') {
channel.invokeMethod(
'onShareResponse',
{'type': 1, 'errCode': 0, 'errStr': ''},
);
return Future.value(true);
}
return '42';
});
});
tearDown(() {
channel.setMockMethodCallHandler(null);
});
group('register', () {
test('success', () async {
expect(await fluwx.registerWxApi(appId: 'wx13124324324'), true);
});
test('failed', () async {
expect(await fluwx.registerWxApi(appId: 'wx131256'), false);
});
});
group('share', () {
test('text', () async {
expect(
await fluwx.shareToWeChat(fluwx.WeChatShareTextModel('text')),
true,
);
});
test('shareImage', () async {
expect(
await fluwx.shareToWeChat(
fluwx.WeChatShareImageModel(
fluwx.WeChatImage.network('http://flutter.dev'),
),
),
true,
);
});
});
group('learn', () {
test('description', () async {
print('argumentsss');
});
});
}
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/src/method_channel/fluwx_method_channel.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
MethodChannelFluwx platform = MethodChannelFluwx();
const MethodChannel channel = MethodChannel('fluwx');
setUp(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
channel,
(MethodCall methodCall) async {
return '42';
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
});
test('getPlatformVersion', () async {
});
}
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/fluwx.dart';
import 'package:fluwx/src/method_channel/fluwx_method_channel.dart';
import 'package:fluwx/src/method_channel/fluwx_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
class MockFluwxPlatform
with MockPlatformInterfaceMixin
implements FluwxPlatform {
@override
Future<String?> getExtMsg() {
// TODO: implement getExtMsg
throw UnimplementedError();
}
@override
Future<bool> get isWeChatInstalled => Future.value(false);
@override
Future<bool> registerApi(
{required String appId,
bool doOnIOS = true,
bool doOnAndroid = true,
String? universalLink}) {
// TODO: implement registerWxApi
throw UnimplementedError();
}
@override
Future<bool> sendAuth(
{required String scope,
String state = 'state',
bool nonAutomatic = false}) {
// TODO: implement sendAuth
throw UnimplementedError();
}
@override
Future<bool> share(WeChatShareModel what) {
// TODO: implement share
throw UnimplementedError();
}
@override
Future<bool> stopAuthByQRCode() {
// TODO: implement stopWeChatAuthByQRCode
throw UnimplementedError();
}
@override
Stream<WeChatResponse> get responseEventHandler => throw UnimplementedError();
@override
Future<bool> open(OpenType target) {
throw UnimplementedError();
}
@override
Future<bool> authBy(AuthType which) {
// TODO: implement authBy
throw UnimplementedError();
}
@override
Future<bool> authByPhoneLogin({required String scope, String state = 'state'}) {
// TODO: implement authByPhoneLogin
throw UnimplementedError();
}
@override
Future<bool> authByQRCode({required String appId, required String scope, required String nonceStr, required String timestamp, required String signature, String? schemeData}) {
// TODO: implement authByQRCode
throw UnimplementedError();
}
@override
Future<bool> autoDeduct(AutoDeduct data) {
// TODO: implement autoDeduct
throw UnimplementedError();
}
@override
// TODO: implement isSupportOpenBusinessView
Future<bool> get isSupportOpenBusinessView => throw UnimplementedError();
@override
Future<bool> pay(PayType which) {
// TODO: implement pay
throw UnimplementedError();
}
}
void main() {
final FluwxPlatform initialPlatform = FluwxPlatform.instance;
test('$MethodChannelFluwx is the default instance', () {
expect(initialPlatform, isInstanceOf<MethodChannelFluwx>());
});
test('isWeChatInstalled', () async {
Fluwx fluwxPlugin = Fluwx();
MockFluwxPlatform fakePlatform = MockFluwxPlatform();
FluwxPlatform.instance = fakePlatform;
expect(await fluwxPlugin.isWeChatInstalled, false);
});
}
/*
* Copyright (c) 2020. OpenFlutter Project
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this
* file to you 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.
*/
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/fluwx.dart';
void main() {
group('create response', () {
test('WeChatShareResponse', () {
var response = BaseWeChatResponse.create(
'onShareResponse',
{'type': 1, 'errCode': 1, 'errStr': 'hehe'},
);
expect(response is WeChatShareResponse, true);
var casted = response as WeChatShareResponse;
expect(casted.type, 1);
expect(casted.errCode, 1);
expect(casted.errStr, 'hehe');
});
test('WeChatAuthResponse', () {
var response = BaseWeChatResponse.create('onAuthResponse', {
'type': 1,
'errCode': 1,
'errStr': 'hehe',
'country': 'cn',
'lang': 'lang',
'code': 'code',
'state': 'ok'
});
expect(response is WeChatAuthResponse, true);
var casted = response as WeChatAuthResponse;
expect(casted.type, 1);
expect(casted.errCode, 1);
expect(casted.errStr, 'hehe');
expect(casted.country, 'cn');
expect(casted.lang, 'lang');
expect(casted.code, 'code');
expect(casted.state, 'ok');
});
test('onLaunchMiniProgramResponse', () {
var response = BaseWeChatResponse.create(
'onLaunchMiniProgramResponse',
{'type': 1, 'errCode': 1, 'errStr': 'hehe', 'extMsg': 'extMsg'},
);
expect(response is WeChatLaunchMiniProgramResponse, true);
var casted = response as WeChatLaunchMiniProgramResponse;
expect(casted.type, 1);
expect(casted.errCode, 1);
expect(casted.errStr, 'hehe');
expect(casted.extMsg, 'extMsg');
});
test('WeChatPaymentResponse', () {
var response = BaseWeChatResponse.create(
'onPayResponse',
{'type': 1, 'errCode': 1, 'errStr': 'hehe', 'extData': 'extData'},
);
expect(response is WeChatPaymentResponse, true);
var casted = response as WeChatPaymentResponse;
expect(casted.type, 1);
expect(casted.errCode, 1);
expect(casted.errStr, 'hehe');
expect(casted.extData, 'extData');
});
test('WeChatSubscribeMsgResponse', () {
var response = BaseWeChatResponse.create('onSubscribeMsgResp', {
'type': 1,
'errCode': 1,
'errStr': 'hehe',
'openid': '425235131',
'templateId': '4252345',
'action': 'action',
'reserved': 'reserved',
'scene': 1
});
expect(response is WeChatSubscribeMsgResponse, true);
var casted = response as WeChatSubscribeMsgResponse;
expect(casted.errCode, 1);
expect(casted.errStr, 'hehe');
expect(casted.openid, '425235131');
expect(casted.templateId, '4252345');
expect(casted.action, 'action');
expect(casted.reserved, 'reserved');
expect(casted.scene, 1);
});
test('WeChatAutoDeductResponse', () {
var response = BaseWeChatResponse.create('onAutoDeductResponse', {
'type': 1,
'errCode': 0,
'errStr': 'hehe',
'businessType': 2,
'resultInfo': 'resultInfo'
});
expect(response is WeChatOpenBusinessWebviewResponse, true);
var casted = response as WeChatOpenBusinessWebviewResponse;
assert(casted.isSuccessful);
expect(casted.type, 1);
expect(casted.errCode, 0);
expect(casted.errStr, 'hehe');
expect(casted.resultInfo, 'resultInfo');
expect(casted.businessType, 2);
});
});
}
/*
* Copyright (c) 2020. OpenFlutter Project
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this
* file to you 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.
*/
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/fluwx.dart';
void main() {
test('test create WeChatShareImageModel with thumbnail', () {
var image = WeChatImage.network('http://openflutter.dev/fluwx.png');
var thumbnail = WeChatImage.network('http://openflutter.dev/fluwx.png');
var model = WeChatShareImageModel(
image,
scene: WeChatScene.FAVORITE,
thumbnail: thumbnail,
);
expect(model.source, image);
expect(model.scene, WeChatScene.FAVORITE);
expect(model.thumbnail, thumbnail);
});
test('test create WeChatShareImageModel without thumbnail', () {
var image = WeChatImage.network('http://openflutter.dev/fluwx.png');
var model = WeChatShareImageModel(image, scene: WeChatScene.FAVORITE);
expect(model.source, image);
expect(model.scene, WeChatScene.FAVORITE);
expect(model.thumbnail, image);
});
test('test WeChatShareImageModel toMap', () {
var image = WeChatImage.network('http://openflutter.dev/fluwx.png');
var thumbnail = WeChatImage.network('http://openflutter.dev/fluwx.png');
var map = WeChatShareImageModel(
image,
scene: WeChatScene.FAVORITE,
thumbnail: thumbnail,
).toMap();
assert(map['thumbnail'] != null);
expect(map['thumbnail']['source'], 'http://openflutter.dev/fluwx.png');
});
}
/*
* Copyright (c) 2020. OpenFlutter Project
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. The ASF licenses this
* file to you 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.
*/
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/fluwx.dart';
void main() {
group("construct", () {
test("non default values", () {
var thumbnail = WeChatImage.network("http://openflutter.dev/fluwx.png");
var model = WeChatShareMiniProgramModel(
webPageUrl: "http://openflutter.dev",
miniProgramType: WXMiniProgramType.PREVIEW,
withShareTicket: true,
thumbnail: thumbnail,
userName: "userName",
path: "path",
hdImagePath: thumbnail);
expect(model.webPageUrl, "http://openflutter.dev");
expect(model.miniProgramType, WXMiniProgramType.PREVIEW);
expect(model.thumbnail, thumbnail);
expect(model.hdImagePath, thumbnail);
expect(model.path, "path");
expect(model.userName, "userName");
});
test("default values", () {
var thumbnail = WeChatImage.network("http://openflutter.dev/fluwx.png");
var model = WeChatShareMiniProgramModel(
webPageUrl: "http://openflutter.dev",
userName: "userName",
thumbnail: thumbnail);
expect(model.webPageUrl, "http://openflutter.dev");
expect(model.miniProgramType, WXMiniProgramType.RELEASE);
expect(model.thumbnail, null);
expect(model.path, "/");
expect(model.userName, "userName");
});
});
group("toMap", () {
test("with thumbnail", () {
var thumbnail = WeChatImage.network("http://openflutter.dev/fluwx.png");
var map = WeChatShareMiniProgramModel(
webPageUrl: "http://openflutter.dev",
miniProgramType: WXMiniProgramType.PREVIEW,
withShareTicket: true,
thumbnail: thumbnail,
userName: "userName",
path: "path")
.toMap();
expect(map["webPageUrl"], "http://openflutter.dev");
expect(map["miniProgramType"], 2);
expect(map["thumbnail"]["source"], "http://openflutter.dev/fluwx.png");
expect(map["path"], "path");
expect(map["userName"], "userName");
});
test("without thumbnail", () {
var thumbnail = WeChatImage.network("http://openflutter.dev/fluwx.png");
var map = WeChatShareMiniProgramModel(
webPageUrl: "http://openflutter.dev",
miniProgramType: WXMiniProgramType.PREVIEW,
withShareTicket: true,
thumbnail: thumbnail,
userName: "userName",
path: "path")
.toMap();
expect(map["webPageUrl"], "http://openflutter.dev");
expect(map["miniProgramType"], 2);
expect(map["thumbnail"], null);
expect(map["path"], "path");
expect(map["userName"], "userName");
});
});
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论