Commit 2d11a1cd authored by 李增强's avatar 李增强

m

parent 6b17ed21
FROM cirrusci/flutter:stable
RUN sudo apt-get update -y
RUN sudo apt-get install -y --no-install-recommends gnupg
# Add repo for gcloud sdk and install it
RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | \
sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
RUN sudo apt-get update && sudo apt-get install -y google-cloud-sdk && \
gcloud config set core/disable_usage_reporting true && \
gcloud config set component_manager/disable_update_check true
RUN yes | sdkmanager \
"platforms;android-29" \
"build-tools;29.0.2" \
"extras;google;m2repository" \
"extras;android;m2repository"
RUN yes | sdkmanager \
"platforms;android-30" \
"build-tools;28.0.3" \
"extras;google;m2repository" \
"extras;android;m2repository"
RUN yes | sdkmanager --licenses
task:
container:
dockerfile: .ci/Dockerfile
cpu: 8
memory: 16G
pub_cache:
folder: ~/.pub-cache
setup_script:
- flutter channel stable
- flutter upgrade
build_script:
- cd example
- flutter build apk
#test_script: flutter test
task:
osx_instance:
image: catalina-xcode-11.3.1-flutter
pub_cache:
folder: ~/.pub-cache
setup_script:
- pod repo update
- flutter channel stable
- flutter upgrade
create_simulator_script:
- xcrun simctl list
- xcrun simctl create Flutter-iPhone com.apple.CoreSimulator.SimDeviceType.iPhone-X com.apple.CoreSimulator.SimRuntime.iOS-13-3 | xargs xcrun simctl boot
build_script:
- cd example
- flutter build ios --no-codesign
#test_script: flutter test
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Which version of Fluwx do you use?**
Tell us the version of your current Fluwx.
**Which device do you run on**
Tell us the device, like BLA-00 and the OS is Android 10.
**Which Flutter do you use?**
Run `flutter doctor` and paste the output here.
.DS_Store
.dart_tool/
.packages
.pub/
pubspec.lock
build/
*.iml
.idea/
.vscode/
.flutter-plugins-dependencies
flutter_export_environment.sh
android/.classpath
android/.project
android/.settings/org.eclipse.buildship.core.prefs
example/android/.project
example/android/.settings/org.eclipse.buildship.core.prefs
example/android/app/.classpath
example/android/app/.project
example/android/app/.settings/org.eclipse.buildship.core.prefs
example/Podfile.lock
.last_build_id
\ No newline at end of file
#Sat Mar 07 20:06:05 CST 2020
gradle.version=5.2.1
# 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: plugin
## 2.6.2
* Fix #338 on Android
## 2.6.1
* Fix #338
## 2.6.0+2
* Remove trailing
## 2.6.0+1
* Nothing
## 2.6.0
* Android支持通过H5冷启动app传递<wx-open-launch-app>中的extinfo数据
* Android新加<meta-data>handleWeChatRequestByFluwx</meta-data>
## 2.5.0+1
* Fix trailing , issue.
## 2.5.0
* App获取开放标签<wx-open-launch-app>中的extinfo数据
## 2.4.2
* Fix #317
## 2.4.1
* 修复Android 11无法分享图片的问题
## 2.4.0
* 支持compressThumbnail
* 升级OkHttp
## 2.3.0
* 适配Flutter 1.20
* 升级Android的Gradle以及更库的版本
## 2.2.0
* Merged #249
## 2.1.0
* Specifying supported platforms
* Fix: Android分享小程序时,缩略图压缩过重的问题
* 更改分享文件的实现形式
## 2.0.9
* Android SDK 升级到6.6.4
* iOS SDK升级到1.8.7.1
* Kotlin->1.3.72
## 2.0.8+3
* Merge #218
## 2.0.8+2
* Merge #218
## 2.0.8+1
* 修复ios编译错误
## 2.0.8
* Fix #212
## 2.0.7
* Fix #207
## 2.0.6+2
* Fix: Android分享大图时存储权限问题
## 2.0.6
* Fix: Android请求权限崩溃的问题
## 2.0.5+1
* 升级
## 2.0.5
* Fix:Android分享file文件时,会crash
## 2.0.4
* Fix:hdImage为空时,ios会crash
## 2.0.3
* 添加混淆文件
## 2.0.2
* Fix #199
## 2.0.1
* 修复Android没有回调的问题
## 2.0.0+1
* 按照pub建议改进
## 2.0.0
* 代码重构,现在代码结构更清晰
* 所有图片由WeChatImage构建
* 现在iOS对分享微信小程序的高清图也会压缩
* 微信回调监听形式变更
* Android增加新的Action以防微信打开小程序出错不会返回原app的问题
* iOS改用Pod引用微信SDK
* iOS隐藏一些header
* kotlin 1.3.70
## 1.2.1+2
* iOS的StringUtil重命名了
## 1.2.1+1
* Fix #178
## 1.2.1
* Fix #175
## 1.2.0
* 分享文件
* compileSdkVersion 29
## 1.1.4
* 注册微信时会对universal link进行简单校验
## 1.1.3
* Fix #146
## 1.1.2
* Fix #122
## 1.1.1+1
* Android CompileSDKVersion 提升到28
### 1.1.1
* registerWxApi
## 1.1.0
* iOS SDK升级至1.8.6.1,本版本开始支持universal link。
* Android SDK更换至without-mat:5.4.3
* Android配置升级
* 移除MTA选项
## 1.0.6
* Fix #110
## 1.0.5
* 增加分享内存图片
## 1.0.4
* 解决Android上打开小程序返回白屏问题(非官方解决方案)
## 1.0.3
* 修复一些小问题
## 1.0.2
* 修复无法Android上分享大图的问题
## 1.0.1
* 修复一些小问题
## 1.0.0
* ios不必再重写AppDelegate
## 0.6.3
* 免密支付
* 支持打开微信App了
* 升级了Android
## 0.6.2
* 对android进行了升级
## 0.6.1
* 支持二维码登录
## 0.6.0
* kotlin升级至1.3.21。
* ios SDK升级至1.8.4。
* android SDK升级至5.3.6。
## 0.5.7
* 修复问题43。
## 0.5.6
## 0.5.5
* 修复ios分享小程序标题不正确的问题。
## 0.5.4
* 增加一次性订阅消息功能。
## 0.5.3
* 修复唤起小程序返回值类型不一致的问题。
## 0.5.2
* 修复ios上sendAuth无返回的问题。
* kotlin升级至1.3.10
* android WeChatSDK升级到5.1.6
## 0.5.1
* Kotlin升级到了1.3.0
* 代码格化
## 0.5.0
* 增加了对拉起小程序的支持
* 删除了一些不必要的类
* 发送Auth验证Api调整
## 0.4.1
* 修复iOS与其他库共存时,会有重复的错误
## 0.4.0
* 移除WeChatPayModel
* 移除ios最小支持。
* 优化*Android*微信回调。
* *build.gradle*升级到了*3.2.1*
## 0.3.2
* *build.gradle*升级到了*3.2.0*
* *kotlin*升级到了*1.2.71*
## 0.3.1
* 修复了由于Flutter-dev-0.9.7-pre在android中添加了*@Nullable*注解而引起的编译问题
## 0.3.0
* 回调方式发生变化,由Map变更为实体类。
* iOS的WeChatSDK更换为内部依赖,并升级到了1.8.3。
* 修复iOS支付返回结果缺少*returnKey*的问题。
* API现在更加友善了。
* 对swift支持更友好了。
## 0.2.1
* 修复在Android处理网络图片后缀不对的问题。
## 0.2.0
* iOS支持Swift了。
## 0.1.9
* 修复了不传*thumbnail*在Android上会崩溃的bug。
## 0.1.8
* `WeChatPayModel`里的字段不再是`dynamic`
* 修复了iOS对支付功能中timestamp处理不正确的问题。
## 0.1.7
* 删除`Fluwx.registerApp(RegisterModel)`,现在使用`Fluwx.register()`
## 0.1.6
* 修复transitive dependencies。
## 0.1.5
* 增加了本地图片的支持
## 0.1.4
* 修复了iOS分享去处错误的问题
## 0.1.3
* `ResponseType` 更名为`WeChatResponseType`
## 0.1.2
* 修复iOS中FluwxShareHandler.h的导入问题
## 0.1.1
* 修复iOS分享去处错误的bug
## 0.1.0
* 增加了MTA选项
* Android部分的微信SDK提供方式由implementation更换为api
## 0.0.8
* 修复了iOS无法分享小程序的bug
* 修复了iOS分享音乐崩溃的问题
* 修复了iOS发送Auth偶尔会崩溃的问题
## 0.0.7
* 修复了iOS回调崩溃的bug
## 0.0.6
* 修复iOS拉起支付崩溃的问题
## 0.0.5
* 格式化代码
## 0.0.4
* 支付
* demo
## 0.0.3
* 发送Auth认证。
## 0.0.2
* 文本分享。
* 网站分享。
* 图片分享。
* 音乐分享。
* 视频分享。
* 小程序分享。
## 0.0.1
* Android部分的分享已完成.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
# Fluwx
![pub package](https://img.shields.io/pub/v/fluwx.svg)
[![Build status](https://img.shields.io/cirrus/github/OpenFlutter/fluwx/master)](https://cirrus-ci.com/github/OpenFlutter/fluwx)
======
![logo](https://github.com/JarvanMo/ImagesStore/blob/master/fluwx/fluwx_logo.png)
## 什么是Fluwx
`Fluwx` 是一个[微信SDK](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Resource_Center_Homepage.html)插件,它允许开发者调用
[微信原生SDK ](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Resource_Center_Homepage.html).
> 加入我们的QQ群: 892398530。
## 能力
- 分享图片,文本,音乐,视频等。支持分享到会话,朋友圈以及收藏.
- 微信支付.
- 在微信登录时,获取Auth Code.
- 拉起小程序.
- 订阅消息.
- 打开微信.
- 从微信标签打开应用
## 准备
`Fluwx` 可以做很多工作但不是所有. 在集成之前,最好读一下[官方文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1).
然后你才知道怎么生成签名,怎么使用universal link以及怎么添加URL schema等.
> [收费视频教程点这里](https://study.163.com/course/introduction.htm?share=2&shareId=480000001896427&courseId=1209174838&_trace_c_p_k2_=e72467dc0df540579287a8ea996344a4)
>
## 安装
`pubspec.yaml` 文件中添加`fluwx`依赖:
`Fluwx`,带支付:
```yaml
dependencies:
fluwx: ^${latestVersion}
```
![pub package](https://img.shields.io/pub/v/fluwx.svg)
`Fluwx`,不带支付:
```yaml
dependencies:
fluwx_no_pay: ^${latestVersion}
```
![pub package](https://img.shields.io/pub/v/fluwx_no_pay.svg)
> NOTE: 别忘记替换 ^${latestVersion} !!!!
## 注册 WxAPI
通过 `fluwx` 注册WxApi.
```dart
registerWxApi(appId: "wxd930ea5d5a228f5f",universalLink: "https://your.univerallink.com/link/");
```
参数 `universalLink` 只在iOS上有用. 查看[文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html) 以便了解如何生成通用链接.
你也可以学习到怎么在iOS工程中添加URL schema,怎么添加`LSApplicationQueriesSchemes`。这很重要。
对于Android, 可以查看[本文](https://developers.weixin.qq.com/doc/oplatform/Downloads/Android_Resource.html)以便了解怎么获取app签名.
然后你需要知道release和debug时,app签名有什么区别。如果签名不对,你会得一个错误 `errCode = -1`.
## 能力文档
- [基础知识](./doc/BASIC_KNOWLEDGE_CN.md)
- [分享](./doc/SHARE_CN.md)
- [支付](./doc/PAYMENT_CN.md)
- [登录](./doc/AUTH_CN.md)
- [从微信标签打开应用](./doc/LAUNCH_APP_FROM_H5_CN.md)
对于更多功能,可以查看源码。
## QA
[这些问题可能对你有帮助](./doc/QA_CN.md)
## 捐助
开源不易,请作者喝杯咖啡。
<img src="https://github.com/JarvanMo/ImagesStore/blob/master/common/wx.jpeg" height="300"> <img src="https://github.com/JarvanMo/ImagesStore/blob/master/common/ali.jpeg" height="300">
## 关注公众号
![subscribe](https://github.com/JarvanMo/ImagesStore/blob/master/fluwx/wx_subscription.png)
## LICENSE
Copyright 2018 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.
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
group 'com.jarvan.fluwx'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.4.10'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 16
consumerProguardFiles 'consumer-proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
api 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:6.6.19'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'top.zibin:Luban:1.1.8'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
testImplementation 'junit:junit:4.12'
}
# 微信
-keep class com.tencent.mm.opensdk.** {*;}
-keep class com.tencent.wxop.** {*;}
-keep class com.tencent.mm.sdk.** {*;}
## Kotlin
# ServiceLoader support
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
-keepnames class kotlinx.coroutines.android.AndroidExceptionPreHandler {}
-keepnames class kotlinx.coroutines.android.AndroidDispatcherFactory {}
# Most of volatile fields are updated with AFU and should not be mangled
-keepclassmembernames class kotlinx.** {
volatile <fields>;
}
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
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-6.1.1-bin.zip
rootProject.name = 'fluwx'
<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.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>
<activity
android:name=".wxapi.FluwxWXEntryActivity"
android:launchMode="singleTask"
android:taskAffinity="${applicationId}"
android:theme="@style/DisablePreviewTheme" />
<activity-alias
android:name="${applicationId}.wxapi.WXEntryActivity"
android:exported="true"
android:launchMode="singleTop"
android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity"
android:taskAffinity="${applicationId}"
android:theme="@style/DisablePreviewTheme">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sdksample" />
</intent-filter>
</activity-alias>
<activity-alias
android:name="${applicationId}.wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleInstance"
android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity"
android:theme="@style/DisablePreviewTheme">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sdksample" />
</intent-filter>
</activity-alias>
<provider
android:name="com.jarvan.fluwx.FluwxFileProvider"
android:authorities="${applicationId}.fluwxprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/fluwx_file_provider_paths" />
</provider>
</application>
</manifest>
package com.jarvan.fluwx
import androidx.core.content.FileProvider
/***
* Created by mo on 2020/5/13
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class FluwxFileProvider: FileProvider()
\ No newline at end of file
package com.jarvan.fluwx
import androidx.annotation.NonNull
import com.jarvan.fluwx.handlers.*
import com.tencent.mm.opensdk.modelbiz.SubscribeMessage
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessWebview
import com.tencent.mm.opensdk.modelpay.PayReq
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry
/** FluwxPlugin */
class FluwxPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
companion object {
var callingChannel:MethodChannel? = null
@JvmStatic
fun registerWith(registrar: PluginRegistry.Registrar) {
val channel = MethodChannel(registrar.messenger(), "com.jarvanmo/fluwx")
val authHandler = FluwxAuthHandler(channel)
WXAPiHandler.setContext(registrar.activity().applicationContext)
channel.setMethodCallHandler(FluwxPlugin().apply {
this.fluwxChannel = channel
this.authHandler = authHandler
this.shareHandler = FluwxShareHandlerCompat(registrar).apply {
permissionHandler = PermissionHandler(registrar.activity())
}
})
}
}
private var shareHandler: FluwxShareHandler? = null
private var authHandler: FluwxAuthHandler? = null
private var fluwxChannel: MethodChannel? = null
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.jarvanmo/fluwx")
channel.setMethodCallHandler(this)
fluwxChannel = channel
authHandler = FluwxAuthHandler(channel)
shareHandler = FluwxShareHandlerEmbedding(flutterPluginBinding.flutterAssets, flutterPluginBinding.applicationContext)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
FluwxPlugin.callingChannel = fluwxChannel
when {
call.method == "registerApp" -> WXAPiHandler.registerApp(call, result)
call.method == "sendAuth" -> authHandler?.sendAuth(call, result)
call.method == "authByQRCode" -> authHandler?.authByQRCode(call, result)
call.method == "stopAuthByQRCode" -> authHandler?.stopAuthByQRCode(result)
call.method == "payWithFluwx" -> pay(call, result)
call.method == "payWithHongKongWallet" -> payWithHongKongWallet(call, result)
call.method == "launchMiniProgram" -> launchMiniProgram(call, result)
call.method == "subscribeMsg" -> subScribeMsg(call, result)
call.method == "autoDeduct" -> signAutoDeduct(call, result)
call.method == "openWXApp" -> openWXApp(result)
call.method.startsWith("share") -> shareHandler?.share(call, result)
call.method == "isWeChatInstalled" -> WXAPiHandler.checkWeChatInstallation(result)
else -> result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
shareHandler?.onDestroy()
authHandler?.removeAllListeners()
}
override fun onDetachedFromActivity() {
shareHandler?.permissionHandler = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
shareHandler?.permissionHandler = PermissionHandler(binding.activity)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
WXAPiHandler.setContext(binding.activity.applicationContext)
shareHandler?.permissionHandler = PermissionHandler(binding.activity)
}
override fun onDetachedFromActivityForConfigChanges() {
}
private fun pay(call: MethodCall, result: MethodChannel.Result) {
if (WXAPiHandler.wxApi == null) {
result.error("Unassigned WxApi", "please config wxapi first", null)
return
} else {
// 将该app注册到微信
val request = PayReq()
request.appId = call.argument("appId")
request.partnerId = call.argument("partnerId")
request.prepayId = call.argument("prepayId")
request.packageValue = call.argument("packageValue")
request.nonceStr = call.argument("nonceStr")
request.timeStamp = call.argument<Long>("timeStamp").toString()
request.sign = call.argument("sign")
request.signType = call.argument("signType")
request.extData = call.argument("extData")
val done = WXAPiHandler.wxApi?.sendReq(request)
result.success(done)
}
}
private fun payWithHongKongWallet(call: MethodCall, result: MethodChannel.Result) {
val prepayId = call.argument<String>("prepayId") ?: ""
val request = WXOpenBusinessWebview.Req()
request.businessType = 1
request.queryInfo = hashMapOf(
"token" to prepayId
)
result.success(WXAPiHandler.wxApi?.sendReq(request))
}
private fun signAutoDeduct(call: MethodCall, result: Result) {
val appId: String = call.argument<String>("appid") ?: ""
val mchId = call.argument<String>("mch_id") ?: ""
val planId = call.argument<String>("plan_id") ?: ""
val contractCode = call.argument<String>("contract_code") ?: ""
val requestSerial = call.argument<String>("request_serial") ?: ""
val contractDisplayAccount = call.argument<String>("contract_display_account") ?: ""
val notifyUrl = call.argument<String>("notify_url") ?: ""
val version = call.argument<String>("version") ?: ""
val sign = call.argument<String>("sign") ?: ""
val timestamp = call.argument<String>("timestamp") ?: ""
val returnApp = call.argument<String>("return_app") ?: ""
val businessType = call.argument<Int>("businessType") ?: 12
val req = WXOpenBusinessWebview.Req()
req.businessType = businessType
req.queryInfo = hashMapOf(
"appid" to appId,
"mch_id" to mchId,
"plan_id" to planId,
"contract_code" to contractCode,
"request_serial" to requestSerial,
"contract_display_account" to contractDisplayAccount,
"notify_url" to notifyUrl,
"version" to version,
"sign" to sign,
"timestamp" to timestamp,
"return_app" to returnApp
)
result.success(WXAPiHandler.wxApi?.sendReq(req))
}
private fun subScribeMsg(call: MethodCall, result: MethodChannel.Result) {
val appId = call.argument<String>("appId")
val scene = call.argument<Int>("scene")
val templateId = call.argument<String>("templateId")
val reserved = call.argument<String>("reserved")
val req = SubscribeMessage.Req()
req.openId = appId
req.scene = scene!!
req.reserved = reserved
req.templateID = templateId
val b = WXAPiHandler.wxApi?.sendReq(req)
result.success(b)
}
private fun launchMiniProgram(call: MethodCall, result: MethodChannel.Result) {
val req = WXLaunchMiniProgram.Req()
req.userName = call.argument<String?>("userName") // 填小程序原始id
req.path = call.argument<String?>("path") ?: "" //拉起小程序页面的可带参路径,不填默认拉起小程序首页
val type = call.argument("miniProgramType") ?: 0
req.miniprogramType = when (type) {
1 -> WXLaunchMiniProgram.Req.MINIPROGRAM_TYPE_TEST
2 -> WXLaunchMiniProgram.Req.MINIPROGRAM_TYPE_PREVIEW
else -> WXLaunchMiniProgram.Req.MINIPTOGRAM_TYPE_RELEASE
}// 可选打开 开发版,体验版和正式版
val done = WXAPiHandler.wxApi?.sendReq(req)
result.success(WXAPiHandler.wxApi?.sendReq(req))
}
private fun openWXApp(result: MethodChannel.Result) = result.success(WXAPiHandler.wxApi?.openWXApp())
}
/*
* Copyright (C) 2020 The OpenFlutter Organization
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jarvan.fluwx.handlers
import com.tencent.mm.opensdk.diffdev.DiffDevOAuthFactory
import com.tencent.mm.opensdk.diffdev.OAuthErrCode
import com.tencent.mm.opensdk.diffdev.OAuthListener
import com.tencent.mm.opensdk.modelmsg.SendAuth
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
internal class FluwxAuthHandler(private val methodChannel: MethodChannel) {
// private DiffDevOAuthFactory.getDiffDevOAuth()
private val qrCodeAuth by lazy {
DiffDevOAuthFactory.getDiffDevOAuth()
}
private val qrCodeAuthListener by lazy {
object : OAuthListener {
override fun onAuthFinish(p0: OAuthErrCode, authCode: String?) {
methodChannel.invokeMethod("onAuthByQRCodeFinished", mapOf(
"errCode" to p0.code,
"authCode" to authCode
))
}
override fun onAuthGotQrcode(p0: String?, p1: ByteArray) {
methodChannel.invokeMethod("onAuthGotQRCode", mapOf("errCode" to 0, "qrCode" to p1))
}
override fun onQrcodeScanned() {
methodChannel.invokeMethod("onQRCodeScanned", mapOf("errCode" to 0))
}
}
}
fun sendAuth(call: MethodCall, result: MethodChannel.Result) {
val req = SendAuth.Req()
req.scope = call.argument("scope")
req.state = call.argument("state")
val openId = call.argument<String?>("openId")
if (!openId.isNullOrBlank()) {
req.openId = call.argument("openId")
}
result.success(WXAPiHandler.wxApi?.sendReq(req))
}
fun authByQRCode(call: MethodCall, result: MethodChannel.Result) {
val appId = call.argument("appId") ?: ""
val scope = call.argument("scope") ?: ""
val nonceStr = call.argument("nonceStr") ?: ""
val timeStamp = call.argument("timeStamp") ?: ""
val signature = call.argument("signature") ?: ""
// val schemeData = call.argument("schemeData")?:""
result.success(qrCodeAuth.auth(appId, scope, nonceStr, timeStamp, signature, qrCodeAuthListener))
}
fun stopAuthByQRCode(result: MethodChannel.Result) {
result.success(qrCodeAuth.stopAuth())
}
fun removeAllListeners() {
qrCodeAuth.removeAllListeners()
}
}
\ No newline at end of file
/*
* Copyright (C) 2020 The OpenFlutter Organization
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jarvan.fluwx.handlers
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import com.jarvan.fluwx.FluwxPlugin
import com.tencent.mm.opensdk.modelmsg.ShowMessageFromWX
import io.flutter.plugin.common.MethodChannel
import com.tencent.mm.opensdk.modelbase.BaseReq
import java.lang.Exception
object FluwxRequestHandler {
private const val KEY_FLUWX_REQUEST_INFO_BUNDLE = "KEY_FLUWX_REQUEST_INFO_BUNDLE"
var customOnReqDelegate: ((baseReq: BaseReq, activity: Activity) -> Unit)? = null
fun handleRequestInfoFromIntent(intent: Intent) {
intent.getBundleExtra(KEY_FLUWX_REQUEST_INFO_BUNDLE)?.run {
val type = getInt("_wxapi_command_type", -9999)
if (type == 4) {
handleShowMessageFromWXBundle(this)
}
}
}
private fun handleShowMessageFromWXBundle(bundle: Bundle) = handleWXShowMessageFromWX(ShowMessageFromWX.Req(bundle))
private fun handleRequest(req: BaseReq) {
when (req) {
is ShowMessageFromWX.Req -> handleWXShowMessageFromWX(req)
}
}
private fun handleWXShowMessageFromWX(req: ShowMessageFromWX.Req) {
val result = mapOf(
"extMsg" to req.message.messageExt
)
FluwxPlugin.callingChannel?.invokeMethod("onWXShowMessageFromWX", result)
}
private fun defaultOnReqDelegate(baseReq: BaseReq, activity: Activity) {
// FIXME: 可能是官方的Bug,从微信拉起APP的Intent类型不对,无法跳转回Flutter Activity
// 稳定复现场景:微信版本为7.0.5,小程序SDK为2.7.7
if (baseReq.type == 4) {
// com.tencent.mm.opensdk.constants.ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX = 4
if (WXAPiHandler.wxApiRegistered) {
handleRequest(baseReq)
startSpecifiedActivity(defaultFlutterActivityAction(activity), activity = activity)
} else {
startSpecifiedActivity(defaultFlutterActivityAction(activity), bundle = Bundle().apply {
baseReq.toBundle(this)
}, bundleKey = KEY_FLUWX_REQUEST_INFO_BUNDLE, activity = activity)
}
}
}
fun onReq(baseReq: BaseReq, activity: Activity) {
try {
val activityInfo = activity.packageManager.getActivityInfo(activity.componentName, PackageManager.GET_META_DATA)
val defaultHandle = activityInfo.metaData.getBoolean("handleWeChatRequestByFluwx", true)
if (defaultHandle) {
defaultOnReqDelegate(baseReq, activity)
} else {
customOnReqDelegate?.invoke(baseReq, activity)
}
} catch (e: Exception) {
Log.i("Fluwx", "can't load meta-data handleWeChatRequestByFluwx")
}
}
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
/*
* Copyright (C) 2020 The OpenFlutter Organization
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jarvan.fluwx.handlers
import com.jarvan.fluwx.FluwxPlugin
import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.modelbiz.SubscribeMessage
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessWebview
import com.tencent.mm.opensdk.modelmsg.SendAuth
import com.tencent.mm.opensdk.modelmsg.SendMessageToWX
import com.tencent.mm.opensdk.modelpay.PayResp
import io.flutter.plugin.common.MethodChannel
object FluwxResponseHandler {
private const val errStr = "errStr"
private const val errCode = "errCode"
private const val openId = "openId"
private const val type = "type"
fun handleResponse(response: BaseResp) {
when (response) {
is SendAuth.Resp -> handleAuthResponse(response)
is SendMessageToWX.Resp -> handleSendMessageResp(response)
is PayResp -> handlePayResp(response)
is WXLaunchMiniProgram.Resp -> handleLaunchMiniProgramResponse(response)
is SubscribeMessage.Resp -> handleSubscribeMessage(response)
is WXOpenBusinessWebview.Resp -> handlerWXOpenBusinessWebviewResponse(response)
}
}
private fun handleSubscribeMessage(response: SubscribeMessage.Resp) {
val result = mapOf(
"openid" to response.openId,
"templateId" to response.templateID,
"action" to response.action,
"reserved" to response.reserved,
"scene" to response.scene,
type to response.type)
FluwxPlugin.callingChannel?.invokeMethod("onSubscribeMsgResp", result)
}
private fun handleLaunchMiniProgramResponse(response: WXLaunchMiniProgram.Resp) {
val result = mutableMapOf(
errStr to response.errStr,
type to response.type,
errCode to response.errCode,
openId to response.openId
)
response.extMsg?.let {
result["extMsg"] = response.extMsg
}
FluwxPlugin.callingChannel?.invokeMethod("onLaunchMiniProgramResponse", result)
}
private fun handlePayResp(response: PayResp) {
val result = mapOf(
"prepayId" to response.prepayId,
"returnKey" to response.returnKey,
"extData" to response.extData,
errStr to response.errStr,
type to response.type,
errCode to response.errCode
)
FluwxPlugin.callingChannel?.invokeMethod("onPayResponse", result)
}
private fun handleSendMessageResp(response: SendMessageToWX.Resp) {
val result = mapOf(
errStr to response.errStr,
type to response.type,
errCode to response.errCode,
openId to response.openId)
FluwxPlugin.callingChannel?.invokeMethod("onShareResponse", result)
}
private fun handleAuthResponse(response: SendAuth.Resp) {
val result = mapOf(
errCode to response.errCode,
"code" to response.code,
"state" to response.state,
"lang" to response.lang,
"country" to response.country,
errStr to response.errStr,
openId to response.openId,
"url" to response.url,
type to response.type)
FluwxPlugin.callingChannel?.invokeMethod("onAuthResponse", result)
}
private fun handlerWXOpenBusinessWebviewResponse(response: WXOpenBusinessWebview.Resp) {
val result = mapOf(
errCode to response.errCode,
"businessType" to response.businessType,
"resultInfo" to response.resultInfo,
errStr to response.errStr,
openId to response.openId,
type to response.type)
FluwxPlugin.callingChannel?.invokeMethod("onWXOpenBusinessWebviewResponse", result)
}
}
\ No newline at end of file
package com.jarvan.fluwx.handlers
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.AssetFileDescriptor
import android.net.Uri
import android.text.TextUtils
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.jarvan.fluwx.io.*
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelmsg.*
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry
import kotlinx.coroutines.*
import java.io.File
import java.util.*
import kotlin.coroutines.CoroutineContext
/***
* Created by mo on 2020/3/6
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
internal class FluwxShareHandlerEmbedding(private val flutterAssets: FlutterPlugin.FlutterAssets, override val context: Context) : FluwxShareHandler {
override val assetFileDescriptor: (String) -> AssetFileDescriptor = {
val uri = Uri.parse(it)
val packageName = uri.getQueryParameter("package")
val subPath = if (packageName.isNullOrBlank()) {
flutterAssets.getAssetFilePathBySubpath(uri.path.orEmpty())
} else {
flutterAssets.getAssetFilePathBySubpath(uri.path.orEmpty(), packageName)
}
context.assets.openFd(subPath)
}
override val job: Job = Job()
override var permissionHandler: PermissionHandler? = null
}
internal class FluwxShareHandlerCompat(private val registrar: PluginRegistry.Registrar) : FluwxShareHandler {
override val assetFileDescriptor: (String) -> AssetFileDescriptor = {
val uri = Uri.parse(it)
val packageName = uri.getQueryParameter("package")
val key = if (TextUtils.isEmpty(packageName)) {
registrar.lookupKeyForAsset(uri.path)
} else {
registrar.lookupKeyForAsset(uri.path, packageName)
}
context.assets.openFd(key)
}
override val context: Context = registrar.context().applicationContext
override val job: Job = Job()
override var permissionHandler: PermissionHandler? = null
}
internal interface FluwxShareHandler : CoroutineScope {
companion object {
const val SHARE_IMAGE_THUMB_LENGTH = 32 * 1024
const val SHARE_MINI_PROGRAM_THUMB_LENGTH = 120 * 1024
private const val keyTitle = "title"
private const val keyThumbnail = "thumbnail"
private const val keyDescription = "description"
}
fun share(call: MethodCall, result: MethodChannel.Result) {
if (WXAPiHandler.wxApi == null) {
result.error("Unassigned WxApi", "please config wxapi first", null)
return
}
when (call.method) {
"shareText" -> shareText(call, result)
"shareMiniProgram" -> shareMiniProgram(call, result)
"shareImage" -> shareImage(call, result)
"shareMusic" -> shareMusic(call, result)
"shareVideo" -> shareVideo(call, result)
"shareWebPage" -> shareWebPage(call, result)
"shareFile" -> shareFile(call, result)
else -> {
result.notImplemented()
}
}
}
private fun shareText(call: MethodCall, result: MethodChannel.Result) {
val textObj = WXTextObject(call.argument("source"))
val msg = WXMediaMessage()
msg.mediaObject = textObj
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
req.message = msg
result.success(WXAPiHandler.wxApi?.sendReq(req))
}
private fun shareMiniProgram(call: MethodCall, result: MethodChannel.Result) {
val miniProgramObj = WXMiniProgramObject()
miniProgramObj.webpageUrl = call.argument("webPageUrl") // 兼容低版本的网页链接
miniProgramObj.miniprogramType = call.argument("miniProgramType") ?: 0// 正式版:0,测试版:1,体验版:2
miniProgramObj.userName = call.argument("userName") // 小程序原始id
miniProgramObj.path = call.argument("path") //小程序页面路径
miniProgramObj.withShareTicket = call.argument("withShareTicket") ?: true
val msg = WXMediaMessage(miniProgramObj)
msg.title = call.argument(keyTitle) // 小程序消息title
msg.description = call.argument(keyDescription) // 小程序消息desc
launch {
msg.thumbData = readThumbnailByteArray(call, length = SHARE_MINI_PROGRAM_THUMB_LENGTH)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
req.message = msg
sendRequestInMain(result, req)
}
}
private fun shareImage(call: MethodCall, result: MethodChannel.Result) {
launch {
val map: Map<String, Any> = call.argument("source") ?: mapOf()
val sourceImage = WeChatFile.createWeChatFile(map, assetFileDescriptor)
val thumbData = readThumbnailByteArray(call)
val sourceByteArray = sourceImage.readByteArray()
val imageObject = when {
sourceByteArray.isEmpty() -> {
WXImageObject()
}
else -> {
WXImageObject().apply {
if (supportFileProvider && targetHigherThanN) {
setImagePath(getFileContentUri(sourceByteArray.toCacheFile(context, sourceImage.suffix)))
} else {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
setImagePath(sourceByteArray.toExternalCacheFile(context, sourceImage.suffix)?.absolutePath)
} else {
permissionHandler?.requestStoragePermission()
}
}
}
}
}
val msg = WXMediaMessage()
msg.mediaObject = imageObject
msg.thumbData = thumbData
msg.description = call.argument(keyDescription)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
req.message = msg
sendRequestInMain(result, req)
}
}
private fun shareMusic(call: MethodCall, result: MethodChannel.Result) {
val music = WXMusicObject()
val musicUrl: String? = call.argument("musicUrl")
val musicLowBandUrl: String? = call.argument("musicLowBandUrl")
if (musicUrl != null && musicUrl.isNotBlank()) {
music.musicUrl = musicUrl
music.musicDataUrl = call.argument("musicDataUrl")
} else {
music.musicLowBandUrl = musicLowBandUrl
music.musicLowBandDataUrl = call.argument("musicLowBandDataUrl")
}
val msg = WXMediaMessage()
msg.mediaObject = music
msg.description = call.argument(keyDescription)
launch {
msg.thumbData = readThumbnailByteArray(call)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
req.message = msg
sendRequestInMain(result, req)
}
}
private fun shareVideo(call: MethodCall, result: MethodChannel.Result) {
val video = WXVideoObject()
val videoUrl: String? = call.argument("videoUrl")
val videoLowBandUrl: String? = call.argument("videoLowBandUrl")
if (videoUrl != null && videoUrl.isNotBlank()) {
video.videoUrl = videoUrl
} else {
video.videoLowBandUrl = videoLowBandUrl
}
val msg = WXMediaMessage()
msg.mediaObject = video
msg.description = call.argument(keyDescription)
launch {
msg.thumbData = readThumbnailByteArray(call)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
req.message = msg
sendRequestInMain(result, req)
}
}
private fun shareWebPage(call: MethodCall, result: MethodChannel.Result) {
val webPage = WXWebpageObject()
webPage.webpageUrl = call.argument("webPage")
val msg = WXMediaMessage()
msg.mediaObject = webPage
msg.description = call.argument(keyDescription)
launch {
msg.thumbData = readThumbnailByteArray(call)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
req.message = msg
sendRequestInMain(result, req)
}
}
private fun shareFile(call: MethodCall, result: MethodChannel.Result) {
launch {
val wxFileObject = WXFileObject()
// val filePath: String? = call.argument("filePath")
// wxFileObject.filePath = filePath
val msg = WXMediaMessage()
msg.mediaObject = wxFileObject
msg.description = call.argument("description")
val map: Map<String, Any> = call.argument("source") ?: mapOf()
val sourceFile = WeChatFile.createWeChatFile(map, assetFileDescriptor)
val sourceByteArray = sourceFile.readByteArray()
wxFileObject.apply {
if (supportFileProvider && targetHigherThanN) {
setFilePath(getFileContentUri(sourceByteArray.toCacheFile(context, sourceFile.suffix)))
} else {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
filePath = sourceByteArray.toExternalCacheFile(context, sourceFile.suffix)?.absolutePath
} else {
permissionHandler?.requestStoragePermission()
}
}
}
msg.thumbData = readThumbnailByteArray(call)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
req.message = msg
sendRequestInMain(result, req)
}
}
private suspend fun sendRequestInMain(result: MethodChannel.Result, request: BaseReq) = withContext(Dispatchers.Main) {
result.success(WXAPiHandler.wxApi?.sendReq(request))
}
private suspend fun readThumbnailByteArray(call: MethodCall, length: Int = SHARE_IMAGE_THUMB_LENGTH): ByteArray? {
val thumbnailMap: Map<String, Any>? = call.argument(keyThumbnail)
val compress:Boolean = call.argument("compressThumbnail")?:true
return thumbnailMap?.run {
val thumbnailImage = WeChatFile.createWeChatFile(thumbnailMap, assetFileDescriptor)
val thumbnailImageIO = ImagesIOIml(thumbnailImage)
if(compress){
compressThumbnail(thumbnailImageIO, length)
}else{
thumbnailImageIO.readByteArray()
}
}
}
private suspend fun compressThumbnail(ioIml: ImagesIO, length: Int) = ioIml.compressedByteArray(context, length)
// SESSION, TIMELINE, FAVORITE
private fun setCommonArguments(call: MethodCall, req: SendMessageToWX.Req, msg: WXMediaMessage) {
msg.messageAction = call.argument("messageAction")
msg.messageExt = call.argument("messageExt")
msg.mediaTagName = call.argument("mediaTagName")
msg.title = call.argument(keyTitle)
msg.description = call.argument(keyDescription)
req.transaction = UUID.randomUUID().toString().replace("-", "")
val sceneIndex = call.argument<Int?>("scene")
req.scene = when (sceneIndex) {
0 -> SendMessageToWX.Req.WXSceneSession
1 -> SendMessageToWX.Req.WXSceneTimeline
2 -> SendMessageToWX.Req.WXSceneFavorite
else -> SendMessageToWX.Req.WXSceneSession
}
}
private fun getFileContentUri(file: File?): String? {
if (file == null || !file.exists())
return null
val contentUri = FileProvider.getUriForFile(context,
"${context.packageName}.fluwxprovider", // 要与`AndroidManifest.xml`里配置的`authorities`一致,假设你的应用包名为com.example.app
file)
// 授权给微信访问路径
context.grantUriPermission("com.tencent.mm", // 这里填微信包名
contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
return contentUri.toString() // contentUri.toString() 即是以"content://"开头的用于共享的路径
}
private val supportFileProvider: Boolean get() = WXAPiHandler.wxApi?.wxAppSupportAPI ?: 0 >= 0x27000D00
private val targetHigherThanN: Boolean get() = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N
val context: Context
val assetFileDescriptor: (String) -> AssetFileDescriptor
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
val job: Job
var permissionHandler: PermissionHandler?
fun onDestroy() = job.cancel()
}
package com.jarvan.fluwx.handlers
import android.Manifest
import android.app.Activity
import android.app.Fragment
import android.os.Build
/***
* Created by mo on 2020/3/27
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class PermissionHandler(private val activity: Activity?) {
private val tag = "Fragment_TAG"
private val fragment: Fragment = Fragment()
fun requestStoragePermission() {
if (oldFragment != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
oldFragment?.requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 12121)
}
} else {
activity?.run {
val ft = fragmentManager.beginTransaction()
ft.add(fragment, tag)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ft.commitNow()
} else {
ft.commit()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
fragment.requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 12121)
}
}
}
}
private val oldFragment get() = activity?.fragmentManager?.findFragmentByTag(tag)
}
\ No newline at end of file
/*
* 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.
*/
package com.jarvan.fluwx.handlers
import android.content.Context
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
object WXAPiHandler {
var wxApi: IWXAPI? = null
private var context: Context? = null
private var registered: Boolean = false
val wxApiRegistered get() = registered
fun setupWxApi(appId: String, context: Context, force: Boolean = true): Boolean {
if (force || !registered) {
setContext(context)
registerWxAPIInternal(appId, context)
}
return registered
}
fun setContext(context: Context?) {
WXAPiHandler.context = context
}
fun registerApp(call: MethodCall, result: MethodChannel.Result) {
if (call.argument<Boolean?>("android") == false) {
return
}
if (wxApi != null) {
result.success(true)
return
}
val appId: String? = call.argument("appId")
if (appId.isNullOrBlank()) {
result.error("invalid app id", "are you sure your app id is correct ?", appId)
return
}
context?.let {
registerWxAPIInternal(appId, it)
}
result.success(registered)
}
fun checkWeChatInstallation(result: MethodChannel.Result) {
if (wxApi == null) {
result.error("Unassigned WxApi", "please config wxapi first", null)
return
} else {
result.success(wxApi?.isWXAppInstalled)
}
}
private fun registerWxAPIInternal(appId: String, context: Context) {
val api = WXAPIFactory.createWXAPI(context.applicationContext, appId)
registered = api.registerApp(appId)
wxApi = api
}
}
\ No newline at end of file
package com.jarvan.fluwx.io
import android.content.Context
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okio.*
import java.io.*
import java.io.IOException
import java.util.*
/***
* Created by mo on 2020/5/13
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
private const val cachePathName = "fluwxSharedData"
internal suspend fun ByteArray.toExternalCacheFile(context: Context, suffix: String): File? {
var file: File? = null
val externalFile = context.externalCacheDir ?: return file
val dir = File(externalFile.absolutePath + File.separator + cachePathName).apply {
if (!exists()) {
mkdirs()
}
}
file = File(dir.absolutePath + File.separator + UUID.randomUUID().toString() + suffix)
return saveToLocal(this, file)
}
internal suspend fun ByteArray.toCacheFile(context: Context, suffix: String): File? {
var file: File? = null
val externalFile = context.cacheDir ?: return file
val dir = File(externalFile.absolutePath + File.separator + cachePathName).apply {
if (!exists()) {
mkdirs()
}
}
file = File(dir.absolutePath + File.separator + UUID.randomUUID().toString() + suffix)
return saveToLocal(this, file)
}
private suspend fun saveToLocal(byteArray: ByteArray, file: File): File? {
return withContext(Dispatchers.IO) {
var sink: BufferedSink? = null
var source: Source? = null
var outputStream: OutputStream? = null
try {
outputStream = FileOutputStream(file)
sink = outputStream.sink().buffer()
source = ByteArrayInputStream(byteArray).source()
sink.writeAll(source)
sink.flush()
} catch (e: IOException) {
Log.w("Fluwx", "failed to create cache files")
} finally {
sink?.close()
source?.close()
outputStream?.close()
}
file
}
}
package com.jarvan.fluwx.io
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okio.*
import top.zibin.luban.Luban
import java.io.*
import java.io.IOException
import java.util.*
import kotlin.math.sqrt
/***
* Created by mo on 2020/3/7
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class ImagesIOIml(override val image: WeChatFile) : ImagesIO {
override suspend fun readByteArray(): ByteArray = image.readByteArray()
override suspend fun compressedByteArray(context: Context, maxSize: Int): ByteArray = withContext(Dispatchers.IO) {
val originalByteArray = readByteArray()
if (originalByteArray.isEmpty())
return@withContext originalByteArray
val originFile = inputStreamToFile(ByteArrayInputStream(originalByteArray))
val compressedFile = Luban
.with(context)
.ignoreBy(maxSize)
.setTargetDir(context.cacheDir.absolutePath)
.get(originFile.absolutePath)
if (compressedFile.length() < maxSize) {
val source = compressedFile.source()
val bufferedSource = source.buffer()
val bytes = bufferedSource.readByteArray()
source.close()
bufferedSource.close()
bytes
} else {
createScaledBitmapWithRatio(compressedFile, maxSize)
}
}
private fun inputStreamToFile(inputStream: InputStream): File {
val file = File.createTempFile(UUID.randomUUID().toString(), image.suffix)
val outputStream: OutputStream = FileOutputStream(file)
val sink = outputStream.sink().buffer()
val source = inputStream.source()
sink.writeAll(source)
source.close()
sink.close()
return file
}
private fun createScaledBitmapWithRatio(file: File, maxSize: Int): ByteArray {
val originBitmap = BitmapFactory.decodeFile(file.absolutePath)
val result: Bitmap? = createScaledBitmapWithRatio(originBitmap, maxSize, true)
result ?: return byteArrayOf()
return bmpToByteArray(result, image.suffix) ?: byteArrayOf()
}
private fun createScaledBitmapWithRatio(bitmap: Bitmap, maxLength: Int, recycle: Boolean): Bitmap? {
var result = bitmap
while (true) {
val ratio = maxLength.toDouble() / result.byteCount
val width = result.width * sqrt(ratio)
val height = result.height * sqrt(ratio)
val tmp = Bitmap.createScaledBitmap(result, width.toInt(), height.toInt(), true)
if (result != bitmap) {
result.recycle()
}
result = tmp
if (result.byteCount < maxLength) {
break
}
}
if (recycle) {
bitmap.recycle()
}
return result
}
private fun bmpToByteArray(bitmap: Bitmap, suffix: String): ByteArray? { // int bytes = bitmap.getByteCount();
val byteArrayOutputStream = ByteArrayOutputStream()
var format = CompressFormat.PNG
if (suffix.toLowerCase(Locale.US) == ".jpg" || suffix.toLowerCase(Locale.US) == ".jpeg") {
format = CompressFormat.JPEG
}
bitmap.compress(format, 100, byteArrayOutputStream)
val inputStream: InputStream = ByteArrayInputStream(byteArrayOutputStream.toByteArray())
var result: ByteArray? = null
bitmap.recycle()
val source = inputStream.source()
val bufferedSource = source.buffer()
try {
result = bufferedSource.readByteArray()
source.close()
bufferedSource.close()
} catch (e: IOException) {
e.printStackTrace()
}
return result
}
}
interface ImagesIO {
val image: WeChatFile
suspend fun readByteArray(): ByteArray
suspend fun compressedByteArray(context: Context, maxSize: Int): ByteArray
}
package com.jarvan.fluwx.io
import android.content.res.AssetFileDescriptor
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.BufferedSource
import okio.buffer
import okio.source
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
/***
* Created by mo on 2020/5/13
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class WeChatFileFile(override val source: Any, override val suffix: String) : WeChatFile {
private var internalSource: File
init {
if (source !is File)
throw IllegalArgumentException("source should be File but it's ${source::class.java.name}")
else
internalSource = source
}
override suspend fun readByteArray(): ByteArray = withContext(Dispatchers.IO) {
var source: BufferedSource? = null
try {
source = internalSource.source().buffer()
val array = source.readByteArray()
array
} catch (e: FileNotFoundException) {
byteArrayOf()
} catch (io: IOException) {
byteArrayOf()
} finally {
source?.close()
}
}
}
private class WeChatAssetFile(override val source: Any, override val suffix: String) : WeChatFile {
private var internalSource: AssetFileDescriptor
init {
if (source !is AssetFileDescriptor)
throw IllegalArgumentException("source should be AssetFileDescriptor but it's ${source::class.java.name}")
else
internalSource = source
}
override suspend fun readByteArray(): ByteArray = withContext(Dispatchers.IO) {
var source: BufferedSource? = null
try {
source = internalSource.createInputStream().source().buffer()
val array = source.readByteArray()
array
} catch (e: FileNotFoundException) {
byteArrayOf()
} catch (io: IOException) {
byteArrayOf()
} finally {
source?.close()
}
}
}
private class WeChatNetworkFile(override val source: Any, override val suffix: String) : WeChatFile {
private var internalSource: String
init {
if (source !is String)
throw IllegalArgumentException("source should be String but it's ${source::class.java.name}")
else
internalSource = source
}
override suspend fun readByteArray(): ByteArray = withContext(Dispatchers.IO) {
val okHttpClient = OkHttpClient.Builder().build()
val request: Request = Request.Builder().url(internalSource).get().build()
try {
val response = okHttpClient.newCall(request).execute()
val responseBody = response.body
if (response.isSuccessful && responseBody != null) {
responseBody.bytes()
} else {
byteArrayOf()
}
} catch (e: IOException) {
Log.w("Fluwx", "reading file from $internalSource failed")
byteArrayOf()
}
}
}
private class WeChatMemoryFile(override val source: Any, override val suffix: String) : WeChatFile {
private var internalSource: ByteArray
init {
if (source !is ByteArray)
throw IllegalArgumentException("source should be String but it's ${source::class.java.name}")
else
internalSource = source
}
override suspend fun readByteArray(): ByteArray = internalSource
}
interface WeChatFile {
val source: Any
val suffix: String
suspend fun readByteArray(): ByteArray
companion object {
// NETWORK,
// ASSET,
// FILE,
// BINARY,
fun createWeChatFile(params: Map<String, Any>, assetFileDescriptor: (String) -> AssetFileDescriptor): WeChatFile {
// Map toMap() => {"source": source, "schema": schema.index, "suffix": suffix};
val suffix = (params["suffix"] as String?) ?: ".jpeg"
return when ((params["schema"] as? Int) ?: 0) {
0 -> WeChatNetworkFile(source = (params["source"] as? String).orEmpty(), suffix = suffix)
1 -> WeChatAssetFile(source = assetFileDescriptor(((params["source"] as? String).orEmpty())), suffix = suffix)
2 -> WeChatFileFile(source = File((params["source"] as? String).orEmpty()), suffix = suffix)
3 -> WeChatMemoryFile(source = (params["source"] as? ByteArray)
?: byteArrayOf(), suffix = suffix)
else -> WeChatNetworkFile(source = (params["source"] as? String).orEmpty(), suffix = suffix)
}
}
}
}
/*
* Copyright (C) 2020 The OpenFlutter Organization
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jarvan.fluwx.wxapi
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.jarvan.fluwx.handlers.FluwxResponseHandler
import com.jarvan.fluwx.handlers.FluwxRequestHandler
import com.jarvan.fluwx.handlers.WXAPiHandler
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
// IWXAPI 是第三方app和微信通信的openapi接口
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
try {
WXAPiHandler.wxApi?.handleIntent(intent, this)
} catch (e: Exception) {
e.printStackTrace()
startSpecifiedActivity(defaultFlutterActivityAction())
finish()
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
try {
WXAPiHandler.wxApi?.handleIntent(intent, this)
} catch (e: Exception) {
e.printStackTrace()
startSpecifiedActivity(defaultFlutterActivityAction())
finish()
}
}
override fun onReq(baseReq: BaseReq) {
// FIXME: 可能是官方的Bug,从微信拉起APP的Intent类型不对,无法跳转回Flutter Activity
// 稳定复现场景:微信版本为7.0.5,小程序SDK为2.7.7
FluwxRequestHandler.onReq(baseReq,this)
}
// 第三方应用发送到微信的请求处理后的响应结果,会回调到该方法
override fun onResp(resp: BaseResp) {
FluwxResponseHandler.handleResponse(resp)
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
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DisablePreviewTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowDisablePreview">true</item>
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="fluwxSharedData" path="fluwxSharedData/"/>
</paths>
\ No newline at end of file
package com.jarvan.fluwx
class FluwxTest {
fun test(){}
}
\ No newline at end of file
## AUTH
The purpose of `sendWeChatAuth` is to get auth code and then get information for WeChat login.
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
sendWeChatAuth(scope: "snsapi_userinfo", state: "wechat_sdk_demo_test");
```
> WHY? I think we shall fetch access_token or user info at backend.
## 登录
`sendWeChatAuth`的目的是为了获取code,拿到了code才能进行微信登录,可以通过[官方文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html)查看具体流程。
```dart
sendWeChatAuth(scope: "snsapi_userinfo", state: "wechat_sdk_demo_test");
```
> 为什么不支持获取用户信息? 我认为获取用户信息应该后端来做,即使没有后端,你也可以在dart层自己实现.
## Basic knowledge
### 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`.
So if you want get the real result you shall do like this:
```dart
fluwx.weChatResponseEventHandler.listen((res) {
if (res is fluwx.WeChatPaymentResponse) {
// do something here
}
});
```
Take a look at subclasses of `BaseWeChatResponse` for help.
> NOTE: If you get `errCode = -1`, please read the WeChatSDK document for help. There are to many cases lead to that.
### Images in WeChat
The are four built-in types of `WeChatImage` in `fluwx`:
```dart
WeChatImage.network(String source, {String suffix});
WeChatImage.file(File source, {String suffix = ".jpeg"});
WeChatImage.asset(String source, {String suffix});
WeChatImage.binary(Uint8List source, {String suffix = ".jpeg"});
```
The priority of `suffix` is highest, `fluwx` will try to read suffix from paths if `suffix` is blank.
The max size of image youcan share to WeChat is `10M`.`Fluwx` wil compress `WeChatImage` itself if it's used as `thumbnail` or `hdImagePath`,
otherwise, it doesn't. However, you'd better compress thumbnail yourself as the result of compression is unpredictable.
## 基础知识
### 微信回调
实际上,像`shareToWeChat` or `payWithWeChat`这种的函数,底层上是调用了原生SDK的`sendRequest`方法,所以他们的返回结果意义不大,他们的返回结果仅仅是`sendRequest`的返回值。
为了获取真实的回调,你应该这样做:
```dart
fluwx.weChatResponseEventHandler.listen((res) {
if (res is fluwx.WeChatPaymentResponse) {
// do something here
}
});
```
> 笔记: 如果你的 `errCode = -1`, 那请阅读微信官方文档,因为-1的原因数不胜数.
### 图片
有四种内置 `WeChatImage`:
```dart
WeChatImage.network(String source, {String suffix});
WeChatImage.file(File source, {String suffix = ".jpeg"});
WeChatImage.asset(String source, {String suffix});
WeChatImage.binary(Uint8List source, {String suffix = ".jpeg"});
```
其中, `suffix` 优先级最高, 如果`suffix`是空白的,`fluwx` 将会尝试从文件路径中读取后缀.
在分享图片的功能,图片不能超过`10M`.如果图片被用作`thumbnail``hdImagePath``Fluwx` 会对 `WeChatImage` 进行压缩,
否则不会压缩. 但是,最好还是自己压缩,因为不保证`fluwx`压缩效果。
## 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`:
```
<action android:name="${applicationId}.FlutterActivity" />
```
If you want to pass `extInfo` to Flutter, you need to add the following code in `MainActivity.kt`:
```kotlin
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
//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 host activity:
```xml
<meta-data
android:name="handleWeChatRequestByFluwx"
android:value="flase" />
```
And then, set `FluwxRequestHandler.customOnReqDelegate` on your own.
\ No newline at end of file
## 从H5启动app
Fluwx 支持从`<wx-open-launch-app>`启动你的app, 并且支持传递`extInfo`给你的app.
对于Android来说,你要在`AndroidManifest.xml`中给你的宿主`Activty`加上一个标签:
```
<action android:name="${applicationId}.FlutterActivity" />
```
如果你想把`extInfo`传给Flutter, 你要在`MainActivity`加上如下代码:
```kotlin
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
//If you didn't configure WxAPI, add the following code
WXAPiHandler.setupWxApi("wxd930ea5d5a258f4f",this)
//Get Ext-Info from Intent.
FluwxRequestHandler.handleRequestInfoFromIntent(intent)
}
```
如果你想自定义你的调用逻辑, 你需要在宿主Activity中加上`<meta-data>`:
```xml
<meta-data
android:name="handleWeChatRequestByFluwx"
android:value="flase" />
```
然后, 自己实现 `FluwxRequestHandler.customOnReqDelegate`.
\ No newline at end of file
## Payment
Calling payment is easy but to make it work isn't not so easy:
```dart
payWithWeChat(
appId: result['appid'],
partnerId: result['partnerid'],
prepayId: result['prepayid'],
packageValue: result['package'],
nonceStr: result['noncestr'],
timeStamp: result['timestamp'],
sign: result['sign'],
);
```
Take a look at [payment document](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1#) for help.
## 支付
调用支付方法很简单,但想成功并不简单:
```dart
payWithWeChat(
appId: result['appid'],
partnerId: result['partnerid'],
prepayId: result['prepayid'],
packageValue: result['package'],
nonceStr: result['noncestr'],
timeStamp: result['timestamp'],
sign: result['sign'],
);
```
更多信息还查看[支付文档](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1#)吧.
## WeChat Not Installed on iOS?
if you have installed WeChat on your iPhone but you still catch an exception called "wechat not installed",just add the following
code to your *info.plist*:
```xml
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
```
## Can't Launch WeChat on Android?
Check your signature please.
## Failed to notify project evalution listener
[Failed to notify project evalution listener](https://www.jianshu.com/p/f74fed94be96)
## Can't receive response after upgrading to 1.0.0 on iOS
There's no need to override `AppDelegate` since `fluwx 1.0.0`. If you have did thad before, please remove
the following code in your `AppDelegate`:
```objective-c
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [WXApi handleOpenURL:url delegate:[FluwxResponseHandler defaultManager]];
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
return [WXApi handleOpenURL:url delegate:[FluwxResponseHandler defaultManager]];
}
```
If you have to override these two functions,make sure you have called the `super`:
```objective-c
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [super application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
return [super application:application openURL:url options:options];
}
```
**!!!!请先看[文档](https://github.com/OpenFlutter/fluwx/blob/master/README_CN.md),再看常见Q&A,再查看issue,自我排查错误,方便你我他。依然无法解决的问题可以加群提问, QQ Group:892398530。!!!!**
## 常见Q&A
#### Fluwx调起失败?
请检查APPID、包名、以及App签名是否一致。debug 和release的签名默认不一样,请注意。
#### Android Flutter编译失败
1、检查Kotlin版本,打开```build.gradle```文件,查看以下配置
```
buildscript {
······
ext.kotlin_version = '1.3.11'
······
}
```
确保项目中使用的Kotlin版本符合要求;
2、检查Android目录下```build.gradle```文件中gradle插件版本:```classpath 'com.android.tools.build:gradle:3.2.1'``````gradle-wrapper.properties```文件中的gradle版本是否匹配:```distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip```,两者的匹配规则见Android官网:[Update Gradle](https://developer.android.com/studio/releases/gradle-plugin.html#updating-gradle)
#### WeChat Not Installed on iOS?
iOS 9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。
受此影响,当你的应用在iOS 9中需要使用微信SDK的相关能力(分享、收藏、支付、登录等)时,需要在“Info.plist”里增加如下代码:
```xml
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
```
#### 如果没有安装微信,微信登录不了,导致iOS审核失败
fluwx提供了检查用户是否安装微信的方法:```isWeChatInstalled()```,iOS使用微信相关功能前,务必先检查微信是否安装。
#### Failed to notify project evalution listener
[Failed to notify project evalution listener](https://www.jianshu.com/p/f74fed94be96)
#### isWeChatInstalled返回false
请查看该 [issue](https://github.com/OpenFlutter/fluwx/issues/34) ,检查```AppDelegate```中配置是否正确。
#### Kotlin报错:XXX is only available since Kotlin 1.3 and cannot be used in Kotlin 1.2
1、请检查IDE安装的Kotlin插件版本是否符合fluwx要求:AS打开设置-->Plugin-->Koltin查看插件版本;
2、请检查项目中使用的Kotlin版本:打开```build.gradle```文件,查看以下配置
```
buildscript {
······
ext.kotlin_version = '1.3.11'
······
}
```
#### listen监听多次调用
请查看该 [issue](https://github.com/OpenFlutter/fluwx/issues/36) 。这个问题是由于listen被多次注册导致的,使用者自己代码的问题,非fluwx导致的,请在合适的时机将listen cancel掉:
```
StreamSubscription<WeChatAuthResponse> _wxlogin;
_wxlogin = fluwx.responseFromAuth.listen((val) {})
@override
void dispose() {
_wxlogin.cancel();
}
```
#### 分享完成或者取消分享后App崩溃
如果你手动注册了```WXEntryActivity```and```WXPayEntryActivity```,请检查```Manifest```中包名是否写对了。
#### IOS编译错误:No such module 'fluwx'
如果项目本身是在Android环境配置的,移到iOS的环境的时候,会出现该问题,请按照正常步骤配置。
#### 支付成功后,按物理按键或手机自动返回商户APP,监听不到返回数据
有人反应会出现```fluwx.responseFromPayment.listen```监听无效,无法获取支付结果,建议可以直接向服务器查询是否成功。
#### iOS报错:Specs satisfying the `fluwx (from `.symlinks/plugins/fluwx/ios`)` dependency were found, but they required a higher minimum deployment target.
请在在pod file里将iOS项目deployment target改到9.0。
#### ResponseType与Dio插件中的命名冲突
使用as的方式导包即可:```import 'package:fluwx/fluwx.dart' as fluwx;```
**!!!!请先看[文档](https://github.com/OpenFlutter/fluwx/blob/master/README_CN.md),再看常见Q&A,再查看issue,自我排查错误,方便你我他。依然无法解决的问题可以加群提问, QQ Group:892398530。!!!!**
## 常见Q&A
[无法拉起微信](#无法拉起微信)
[安卓端编译失败](#安卓端编译失败)
[无法调起支付](#无法调起支付)
[安卓端编译报错AndroidX相关](#安卓端编译报错androidx相关)
[WeChat Not Installed on iOS?](#wechat-not-installed-on-ios)
[没有安装微信,微信登录不了,导致iOS审核失败](#没有安装微信微信登录不了导致ios审核失败)
[Failed to notify project evalution listener](#failed-to-notify-project-evalution-listener)
[isWeChatInstalled返回false](#iswechatinstalled返回false)
[Kotlin报错:XXX is only available since Kotlin x.xx and cannot be used in Kotlin x.xx](#kotlin报错xxx-is-only-available-since-kotlin-xxx-and-cannot-be-used-in-kotlin-xxx)
[listen监听多次调用](#listen监听多次调用)
[分享完成或者取消分享后App崩溃](#分享完成或者取消分享后app崩溃)
[IOS编译错误:No such module 'fluwx'](#ios编译错误no-such-module-fluwx)
[支付成功后,按物理按键或手机自动返回商户APP,监听不到返回数据](#支付成功后按物理按键或手机自动返回商户app监听不到返回数据)
[iOS报错:Specs satisfying the fluwx (from .symlinks/plugins/fluwx/ios) dependency were found, but they required a higher minimum deployment target.](#ios报错specs-satisfying-the-fluwx-from-symlinkspluginsfluwxios-dependency-were-found-but-they-required-a-higher-minimum-deployment-target)
[ResponseType与Dio插件中的命名冲突](#responsetype与dio插件中的命名冲突)
[ShareSDK(分享插件)和Fluwx(微信支付插件)存在冲突](#sharesdk分享插件和fluwx微信支付插件存在冲突)
[图片加载失败?](#图片加载失败)
[iOS registerWxApi 返回 -1](#iosregisterwxapi返回-1)
[分享后,打开微信出现未审核应用](#分享后打开微信出现未审核应用)
[分享怎样知道是成功分享了还是取消了没有分享](#分享怎样知道是成功分享了还是取消了没有分享)
[运行时报错kotlinx相关](#运行时报错kotlinx相关)
[android手机在拉起微信前会弹出选择微信分身页面](#android手机在拉起微信前会弹出选择微信分身页面)
[android代码混淆](#android代码混淆)
### 无法拉起微信?
1. Android 端:
* 请检查 AppId、App签名是否和微信开放平台中填写的一致。
* debug 和 release 的签名默认不一样,请注意。注意签名规则:[issue 89](https://github.com/OpenFlutter/fluwx/issues/89#issuecomment-515948671) ,keystore工具生成的MD5需要去除横杆并且全部转换为小写,然后配置到 ```build.gradle```
* 签名的生成建议使用微信提供的 sign 工具:[签名校验工具](https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk)
* 签名算法请参考微信文档:[签名算法](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3)
2. iOS 端:
* 检查 UniversalLink 是否配置正确,简单的检验方法为:将链接写入备忘录,点击后是否可以跳转至 Safari,且顶部提示打开 App。
* 检查是否将微信加入了白名单,具体参考 example 中 [Info.plist](https://github.com/OpenFlutter/fluwx/blob/master/example/ios/Runner/Info.plist) 中的配置
### 安卓端编译失败
1. 检查Kotlin版本,打开```build.gradle```文件,查看以下配置
```
buildscript {
······
ext.kotlin_version = '1.4.10'
······
}
```
确保项目中使用的 Kotlin 版本符合要求(具体版本号以 [example](https://github.com/OpenFlutter/fluwx/blob/master/example/android/build.gradle) 中为准);
2. 检查 Android 目录下```build.gradle```文件中 gradle 插件版本:```classpath 'com.android.tools.build:gradle:3.6.3'``````gradle-wrapper.properties```文件中的 gradle 版本是否匹配:```distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip```,两者的匹配规则见Android官网:[Update Gradle](https://developer.android.com/studio/releases/gradle-plugin.html#updating-gradle)
### 无法调起支付
1. 先认真阅读 [微信支付业务流程](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3),确保操作没问题;
2. 打印微信返回的错误码,对照下面的表格排查:
| 名称 | 描述 | 解决方案 |
| ----- | ------- | ------------------- |
| 0 | 成功 | 展示成功页面 |
| -1 | 错误 | 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。|
| -2 | 用户取消 | 无需处理。发生场景:用户不支付了,点击取消,返回APP。 |
3. 大部分情况下 errorCode 为 -1,请按照以下两部来进一步检查:
* 检查签名、AppId、商户 ID 等配置是否正确(前两项可以通过检查是否可以成功调用分享来决定);
* 如果确保配置正确,那就只可能是后端生成的订单信息有误,请让后端排查错误,大概率是 AppId 写错了。
### 安卓端编译报错AndroidX相关
如果报错含有 “AndroidX”、“support” 等相关字眼,提示包重复,请将自己的项目升级,支持 AndroidX,具体参考:[Migrating to AndroidX](https://www.kikt.top/posts/flutter/migrate-android-x/)
### WeChat Not Installed on iOS?
iOS 9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。
受此影响,当你的应用在iOS 9中需要使用微信SDK的相关能力(分享、收藏、支付、登录等)时,需要在“Info.plist”里增加如下代码(不会配请参考[example](https://github.com/OpenFlutter/fluwx/blob/master/example/ios/Runner/Info.plist)):
```
xml
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
<string>weixinULAPI</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
```
### 如果没有安装微信,微信登录不了,导致iOS审核失败
fluwx 提供了检查用户是否安装微信的方法:```isWeChatInstalled()```,iOS 使用微信相关功能前,务必先检查微信是否安装,没有安装微信的话请务必隐藏微信相关功能。
### Failed to notify project evalution listener
[Failed to notify project evalution listener](https://www.jianshu.com/p/f74fed94be96)
### isWeChatInstalled返回false
请查看该 [issue 34](https://github.com/OpenFlutter/fluwx/issues/34) ,检查 ```UniversalLink``` 是否配置正确,是否配置了白名单;如果重写了 ```AppDelegate```,请检查配置是否正确。
### Kotlin报错:XXX is only available since Kotlin x.xx and cannot be used in Kotlin x.xx
1. 请检查 IDE 安装的 Kotlin 插件版本是否符合 fluwx 要求:AS 打开"设置-->Plugin-->Koltin"查看插件版本;
2. 请检查项目中使用的 Kotlin 版本:打开 ```build.gradle``` 文件,查看以下配置(具体版本号以 [example](https://github.com/OpenFlutter/fluwx/blob/master/example/android/build.gradle) 为准)
```
buildscript {
······
ext.kotlin_version = '1.3.11'
······
}
```
### listen监听多次调用
请查看该 [issue 36](https://github.com/OpenFlutter/fluwx/issues/36) 。这个问题是由于 listen 被多次注册导致的,使用者自己代码的问题,非 fluwx 导致的,请在合适的时机将 listen cancel 掉:
```
StreamSubscription<WeChatAuthResponse> _wxlogin;
_wxlogin = fluwx.responseFromAuth.listen((val) {})
@override
void dispose() {
_wxlogin.cancel();
}
```
### 分享完成或者取消分享后App崩溃
如果你手动注册了 ```WXEntryActivity``````WXPayEntryActivity```,请检查 ```Manifest``` 中包名是否写对了。
### iOS编译错误:No such module 'fluwx'
如果项目本身是在 Android 环境配置的,移到 iOS 环境的时候,会出现该问题,请按照正常步骤配置。
### 支付成功后,按物理按键或手机自动返回商户APP,监听不到返回数据
有人反应会出现 ```fluwx.responseFromPayment.listen``` 监听无效,无法获取支付结果,建议可以直接向服务器查询是否成功。
### iOS报错:Specs satisfying the `fluwx (from `.symlinks/plugins/fluwx/ios`)` dependency were found, but they required a higher minimum deployment target.
请在在 pod file 里将 iOS 项目 deployment target 改到 9.0。
### ResponseType与Dio插件中的命名冲突
使用as的方式导包即可:```import 'package:fluwx/fluwx.dart' as fluwx;```
### ShareSDK(分享插件)和Fluwx(微信支付插件)存在冲突
1. 将 ShareSDK 的```/ios/sharesdk.podspec```里的 ```s.dependency 'mob_sharesdk/ShareSDKPlatforms/WeChat' ```改为 ``` s.dependency 'mob_sharesdk/ShareSDKPlatforms/WeChatFull'```
2. 删除 fluwx 的```/ios/Lib```里的```libWeChatSDK.a```,在```/ios/fluwx.podspec```里添加```s.dependency 'mob_sharesdk/ShareSDKPlatforms/WeChatFull'```
### 图片加载失败?
1. 检查是否是图片大小过大,过大请压缩;
2. 检查图片路径是否是符合要求的 scheme 形式,具体规则请看:[都支持什么图片](https://github.com/yumi0629/fluwx/blob/master/doc/SHARE_CN.md#%E9%83%BD%E6%94%AF%E6%8C%81%E4%BB%80%E4%B9%88%E5%9B%BE%E7%89%87)
3. 如果是使用的 Asset 图片,请不要将图片放在```assets```文件夹中,可能会读取不到。
### iOS registerWxApi 返回 -1
检查初始化时 APP ID 以及 Universalink 是不是写对了,简单的检验方法为:将链接写入备忘录,点击后是否可以跳转至 Safari,且顶部提示打开 App。
### 分享后,打开微信出现未审核应用
微信自己做的限制,非 fluwx 问题,建议找微信客服
### 分享怎样知道是成功分享了还是取消了没有分享
无法获取。请阅读微信分享文档:[微信App分享功能调整](https://open.weixin.qq.com/cgi-bin/announce?spm=a311a.9588098.0.0&action=getannouncement&key=11534138374cE6li&version=)。现在开始微信SDK不再返回用户是否分享完成事件,取消/成功统一全部返回成功。
### 运行时报错kotlinx相关
检查是否开启混淆;可以参考这个 [issue](https://github.com/OpenFlutter/fluwx/issues/94),请确保宿主 app Kotlin 版本支持 kotlinx。
### android手机在拉起微信前会弹出选择微信分身页面
微信分身是属于系统范畴的问题,与Fluwx无关。
### android代码混淆
请手动给 lib 里的 package 添加混淆规则,具体参考微信文档。
### 参考文章
[iOS-微信SDK更新后需要UniversalLink解决方案](https://juejin.cn/post/6844904051042156551)
[flutter使用fluwx调通微信支付](https://www.jianshu.com/p/caf5b32c4772)
[Flutter ios 使用fluwx配置微信分享](https://www.jianshu.com/p/3d8b2d65e57a)
## Share
Simple and easy:
```dart
shareToWeChat(WeChatShareTextModel("source text", scene: WeChatScene.SESSION));
```
The destination of sharing can be SESSION(default),TIMELINE or FAVORITE.However,mini-program only support SESSION.
```dart
///[WeChatScene.SESSION]会话
///[WeChatScene.TIMELINE]朋友圈
///[WeChatScene.FAVORITE]收藏
enum WeChatScene {
SESSION,
TIMELINE,
FAVORITE
}
```
You can share these models:
- WeChatShareTextModel
- WeChatShareMiniProgramModel
- WeChatShareImageModel
- WeChatShareMusicModel
- WeChatShareVideoModel
- WeChatShareWebPageModel
- WeChatShareFileModel
## 分享
简单:
```dart
shareToWeChat(WeChatShareTextModel("source text", scene: WeChatScene.SESSION));
```
绝大部分分享可以分享到会话,朋友圈,收藏(小程序目前只能分享到会话)。默认分享到会话。
```dart
///[WeChatScene.SESSION]会话
///[WeChatScene.TIMELINE]朋友圈
///[WeChatScene.FAVORITE]收藏
enum WeChatScene {
SESSION,
TIMELINE,
FAVORITE
}
```
支持的分享各类:
- WeChatShareTextModel
- WeChatShareMiniProgramModel
- WeChatShareImageModel
- WeChatShareMusicModel
- WeChatShareVideoModel
- WeChatShareWebPageModel
- WeChatShareFileModel
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
flutter_export_environment.sh
# Web related
lib/generated_plugin_registrant.dart
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
# 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
# fluwx_example
Demonstrates how to use the fluwx plugin.
## Getting Started
This project is a starting point for a Flutter application.
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)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "net.sourceforge.simcpux"
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
flavorDimensions "branch"
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
debug {
signingConfig signingConfigs.debug
}
}
signingConfigs {
debug {
storeFile file("debug.keystore")
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jarvan.fluwx_example">
<!-- Flutter 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"
package="com.jarvan.fluwx_example">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name="io.flutter.app.FlutterApplication"
android:icon="@mipmap/ic_launcher"
android:label="fluwx_example">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="${applicationId}.FlutterActivity" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="handleWeChatRequestByFluwx"
android:value="true" />
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
package com.jarvan.fluwx_example
import androidx.annotation.NonNull;
import com.jarvan.fluwx.handlers.FluwxRequestHandler
import com.jarvan.fluwx.handlers.WXAPiHandler
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
//If you didn't configure WxAPI, add the following code
WXAPiHandler.setupWxApi("wxd930ea5d5a258f4f",this)
//Get Ext-Info from Intent.
FluwxRequestHandler.handleRequestInfoFromIntent(intent)
}
}
<?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:color/white" />
<!-- 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>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jarvan.fluwx_example">
<!-- Flutter 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>
buildscript {
ext.kotlin_version = '1.4.10'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
#Tue Oct 20 17:14:58 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
</dict>
</plist>
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
PODS:
- Flutter (1.0.0)
- fluwx (0.0.1):
- Flutter
- WechatOpenSDK (= 1.8.7.1)
- WechatOpenSDK (1.8.7.1)
DEPENDENCIES:
- Flutter (from `Flutter`)
- fluwx (from `.symlinks/plugins/fluwx/ios`)
SPEC REPOS:
trunk:
- WechatOpenSDK
EXTERNAL SOURCES:
Flutter:
:path: Flutter
fluwx:
:path: ".symlinks/plugins/fluwx/ios"
SPEC CHECKSUMS:
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
fluwx: 07a55ed66bf3a4961e836a2a411b02dcada32902
WechatOpenSDK: 6a4d1436c15b3b5fe2a0bd383f3046010186da44
PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d
COCOAPODS: 1.10.0
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
62B88C18565D88C02906C10C /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27C08EA6C421C7E89069DE6B /* libPods-Runner.a */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
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>"; };
27C08EA6C421C7E89069DE6B /* 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>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; 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>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; 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>"; };
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>"; };
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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
62B88C18565D88C02906C10C /* libPods-Runner.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
3510C167085BA3D3F8540F37 /* Pods */ = {
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;
children = (
27C08EA6C421C7E89069DE6B /* libPods-Runner.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
3510C167085BA3D3F8540F37 /* Pods */,
8513A54653CE3C4FB2C564F0 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
97C146F21CF9000F007C117D /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
E2E8F9B5C8F1E9545B8C092B /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
193B0A2F7BBE7B22CD4D85EA /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
193B0A2F7BBE7B22CD4D85EA /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../Flutter/Flutter.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
E2E8F9B5C8F1E9545B8C092B /* [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;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample1;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample1;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.jarvan.fluwxExample1;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
<?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>
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : FlutterAppDelegate
@end
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>fluwx_example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>wxd930ea5d5a258f4f</string>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
<string>weixinULAPI</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart' as fluwx;
class AuthByQRCodePage extends StatefulWidget {
@override
_AuthByQRCodePageState createState() => _AuthByQRCodePageState();
}
class _AuthByQRCodePageState extends State<AuthByQRCodePage> {
String _status = "status";
Uint8List _image;
@override
void initState() {
super.initState();
// fluwx.onAuthByQRCodeFinished.listen((data) {
// setState(() {
// _status = "errorCode=>${data.errorCode}\nauthCode=>${data.authCode}";
// });
// });
// fluwx.onAuthGotQRCode.listen((image) {
// setState(() {
// _image = image;
// });
// });
//
// fluwx.onQRCodeScanned.listen((scanned) {
// setState(() {
// _status = "scanned";
// });
// });
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AuthByQRCode"),
),
body: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
// fluwx.authByQRCode(
// appId: "wxd930ea5d5a258f4f",
// scope: "noncestr",
// nonceStr: "nonceStr",
// timeStamp: "1417508194",
// signature: "429eaaa13fd71efbc3fd344d0a9a9126835e7303");
},
child: Text("AUTH NOW"),
),
Text(_status),
_qrCode()
],
),
);
}
Widget _qrCode() {
if (_image == null) {
return Container();
} else {
return Image.memory(_image);
}
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class LaunchMiniProgramPage extends StatefulWidget {
@override
_LaunchMiniProgramPageState createState() => _LaunchMiniProgramPageState();
}
class _LaunchMiniProgramPageState extends State<LaunchMiniProgramPage> {
String _result = "无";
@override
void initState() {
super.initState();
weChatResponseEventHandler.listen((res) {
if (res is WeChatLaunchMiniProgramResponse) {
if (mounted) {
setState(() {
_result = "isSuccessful:${res.isSuccessful}";
});
}
}
});
}
@override
void dispose() {
super.dispose();
_result = null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Launch MiniProgrom"),
),
body: Column(
children: <Widget>[
OutlineButton(
onPressed: () {
launchWeChatMiniProgram(username: "gh_d43f693ca31f");
},
child: const Text("Launch MiniProgrom"),
),
const Text("响应结果;"),
Text(_result)
],
),
);
}
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
import 'auth_by_qr_code_page.dart';
import 'launch_mini_program_page.dart';
import 'pay_page.dart';
import 'send_auth.dart';
import 'share_image_page.dart';
import 'share_mini_program.dart';
import 'share_music.dart';
import 'share_text.dart';
import 'share_video_page.dart';
import 'share_web_page.dart';
import 'sign_auto_deduct_page.dart';
import 'subscribe_message_page.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
_initFluwx();
}
_initFluwx() async {
await registerWxApi(
appId: "wxd930ea5d5a258f4f",
doOnAndroid: true,
doOnIOS: true,
universalLink: "https://your.univerallink.com/link/");
var result = await isWeChatInstalled;
print("is installed $result");
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {}
@override
Widget build(BuildContext context) {
return new MaterialApp(
routes: <String, WidgetBuilder>{
"shareText": (context) => ShareTextPage(),
"shareImage": (context) => ShareImagePage(),
"shareWebPage": (context) => ShareWebPagePage(),
"shareMusic": (context) => ShareMusicPage(),
"shareVideo": (context) => ShareVideoPage(),
"sendAuth": (context) => SendAuthPage(),
"shareMiniProgram": (context) => ShareMiniProgramPage(),
"pay": (context) => PayPage(),
"launchMiniProgram": (context) => LaunchMiniProgramPage(),
"subscribeMessage": (ctx) => SubscribeMessagePage(),
"AuthByQRCode": (ctx) => AuthByQRCodePage(),
'AutoDeduct': (ctx) => SignAutoDeductPage(),
},
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: ShareSelectorPage()),
);
}
}
class ShareSelectorPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: new ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("shareText");
},
child: const Text("share text")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("shareImage");
},
child: const Text("share image")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("shareWebPage");
},
child: const Text("share webpage")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("shareMusic");
},
child: const Text("share music")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("shareVideo");
},
child: const Text("share video")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("shareMiniProgram");
},
child: const Text("share mini program")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("sendAuth");
},
child: const Text("send auth")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("pay");
},
child: const Text("pay")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("launchMiniProgram");
},
child: const Text("Launch MiniProgram")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("subscribeMessage");
},
child: const Text("SubscribeMessage")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("AuthByQRCode");
},
child: const Text("AuthByQRCode")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
Navigator.of(context).pushNamed("AutoDeduct");
},
child: const Text("SignAuto-deduct")),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: new OutlineButton(
onPressed: () {
openWeChatApp();
},
child: const Text("Open WeChat App")),
),
],
),
);
}
}
import 'dart:convert';
import 'dart:io' as H;
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart' as fluwx;
class PayPage extends StatefulWidget {
@override
_PayPageState createState() => _PayPageState();
}
class _PayPageState extends State<PayPage> {
String _url = "https://wxpay.wxutil.com/pub_v2/app/app_pay.php";
String _result = "无";
@override
void initState() {
super.initState();
fluwx.weChatResponseEventHandler.listen((res) {
if (res is fluwx.WeChatPaymentResponse) {
setState(() {
_result = "pay :${res.isSuccessful}";
});
}
});
// fluwx.responseFromPayment.listen((data) {
// setState(() {
// _result = "${data.errCode}";
// });
// });
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("pay"),
),
body: Column(
children: <Widget>[
OutlineButton(
onPressed: () async {
var h = H.HttpClient();
h.badCertificateCallback = (cert, String host, int port) {
return true;
};
var request = await h.getUrl(Uri.parse(_url));
var response = await request.close();
var data = await Utf8Decoder().bind(response).join();
Map<String, dynamic> result = json.decode(data);
print(result['appid']);
print(result["timestamp"]);
fluwx.payWithWeChat(
appId: result['appid'].toString(),
partnerId: result['partnerid'].toString(),
prepayId: result['prepayid'].toString(),
packageValue: result['package'].toString(),
nonceStr: result['noncestr'].toString(),
timeStamp: result['timestamp'],
sign: result['sign'].toString(),
)
.then((data) {
print("---》$data");
});
},
child: const Text("pay"),
),
const Text("响应结果;"),
Text(_result)
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart' as fluwx;
class SendAuthPage extends StatefulWidget {
@override
_SendAuthPageState createState() => _SendAuthPageState();
}
class _SendAuthPageState extends State<SendAuthPage> {
String _result = "无";
@override
void initState() {
super.initState();
fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) {
if (res is fluwx.WeChatAuthResponse) {
setState(() {
_result = "state :${res.state} \n code:${res.code}";
});
}
});
}
@override
void dispose() {
super.dispose();
_result = null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Send Auth"),
),
body: Column(
children: <Widget>[
OutlineButton(
onPressed: () {
fluwx
.sendWeChatAuth(
scope: "snsapi_userinfo", state: "wechat_sdk_demo_test")
.then((data) {});
},
child: const Text("send auth"),
),
const Text("响应结果;"),
Text(_result)
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class ShareImagePage extends StatefulWidget {
@override
_ShareImagePageState createState() => _ShareImagePageState();
}
class _ShareImagePageState extends State<ShareImagePage> {
WeChatScene scene = WeChatScene.SESSION;
String _imagePath =
// "http://img-download.pchome.net/download/1k1/3a/3e/ofskcd-s1a.jpg"
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1534614311230&di=b17a892b366b5d002f52abcce7c4eea0&imgtype=0&src=http%3A%2F%2Fimg.mp.sohu.com%2Fupload%2F20170516%2F51296b2673704ae2992d0a28c244274c_th.png";
String _response = "";
WeChatImage source;
WeChatImage thumbnail;
@override
void initState() {
super.initState();
weChatResponseEventHandler.listen((res) {
if (res is WeChatShareResponse) {
setState(() {
_response = "state :${res.isSuccessful}";
});
}
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: const Text("shareImage"),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.share,
color: Colors.white,
),
onPressed: _shareImage)
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: "图片地址(仅限网络)"),
controller: TextEditingController(
text:
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1534614311230&di=b17a892b366b5d002f52abcce7c4eea0&imgtype=0&src=http%3A%2F%2Fimg.mp.sohu.com%2Fupload%2F20170516%2F51296b2673704ae2992d0a28c244274c_th.png"),
onChanged: (value) {
source = WeChatImage.network(value);
},
keyboardType: TextInputType.multiline,
),
TextField(
decoration: InputDecoration(labelText: "缩略地址"),
controller: TextEditingController(text: "//images/logo.png"),
onChanged: (value) {
thumbnail = WeChatImage.asset(value);
},
),
new Row(
children: <Widget>[
const Text("分享至"),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.SESSION,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("会话")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("朋友圈")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("收藏")
],
)
],
),
Text(_response)
],
),
),
);
}
void _shareImage() {
shareToWeChat(WeChatShareImageModel(source, thumbnail: thumbnail));
}
void handleRadioValueChanged(WeChatScene scene) {
setState(() {
this.scene = scene;
});
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class ShareMiniProgramPage extends StatefulWidget {
@override
_ShareMiniProgramPageState createState() => _ShareMiniProgramPageState();
}
class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
WeChatScene scene = WeChatScene.SESSION;
String _webPageUrl = "http://www.qq.com";
String _thumbnail =
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1534614311230&di=b17a892b366b5d002f52abcce7c4eea0&imgtype=0&src=http%3A%2F%2Fimg.mp.sohu.com%2Fupload%2F20170516%2F51296b2673704ae2992d0a28c244274c_th.png";
String _title = "Fluwx";
String _userName = "gh_d43f693ca31f";
String _path = "/pages/media";
String _description = "Fluwx";
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: const Text("ShareMiniProgram"),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.share,
color: Colors.white,
),
onPressed: _share)
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: new ListView(
children: <Widget>[
new TextField(
controller: TextEditingController(text: "http://www.qq.com"),
onChanged: (str) {
_webPageUrl = str;
},
decoration: InputDecoration(labelText: "web page url"),
),
new TextField(
controller: TextEditingController(text: "gh_d43f693ca31f"),
onChanged: (str) {
_userName = str;
},
decoration: InputDecoration(labelText: "user name"),
),
new TextField(
controller: TextEditingController(text: "/pages/media"),
onChanged: (str) {
_path = str;
},
decoration: InputDecoration(labelText: "user name"),
),
new TextField(
controller: TextEditingController(text: "Fluwx"),
onChanged: (str) {
_title = str;
},
decoration: InputDecoration(labelText: "title"),
),
new TextField(
controller: TextEditingController(text: "Fluwx"),
onChanged: (str) {
_description = str;
},
decoration: InputDecoration(labelText: "description"),
),
new TextField(
controller:
TextEditingController(text: "images/logo.png"),
onChanged: (str) {
_thumbnail = str;
},
decoration: InputDecoration(labelText: "thumbnail"),
),
],
),
),
);
}
void _share() {
var model = new WeChatShareMiniProgramModel(
webPageUrl: _webPageUrl,
userName: _userName,
title: _title,
path: _path,
description: _description,
thumbnail: WeChatImage.network(_thumbnail)
);
shareToWeChat(model);
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class ShareMusicPage extends StatefulWidget {
@override
_ShareMusicPageState createState() => _ShareMusicPageState();
}
class _ShareMusicPageState extends State<ShareMusicPage> {
String _musicUrl =
"http://staff2.ustc.edu.cn/~wdw/softdown/index.asp/0042515_05.ANDY.mp3";
String _musicLowBandUrl = "http://www.qq.com";
String _title = "Beyond";
String _description = "A Popular Rock Band From China";
String _thumnail = "images/logo.png";
WeChatScene scene = WeChatScene.SESSION;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: const Text("ShareMusicPage"),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.share,
color: Colors.white,
),
onPressed: _share)
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: new Column(
children: <Widget>[
new TextField(
controller: TextEditingController(
text:
"http://staff2.ustc.edu.cn/~wdw/softdown/index.asp/0042515_05.ANDY.mp3"),
onChanged: (str) {
_musicUrl = str;
},
decoration: InputDecoration(labelText: "music url"),
),
new TextField(
controller: TextEditingController(text: "http://www.qq.com"),
onChanged: (str) {
_musicLowBandUrl = str;
},
decoration: InputDecoration(labelText: "music low band url"),
),
new TextField(
controller: TextEditingController(text: "Beyond"),
onChanged: (str) {
_title = str;
},
decoration: InputDecoration(labelText: "title"),
),
new TextField(
controller:
TextEditingController(text: "A Popular Rock Band From China"),
onChanged: (str) {
_description = str;
},
decoration: InputDecoration(labelText: "description"),
),
new TextField(
controller:
TextEditingController(text: "images/logo.png"),
onChanged: (str) {
_thumnail = str;
},
decoration: InputDecoration(labelText: "thumbnail"),
),
new Row(
children: <Widget>[
const Text("分享至"),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.SESSION,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("会话")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("朋友圈")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("收藏")
],
)
],
)
],
),
),
);
}
void _share() {
var model = WeChatShareMusicModel(
title: _title,
description: _description,
musicUrl: _musicUrl,
scene: scene,
musicLowBandUrl: _musicLowBandUrl,
thumbnail: WeChatImage.network(_thumnail));
shareToWeChat(model);
}
void handleRadioValueChanged(WeChatScene scene) {
setState(() {
this.scene = scene;
});
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class ShareTextPage extends StatefulWidget {
@override
_ShareTextPageState createState() => _ShareTextPageState();
}
class _ShareTextPageState extends State<ShareTextPage> {
String _text = "share text from fluwx";
WeChatScene scene = WeChatScene.SESSION;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: const Text("ShareText"),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.share,
color: Colors.white,
),
onPressed: _shareText)
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: new Column(
children: <Widget>[
new TextField(
controller: TextEditingController(text: "share text from fluwx"),
onChanged: (str) {
_text = str;
},
decoration: InputDecoration(labelText: "TextToShare"),
),
new Row(
children: <Widget>[
const Text("分享至"),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.SESSION,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("会话")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("朋友圈")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("收藏")
],
)
],
)
],
),
),
);
}
void _shareText() {
shareToWeChat(WeChatShareTextModel(_text, scene: scene)).then((data) {
print("-->$data");
});
// sendAuth(WeChatSendAuthModel(scope: "snsapi_userinfo",state: "wechat_sdk_demo_test"));
}
void handleRadioValueChanged(WeChatScene scene) {
setState(() {
this.scene = scene;
});
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class ShareVideoPage extends StatefulWidget {
@override
_ShareMusicPageState createState() => _ShareMusicPageState();
}
class _ShareMusicPageState extends State<ShareVideoPage> {
String _videoUrl = "http://www.qq.com";
String _videoLowBandUrl = "http://www.qq.com";
String _title = "Beyond";
String _description = "A Popular Rock Band From China";
String _thumnail = "images/logo.png";
WeChatScene scene = WeChatScene.SESSION;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: const Text("ShareVideoPage"),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.share,
color: Colors.white,
),
onPressed: _share)
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: new Column(
children: <Widget>[
new TextField(
controller: TextEditingController(
text:
"http://staff2.ustc.edu.cn/~wdw/softdown/index.asp/0042515_05.ANDY.mp3"),
onChanged: (str) {
_videoUrl = str;
},
decoration: InputDecoration(labelText: "video url"),
),
new TextField(
controller: TextEditingController(text: "http://www.qq.com"),
onChanged: (str) {
_videoLowBandUrl = str;
},
decoration: InputDecoration(labelText: "video low band url"),
),
new TextField(
controller: TextEditingController(text: "Beyond"),
onChanged: (str) {
_title = str;
},
decoration: InputDecoration(labelText: "title"),
),
new TextField(
controller:
TextEditingController(text: "A Popular Rock Band From China"),
onChanged: (str) {
_description = str;
},
decoration: InputDecoration(labelText: "description"),
),
new TextField(
controller:
TextEditingController(text: "images/logo.png"),
onChanged: (str) {
_thumnail = str;
},
decoration: InputDecoration(labelText: "thumbnail"),
),
new Row(
children: <Widget>[
const Text("分享至"),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.SESSION,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("会话")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("朋友圈")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("收藏")
],
)
],
)
],
),
),
);
}
void _share() {
var model = new WeChatShareVideoModel(
videoUrl: _videoUrl,
videoLowBandUrl: _videoLowBandUrl,
thumbnail: WeChatImage.network(_thumnail),
description: _description,
scene: this.scene,
title: _title);
shareToWeChat(model);
}
void handleRadioValueChanged(WeChatScene scene) {
setState(() {
this.scene = scene;
});
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class ShareWebPagePage extends StatefulWidget {
@override
ShareWebPagePageState createState() {
return new ShareWebPagePageState();
}
}
class ShareWebPagePageState extends State<ShareWebPagePage> {
String _url = "share text from fluwx";
String _title = "Fluwx";
String _thumnail = "images/logo.png";
WeChatScene scene = WeChatScene.SESSION;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: const Text("ShareWebPage"),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.share,
color: Colors.white,
),
onPressed: _share)
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: new Column(
children: <Widget>[
new TextField(
controller: TextEditingController(
text: "https://github.com/JarvanMo/fluwx"),
onChanged: (str) {
_url = str;
},
decoration: InputDecoration(labelText: "web page"),
),
new TextField(
controller: TextEditingController(text: "Fluwx"),
onChanged: (str) {
_title = str;
},
decoration: InputDecoration(labelText: "thumbnail"),
),
new TextField(
controller:
TextEditingController(text: "images/logo.png"),
onChanged: (str) {
_thumnail = str;
},
decoration: InputDecoration(labelText: "thumbnail"),
),
new Row(
children: <Widget>[
const Text("分享至"),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.SESSION,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("会话")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.TIMELINE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("朋友圈")
],
),
Row(
children: <Widget>[
new Radio<WeChatScene>(
value: WeChatScene.FAVORITE,
groupValue: scene,
onChanged: handleRadioValueChanged),
const Text("收藏")
],
)
],
)
],
),
),
);
}
void _share() {
var model = WeChatShareWebPageModel(
_url,
title: _title,
thumbnail: WeChatImage.network(_thumnail),
scene: scene,
);
shareToWeChat(model);
}
void handleRadioValueChanged(WeChatScene scene) {
setState(() {
this.scene = scene;
});
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class SignAutoDeductPage extends StatefulWidget {
@override
_SignAutoDeductPageState 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> {
TextEditingController appId =
TextEditingController(text: "wx316f9c82e99ac105");
TextEditingController mchId = TextEditingController(text: "");
TextEditingController planId = TextEditingController(text: "");
TextEditingController contractCode = TextEditingController(text: "");
TextEditingController requestSerial = TextEditingController(text: "");
TextEditingController contractDisplayAccount =
TextEditingController(text: "");
TextEditingController notifyUrl = TextEditingController(text: "");
TextEditingController version = TextEditingController(text: "1.0");
TextEditingController sign = TextEditingController(text: "");
TextEditingController timestamp = TextEditingController(text: "");
TextEditingController returnApp = TextEditingController(text: "3");
@override
void initState() {
super.initState();
weChatResponseEventHandler.listen((resp) {
print("resp = ${resp.isSuccessful}");
});
}
@override
void dispose() {
appId.dispose();
mchId.dispose();
planId.dispose();
contractCode.dispose();
contractDisplayAccount.dispose();
requestSerial.dispose();
notifyUrl.dispose();
version.dispose();
sign.dispose();
timestamp.dispose();
returnApp.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SubscribeMessagePage'),
),
body: Container(
child: ListView(
children: <Widget>[
_buildTextField(title: "appId", textEditController: appId),
_buildTextField(title: "mchId", textEditController: mchId),
_buildTextField(title: "planId", textEditController: planId),
_buildTextField(
title: "contractCode", textEditController: contractCode),
_buildTextField(
title: "requestSerial", textEditController: requestSerial),
_buildTextField(
title: "contractDisplayAccount",
textEditController: contractDisplayAccount),
_buildTextField(title: "notifyUrl", textEditController: notifyUrl),
_buildTextField(title: "version", textEditController: version),
_buildTextField(title: "sign", textEditController: sign),
_buildTextField(title: "timestamp", textEditController: timestamp),
_buildTextField(title: "returnApp", textEditController: returnApp),
CupertinoButton(
child: Text('request once sign auto-deduct message'),
onPressed: _signAutoDeduct,
),
],
),
),
);
}
Widget _buildTextField({
String title,
TextEditingController textEditController,
}) {
return TextField(
decoration: InputDecoration(
labelText: title,
),
controller: textEditController,
);
}
void _signAutoDeduct() {
autoDeDuctWeChat(
appId: appId.text ?? "",
mchId: mchId.text ?? "",
planId: planId.text ?? "",
contractCode: contractCode.text ?? "",
requestSerial: requestSerial.text ?? "",
contractDisplayAccount: contractDisplayAccount.text ?? "",
notifyUrl: notifyUrl.text ?? "",
version: version.text ?? "",
sign: sign.text ?? "",
timestamp: timestamp.text ?? "",
returnApp: '3');
}
}
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
class SubscribeMessagePage extends StatefulWidget {
@override
_SubscribeMessagePageState 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)
class _SubscribeMessagePageState extends State<SubscribeMessagePage> {
TextEditingController appId =
TextEditingController(text: "wx316f9c82e99ac105");
TextEditingController scene = TextEditingController(text: "1");
TextEditingController templateId = TextEditingController(
text: "cm_vM2k3IjHcYbkGUeAfL6Fja_7Pgv4Hx_q4tA253Ss");
TextEditingController reserved = TextEditingController(text: "123");
@override
void initState() {
super.initState();
weChatResponseEventHandler.listen((resp) {
print("resp = $resp");
});
}
@override
void dispose() {
appId.dispose();
scene.dispose();
templateId.dispose();
reserved.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SubscribeMessagePage'),
),
body: Container(
child: Column(
children: <Widget>[
_buildTextField(title: "appId", textEditController: appId),
_buildTextField(title: "scene", textEditController: scene),
_buildTextField(
title: "templateId", textEditController: templateId),
_buildTextField(title: "reserved", textEditController: reserved),
FlatButton(
child: Text('request once subscribe message'),
onPressed: _requestSubMsg,
),
],
),
),
);
}
Widget _buildTextField({
String title,
TextEditingController textEditController,
}) {
return TextField(
decoration: InputDecoration(
labelText: title,
),
controller: textEditController,
);
}
void _requestSubMsg() {
subscribeWeChatMsg(
appId: appId.text,
scene: int.tryParse(scene.text) ?? 1,
templateId: templateId.text,
reserved: reserved.text,
);
}
}
name: fluwx_example
description: Demonstrates how to use the fluwx plugin.
publish_to: 'none'
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
characters: ^1.0.0
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
dev_dependencies:
flutter_test:
sdk: flutter
fluwx:
path: ../
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- 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
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
// This is a basic Flutter widget test.
//
// 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
// 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.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx_example/main.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data.startsWith('Running on:'),
),
findsOneWidget,
);
});
}
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/flutter_export_environment.sh
.gradle
\ No newline at end of file
//
// Created by mo on 2020/3/7.
//
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
#import "FluwxPlugin.h"
#import "WXApiRequestHandler.h"
#import <WechatOpenSDK/WechatAuthSDK.h>
@class FluwxStringUtil;
@interface FluwxAuthHandler : NSObject <WechatAuthAPIDelegate>
- (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar methodChannel:(FlutterMethodChannel *)flutterMethodChannel;
- (void)handleAuth:(FlutterMethodCall *)call result:(FlutterResult)result;
- (void)authByQRCode:(FlutterMethodCall *)call result:(FlutterResult)result;
- (void)stopAuthByQRCode:(FlutterMethodCall *)call result:(FlutterResult)result;
- (void)handleAuthByPhoneLogin:(FlutterMethodCall*)call result:(FlutterResult)result;
@end
//
// Created by mo on 2020/3/7.
//
#import "FluwxAuthHandler.h"
@implementation FluwxAuthHandler
WechatAuthSDK *_qrauth;
FlutterMethodChannel *_fluwxMethodChannel = nil;
- (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar methodChannel:(FlutterMethodChannel *)flutterMethodChannel {
self = [super init];
if (self) {
_qrauth = [[WechatAuthSDK alloc] init];
_qrauth.delegate = self;
_fluwxMethodChannel = flutterMethodChannel;
}
return self;
}
- (void)handleAuthByPhoneLogin:(FlutterMethodCall*)call result:(FlutterResult)result{
UIViewController *vc = UIApplication.sharedApplication.keyWindow.rootViewController;
SendAuthReq *authReq = [[SendAuthReq alloc]init];
authReq.scope = call.arguments[@"scope"];
authReq.state = (call.arguments[@"state"] == (id) [NSNull null]) ? nil : call.arguments[@"state"];
[WXApi sendAuthReq:authReq viewController:vc delegate:[FluwxResponseHandler defaultManager] completion:^(BOOL success) {
result(@(success));
}];
}
- (void)handleAuth:(FlutterMethodCall *)call result:(FlutterResult)result {
NSString *openId = call.arguments[@"openId"];
[WXApiRequestHandler sendAuthRequestScope:call.arguments[@"scope"]
State:(call.arguments[@"state"] == (id) [NSNull null]) ? nil : call.arguments[@"state"]
OpenID:(openId == (id) [NSNull null]) ? nil : openId completion:^(BOOL done) {
result(@(done));
}];
}
- (void)authByQRCode:(FlutterMethodCall *)call result:(FlutterResult)result {
NSString *appId = call.arguments[@"appId"];
NSString *scope = call.arguments[@"scope"];
NSString *nonceStr = call.arguments[@"nonceStr"];
NSString *timeStamp = call.arguments[@"timeStamp"];
NSString *signature = call.arguments[@"signature"];
NSString *schemeData = (call.arguments[@"schemeData"] == (id) [NSNull null]) ? nil : call.arguments[@"schemeData"];
BOOL done = [_qrauth Auth:appId nonceStr:nonceStr timeStamp:timeStamp scope:scope signature:signature schemeData:schemeData];
result(@(done));
}
- (void)stopAuthByQRCode:(FlutterMethodCall *)call result:(FlutterResult)result {
BOOL done = [_qrauth StopAuth];
result(@(done));
}
- (void)onQrcodeScanned {
[_fluwxMethodChannel invokeMethod:@"onQRCodeScanned" arguments:@{@"errCode": @0}];
}
- (void)onAuthGotQrcode:(UIImage *)image {
NSData *imageData = UIImagePNGRepresentation(image);
// if (imageData == nil) {
// imageData = UIImageJPEGRepresentation(image, 1);
// }
[_fluwxMethodChannel invokeMethod:@"onAuthGotQRCode" arguments:@{@"errCode": @0, @"qrCode": imageData}];
}
- (void)onAuthFinish:(int)errCode AuthCode:(nullable NSString *)authCode {
NSDictionary *errorCode = @{@"errCode": @(errCode)};
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:errorCode];
if (authCode != nil) {
result[@"authCode"] = authCode;
}
[_fluwxMethodChannel invokeMethod:@"onAuthByQRCodeFinished" arguments:result];
}
@end
#import "FluwxPlugin.h"
#import "FluwxResponseHandler.h"
#import "FluwxStringUtil.h"
#import "FluwxAuthHandler.h"
#import "FluwxShareHandler.h"
@implementation FluwxPlugin
FluwxAuthHandler *_fluwxAuthHandler;
FluwxShareHandler *_fluwxShareHandler;
BOOL handleOpenURLByFluwx = YES;
FlutterMethodChannel *channel = nil;
+ (void)registerWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
#if TARGET_OS_IPHONE
if (channel == nil) {
#endif
channel = [FlutterMethodChannel
methodChannelWithName:@"com.jarvanmo/fluwx"
binaryMessenger:[registrar messenger]];
FluwxPlugin *instance = [[FluwxPlugin alloc] initWithRegistrar:registrar methodChannel:channel];
[registrar addMethodCallDelegate:instance channel:channel];
[[FluwxResponseHandler defaultManager] setMethodChannel:channel];
[registrar addApplicationDelegate:instance];
#if TARGET_OS_IPHONE
}
#endif
}
- (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar methodChannel:(FlutterMethodChannel *)flutterMethodChannel {
self = [super init];
if (self) {
_fluwxAuthHandler = [[FluwxAuthHandler alloc] initWithRegistrar:registrar methodChannel:flutterMethodChannel];
_fluwxShareHandler = [[FluwxShareHandler alloc] initWithRegistrar:registrar];
}
return self;
}
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
if ([@"registerApp" isEqualToString:call.method]) {
[self registerApp:call result:result];
} else if ([@"isWeChatInstalled" isEqualToString:call.method]) {
[self checkWeChatInstallation:call result:result];
} else if ([@"sendAuth" isEqualToString:call.method]) {
[_fluwxAuthHandler handleAuth:call result:result];
} else if ([@"authByQRCode" isEqualToString:call.method]) {
[_fluwxAuthHandler authByQRCode:call result:result];
} else if ([@"stopAuthByQRCode" isEqualToString:call.method]) {
[_fluwxAuthHandler stopAuthByQRCode:call result:result];
} else if ([@"openWXApp" isEqualToString:call.method]) {
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]) {
[self handleLaunchMiniProgram:call result:result];
} else if ([@"subscribeMsg" isEqualToString:call.method]) {
[self handleSubscribeWithCall:call result:result];
} else if ([@"autoDeduct" isEqualToString:call.method]) {
[self handleAutoDeductWithCall:call result:result];
}else if([@"authByPhoneLogin" isEqualToString:call.method]){
[_fluwxAuthHandler handleAuthByPhoneLogin:call result:result];
} else if ([call.method hasPrefix:@"share"]) {
[_fluwxShareHandler handleShare:call result:result];
} else {
result(FlutterMethodNotImplemented);
}
}
- (void)registerApp:(FlutterMethodCall *)call result:(FlutterResult)result {
NSNumber* doOnIOS =call.arguments[@"iOS"];
if (![doOnIOS boolValue]) {
result(@NO);
return;
}
NSString *appId = call.arguments[@"appId"];
if ([FluwxStringUtil isBlank:appId]) {
result([FlutterError errorWithCode:@"invalid app id" message:@"are you sure your app id is correct ? " details:appId]);
return;
}
NSString *universalLink = call.arguments[@"universalLink"];
if ([FluwxStringUtil isBlank:universalLink]) {
result([FlutterError errorWithCode:@"invalid universal link" message:@"are you sure your universal link is correct ? " details:universalLink]);
return;
}
BOOL isWeChatRegistered = [WXApi registerApp:appId universalLink:universalLink];
result(@(isWeChatRegistered));
}
- (void)checkWeChatInstallation:(FlutterMethodCall *)call result:(FlutterResult)result {
result(@([WXApi isWXAppInstalled]));
}
- (void)handlePayment:(FlutterMethodCall *)call result:(FlutterResult)result {
NSNumber *timestamp = call.arguments[@"timeStamp"];
NSString *partnerId = call.arguments[@"partnerId"];
NSString *prepayId = call.arguments[@"prepayId"];
NSString *packageValue = call.arguments[@"packageValue"];
NSString *nonceStr = call.arguments[@"nonceStr"];
UInt32 timeStamp = [timestamp unsignedIntValue];
NSString *sign = call.arguments[@"sign"];
[WXApiRequestHandler sendPayment:call.arguments[@"appId"]
PartnerId:partnerId
PrepayId:prepayId
NonceStr:nonceStr
Timestamp:timeStamp
Package:packageValue
Sign:sign completion:^(BOOL done) {
result(@(done));
}];
}
- (void)handleHongKongWalletPayment:(FlutterMethodCall *)call result:(FlutterResult)result {
NSString *partnerId = call.arguments[@"prepayId"];
WXOpenBusinessWebViewReq *req = [[WXOpenBusinessWebViewReq alloc] init];
req.businessType = 1;
NSMutableDictionary *queryInfoDic = [NSMutableDictionary dictionary];
[queryInfoDic setObject:partnerId forKey:@"token"];
req.queryInfoDic = queryInfoDic;
[WXApi sendReq:req completion:^(BOOL done) {
result(@(done));
}];
}
- (void)handleLaunchMiniProgram:(FlutterMethodCall *)call result:(FlutterResult)result {
NSString *userName = call.arguments[@"userName"];
NSString *path = call.arguments[@"path"];
// WXMiniProgramType *miniProgramType = call.arguments[@"miniProgramType"];
NSNumber *typeInt = call.arguments[@"miniProgramType"];
WXMiniProgramType miniProgramType = WXMiniProgramTypeRelease;
if ([typeInt isEqualToNumber:@1]) {
miniProgramType = WXMiniProgramTypeTest;
} else if ([typeInt isEqualToNumber:@2]) {
miniProgramType = WXMiniProgramTypePreview;
}
[WXApiRequestHandler launchMiniProgramWithUserName:userName
path:path
type:miniProgramType completion:^(BOOL done) {
result(@(done));
}];
}
- (void)handleSubscribeWithCall:(FlutterMethodCall *)call result:(FlutterResult)result {
NSDictionary *params = call.arguments;
NSString *appId = [params valueForKey:@"appId"];
int scene = [[params valueForKey:@"scene"] intValue];
NSString *templateId = [params valueForKey:@"templateId"];
NSString *reserved = [params valueForKey:@"reserved"];
WXSubscribeMsgReq *req = [WXSubscribeMsgReq new];
req.scene = (UInt32) scene;
req.templateId = templateId;
req.reserved = reserved;
req.openID = appId;
[WXApi sendReq:req completion:^(BOOL done) {
result(@(done));
}];
}
- (void)handleAutoDeductWithCall:(FlutterMethodCall *)call result:(FlutterResult)result {
NSMutableDictionary *paramsFromDart = [NSMutableDictionary dictionaryWithDictionary:call.arguments];
[paramsFromDart removeObjectForKey:@"businessType"];
WXOpenBusinessWebViewReq *req = [[WXOpenBusinessWebViewReq alloc] init];
NSNumber *businessType = call.arguments[@"businessType"];
req.businessType = [businessType unsignedIntValue];
req.queryInfoDic = paramsFromDart;
[WXApi sendReq:req completion:^(BOOL done) {
result(@(done));
}];
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [WXApi handleOpenURL:url delegate:[FluwxResponseHandler defaultManager]];
}
// NOTE: 9.0以后使用新API接口
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options {
return [WXApi handleOpenURL:url delegate:[FluwxResponseHandler defaultManager]];
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nonnull))restorationHandler{
return [WXApi handleOpenUniversalLink:userActivity delegate:[FluwxResponseHandler defaultManager]];
}
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity API_AVAILABLE(ios(13.0)){
[WXApi handleOpenUniversalLink:userActivity delegate:[FluwxResponseHandler defaultManager]];
}
- (BOOL)handleOpenURL:(NSNotification *)aNotification {
if (handleOpenURLByFluwx) {
NSString *aURLString = [aNotification userInfo][@"url"];
NSURL *aURL = [NSURL URLWithString:aURLString];
return [WXApi handleOpenURL:aURL delegate:[FluwxResponseHandler defaultManager]];
} else {
return NO;
}
}
@end
//
// Created by mo on 2020/3/7.
//
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
#import <Flutter/Flutter.h>
#import <WechatOpenSDK/WXApi.h>
#import <WechatOpenSDK/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 <WechatOpenSDK/WXApiObject.h>
#import "FluwxResponseHandler.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;
}
#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]]) {
if (_delegate
&& [_delegate respondsToSelector:@selector(managerDidRecvChooseInvoiceResponse:)]) {
WXChooseInvoiceResp *chooseInvoiceResp = (WXChooseInvoiceResp *) resp;
[_delegate managerDidRecvChooseInvoiceResponse:chooseInvoiceResp];
}
} else if ([resp isKindOfClass:[WXSubscribeMsgResp class]]) {
if ([_delegate respondsToSelector:@selector(managerDidRecvSubscribeMsgResponse:)]) {
[_delegate managerDidRecvSubscribeMsgResponse:(WXSubscribeMsgResp *) resp];
}
WXSubscribeMsgResp *subscribeMsgResp = (WXSubscribeMsgResp *) resp;
NSMutableDictionary *result = [NSMutableDictionary dictionary];
if(subscribeMsgResp.openId != nil){
result[@"openid"] = subscribeMsgResp.openId;
}
if(subscribeMsgResp.openId != nil){
result[@"templateId"] = subscribeMsgResp.templateId;
}
if(subscribeMsgResp.openId != nil){
result[@"action"] = subscribeMsgResp.action;
}
if(subscribeMsgResp.openId != nil){
result[@"reserved"] = subscribeMsgResp.reserved;
}
if(subscribeMsgResp.openId != nil){
result[@"scene"] = @(subscribeMsgResp.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),
@"returnKey": payResp.returnKey == nil ? @"" : payResp.returnKey,
};
[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": businessResp.result,
@"businessType": @(businessResp.businessType),
};
[fluwxMethodChannel invokeMethod:@"onWXOpenBusinessWebviewResponse" 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;
NSDictionary *result = @{
@"extMsg": launchFromWXReq.message.messageExt
};
[fluwxMethodChannel invokeMethod:@"onWXShowMessageFromWX" arguments:result];
}
@end
//
// Created by mo on 2020/3/7.
//
#import "FluwxShareHandler.h"
#import "WXApiRequestHandler.h"
#import "FluwxStringUtil.h"
#import "NSStringWrapper.h"
#import "ThumbnailHelper.h"
@implementation FluwxShareHandler
NSString *const fluwxKeyTitle = @"title";
NSString *const fluwxKeyImage = @ "image";
NSString *const fluwxKeyImageData = @ "imageData";
NSString *const fluwxKeyThumbnail = @"thumbnail";
NSString *const fluwxKeyDescription = @"description";
NSString *const fluwxKeyPackage = @"?package=";
NSString *const fluwxKeyMessageExt = @"messageExt";
NSString *const fluwxKeyMediaTagName = @"mediaTagName ";
NSString *const fluwxKeyMessageAction = @"messageAction";
NSString *const fluwxKeyScene = @"scene";
NSString *const fluwxKeyTimeline = @"timeline";
NSString *const fluwxKeySession = @"session";
NSString *const fluwxKeyFavorite = @"favorite";
NSString *const fluwxKeyCompressThumbnail = @"compressThumbnail";
NSString *const keySource = @"source";
NSString *const keySuffix = @"suffix";
CGFloat thumbnailWidth;
NSUInteger defaultThumbnailSize = 32 * 1024;
NSObject <FlutterPluginRegistrar> *_fluwxRegistrar;
- (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
self = [super init];
if (self) {
_fluwxRegistrar = registrar;
thumbnailWidth = 150;
}
return self;
}
- (void)handleShare:(FlutterMethodCall *)call result:(FlutterResult)result {
if ([@"shareText" isEqualToString:call.method]) {
[self shareText:call result:result];
} else if ([@"shareImage" isEqualToString:call.method]) {
[self shareImage:call result:result];
} else if ([@"shareWebPage" isEqualToString:call.method]) {
[self shareWebPage:call result:result];
} else if ([@"shareMusic" isEqualToString:call.method]) {
[self shareMusic:call result:result];
} else if ([@"shareVideo" isEqualToString:call.method]) {
[self shareVideo:call result:result];
} else if ([@"shareMiniProgram" isEqualToString:call.method]) {
[self shareMiniProgram:call result:result];
} else if ([@"shareFile" isEqualToString:call.method]) {
[self shareFile:call result:result];
}
}
- (void)shareText:(FlutterMethodCall *)call result:(FlutterResult)result {
NSString *text = call.arguments[@"source"];
NSNumber *scene = call.arguments[fluwxKeyScene];
[WXApiRequestHandler sendText:text InScene:[self intToWeChatScene:scene] completion:^(BOOL done) {
result(@(done));
}];
}
- (void)shareImage:(FlutterMethodCall *)call result:(FlutterResult)result {
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
NSDictionary *sourceImage = call.arguments[keySource];
NSData *sourceImageData = [self getNsDataFromWeChatFile:sourceImage];
UIImage *thumbnailImage = [self getCommonThumbnail:call];
UIImage *realThumbnailImage;
if (thumbnailImage == nil) {
NSString *suffix = sourceImage[@"suffix"];
BOOL isPNG = [self isPNG:suffix];
BOOL compress = call.arguments[fluwxKeyCompressThumbnail];
realThumbnailImage = [self getThumbnailFromNSData:sourceImageData size:defaultThumbnailSize isPNG:isPNG compress:compress];
} else {
realThumbnailImage = thumbnailImage;
}
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *scene = call.arguments[fluwxKeyScene];
[WXApiRequestHandler sendImageData:sourceImageData
TagName:call.arguments[fluwxKeyMediaTagName]
MessageExt:call.arguments[fluwxKeyMessageExt]
Action:call.arguments[fluwxKeyMessageAction]
ThumbImage:realThumbnailImage
InScene:[self intToWeChatScene:scene]
title:call.arguments[fluwxKeyTitle]
description:call.arguments[fluwxKeyDescription]
completion:^(BOOL done) {
result(@(done));
}
];
});
});
}
- (void)shareWebPage:(FlutterMethodCall *)call result:(FlutterResult)result {
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
UIImage *thumbnailImage = [self getCommonThumbnail:call];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *webPageUrl = call.arguments[@"webPage"];
NSNumber *scene = call.arguments[fluwxKeyScene];
[WXApiRequestHandler sendLinkURL:webPageUrl
TagName:call.arguments[fluwxKeyMediaTagName]
Title:call.arguments[fluwxKeyTitle]
Description:call.arguments[fluwxKeyDescription]
ThumbImage:thumbnailImage
MessageExt:call.arguments[fluwxKeyMessageExt]
MessageAction:call.arguments[fluwxKeyMessageAction]
InScene:[self intToWeChatScene:scene]
completion:^(BOOL done) {
result(@(done));
}];
});
});
}
- (void)shareMusic:(FlutterMethodCall *)call result:(FlutterResult)result {
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
UIImage *thumbnailImage = [self getCommonThumbnail:call];
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *scene = call.arguments[fluwxKeyScene];
[WXApiRequestHandler sendMusicURL:call.arguments[@"musicUrl"]
dataURL:call.arguments[@"musicDataUrl"]
MusicLowBandUrl:call.arguments[@"musicLowBandUrl"]
MusicLowBandDataUrl:call.arguments[@"musicLowBandDataUrl"]
Title:call.arguments[fluwxKeyTitle]
Description:call.arguments[fluwxKeyDescription]
ThumbImage:thumbnailImage
MessageExt:call.arguments[fluwxKeyMessageExt]
MessageAction:call.arguments[fluwxKeyMessageAction]
TagName:call.arguments[fluwxKeyMediaTagName]
InScene:[self intToWeChatScene:scene]
completion:^(BOOL done) {
result(@(done));
}
];
});
});
}
- (void)shareVideo:(FlutterMethodCall *)call result:(FlutterResult)result {
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
UIImage *thumbnailImage = [self getCommonThumbnail:call];
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *scene = call.arguments[fluwxKeyScene];
[WXApiRequestHandler sendVideoURL:call.arguments[@"videoUrl"]
VideoLowBandUrl:call.arguments[@"videoLowBandUrl"]
Title:call.arguments[fluwxKeyTitle]
Description:call.arguments[fluwxKeyDescription]
ThumbImage:thumbnailImage
MessageExt:call.arguments[fluwxKeyMessageExt]
MessageAction:call.arguments[fluwxKeyMessageAction]
TagName:call.arguments[fluwxKeyMediaTagName]
InScene:[self intToWeChatScene:scene]
completion:^(BOOL done) {
result(@(done));
}];
});
});
}
- (void)shareFile:(FlutterMethodCall *)call result:(FlutterResult)result {
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
NSDictionary *sourceFile = call.arguments[keySource];
UIImage *thumbnailImage = [self getCommonThumbnail:call];
NSString *fileExtension;
NSString *suffix = sourceFile[keySuffix];
fileExtension = suffix;
if ([suffix hasPrefix:@"."]) {
NSRange range = NSMakeRange(0, 1);
fileExtension = [suffix stringByReplacingCharactersInRange:range withString:@""];
}
NSData *data = [self getNsDataFromWeChatFile:sourceFile];
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *scene = call.arguments[fluwxKeyScene];
[WXApiRequestHandler sendFileData:data
fileExtension:fileExtension
Title:call.arguments[fluwxKeyTitle]
Description:call.arguments[fluwxKeyDescription]
ThumbImage:thumbnailImage
InScene:[self intToWeChatScene:scene]
completion:^(BOOL success) {
result(@(success));
}];
});
});
}
- (void)shareMiniProgram:(FlutterMethodCall *)call result:(FlutterResult)result {
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
UIImage *thumbnailImage = [self getCommonThumbnail:call];
NSData *hdImageData = nil;
NSDictionary *hdImagePath = call.arguments[@"hdImagePath"];
if (hdImagePath != (id) [NSNull null]) {
NSData *imageData = [self getNsDataFromWeChatFile:hdImagePath];
NSString *suffix = hdImagePath[@"suffix"];
BOOL isPNG = [self isPNG:suffix];
BOOL compress = call.arguments[fluwxKeyCompressThumbnail];
UIImage *uiImage = [self getThumbnailFromNSData:imageData size:120 * 1024 isPNG:isPNG compress:compress];
if (isPNG) {
hdImageData = UIImagePNGRepresentation(uiImage);
} else {
hdImageData = UIImageJPEGRepresentation(uiImage, 1);
}
}
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *scene = call.arguments[fluwxKeyScene];
NSNumber *typeInt = call.arguments[@"miniProgramType"];
WXMiniProgramType miniProgramType = WXMiniProgramTypeRelease;
if ([typeInt isEqualToNumber:@1]) {
miniProgramType = WXMiniProgramTypeTest;
} else if ([typeInt isEqualToNumber:@2]) {
miniProgramType = WXMiniProgramTypePreview;
}
[WXApiRequestHandler sendMiniProgramWebpageUrl:call.arguments[@"webPageUrl"]
userName:call.arguments[@"userName"]
path:call.arguments[@"path"]
title:call.arguments[fluwxKeyTitle]
Description:call.arguments[fluwxKeyDescription]
ThumbImage:thumbnailImage
hdImageData:hdImageData
withShareTicket:[call.arguments[@"withShareTicket"] boolValue]
miniProgramType:miniProgramType
MessageExt:call.arguments[fluwxKeyMessageExt]
MessageAction:call.arguments[fluwxKeyMessageAction]
TagName:call.arguments[fluwxKeyMediaTagName]
InScene:[self intToWeChatScene:scene]
completion:^(BOOL done) {
result(@(done));
}
];
});
});
}
- (UIImage *)getCommonThumbnail:(FlutterMethodCall *)call {
NSDictionary *thumbnail = call.arguments[fluwxKeyThumbnail];
if (thumbnail == nil || thumbnail == (id) [NSNull null]) {
return nil;
}
NSString *suffix = thumbnail[@"suffix"];
NSNumber* compress = call.arguments[fluwxKeyCompressThumbnail];
NSData *thumbnailData = [self getNsDataFromWeChatFile:thumbnail];
UIImage *thumbnailImage = [self getThumbnailFromNSData:thumbnailData size:defaultThumbnailSize isPNG:[self isPNG:suffix] compress:[compress boolValue]];
return thumbnailImage;
}
//enum ImageSchema {
// NETWORK,
// ASSET,
// FILE,
// BINARY,
//}
- (NSData *)getNsDataFromWeChatFile:(NSDictionary *)weChatFile {
NSNumber *schema = weChatFile[@"schema"];
if ([schema isEqualToNumber:@0]) {
NSString *source = weChatFile[keySource];
NSURL *imageURL = [NSURL URLWithString:source];
//下载图片
return [NSData dataWithContentsOfURL:imageURL];
} else if ([schema isEqualToNumber:@1]) {
NSString *source = weChatFile[keySource];
return [NSData dataWithContentsOfFile:[self readFileFromAssets:source]];
} else if ([schema isEqualToNumber:@2]) {
NSString *source = weChatFile[keySource];
return [NSData dataWithContentsOfFile:source];
} else if ([schema isEqualToNumber:@3]) {
FlutterStandardTypedData *imageData = weChatFile[@"source"];
return imageData.data;
} else {
return nil;
}
}
- (UIImage *)getThumbnailFromNSData:(NSData *)data size:(NSUInteger)size isPNG:(BOOL)isPNG compress:(BOOL)compress{
UIImage *uiImage = [UIImage imageWithData:data];
if(compress)
return [ThumbnailHelper compressImage:uiImage toByte:size isPNG:isPNG];
else
return uiImage;
}
- (NSString *)readFileFromAssets:(NSString *)imagePath {
NSArray *array = [self formatAssets:imagePath];
NSString *key;
if ([FluwxStringUtil isBlank:array[1]]) {
key = [_fluwxRegistrar lookupKeyForAsset:array[0]];
} else {
key = [_fluwxRegistrar lookupKeyForAsset:array[0] fromPackage:array[1]];
}
return [[NSBundle mainBundle] pathForResource:key ofType:nil];
}
- (NSArray *)formatAssets:(NSString *)originPath {
NSString *path = nil;
NSString *packageName = @"";
NSString *pathWithoutSchema = originPath;
NSInteger indexOfPackage = [pathWithoutSchema lastIndexOfString:@"?package="];
if (indexOfPackage != JavaNotFound) {
path = [pathWithoutSchema substringFromIndex:0 toIndex:indexOfPackage];
NSInteger begin = indexOfPackage + [fluwxKeyPackage length];
packageName = [pathWithoutSchema substringFromIndex:begin toIndex:[pathWithoutSchema length]];
} else {
path = pathWithoutSchema;
}
return @[path, packageName];
}
- (BOOL)isPNG:(NSString *)suffix {
return [@".png" equals:suffix];
}
- (enum WXScene)intToWeChatScene:(NSNumber *)value {
// enum WeChatScene { SESSION, TIMELINE, FAVORITE }
if ([value isEqual: @0]) {
return WXSceneSession;
} else if ([value isEqual: @1]) {
return WXSceneTimeline;
} else if ([value isEqual: @2]) {
return WXSceneFavorite;
} else {
return WXSceneSession;
}
}
@end
//
// Created by mo on 2020/3/7.
//
#import <Foundation/Foundation.h>
@interface FluwxStringUtil : NSObject
+ (BOOL)isBlank:(NSString *)string;
+ (NSString *)nilToEmpty:(NSString *)string;
@end
\ No newline at end of file
//
// Created by mo on 2020/3/7.
//
#import "FluwxStringUtil.h"
@implementation FluwxStringUtil
+ (BOOL)isBlank:(NSString *)string {
if (string == nil) {
return YES;
}
if ([string isKindOfClass:[NSNull class]]) {
return YES;
}
return [[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0;
}
+ (NSString *)nilToEmpty:(NSString *)string {
return string == nil ? @"" : string;
}
@end
\ No newline at end of file
//
// Created by mo on 2020/3/8.
//
#import <Foundation/Foundation.h>
#define JavaNotFound -1
@interface NSString (Wrapper)
/** Return the char value at the specified index. */
- (unichar)charAt:(int)index;
/**
* Compares two strings lexicographically.
* the value 0 if the argument string is equal to this string;
* a value less than 0 if this string is lexicographically less than the string argument;
* and a value greater than 0 if this string is lexicographically greater than the string argument.
*/
- (int)compareTo:(NSString *)anotherString;
- (int)compareToIgnoreCase:(NSString *)str;
- (BOOL)contains:(NSString *)str;
- (BOOL)startsWith:(NSString *)prefix;
- (BOOL)endsWith:(NSString *)suffix;
- (BOOL)equals:(NSString *)anotherString;
- (BOOL)equalsIgnoreCase:(NSString *)anotherString;
- (int)indexOfChar:(unichar)ch;
- (int)indexOfChar:(unichar)ch fromIndex:(int)index;
- (int)indexOfString:(NSString *)str;
- (int)indexOfString:(NSString *)str fromIndex:(int)index;
- (int)lastIndexOfChar:(unichar)ch;
- (int)lastIndexOfChar:(unichar)ch fromIndex:(int)index;
- (int)lastIndexOfString:(NSString *)str;
- (int)lastIndexOfString:(NSString *)str fromIndex:(int)index;
- (NSString *)substringFromIndex:(NSInteger)beginIndex
toIndex:(NSInteger)endIndex;
- (NSString *)toLowerCase;
- (NSString *)toUpperCase;
- (NSString *)trim;
- (NSString *)replaceAll:(NSString *)origin with:(NSString *)replacement;
- (NSArray *)split:(NSString *)separator;
@end
//
// Created by mo on 2020/3/8.
//
#import "NSStringWrapper.h"
@implementation NSString (Wrapper)
/** Java-like method. Returns the char value at the specified index. */
- (unichar)charAt:(int)index {
return [self characterAtIndex:index];
}
/**
* Java-like method. Compares two strings lexicographically.
* the value 0 if the argument string is equal to this string;
* a value less than 0 if this string is lexicographically less than the string argument;
* and a value greater than 0 if this string is lexicographically greater than the string argument.
*/
- (int)compareTo:(NSString *)anotherString {
return (int)[self compare:anotherString];
}
/** Java-like method. Compares two strings lexicographically, ignoring case differences. */
- (int)compareToIgnoreCase:(NSString *)str {
return (int)[self compare:str options:NSCaseInsensitiveSearch];
}
/** Java-like method. Returns true if and only if this string contains the specified sequence of char values. */
- (BOOL)contains:(NSString *)str {
NSRange range = [self rangeOfString:str];
return (range.location != NSNotFound);
}
- (BOOL)startsWith:(NSString *)prefix {
return [self hasPrefix:prefix];
}
- (BOOL)endsWith:(NSString *)suffix {
return [self hasSuffix:suffix];
}
- (BOOL)equals:(NSString *)anotherString {
return [self isEqualToString:anotherString];
}
- (BOOL)equalsIgnoreCase:(NSString *)anotherString {
return [[self toLowerCase] equals:[anotherString toLowerCase]];
}
- (int)indexOfChar:(unichar)ch {
return [self indexOfChar:ch fromIndex:0];
}
- (int)indexOfChar:(unichar)ch fromIndex:(int)index {
int len = (int)self.length;
for (int i = index; i < len; ++i) {
if (ch == [self charAt:i]) {
return i;
}
}
return JavaNotFound;
}
- (int)indexOfString:(NSString *)str {
NSRange range = [self rangeOfString:str];
if (range.location == NSNotFound) {
return JavaNotFound;
}
return (int)range.location;
}
- (int)indexOfString:(NSString *)str fromIndex:(int)index {
NSRange fromRange = NSMakeRange(index, self.length - index);
NSRange range = [self rangeOfString:str options:NSLiteralSearch range:fromRange];
if (range.location == NSNotFound) {
return JavaNotFound;
}
return (int)range.location;
}
- (int)lastIndexOfChar:(unichar)ch {
int len = (int)self.length;
for (int i = len - 1; i >= 0; --i) {
if ([self charAt:i] == ch) {
return i;
}
}
return JavaNotFound;
}
- (int)lastIndexOfChar:(unichar)ch fromIndex:(int)index {
int len = (int)self.length;
if (index >= len) {
index = len - 1;
}
for (int i = index; i >= 0; --i) {
if ([self charAt:i] == ch) {
return index;
}
}
return JavaNotFound;
}
- (int)lastIndexOfString:(NSString *)str {
NSRange range = [self rangeOfString:str options:NSBackwardsSearch];
if (range.location == NSNotFound) {
return JavaNotFound;
}
return (int)range.location;
}
- (int)lastIndexOfString:(NSString *)str fromIndex:(int)index {
NSRange fromRange = NSMakeRange(0, index);
NSRange range = [self rangeOfString:str options:NSBackwardsSearch range:fromRange];
if (range.location == NSNotFound) {
return JavaNotFound;
}
return (int)range.location;
}
- (NSString *)substringFromIndex:(NSInteger)beginIndex
toIndex:(NSInteger)endIndex {
if (endIndex <= beginIndex) {
return @"";
}
NSRange range = NSMakeRange(beginIndex, endIndex - beginIndex);
return [self substringWithRange:range];
}
- (NSString *)toLowerCase {
return [self lowercaseString];
}
- (NSString *)toUpperCase {
return [self uppercaseString];
}
- (NSString *)trim {
return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (NSString *)replaceAll:(NSString *)origin with:(NSString *)replacement {
return [self stringByReplacingOccurrencesOfString:origin withString:replacement];
}
- (NSArray *)split:(NSString *)separator {
return [self componentsSeparatedByString:separator];
}
@end
//
// SendMessageToWXReq+requestWithTextOrMediaMessage.h
// SDKSample
//
// Created by Jeason on 15/7/14.
//
//
#import <WechatOpenSDK/WXApiObject.h>
@interface SendMessageToWXReq (requestWithTextOrMediaMessage)
+ (SendMessageToWXReq *)requestWithText:(NSString *)text
OrMediaMessage:(WXMediaMessage *)message
bText:(BOOL)bText
InScene:(enum WXScene)scene;
@end
//
// SendMessageToWXReq+requestWithTextOrMediaMessage.m
// SDKSample
//
// Created by Jeason on 15/7/14.
//
//
#import "SendMessageToWXReq+requestWithTextOrMediaMessage.h"
@implementation SendMessageToWXReq (requestWithTextOrMediaMessage)
+ (SendMessageToWXReq *)requestWithText:(NSString *)text
OrMediaMessage:(WXMediaMessage *)message
bText:(BOOL)bText
InScene:(enum WXScene)scene {
SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
req.bText = bText;
req.scene = scene;
if (bText)
req.text = text;
else
req.message = message;
return req;
}
@end
//
// Created by mo on 2020/3/7.
//
#import <Foundation/Foundation.h>
@interface ThumbnailHelper : NSObject
+ (UIImage *)compressImage:(UIImage *)image toByte:(NSUInteger)maxLength isPNG:(BOOL)isPNG;
@end
\ No newline at end of file
//
// Created by mo on 2020/3/7.
//
#import "ThumbnailHelper.h"
@implementation ThumbnailHelper
+ (UIImage *)compressImage:(UIImage *)image toByte:(NSUInteger)maxLength isPNG:(BOOL)isPNG {
// Compress by quality
CGFloat compression = 1;
NSData *data = UIImageJPEGRepresentation(image, compression);
if (data.length < maxLength) return image;
CGFloat max = 1;
CGFloat min = 0;
for (int i = 0; i < 6; ++i) {
compression = (max + min) / 2;
data = UIImageJPEGRepresentation(image, compression);
if (data.length < maxLength * 0.9) {
min = compression;
} else if (data.length > maxLength) {
max = compression;
} else {
break;
}
}
UIImage *resultImage;
if (isPNG) {
NSData *tmp = UIImagePNGRepresentation([UIImage imageWithData:data]);
resultImage = [UIImage imageWithData:tmp];
} else {
resultImage = [UIImage imageWithData:data];
}
if (data.length < maxLength) return resultImage;
// Compress by size
NSUInteger lastDataLength = 0;
while (data.length > maxLength && data.length != lastDataLength) {
lastDataLength = data.length;
CGFloat ratio = (CGFloat) maxLength / data.length;
CGSize size = CGSizeMake((NSUInteger) (resultImage.size.width * sqrtf(ratio)),
(NSUInteger) (resultImage.size.height * sqrtf(ratio))); // Use NSUInteger to prevent white blank
UIGraphicsBeginImageContext(size);
[resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
data = UIImageJPEGRepresentation(resultImage, compression);
}
return resultImage;
}
- (UIImage *)scaleFromImage:(UIImage *)image width:(CGSize)newSize {
CGSize imageSize = image.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
if (width <= newSize.width && height <= newSize.height) {
return image;
}
if (width == 0 || height == 0) {
return image;
}
CGFloat widthFactor = newSize.width / width;
CGFloat heightFactor = newSize.height / height;
CGFloat scaleFactor = (widthFactor < heightFactor ? widthFactor : heightFactor);
CGFloat scaledWidth = width * scaleFactor;
CGFloat scaledHeight = height * scaleFactor;
CGSize targetSize = CGSizeMake(scaledWidth, scaledHeight);
UIGraphicsBeginImageContext(targetSize);
[image drawInRect:CGRectMake(0, 0, scaledWidth, scaledHeight)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end
\ No newline at end of file
//
// Created by mo on 2020/3/7.
//
#import <Foundation/Foundation.h>
#import <WechatOpenSDK/WXApiObject.h>
#import "FluwxResponseHandler.h"
NS_ASSUME_NONNULL_BEGIN
@interface WXApiRequestHandler : NSObject
+ (void)sendText:(NSString *)text
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendImageData:(NSData *)imageData
TagName:(NSString *)tagName
MessageExt:(NSString *)messageExt
Action:(NSString *)action
ThumbImage:(UIImage *)thumbImage
InScene:(enum WXScene)scene
title:(NSString *)title
description:(NSString *)description
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendLinkURL:(NSString *)urlString
TagName:(NSString *)tagName
Title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)messageAction
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendMusicURL:(NSString *)musicURL
dataURL:(NSString *)dataURL
MusicLowBandUrl:(NSString *)musicLowBandUrl
MusicLowBandDataUrl:(NSString *)musicLowBandDataUrl
Title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)messageAction
TagName:(NSString *)tagName
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendVideoURL:(NSString *)videoURL
VideoLowBandUrl:(NSString *)videoLowBandUrl
Title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)messageAction
TagName:(NSString *)tagName
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendEmotionData:(NSData *)emotionData
ThumbImage:(UIImage *)thumbImage
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendFileData:(NSData *)fileData
fileExtension:(NSString *)extension
Title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendMiniProgramWebpageUrl:(NSString *)webpageUrl
userName:(NSString *)userName
path:(NSString *)path
title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
hdImageData:(NSData *)hdImageData
withShareTicket:(BOOL)withShareTicket
miniProgramType:(WXMiniProgramType)programType
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)messageAction
TagName:(NSString *)tagName
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)launchMiniProgramWithUserName:(NSString *)userName
path:(NSString *)path
type:(WXMiniProgramType)miniProgramType
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendAppContentData:(NSData *)data
ExtInfo:(NSString *)info
ExtURL:(NSString *)url
Title:(NSString *)title
Description:(NSString *)description
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)action
ThumbImage:(UIImage *)thumbImage
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)addCardsToCardPackage:(NSArray *)cardIds cardExts:(NSArray *)cardExts
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendAuthRequestScope:(NSString *)scope
State:(NSString *)state
OpenID:(NSString *)openID
InViewController:(UIViewController *)viewController
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)sendAuthRequestScope:(NSString *)scope
State:(NSString *)state
OpenID:(NSString *)openID
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)chooseCard:(NSString *)appid
cardSign:(NSString *)cardSign
nonceStr:(NSString *)nonceStr
signType:(NSString *)signType
timestamp:(UInt32)timestamp
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)openUrl:(NSString *)url
completion:(void (^ __nullable)(BOOL success))completion;
+ (void)chooseInvoice:(NSString *)appid
cardSign:(NSString *)cardSign
nonceStr:(NSString *)nonceStr
signType:(NSString *)signType
timestamp:(UInt32)timestamp
completion:(void (^ __nullable)(BOOL success))completion;
+ (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;
@end
NS_ASSUME_NONNULL_END
//
// Created by mo on 2020/3/7.
//
#import <WechatOpenSDK/WXApi.h>
#import "WXApiRequestHandler.h"
#import "SendMessageToWXReq+requestWithTextOrMediaMessage.h"
#import "WXMediaMessage+messageConstruct.h"
#import "FluwxStringUtil.h"
#import <WechatOpenSDK/WXApiObject.h>
@implementation WXApiRequestHandler
#pragma mark - Public Methods
+ (void)sendText:(NSString *)text
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion {
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:text
OrMediaMessage:nil
bText:YES
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)sendImageData:(NSData *)imageData
TagName:(NSString *)tagName
MessageExt:(NSString *)messageExt
Action:(NSString *)action
ThumbImage:(UIImage *)thumbImage
InScene:(enum WXScene)scene
title:(NSString *)title
description:(NSString *)description
completion:(void (^ __nullable)(BOOL success))completion {
WXImageObject *ext = [WXImageObject object];
ext.imageData = imageData;
WXMediaMessage *message = [WXMediaMessage messageWithTitle:(title == (id) [NSNull null]) ? nil : title
Description:(description == (id) [NSNull null]) ? nil : description
Object:ext
MessageExt:(messageExt == (id) [NSNull null]) ? nil : messageExt
MessageAction:(action == (id) [NSNull null]) ? nil : action
ThumbImage:thumbImage
MediaTag:(tagName == (id) [NSNull null]) ? nil : tagName];
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:nil
OrMediaMessage:message
bText:NO
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)sendLinkURL:(NSString *)urlString
TagName:(NSString *)tagName
Title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)messageAction
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion {
WXWebpageObject *ext = [WXWebpageObject object];
ext.webpageUrl = urlString;
WXMediaMessage *message = [WXMediaMessage messageWithTitle:(title == (id) [NSNull null]) ? nil : title
Description:(description == (id) [NSNull null]) ? nil : description
Object:ext
MessageExt:(messageExt == (id) [NSNull null]) ? nil : messageExt
MessageAction:(messageAction == (id) [NSNull null]) ? nil : messageAction
ThumbImage:thumbImage
MediaTag:(tagName == (id) [NSNull null]) ? nil : tagName];
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:nil
OrMediaMessage:message
bText:NO
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)sendMusicURL:(NSString *)musicURL
dataURL:(NSString *)dataURL
MusicLowBandUrl:(NSString *)musicLowBandUrl
MusicLowBandDataUrl:(NSString *)musicLowBandDataUrl
Title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)messageAction
TagName:(NSString *)tagName
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion {
WXMusicObject *ext = [WXMusicObject object];
if ([FluwxStringUtil isBlank:musicURL]) {
ext.musicLowBandUrl = musicLowBandUrl;
ext.musicLowBandDataUrl = (musicLowBandDataUrl == (id) [NSNull null]) ? nil : musicLowBandDataUrl;
} else {
ext.musicUrl = musicURL;
ext.musicDataUrl = (dataURL == (id) [NSNull null]) ? nil : dataURL;
}
WXMediaMessage *message = [WXMediaMessage messageWithTitle:(title == (id) [NSNull null]) ? nil : title
Description:description
Object:ext
MessageExt:(messageExt == (id) [NSNull null]) ? nil : messageExt
MessageAction:(messageAction == (id) [NSNull null]) ? nil : messageAction
ThumbImage:thumbImage
MediaTag:(tagName == (id) [NSNull null]) ? nil : tagName];
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:nil
OrMediaMessage:message
bText:NO
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)sendVideoURL:(NSString *)videoURL
VideoLowBandUrl:(NSString *)videoLowBandUrl
Title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)messageAction
TagName:(NSString *)tagName
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion {
WXMediaMessage *message = [WXMediaMessage message];
message.title = (title == (id) [NSNull null]) ? nil : title;
message.description = (description == (id) [NSNull null]) ? nil : description;
message.messageExt = (messageExt == (id) [NSNull null]) ? nil : messageExt;
message.messageAction = (messageAction == (id) [NSNull null]) ? nil : messageAction;
message.mediaTagName = (tagName == (id) [NSNull null]) ? nil : tagName;
[message setThumbImage:thumbImage];
WXVideoObject *ext = [WXVideoObject object];
if ([FluwxStringUtil isBlank:videoURL]) {
ext.videoLowBandUrl = videoLowBandUrl;
} else {
ext.videoUrl = videoURL;
}
message.mediaObject = ext;
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:nil
OrMediaMessage:message
bText:NO
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)sendEmotionData:(NSData *)emotionData
ThumbImage:(UIImage *)thumbImage
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion {
WXMediaMessage *message = [WXMediaMessage message];
[message setThumbImage:thumbImage];
WXEmoticonObject *ext = [WXEmoticonObject object];
ext.emoticonData = emotionData;
message.mediaObject = ext;
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:nil
OrMediaMessage:message
bText:NO
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)sendFileData:(NSData *)fileData
fileExtension:(NSString *)extension
Title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion {
WXMediaMessage *message = [WXMediaMessage message];
message.title = title;
message.description = description;
[message setThumbImage:thumbImage];
WXFileObject *ext = [WXFileObject object];
ext.fileExtension = extension;
ext.fileData = fileData;
message.mediaObject = ext;
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:nil
OrMediaMessage:message
bText:NO
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)sendMiniProgramWebpageUrl:(NSString *)webpageUrl
userName:(NSString *)userName
path:(NSString *)path
title:(NSString *)title
Description:(NSString *)description
ThumbImage:(UIImage *)thumbImage
hdImageData:(NSData *)hdImageData
withShareTicket:(BOOL)withShareTicket
miniProgramType:(WXMiniProgramType)programType
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)messageAction
TagName:(NSString *)tagName
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion {
WXMiniProgramObject *ext = [WXMiniProgramObject object];
ext.webpageUrl = (webpageUrl == (id) [NSNull null]) ? nil : webpageUrl;
ext.userName = (userName == (id) [NSNull null]) ? nil : userName;
ext.path = (path == (id) [NSNull null]) ? nil : path;
ext.hdImageData = (hdImageData == (id) [NSNull null]) ? nil : hdImageData;
ext.withShareTicket = withShareTicket;
// WXMiniProgramType miniProgramType = WXMiniProgramTypeRelease;
// if(programType == 0){
// miniProgramType = WXMiniProgramTypeRelease;
// } else if(programType == 1){
// miniProgramType =WXMiniProgramTypeTest;
// } else if(programType == 2){
// miniProgramType = WXMiniProgramTypePreview;
// }
ext.miniProgramType = programType;
WXMediaMessage *message = [WXMediaMessage messageWithTitle:(title == (id) [NSNull null]) ? nil : title
Description:(description == (id) [NSNull null]) ? nil : description
Object:ext
MessageExt:(messageExt == (id) [NSNull null]) ? nil : messageExt
MessageAction:(messageAction == (id) [NSNull null]) ? nil : messageAction
ThumbImage:thumbImage
MediaTag:(tagName == (id) [NSNull null]) ? nil : tagName];
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:nil
OrMediaMessage:message
bText:NO
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)launchMiniProgramWithUserName:(NSString *)userName
path:(NSString *)path
type:(WXMiniProgramType)miniProgramType
completion:(void (^ __nullable)(BOOL success))completion {
WXLaunchMiniProgramReq *launchMiniProgramReq = [WXLaunchMiniProgramReq object];
launchMiniProgramReq.userName = userName;
launchMiniProgramReq.path = (path == (id) [NSNull null]) ? nil : path;
launchMiniProgramReq.miniProgramType = miniProgramType;
[WXApi sendReq:launchMiniProgramReq completion:completion];
}
+ (void)sendAppContentData:(NSData *)data
ExtInfo:(NSString *)info
ExtURL:(NSString *)url
Title:(NSString *)title
Description:(NSString *)description
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)action
ThumbImage:(UIImage *)thumbImage
InScene:(enum WXScene)scene
completion:(void (^ __nullable)(BOOL success))completion {
WXAppExtendObject *ext = [WXAppExtendObject object];
ext.extInfo = info;
ext.url = url;
ext.fileData = data;
WXMediaMessage *message = [WXMediaMessage messageWithTitle:title
Description:description
Object:ext
MessageExt:messageExt
MessageAction:action
ThumbImage:thumbImage
MediaTag:nil];
SendMessageToWXReq *req = [SendMessageToWXReq requestWithText:nil
OrMediaMessage:message
bText:NO
InScene:scene];
[WXApi sendReq:req completion:completion];
}
+ (void)addCardsToCardPackage:(NSArray *)cardIds cardExts:(NSArray *)cardExts
completion:(void (^ __nullable)(BOOL success))completion {
NSMutableArray *cardItems = [NSMutableArray array];
for (NSString *cardId in cardIds) {
WXCardItem *item = [[WXCardItem alloc] init];
item.cardId = cardId;
item.appID = @"wxf8b4f85f3a794e77";
[cardItems addObject:item];
}
for (NSInteger index = 0; index < cardItems.count; index++) {
WXCardItem *item = cardItems[index];
NSString *ext = cardExts[index];
item.extMsg = ext;
}
AddCardToWXCardPackageReq *req = [[AddCardToWXCardPackageReq alloc] init];
req.cardAry = cardItems;
[WXApi sendReq:req completion:completion];
}
+ (void)chooseCard:(NSString *)appid
cardSign:(NSString *)cardSign
nonceStr:(NSString *)nonceStr
signType:(NSString *)signType
timestamp:(UInt32)timestamp
completion:(void (^ __nullable)(BOOL success))completion {
WXChooseCardReq *chooseCardReq = [[WXChooseCardReq alloc] init];
chooseCardReq.appID = appid;
chooseCardReq.cardSign = cardSign;
chooseCardReq.nonceStr = nonceStr;
chooseCardReq.signType = signType;
chooseCardReq.timeStamp = timestamp;
[WXApi sendReq:chooseCardReq completion:completion];
}
+ (void)sendAuthRequestScope:(NSString *)scope
State:(NSString *)state
OpenID:(NSString *)openID
InViewController:(UIViewController *)viewController
completion:(void (^ __nullable)(BOOL success))completion {
SendAuthReq *req = [[SendAuthReq alloc] init];
req.scope = scope; // @"post_timeline,sns"
req.state = state;
req.openID = openID;
return [WXApi sendAuthReq:req
viewController:viewController
delegate:[FluwxResponseHandler defaultManager]
completion:completion];
}
+ (void)sendAuthRequestScope:(NSString *)scope
State:(NSString *)state
OpenID:(NSString *)openID
completion:(void (^ __nullable)(BOOL success))completion {
SendAuthReq *req = [[SendAuthReq alloc] init];
req.scope = scope; // @"post_timeline,sns"
req.state = state;
req.openID = openID;
[WXApi sendReq:req completion:completion];
}
+ (void)openUrl:(NSString *)url
completion:(void (^ __nullable)(BOOL success))completion {
OpenWebviewReq *req = [[OpenWebviewReq alloc] init];
req.url = url;
[WXApi sendReq:req completion:completion];
}
+ (void)chooseInvoice:(NSString *)appid
cardSign:(NSString *)cardSign
nonceStr:(NSString *)nonceStr
signType:(NSString *)signType
timestamp:(UInt32)timestamp
completion:(void (^ __nullable)(BOOL success))completion {
WXChooseInvoiceReq *chooseInvoiceReq = [[WXChooseInvoiceReq alloc] init];
chooseInvoiceReq.appID = appid;
chooseInvoiceReq.cardSign = cardSign;
chooseInvoiceReq.nonceStr = nonceStr;
chooseInvoiceReq.signType = signType;
// chooseCardReq.cardType = @"INVOICE";
chooseInvoiceReq.timeStamp = timestamp;
// chooseCardReq.canMultiSelect = 1;
[WXApi sendReq:chooseInvoiceReq completion:completion];
}
+ (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 {
PayReq *req = [[PayReq alloc] init];
req.openID = (appId == (id) [NSNull null]) ? nil : appId;
req.partnerId = partnerId;
req.prepayId = prepayId;
req.nonceStr = nonceStr;
req.timeStamp = timestamp;
req.package = package;
req.sign = sign;
[WXApi sendReq:req completion:completion];
}
@end
//
// WXMediaMessage+messageConstruct.h
// SDKSample
//
// Created by Jeason on 15/7/14.
//
//
#import <WechatOpenSDK/WXApiObject.h>
@interface WXMediaMessage (messageConstruct)
+ (WXMediaMessage *)messageWithTitle:(NSString *)title
Description:(NSString *)description
Object:(id)mediaObject
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)action
ThumbImage:(UIImage *)thumbImage
MediaTag:(NSString *)tagName;
@end
//
// WXMediaMessage+messageConstruct.m
// SDKSample
//
// Created by Jeason on 15/7/14.
//
//
#import "WXMediaMessage+messageConstruct.h"
@implementation WXMediaMessage (messageConstruct)
+ (WXMediaMessage *)messageWithTitle:(NSString *)title
Description:(NSString *)description
Object:(id)mediaObject
MessageExt:(NSString *)messageExt
MessageAction:(NSString *)action
ThumbImage:(UIImage *)thumbImage
MediaTag:(NSString *)tagName {
WXMediaMessage *message = [WXMediaMessage message];
message.title = title;
message.description = description;
message.mediaObject = mediaObject;
message.messageExt = messageExt;
message.messageAction = action;
message.mediaTagName = tagName;
[message setThumbImage:thumbImage];
return message;
}
@end
#import <Flutter/Flutter.h>
@interface FluwxPlugin : NSObject<FlutterPlugin>
@end
//
// Created by mo on 2020/3/7.
//
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
@class FluwxStringUtil;
@interface FluwxShareHandler : NSObject
- (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar;
- (void)handleShare:(FlutterMethodCall *)call result:(FlutterResult)result;
@end
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint fluwx.podspec' to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'fluwx'
s.version = '0.0.1'
s.summary = 'A new Flutter plugin for Wechat SDK.'
s.description = <<-DESC
A new Flutter plugin for Wechat SDK.
DESC
s.homepage = 'https://github.com/OpenFlutter/fluwx'
s.license = { :file => '../LICENSE' }
s.author = { 'JarvanMo' => 'jarvan.mo@gmail.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/public/*.h'
s.static_framework = true
s.dependency 'Flutter'
s.dependency 'WechatOpenSDK', '1.8.7.1'
# s.dependency 'OpenWeChatSDK','~> 1.8.3+10'
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/Headers/Public/#{s.name}" }
s.frameworks = ["SystemConfiguration", "CoreTelephony","WebKit"]
s.libraries = ["z", "sqlite3.0", "c++"]
s.preserve_paths = 'Lib/*.a'
s.vendored_libraries = "**/*.a"
s.ios.deployment_target = '8.0'
# Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
end
/// Fluwx is a powerful plugin for WeChatSDK.
/// easy to use.
///
/// A open sou;rce project authorized by [OpenFlutter](https://github.com/OpenFlutter).
library fluwx;
export 'package:fluwx/src/fluwx_iml.dart';
export 'package:fluwx/src/response/wechat_response.dart';
export 'package:fluwx/src/share/share_models.dart';
export 'package:fluwx/src/wechat_enums.dart';
export 'package:fluwx/src/wechat_file.dart' hide FileSchema;
/*
* 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/foundation.dart';
import 'package:flutter/services.dart';
import 'package:fluwx/fluwx.dart';
import 'package:fluwx/src/wechat_enums.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 =
new 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 to get the correct result 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(
{String appId,
bool doOnIOS: true,
bool doOnAndroid: true,
String universalLink}) async {
if (doOnIOS && Platform.isIOS) {
if (universalLink.trim().isEmpty || !universalLink.startsWith("https")) {
throw ArgumentError.value(universalLink,
"your universal link is illegal, see https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html for detail");
}
}
return await _channel.invokeMethod("registerApp", {
"appId": appId,
"iOS": doOnIOS,
"android": doOnAndroid,
"universalLink": universalLink
});
}
///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)) {
return await _channel.invokeMethod(
_shareModelMethodMapper[model.runtimeType], model.toMap());
} else {
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}) async {
assert(scope != null && scope.trim().isNotEmpty);
return await _channel
.invokeMethod("sendAuth", {"scope": scope, "state": state});
}
/// open mini-program
/// see [WXMiniProgramType]
Future<bool> launchWeChatMiniProgram(
{@required String username,
String path,
WXMiniProgramType miniProgramType = WXMiniProgramType.RELEASE}) async {
assert(username != null && 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
});
}
/// 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 != null && appId.isNotEmpty);
assert(scope != null && scope.isNotEmpty);
assert(nonceStr != null && nonceStr.isNotEmpty);
assert(timeStamp != null && timeStamp.isNotEmpty);
assert(signature != null && 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) {
var response =
BaseWeChatResponse.create(methodCall.method, methodCall.arguments);
_weChatResponseEventHandlerController.add(response);
return Future.value();
}
///IOS only
Future<bool> authWeChatByPhoneLogin(
{@required String scope, String state}) async {
return await _channel
.invokeMethod("authByPhoneLogin", {"scope": scope, "state": state});
}
/*
* 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:typed_data';
const String _errCode = "errCode";
const String _errStr = "errStr";
typedef BaseWeChatResponse _WeChatResponseInvoker(Map argument);
Map<String, _WeChatResponseInvoker> _nameAndResponseMapper = {
"onShareResponse": (Map argument) => WeChatShareResponse.fromMap(argument),
"onAuthResponse": (Map argument) => WeChatAuthResponse.fromMap(argument),
"onLaunchMiniProgramResponse": (Map argument) =>
WeChatLaunchMiniProgramResponse.fromMap(argument),
"onPayResponse": (Map argument) => WeChatPaymentResponse.fromMap(argument),
"onSubscribeMsgResp": (Map argument) =>
WeChatSubscribeMsgResponse.fromMap(argument),
"onWXOpenBusinessWebviewResponse": (Map argument) =>
WeChatOpenBusinessWebviewResponse.fromMap(argument),
"onAuthByQRCodeFinished": (Map argument) =>
WeChatAuthByQRCodeFinishedResponse.fromMap(argument),
"onAuthGotQRCode": (Map argument) =>
WeChatAuthGotQRCodeResponse.fromMap(argument),
"onQRCodeScanned": (Map argument) =>
WeChatQRCodeScannedResponse.fromMap(argument),
"onWXShowMessageFromWX": (Map argument) =>
WeChatShowMessageFromWXRequest.fromMap(argument),
};
class BaseWeChatResponse {
final int errCode;
final String errStr;
bool get isSuccessful => errCode == 0;
BaseWeChatResponse._(this.errCode, this.errStr);
/// create response from response pool
factory BaseWeChatResponse.create(String name, Map argument) =>
_nameAndResponseMapper[name](argument);
}
class WeChatShareResponse extends BaseWeChatResponse {
final int type;
WeChatShareResponse.fromMap(Map map)
: type = map["type"],
super._(map[_errCode], map[_errStr]);
}
class WeChatAuthResponse extends BaseWeChatResponse {
final int type;
final String country;
final String lang;
final String code;
final String state;
WeChatAuthResponse.fromMap(Map map)
: type = map["type"],
country = map["country"],
lang = map["lang"],
code = map["code"],
state = map["state"],
super._(map[_errCode], map[_errStr]);
@override
bool operator ==(other) {
if (other is WeChatAuthResponse) {
return code == other.code &&
country == other.country &&
lang == other.lang &&
state == other.state;
} else {
return false;
}
}
@override
int get hashCode =>
super.hashCode + errCode.hashCode &
1345 + errStr.hashCode &
15 + (code ?? "").hashCode &
1432;
}
class WeChatLaunchMiniProgramResponse extends BaseWeChatResponse {
final int type;
final String extMsg;
WeChatLaunchMiniProgramResponse.fromMap(Map map)
: type = map["type"],
extMsg = map["extMsg"],
super._(map[_errCode], map[_errStr]);
}
class WeChatPaymentResponse extends BaseWeChatResponse {
final int type;
final String extData;
WeChatPaymentResponse.fromMap(Map map)
: type = map["type"],
extData = map["extData"],
super._(map[_errCode], map[_errStr]);
}
class WeChatSubscribeMsgResponse extends BaseWeChatResponse {
final String openid;
final String templateId;
final String action;
final String reserved;
final int scene;
WeChatSubscribeMsgResponse.fromMap(Map map)
: openid = map["openid"],
templateId = map["templateId"],
action = map["action"],
reserved = map["reserved"],
scene = map["scene"],
super._(map[_errCode], map[_errStr]);
}
class WeChatOpenBusinessWebviewResponse extends BaseWeChatResponse {
final int type;
final int errCode;
final int businessType;
final String resultInfo;
WeChatOpenBusinessWebviewResponse.fromMap(Map map)
: type = map["type"],
errCode = map[_errCode],
businessType = map["businessType"],
resultInfo = map["resultInfo"],
super._(map[_errCode], map[_errStr]);
}
class WeChatAuthByQRCodeFinishedResponse extends BaseWeChatResponse {
final String authCode;
final AuthByQRCodeErrorCode qrCodeErrorCode;
WeChatAuthByQRCodeFinishedResponse.fromMap(Map map)
: authCode = map["authCode"],
qrCodeErrorCode = (_authByQRCodeErrorCodes[_errCode] ??
AuthByQRCodeErrorCode.UNKNOWN),
super._(map[_errCode], map[_errStr]);
}
///[qrCode] in memory.
class WeChatAuthGotQRCodeResponse extends BaseWeChatResponse {
final Uint8List qrCode;
WeChatAuthGotQRCodeResponse.fromMap(Map map)
: qrCode = map["qrCode"],
super._(map[_errCode], map[_errStr]);
}
class WeChatQRCodeScannedResponse extends BaseWeChatResponse {
WeChatQRCodeScannedResponse.fromMap(Map map)
: super._(map[_errCode], map[_errStr]);
}
// 获取微信打开App时携带的参数
class WeChatShowMessageFromWXRequest extends BaseWeChatResponse {
final String extMsg;
WeChatShowMessageFromWXRequest.fromMap(Map map)
: extMsg = map["extMsg"],
super._(0, '');
}
///WechatAuth_Err_OK(0),
///WechatAuth_Err_NormalErr(-1),
///WechatAuth_Err_NetworkErr(-2),
///WechatAuth_Err_JsonDecodeErr(-3),
///WechatAuth_Err_Cancel(-4),
///WechatAuth_Err_Timeout(-5),
///WechatAuth_Err_Auth_Stopped(-6);
///[AuthByQRCodeErrorCode.JSON_DECODE_ERR] means WechatAuth_Err_GetQrcodeFailed when platform is iOS
///only Android will get [AUTH_STOPPED]
enum AuthByQRCodeErrorCode {
OK,
NORMAL_ERR,
NETWORK_ERR,
JSON_DECODE_ERR,
CANCEL,
TIMEOUT,
AUTH_STOPPED,
UNKNOWN
}
const Map<int, AuthByQRCodeErrorCode> _authByQRCodeErrorCodes = {
0: AuthByQRCodeErrorCode.OK,
-1: AuthByQRCodeErrorCode.NORMAL_ERR,
-2: AuthByQRCodeErrorCode.NETWORK_ERR,
-3: AuthByQRCodeErrorCode.JSON_DECODE_ERR,
-4: AuthByQRCodeErrorCode.CANCEL,
-5: AuthByQRCodeErrorCode.AUTH_STOPPED
};
/*
* 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/foundation.dart';
import 'package:fluwx/fluwx.dart';
import 'package:fluwx/src/wechat_enums.dart';
const String _scene = "scene";
const String _source = "source";
const String _thumbnail = "thumbnail";
const String _title = "title";
const String _description = "description";
const String _messageExt = "messageExt";
const String _mediaTagName = "mediaTagName ";
const String _messageAction = "messageAction";
const String _compressThumbnail = "compressThumbnail";
mixin WeChatShareBaseModel {
Map toMap();
}
///[source] the text you want to send to WeChat
///[scene] the target you want to send
class WeChatShareTextModel implements WeChatShareBaseModel {
final String source;
final WeChatScene scene;
final String messageExt;
final String messageAction;
final String mediaTagName;
final String title;
final String description;
WeChatShareTextModel(this.source,
{this.scene = WeChatScene.SESSION,
this.mediaTagName,
this.messageAction,
this.messageExt,
String description,
String title})
: assert(scene != null),
this.title = title ?? source,
this.description = description ?? source;
@override
Map toMap() {
return {
_scene: scene.index,
_source: source,
_messageExt: messageExt,
_messageAction: messageAction,
_mediaTagName: mediaTagName,
_title: title,
_description: description
};
}
}
///
/// the default value is [MINI_PROGRAM_TYPE_RELEASE]
///[hdImagePath] only works on iOS, not sure the relationship between [thumbnail] and [hdImagePath].
class WeChatShareMiniProgramModel implements WeChatShareBaseModel {
final String webPageUrl;
final WXMiniProgramType miniProgramType;
final String userName;
final String path;
final WeChatImage hdImagePath;
final String title;
final String description;
final WeChatImage thumbnail;
final bool withShareTicket;
final String messageExt;
final String messageAction;
final String mediaTagName;
final bool compressThumbnail;
WeChatShareMiniProgramModel(
{@required this.webPageUrl,
this.miniProgramType = WXMiniProgramType.RELEASE,
@required this.userName,
this.path: "/",
this.title,
this.description,
this.withShareTicket: false,
this.thumbnail,
this.hdImagePath,
this.mediaTagName,
this.messageAction,
this.messageExt,
this.compressThumbnail = true})
: assert(miniProgramType != null),
assert(webPageUrl != null && webPageUrl.isNotEmpty),
assert(userName != null && userName.isNotEmpty),
assert(path != null && path.isNotEmpty);
@override
Map toMap() {
return {
'webPageUrl': webPageUrl,
"miniProgramType": miniProgramType.toNativeInt(),
"userName": userName,
"path": path,
"title": title,
_description: description,
"withShareTicket": withShareTicket,
_thumbnail: thumbnail?.toMap(),
"hdImagePath": hdImagePath?.toMap(),
_messageAction: messageAction,
_mediaTagName: mediaTagName,
_compressThumbnail: compressThumbnail
};
}
}
///[source] the image you want to send to WeChat
///[scene] the target you want to send
///[thumbnail] the preview of your image, will be created from [scene] if null.
class WeChatShareImageModel implements WeChatShareBaseModel {
final WeChatImage source;
final WeChatImage thumbnail;
final String title;
final WeChatScene scene;
final String description;
final String messageExt;
final String messageAction;
final String mediaTagName;
final bool compressThumbnail;
WeChatShareImageModel(this.source,
{WeChatImage thumbnail,
this.title,
this.scene = WeChatScene.SESSION,
this.description,
this.mediaTagName,
this.messageAction,
this.messageExt,
this.compressThumbnail = true})
: assert(source != null),
assert(scene != null),
this.thumbnail = thumbnail ?? source;
@override
Map toMap() {
return {
_scene: scene.index,
_source: source.toMap(),
_thumbnail: thumbnail.toMap(),
_title: title,
_description: description,
_messageAction: messageAction,
_mediaTagName: mediaTagName,
_compressThumbnail: compressThumbnail
};
}
}
/// if [musicUrl] and [musicLowBandUrl] are both provided,
/// only [musicUrl] will be used.
class WeChatShareMusicModel implements WeChatShareBaseModel {
final String musicUrl;
final String musicDataUrl;
final String musicLowBandUrl;
final String musicLowBandDataUrl;
final WeChatImage thumbnail;
final String title;
final String description;
final WeChatScene scene;
final String messageExt;
final String messageAction;
final String mediaTagName;
final bool compressThumbnail;
WeChatShareMusicModel(
{this.musicUrl,
this.musicLowBandUrl,
this.title: "",
this.description: "",
this.musicDataUrl,
this.musicLowBandDataUrl,
this.thumbnail,
this.mediaTagName,
this.messageAction,
this.messageExt,
this.scene = WeChatScene.SESSION,
this.compressThumbnail = true})
: assert(musicUrl != null || musicLowBandUrl != null),
assert(scene != null);
@override
Map toMap() {
return {
_scene: scene.index,
"musicUrl": musicUrl,
"musicDataUrl": musicDataUrl,
"musicLowBandUrl": musicLowBandUrl,
"musicLowBandDataUrl": musicLowBandDataUrl,
_thumbnail: thumbnail?.toMap(),
_title: title,
_description: description,
_messageAction: messageAction,
_mediaTagName: mediaTagName,
_compressThumbnail: compressThumbnail
};
}
}
/// if [videoUrl] and [videoLowBandUrl] are both provided,
/// only [videoUrl] will be used.
class WeChatShareVideoModel implements WeChatShareBaseModel {
final String videoUrl;
final String videoLowBandUrl;
final WeChatImage thumbnail;
final String title;
final String description;
final WeChatScene scene;
final String messageExt;
final String messageAction;
final String mediaTagName;
final bool compressThumbnail;
WeChatShareVideoModel(
{this.scene = WeChatScene.SESSION,
this.videoUrl,
this.videoLowBandUrl,
this.title: "",
this.description: "",
this.thumbnail,
this.mediaTagName,
this.messageAction,
this.messageExt,
this.compressThumbnail = true})
: assert(videoUrl != null || videoLowBandUrl != null),
assert(thumbnail != null),
assert(scene != null);
@override
Map toMap() {
return {
_scene: scene.index,
"videoUrl": videoUrl,
"videoLowBandUrl": videoLowBandUrl,
_thumbnail: thumbnail?.toMap(),
_title: title,
_description: description,
_messageAction: messageAction,
_mediaTagName: mediaTagName,
_compressThumbnail: compressThumbnail
};
}
}
///[webPage] url you want to send to wechat
///[thumbnail] logo of your website
class WeChatShareWebPageModel implements WeChatShareBaseModel {
final String webPage;
final WeChatImage thumbnail;
final String title;
final String description;
final WeChatScene scene;
final String messageExt;
final String messageAction;
final String mediaTagName;
final bool compressThumbnail;
WeChatShareWebPageModel(this.webPage,
{this.title: "",
String description,
this.thumbnail,
this.scene = WeChatScene.SESSION,
this.mediaTagName,
this.messageAction,
this.messageExt,
this.compressThumbnail = true})
: assert(webPage != null && webPage.isNotEmpty),
assert(scene != null),
this.description = description ?? webPage;
@override
Map toMap() {
return {
_scene: scene.index,
"webPage": webPage,
_thumbnail: thumbnail?.toMap(),
_title: title,
_messageAction: messageAction,
_mediaTagName: mediaTagName,
_description: description,
_compressThumbnail: compressThumbnail
};
}
}
/// [source] the file you want to share, [source.suffix] is necessary on iOS.
/// [scene] can't be [WeChatScene.TIMELINE], otherwise, sharing nothing.
/// send files to WeChat
class WeChatShareFileModel implements WeChatShareBaseModel {
final WeChatFile source;
final WeChatImage thumbnail;
final String title;
final String description;
final WeChatScene scene;
final String messageExt;
final String messageAction;
final String mediaTagName;
final bool compressThumbnail;
WeChatShareFileModel(this.source,
{this.title: "",
this.description: "",
this.thumbnail,
this.scene = WeChatScene.SESSION,
this.mediaTagName,
this.messageAction,
this.messageExt,
this.compressThumbnail = true})
: assert(source != null),
assert(scene != null);
@override
Map toMap() {
return {
_scene: scene.index,
_source: source.toMap(),
_thumbnail: thumbnail?.toMap(),
_title: title,
_description: description,
_messageAction: messageAction,
_mediaTagName: mediaTagName,
_compressThumbnail: compressThumbnail
};
}
}
/*
* 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.
*/
///[WXMiniProgramType.RELEASE]正式版
///[WXMiniProgramType.TEST]测试版
///[WXMiniProgramType.PREVIEW]预览版
enum WXMiniProgramType { RELEASE, TEST, PREVIEW }
///[WeChatScene.SESSION]会话
///[WeChatScene.TIMELINE]朋友圈
///[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;
}
return 0;
}
}
/*
* 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:io';
import 'dart:typed_data';
const String defaultSuffixJpeg = ".jpeg";
const String defaultSuffixTxt = ".txt";
class WeChatImage extends WeChatFile {
WeChatImage.network(String source, {String suffix = defaultSuffixJpeg})
: super.network(source, suffix: suffix);
WeChatImage.asset(String source, {String suffix = defaultSuffixJpeg})
: super.asset(source, suffix: suffix);
WeChatImage.file(File source, {String suffix = defaultSuffixJpeg})
: super.file(source, suffix: suffix);
WeChatImage.binary(Uint8List source, {String suffix = defaultSuffixJpeg})
: super.binary(source, suffix: suffix);
}
class WeChatFile {
final dynamic source;
final FileSchema schema;
final String suffix;
/// [source] must begin with http or https
WeChatFile.network(String source, {String suffix})
: assert(source != null && source.startsWith("http")),
this.source = source,
this.schema = FileSchema.NETWORK,
this.suffix = source.readSuffix(suffix, defaultSuffixTxt);
///[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
WeChatFile.asset(String source, {String suffix})
: assert(source != null && source.trim().isNotEmpty),
this.source = source,
this.schema = FileSchema.ASSET,
this.suffix = source.readSuffix(suffix, defaultSuffixTxt);
WeChatFile.file(File source, {String suffix = defaultSuffixTxt})
: assert(source != null),
this.source = source.path,
this.schema = FileSchema.FILE,
this.suffix = source.path.readSuffix(suffix, defaultSuffixTxt);
WeChatFile.binary(Uint8List source, {String suffix = defaultSuffixTxt})
: assert(source != null),
assert(suffix != null && suffix.trim().isNotEmpty),
this.source = source,
this.schema = FileSchema.BINARY,
this.suffix = suffix;
Map toMap() =>
{"source": source, "schema": schema.index, "suffix": suffix ?? ""};
}
///Types of image, usually there are for types listed below.
///[FileSchema.NETWORK] is online images.
///[FileSchema.ASSET] is flutter asset image.
///[FileSchema.BINARY] is binary image, shall be be [Uint8List]
///[FileSchema.FILE] is local file, usually not comes from flutter asset.
enum FileSchema {
NETWORK,
ASSET,
FILE,
BINARY,
}
extension _FileSuffix on String {
/// returns [suffix] if [suffix] not blank.
/// If [suffix] is blank, then try to read from url
/// if suffix in url not found, then return jpg as default.
String readSuffix(String suffix, String defaultSuffix) {
if (suffix != null && suffix.trim().isNotEmpty) {
if (suffix.startsWith(".")) {
return suffix;
} else {
return ".$suffix";
}
}
var path = Uri.parse(this).path;
var index = path.lastIndexOf(".");
if (index >= 0) {
return path.substring(index);
}
return defaultSuffix;
}
}
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.
version: 2.6.2
homepage: https://github.com/JarvanMo/fluwx
environment:
sdk: ">=2.6.0 <3.0.0"
flutter: ">=1.12.0 <2.0.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# This section identifies this Flutter project as a plugin project.
# The androidPackage and pluginClass identifiers should not ordinarily
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
android:
package: com.jarvan.fluwx
pluginClass: FluwxPlugin
ios:
pluginClass: FluwxPlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# 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
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
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");
});
});
}
/*
* 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';
import 'package:fluwx/src/share/share_models.dart';
import 'package:fluwx/src/wechat_enums.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';
import 'package:fluwx/src/share/share_models.dart';
import 'package:fluwx/src/wechat_enums.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 model = WeChatShareMiniProgramModel(
webPageUrl: "http://openflutter.dev", userName: "userName");
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 map = WeChatShareMiniProgramModel(
webPageUrl: "http://openflutter.dev",
miniProgramType: WXMiniProgramType.PREVIEW,
withShareTicket: true,
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");
});
});
}
/*
* 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/src/share/share_models.dart';
import 'package:fluwx/src/wechat_enums.dart';
void main() {
test("create WeChatTextModel", () {
var model = WeChatShareTextModel("text", scene: WeChatScene.FAVORITE);
expect(model.source, "text");
expect(model.scene, WeChatScene.FAVORITE);
});
test("WeChatTextModel toMap", () {
var map = WeChatShareTextModel("text", scene: WeChatScene.FAVORITE).toMap();
expect(map["source"], "text");
expect(map["scene"], 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';
import 'package:fluwx/src/wechat_file.dart';
void main() {
test("test WeChatImage.fromNetwork", () {
var withSuffixImage =
WeChatImage.network("http://image.openflutter.dev/fluwx.png");
expect(withSuffixImage.source, "http://image.openflutter.dev/fluwx.png");
expect(withSuffixImage.suffix, ".png");
expect(FileSchema.NETWORK, withSuffixImage.schema);
var withNoSuffixNoUrlSuffixImage =
WeChatImage.network("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(
"http://image.openflutter.dev/fluwx.jpeg",
suffix: ".png");
expect(withSpecifiedSuffixImage.source, "http://image.openflutter.dev/fluwx.jpeg");
expect(withSpecifiedSuffixImage.suffix, ".png");
expect(withSpecifiedSuffixImage.schema, FileSchema.NETWORK);
});
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment