提交 90c3ad59 authored 作者: JarvanMo's avatar JarvanMo

refactor android

上级 8d26dffb
*.dart linguist-language=Dart
*.m linguist-language=Dart
*.h linguist-language=Dart
---
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
**Expected behavior**
A clear and concise description of what you expected to happen.
**Flutter Version:**
Run `flutter doctor` in terminal or powershell , and copy the outputs.
**Testing Platform:**
Android or iOS?
**Additional context**
Add any other context about the problem here.
...@@ -3,18 +3,5 @@ ...@@ -3,18 +3,5 @@
.packages .packages
.pub/ .pub/
pubspec.lock
build/ build/
*.iml
.idea/
.vscode/
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
\ No newline at end of file
# Default ignored files
/workspace.xml
\ No newline at end of file
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>
\ No newline at end of file
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file:///Users/mo/Development/flutter/bin/cache/dart-sdk/lib/typed_data" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
\ No newline at end of file
<component name="libraryTable">
<library name="Flutter Plugins" type="FlutterPluginsLibraryType">
<CLASSES>
<root url="file://$PROJECT_DIR$" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
\ No newline at end of file
<component name="libraryTable">
<library name="Flutter for Android">
<CLASSES>
<root url="jar:///Users/mo/Development/flutter/bin/cache/artifacts/engine/android-arm/flutter.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownEnhProjectSettings">
<AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" />
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imagePathType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
<component name="MarkdownNavigatorHistory">
<PasteImageHistory checkeredTransparentBackground="false" filename="image" directory="" onPasteImageTargetRef="3" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteReferenceElement="2" cornerRadius="20" borderColor="0" transparentColor="16777215" borderWidth="1" trimTop="0" trimBottom="0" trimLeft="0" trimRight="0" transparent="false" roundCorners="false" showPreview="true" bordered="false" scaled="false" cropped="false" hideInapplicableOperations="false" preserveLinkFormat="false" scale="50" scalingInterpolation="1" transparentTolerance="0" saveAsDefaultOnOK="false" linkFormat="0" addHighlights="false" showHighlightCoordinates="true" showHighlights="false" mouseSelectionAddsHighlight="false" outerFilled="false" outerFillColor="0" outerFillTransparent="true" outerFillAlpha="30">
<highlightList />
<directories />
<filenames />
</PasteImageHistory>
<CopyImageHistory checkeredTransparentBackground="false" filename="image" directory="" onPasteImageTargetRef="3" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteReferenceElement="2" cornerRadius="20" borderColor="0" transparentColor="16777215" borderWidth="1" trimTop="0" trimBottom="0" trimLeft="0" trimRight="0" transparent="false" roundCorners="false" showPreview="true" bordered="false" scaled="false" cropped="false" hideInapplicableOperations="false" preserveLinkFormat="false" scale="50" scalingInterpolation="1" transparentTolerance="0" saveAsDefaultOnOK="false" linkFormat="0" addHighlights="false" showHighlightCoordinates="true" showHighlights="false" mouseSelectionAddsHighlight="false" outerFilled="false" outerFillColor="0" outerFillTransparent="true" outerFillAlpha="30">
<highlightList />
<directories />
<filenames />
</CopyImageHistory>
<PasteLinkHistory onPasteImageTargetRef="3" onPasteTargetRef="1" onPasteLinkText="0" onPasteImageElement="1" onPasteLinkElement="1" onPasteWikiElement="2" onPasteReferenceElement="2" hideInapplicableOperations="false" preserveLinkFormat="false" useHeadingForLinkText="false" linkFormat="0" saveAsDefaultOnOK="false" />
<TableToJsonHistory>
<entries />
</TableToJsonHistory>
<TableSortHistory>
<entries />
</TableSortHistory>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="FlexmarkProjectSettings">
<FlexmarkHtmlSettings flexmarkSpecExampleRendering="0" flexmarkSpecExampleRenderHtml="false">
<flexmarkSectionLanguages>
<option name="1" value="Markdown" />
<option name="2" value="HTML" />
<option name="3" value="flexmark-ast:1" />
</flexmarkSectionLanguages>
</FlexmarkHtmlSettings>
</component>
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.md.nav.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" correctedInvalidSettings="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ANCHORLINKS" value="true" />
<option name="ATXHEADERSPACE" value="true" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="INTELLIJ_DUMMY_IDENTIFIER" value="true" />
<option name="RELAXEDHRULES" value="true" />
<option name="STRIKETHROUGH" value="true" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="true" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="PRODUCTION_SPEC_PARSER" value="true" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" addPageHeader="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" plantUmlConversion="0">
<GeneratorProvider>
<provider providerId="com.vladsch.md.nav.editor.text.html.generator" providerName="Unmodified HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.md.nav.editor.text.html.css" providerName="No Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Android API 28 Platform" project-jdk-type="Android SDK" />
<component name="ProjectType">
<option name="id" value="io.flutter" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/fluwx.iml" filepath="$PROJECT_DIR$/fluwx.iml" />
<module fileurl="file://$PROJECT_DIR$/android/fluwx_android.iml" filepath="$PROJECT_DIR$/android/fluwx_android.iml" />
<module fileurl="file://$PROJECT_DIR$/example/android/fluwx_example_android.iml" filepath="$PROJECT_DIR$/example/android/fluwx_example_android.iml" />
</modules>
</component>
</project>
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="example/lib/main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/example/lib/main.dart" />
<method />
</configuration>
</component>
\ No newline at end of file
# 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
## 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 ## 0.0.1
* Android部分的分享已完成. * TODO: Describe initial release.
差异被折叠。
# Fluwx # 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](./arts/fluwx_logo.png) A new Flutter plugin.
[中文请移步此处](./README_CN.md) ## Getting Started
`Fluwx` makes easier using WeChatSDK on Flutter. This project is a starting point for a Flutter
QQ Group:892398530。 [plug-in package](https://flutter.dev/developing-packages/),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
# V2.0 is coming. Broken changes!!! For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
## Before samples, guidance on mobile development, and a full API reference.
Before using`Fluwx`,read [the official documents](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1) first.
This is very important because some configurations or details are not listed here.
### What does Fluwx support?
* Share Text.
* Share WebPage.
* Share Image.
* Share Music.
* Share Video.
* Share MiniProgram.
* Send Auth(Login).
* Pay.
* Launch Mini-Program.
* Subscribe Message.
* Auth By QRCode.
* Sign Auto-Deduct.
* Open WeChat App.
## Sample
[See sample here](./example)
[watch charged video here](https://study.163.com/course/introduction.htm?share=2&shareId=480000001896427&courseId=1209174838&_trace_c_p_k2_=e72467dc0df540579287a8ea996344a4)
[upgrade to 1.0.0 or above](./doc/QUESTIONS.md)
## Dependencies
Add the following dependencies in your `pubspec.yaml` file:
```yaml
dependencies:
fluwx: ^${latestVersion}
```
> Latest version is ![pub package](https://img.shields.io/pub/v/fluwx.svg)
For using the snapshot:
```yaml
dependencies:
fluwx:
git:
url: https://github.com/OpenFlutter/fluwx
```
please check *no_pay* branch for no pay-sdk support.
## Register WeChatSDK via Fluwx
Before using`Fluwx`,you should init `FLuwx`
```dart
import 'package:fluwx/fluwx.dart' as fluwx;
fluwx.registerWxApi(appId:"wxd930ea5d5a258f4f",universalLink:"https://your.univeral.link.com/placeholder/");
```
Developers must provide `universalLink` if you want register WeChat via fluwx, otherwise, ignore. Incorrect `universalLink` won't pass the register of SDK.
> NOTE:Although we can register WXApi via Fluwx,but there's still some work you have to do on the particular platform.For example, add *URLSchema, LSApplicationQueriesSchemes or universal link* for iOS.
Please read the official documents for details.
### More
* [Share](./doc/SHARE.md)
* [Auth](./doc/SEND_AUTH.md)
* [Payment](./doc/WXPay.md)
* [Launch Mini-Program](./doc/LAUNCH_MINI_PROGRAM.md)
* [Subscribe Message](./doc/SUBSCRIBE_MESSAGE.md)
* [Auth By QRCode](./doc/AUTH_BY_QR_CODE.md)
* [Sign Auto-Deduct](./doc/AUTO_DEDUCT.md)
* [Receive Response Or Callback From WeChat](./doc/RESPONSE.md)
### Other
* [Having Questions?](./doc/QUESTIONS.md)
### Waiting
### Donate
Buy the writer a cup of coffee。
<img src="./arts/wx.jpeg" height="300"> <img src="./arts/ali.jpeg" height="300">
### Subscribe Us On WeChat
![subscribe](./arts/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.
# 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](./arts/fluwx_logo.png)
适用于Flutter的微信SDK,方便快捷。
QQ群:892398530。
# V2.0 正在开发. 破坏性更新!!!
## 使用需知
使用`Fluwx`之前,强烈建议先阅读[微信SDK官方文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1)
这有助于你使用`Fluwx`
这很重要,因为有一些概念以及配置不会体现在本文档中。
### 目前功能
* 文本分享。
* 网站分享。
* 图片分享。
* 音乐分享。
* 视频分享。
* 小程序分享。
* 发送Auth认证(登录)。
* 支付。
* 打开小程序。
* 一次性订阅消息。
* 二维码登录。
* 签约免密支付。
* 打开微信。
## 示例
[点此查看示例](./example)
[收费视频教程点这里](https://study.163.com/course/introduction.htm?share=2&shareId=480000001896427&courseId=1209174838&_trace_c_p_k2_=e72467dc0df540579287a8ea996344a4)
[升级到1.0.0或者更高](./doc/QUESTIONS_CN.md)
## 引入
在你的 `pubspec.yaml` 文件中添加如下依赖:
```yaml
dependencies:
fluwx: ^${latestVersion}
```
切换到*no_pay*分支以使用没有支付sdk的fluwx.
> 最新版本为 ![pub package](https://img.shields.io/pub/v/fluwx.svg)
如果想用github上最新版本:
```yaml
dependencies:
fluwx:
git:
url: https://github.com/OpenFlutter/fluwx
```
## 初始化
使用`Fluwx`前,需要进行初始化操作:
```dart
import 'package:fluwx/fluwx.dart' as fluwx;
fluwx.registerWxApi(appId:"wxd930ea5d5a258f4f",universalLink:"https://your.univeral.link.com/placeholder/");
```
如果你想通过fluwx在iOS端注册微信,请务必提供 `universalLink` ,否则无视这句话。乱填`universalLink`会导致iOS端微信SDK无法正常初始化!!!
> 注意:尽管可以通过Fluwx完成微信注册,但一些操作依然需要在对应平台进行设置,如配置iOS的*URLSchema,LSApplicationQueriesSchemes,universal link*等。
### 传送门
* [分享](./doc/SHARE_CN.md)
* [Auth](./doc/SEND_AUTH_CN.md)
* [支付](./doc/WXPay_CN.md)
* [打开小程序](./doc/LAUNCH_MINI_PROGRAM_CN.md)
* [一次性订阅消息](./doc/SUBSCRIBE_MESSAGE_CN.md)。。
* [二维码登录](./doc/AUTH_BY_QR_CODE_CN.md)
* [签约免密支付](./doc/AUTO_DEDUCT_CN.md)
* [接收微信响应](./doc/RESPONSE_CN.md)
### Q&A
请先看文档,再看Q&A,再查看issue,自我排查错误,方便你我他。依然无法解决的问题可以加群提问。
* [常见问题Q&A](./doc/QUESTIONS_CN.md)
### 更多功能敬请请期待
### 捐助
请作者喝杯咖啡。
<img src="./arts/wx.jpeg" height="300"> <img src="./arts/ali.jpeg" height="300">
### 欢迎关注公众号
![subscribe](./arts/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.
...@@ -2,14 +2,14 @@ group 'com.jarvan.fluwx' ...@@ -2,14 +2,14 @@ group 'com.jarvan.fluwx'
version '1.0-SNAPSHOT' version '1.0-SNAPSHOT'
buildscript { buildscript {
ext.kotlin_version = '1.3.50' ext.kotlin_version = '1.3.61'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.1' classpath 'com.android.tools.build:gradle:3.6.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
...@@ -25,7 +25,7 @@ apply plugin: 'com.android.library' ...@@ -25,7 +25,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
android { android {
compileSdkVersion 29 compileSdkVersion 28
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
...@@ -43,8 +43,10 @@ android { ...@@ -43,8 +43,10 @@ android {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
api 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:5.4.3' api 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:5.4.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-M2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'top.zibin:Luban:1.1.8' implementation 'top.zibin:Luban:1.1.8'
implementation 'com.squareup.okhttp3:okhttp:4.0.0' implementation 'com.squareup.okhttp3:okhttp:4.0.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 org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
#Tue Aug 13 10:22:39 CST 2019
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
...@@ -7,11 +7,7 @@ ...@@ -7,11 +7,7 @@
<application> <application>
<activity <activity
android:name=".wxapi.WXEntryActivity" android:name=".wxapi.FluwxWXEntryActivity"
android:launchMode="singleTask"
android:theme="@style/DisablePreviewTheme" />
<activity
android:name=".wxapi.WXPayEntryActivity"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/DisablePreviewTheme" /> android:theme="@style/DisablePreviewTheme" />
...@@ -19,10 +15,9 @@ ...@@ -19,10 +15,9 @@
android:name="${applicationId}.wxapi.WXEntryActivity" android:name="${applicationId}.wxapi.WXEntryActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:targetActivity="com.jarvan.fluwx.wxapi.WXEntryActivity" android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity"
android:theme="@style/DisablePreviewTheme"> android:theme="@style/DisablePreviewTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
...@@ -33,7 +28,7 @@ ...@@ -33,7 +28,7 @@
android:name="${applicationId}.wxapi.WXPayEntryActivity" android:name="${applicationId}.wxapi.WXPayEntryActivity"
android:exported="true" android:exported="true"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:targetActivity="com.jarvan.fluwx.wxapi.WXPayEntryActivity" android:targetActivity="com.jarvan.fluwx.wxapi.FluwxWXEntryActivity"
android:theme="@style/DisablePreviewTheme"> android:theme="@style/DisablePreviewTheme">
......
/*___Generated_by_IDEA___*/
package com.jarvan.fluwx;
/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
public final class BuildConfig {
public final static boolean DEBUG = Boolean.parseBoolean(null);
}
\ No newline at end of file
/*___Generated_by_IDEA___*/
package com.jarvan.fluwx;
/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
public final class Manifest {
}
\ No newline at end of file
/*___Generated_by_IDEA___*/
package com.jarvan.fluwx;
/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
public final class R {
}
\ No newline at end of file
/*
* Copyright (C) 2018 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;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
public class AppRegister extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final IWXAPI api = WXAPIFactory.createWXAPI(context, null);
// 将该app注册到微信
}
}
package com.jarvan.fluwx.constant;
public class CallResult {
public static final String RESULT_DONE = "done";
public static final String RESULT_ERROR = "error";
public static final String RESULT_API_NULL = "WxApi Not Registered";
public static final String RESULT_WE_CHAT_NOT_INSTALLED = "wechat not installed";
public static final String RESULT_FILE_NOT_EXIST = "file not exists";
}
package com.jarvan.fluwx.constant;
public class WeChatPluginImageSchema {
public static final String SCHEMA_ASSETS = "assets://";
public static final String SCHMEA_HTTP = "http://";
public static final String SCHMEA_HTTPS = "https://";
public static final String SCHEMA_FILE = "file://";
public static final String SCHEMA_CONTENT = "content://";
}
package com.jarvan.fluwx.constant;
/***
* Created by mo on 2018/8/8
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
public class WeChatPluginMethods {
public static final String REGISTER_APP = "registerApp";
public static final String UNREGISTER_APP = "unregisterApp";
public static final String WE_CHAT_SHARE_RESPONSE = "onShareResponse";
public static final String WE_CHAT_LAUNCHMINIPROGRAM_RESPONSE = "onLaunchMiniProgramResponse";
public static final String ON_SUBSCRIBE_MSG_RESP = "onSubscribeMsgResp";
public static final String IS_WE_CHAT_INSTALLED = "isWeChatInstalled";
public static final String SHARE_TEXT = "shareText";
public static final String SHARE_IMAGE = "shareImage";
public static final String SHARE_MUSIC = "shareMusic";
public static final String SHARE_VIDEO = "shareVideo";
public static final String SHARE_WEB_PAGE = "shareWebPage";
public static final String SHARE_MINI_PROGRAM = "shareMiniProgram";
public static final String SHARE_FILE = "shareFile";
public static final String LAUNCH_MINI_PROGRAM = "launchMiniProgram";
public static final String PAY = "payWithFluwx";
public static final String WE_CHAT_PAY_RESPONSE = "onPayResponse";
public static final String SUBSCRIBE_MSG = "subscribeMsg";
public static final String AUTO_DEDUCT = "autoDeduct";
}
package com.jarvan.fluwx.constant;
/***
* Created by mo on 2018/8/8
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
public class WechatPluginKeys {
public static final String ANDROID = "android";
public static final String APP_ID = "appId";
public static final String PLATFORM = "platform";
public static final String RESULT = "result";
public static final String SCENE = "scene";
public static final String SCENE_TIMELINE = "WeChatScene.TIMELINE";
public static final String SCENE_SESSION = "WeChatScene.SESSION";
public static final String SCENE_FAVORITE = "WeChatScene.FAVORITE";
public static final String TRANSACTION = "transaction";
public static final String TEXT = "text";
public static final String TITLE = "title";
public static final String IMAGE = "image";
public static final String IMAGE_DATA = "imageData";
public static final String THUMBNAIL = "thumbnail";
public static final String DESCRIPTION = "description";
public static final String PACKAGE = "?package=";
public static final String MESSAGE_EXT = "messageExt";
public static final String MEDIA_TAG_NAME = "mediaTagName ";
public static final String MESSAGE_ACTION = "messageAction";
}
package com.jarvan.fluwx.handler
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessWebview
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.util.*
class FluwxAutoDeductHandler {
fun signAutoDeduct(call: MethodCall, result: MethodChannel.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 map = HashMap<String, String>()
map["appid"] = appId
map["mch_id"] = mchId
map["plan_id"] = planId
map["contract_code"] = contractCode
map["request_serial"] = requestSerial
map["contract_display_account"] = contractDisplayAccount
map["notify_url"] = notifyUrl
map["version"] = version
map["sign"] = sign
map["timestamp"] = timestamp
map["return_app"] = returnApp
val req = WXOpenBusinessWebview.Req()
req.businessType = businessType
req.queryInfo = map
val b = WXAPiHandler.wxApi?.sendReq(req)
result.success(b)
}
}
\ No newline at end of file
package com.jarvan.fluwx.handler
import com.jarvan.fluwx.constant.WechatPluginKeys
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
internal class FluwxLaunchMiniProgramHandler {
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(mapOf(
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID,
WechatPluginKeys.RESULT to done
))
}
}
\ No newline at end of file
/*
* Copyright (C) 2018 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.handler
import com.jarvan.fluwx.constant.CallResult
import com.jarvan.fluwx.constant.WechatPluginKeys
import com.tencent.mm.opensdk.modelpay.PayReq
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class FluwxPayHandler {
fun pay(call: MethodCall, result: MethodChannel.Result) {
if (WXAPiHandler.wxApi == null) {
result.error(CallResult.RESULT_API_NULL, "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(
mapOf(
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID,
WechatPluginKeys.RESULT to done
)
)
}
}
}
\ No newline at end of file
/*
* Copyright (C) 2018 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.handler
import io.flutter.plugin.common.PluginRegistry
object FluwxRequestHandler {
private var registrar: PluginRegistry.Registrar? = null
fun setRegistrar(reg: PluginRegistry.Registrar) {
registrar = reg
}
fun getRegistrar(): PluginRegistry.Registrar? {
return registrar;
}
}
\ No newline at end of file
package com.jarvan.fluwx.handler
import com.tencent.mm.opensdk.modelbiz.SubscribeMessage
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
/// create 2018/12/20 by cai
class FluwxSubscribeMsgHandler {
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)
}
}
\ No newline at end of file
/* /*
* Copyright (C) 2018 The OpenFlutter Organization * Copyright (C) 2020 The OpenFlutter Organization
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.jarvan.fluwx.handler package com.jarvan.fluwx.handlers
import com.tencent.mm.opensdk.diffdev.DiffDevOAuthFactory import com.tencent.mm.opensdk.diffdev.DiffDevOAuthFactory
...@@ -73,7 +73,6 @@ internal class FluwxAuthHandler(private val methodChannel: MethodChannel) { ...@@ -73,7 +73,6 @@ internal class FluwxAuthHandler(private val methodChannel: MethodChannel) {
// val schemeData = call.argument("schemeData")?:"" // val schemeData = call.argument("schemeData")?:""
result.success(qrCodeAuth.auth(appId, scope, nonceStr, timeStamp, signature, qrCodeAuthListener)) result.success(qrCodeAuth.auth(appId, scope, nonceStr, timeStamp, signature, qrCodeAuthListener))
} }
fun stopAuthByQRCode(result: MethodChannel.Result) { fun stopAuthByQRCode(result: MethodChannel.Result) {
......
/* /*
* Copyright (C) 2018 The OpenFlutter Organization * Copyright (C) 2020 The OpenFlutter Organization
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -13,10 +13,8 @@ ...@@ -13,10 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.jarvan.fluwx.handler package com.jarvan.fluwx.handlers
import com.jarvan.fluwx.constant.WeChatPluginMethods
import com.jarvan.fluwx.constant.WechatPluginKeys
import com.tencent.mm.opensdk.modelbase.BaseResp import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.modelbiz.SubscribeMessage import com.tencent.mm.opensdk.modelbiz.SubscribeMessage
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram
...@@ -27,7 +25,6 @@ import com.tencent.mm.opensdk.modelpay.PayResp ...@@ -27,7 +25,6 @@ import com.tencent.mm.opensdk.modelpay.PayResp
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
object FluwxResponseHandler { object FluwxResponseHandler {
private var channel: MethodChannel? = null private var channel: MethodChannel? = null
private const val errStr = "errStr" private const val errStr = "errStr"
...@@ -39,7 +36,6 @@ object FluwxResponseHandler { ...@@ -39,7 +36,6 @@ object FluwxResponseHandler {
FluwxResponseHandler.channel = channel FluwxResponseHandler.channel = channel
} }
fun handleResponse(response: BaseResp) { fun handleResponse(response: BaseResp) {
when (response) { when (response) {
is SendAuth.Resp -> handleAuthResponse(response) is SendAuth.Resp -> handleAuthResponse(response)
...@@ -57,66 +53,51 @@ object FluwxResponseHandler { ...@@ -57,66 +53,51 @@ object FluwxResponseHandler {
"templateId" to response.templateID, "templateId" to response.templateID,
"action" to response.action, "action" to response.action,
"reserved" to response.reserved, "reserved" to response.reserved,
"scene" to response.scene "scene" to response.scene,
) type to response.type)
channel?.invokeMethod(WeChatPluginMethods.ON_SUBSCRIBE_MSG_RESP, result) channel?.invokeMethod("onSubscribeMsgResp", result)
} }
private fun handleLaunchMiniProgramResponse(response: WXLaunchMiniProgram.Resp) { private fun handleLaunchMiniProgramResponse(response: WXLaunchMiniProgram.Resp) {
val result = mutableMapOf( val result = mutableMapOf(
errStr to response.errStr, errStr to response.errStr,
WechatPluginKeys.TRANSACTION to response.transaction,
type to response.type, type to response.type,
errCode to response.errCode, errCode to response.errCode,
openId to response.openId, openId to response.openId
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID
) )
response.extMsg?.let { response.extMsg?.let {
// "extMsg" to response.extMsg,
result["extMsg"] = response.extMsg result["extMsg"] = response.extMsg
} }
channel?.invokeMethod(WeChatPluginMethods.WE_CHAT_LAUNCHMINIPROGRAM_RESPONSE, result) channel?.invokeMethod("onLaunchMiniProgramResponse", result)
} }
private fun handlePayResp(response: PayResp) { private fun handlePayResp(response: PayResp) {
val result = mapOf( val result = mapOf(
"prepayId" to response.prepayId, "prepayId" to response.prepayId,
"returnKey" to response.returnKey, "returnKey" to response.returnKey,
"extData" to response.extData, "extData" to response.extData,
errStr to response.errStr, errStr to response.errStr,
WechatPluginKeys.TRANSACTION to response.transaction,
type to response.type, type to response.type,
errCode to response.errCode, errCode to response.errCode
openId to response.openId,
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID
) )
channel?.invokeMethod(WeChatPluginMethods.WE_CHAT_PAY_RESPONSE, result) channel?.invokeMethod("onPayResponse", result)
} }
private fun handleSendMessageResp(response: SendMessageToWX.Resp) { private fun handleSendMessageResp(response: SendMessageToWX.Resp) {
val result = mapOf( val result = mapOf(
errStr to response.errStr, errStr to response.errStr,
WechatPluginKeys.TRANSACTION to response.transaction,
type to response.type, type to response.type,
errCode to response.errCode, errCode to response.errCode,
openId to response.openId, openId to response.openId)
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID
)
channel?.invokeMethod(WeChatPluginMethods.WE_CHAT_SHARE_RESPONSE, result)
channel?.invokeMethod("onShareResponse", result)
} }
private fun handleAuthResponse(response: SendAuth.Resp) { private fun handleAuthResponse(response: SendAuth.Resp) {
val result = mapOf( val result = mapOf(
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID,
errCode to response.errCode, errCode to response.errCode,
"code" to response.code, "code" to response.code,
"state" to response.state, "state" to response.state,
...@@ -125,9 +106,7 @@ object FluwxResponseHandler { ...@@ -125,9 +106,7 @@ object FluwxResponseHandler {
errStr to response.errStr, errStr to response.errStr,
openId to response.openId, openId to response.openId,
"url" to response.url, "url" to response.url,
type to response.type, type to response.type)
WechatPluginKeys.TRANSACTION to response.transaction
)
channel?.invokeMethod("onAuthResponse", result) channel?.invokeMethod("onAuthResponse", result)
} }
...@@ -135,18 +114,13 @@ object FluwxResponseHandler { ...@@ -135,18 +114,13 @@ object FluwxResponseHandler {
private fun handlerWXOpenBusinessWebviewResponse(response: WXOpenBusinessWebview.Resp) { private fun handlerWXOpenBusinessWebviewResponse(response: WXOpenBusinessWebview.Resp) {
val result = mapOf( val result = mapOf(
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID,
errCode to response.errCode, errCode to response.errCode,
"businessType" to response.businessType, "businessType" to response.businessType,
"resultInfo" to response.resultInfo, "resultInfo" to response.resultInfo,
errStr to response.errStr, errStr to response.errStr,
openId to response.openId, openId to response.openId,
type to response.type, type to response.type)
WechatPluginKeys.TRANSACTION to response.transaction
)
channel?.invokeMethod("onAutoDeductResponse", result) channel?.invokeMethod("onAutoDeductResponse", result)
} }
} }
\ No newline at end of file
package com.jarvan.fluwx.handlers
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.jarvan.fluwx.io.ImagesIO
import com.jarvan.fluwx.io.ImagesIOIml
import com.jarvan.fluwx.io.WeChatImage
import com.jarvan.fluwx.io.toExternalCacheFile
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 kotlinx.coroutines.*
import java.util.*
import kotlin.coroutines.CoroutineContext
/***
* Created by mo on 2020/3/6
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
internal class FluwxShareHandler(private val flutterAssets: FlutterPlugin.FlutterAssets, private val context: Context) : CoroutineScope {
companion object {
const val SHARE_IMAGE_THUMB_LENGTH = 32 * 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()
textObj.text = call.argument("source")
val msg = WXMediaMessage()
msg.mediaObject = textObj
val req = SendMessageToWX.Req()
req.message = msg
setCommonArguments(call, req, 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)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
req.message = msg
sendRequestInMain(result, req)
}
}
private fun shareImage(call: MethodCall, result: MethodChannel.Result) {
launch {
val sourceImage = WeChatImage.createWeChatImage(call.argument("source")
?: mapOf(), flutterAssets, context)
val thumbData = readThumbnailByteArray(call)
val sourceByteArray = sourceImage.readByteArray()
val imageObject = when {
sourceByteArray.isEmpty() -> {
WXImageObject()
}
sourceByteArray.size > 512 * 1024 -> {
WXImageObject().apply {
if (ContextCompat.checkSelfPermission(context, Manifest.permission_group.STORAGE) == PackageManager.PERMISSION_GRANTED) {
setImagePath(sourceByteArray.toExternalCacheFile(context, sourceImage.suffix)?.absolutePath)
} else {
Fragment().requestPermissions(arrayOf(Manifest.permission_group.STORAGE), 12121)
}
}
}
else -> {
WXImageObject(sourceByteArray)
}
}
val msg = WXMediaMessage()
msg.mediaObject = imageObject
msg.thumbData = thumbData
msg.title = call.argument<String>(keyTitle)
msg.description = call.argument<String>(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.title = call.argument(keyTitle)
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.title = call.argument(keyTitle)
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.title = call.argument(keyTitle)
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) {
val file = WXFileObject()
val filePath: String? = call.argument("filePath")
file.filePath = filePath
val msg = WXMediaMessage()
msg.mediaObject = file
msg.title = call.argument("title")
msg.description = call.argument("description")
launch {
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): ByteArray? {
val thumbnailMap: Map<String, Any>? = call.argument(keyThumbnail)
return thumbnailMap?.run {
val thumbnailImage = WeChatImage.createWeChatImage(thumbnailMap, flutterAssets, context)
val thumbnailImageIO = ImagesIOIml(thumbnailImage)
compressThumbnail(thumbnailImageIO)
}
}
private suspend fun compressThumbnail(ioIml: ImagesIO) = ioIml.compressedByteArray(context, SHARE_IMAGE_THUMB_LENGTH)
// SESSION, TIMELINE, FAVORITE
private fun setCommonArguments(call: MethodCall, req: SendMessageToWX.Req, msg: WXMediaMessage) {
msg.messageAction = call.argument<String?>("messageAction")
msg.messageExt = call.argument<String?>("messageExt")
msg.mediaTagName = call.argument<String?>("mediaTagName")
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
}
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
private val job = Job()
fun onDestroy() {
job.cancel()
}
}
\ No newline at end of file
/* /*
* Copyright (C) 2018 The OpenFlutter Organization * Copyright (c) 2020. OpenFlutter Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed to the Apache Software Foundation (ASF) under one or more contributor
* you may not use this file except in compliance with the License. * license agreements. See the NOTICE file distributed with this work for
* You may obtain a copy of the License at * 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 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* See the License for the specific language governing permissions and * License for the specific language governing permissions and limitations under
* limitations under the License. * the License.
*/ */
package com.jarvan.fluwx.handler package com.jarvan.fluwx.handlers
import com.jarvan.fluwx.constant.CallResult import android.content.Context
import com.jarvan.fluwx.constant.WechatPluginKeys
import com.tencent.mm.opensdk.openapi.IWXAPI import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory import com.tencent.mm.opensdk.openapi.WXAPIFactory
import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry
object WXAPiHandler { object WXAPiHandler {
private var registrar: PluginRegistry.Registrar? = null
var wxApi: IWXAPI? = null var wxApi: IWXAPI? = null
private var context: Context? = null
fun setRegistrar(registrar: PluginRegistry.Registrar) { fun setContext(context: Context?) {
WXAPiHandler.registrar = registrar WXAPiHandler.context = context
} }
fun registerApp(call: MethodCall, result: MethodChannel.Result) { fun registerApp(call: MethodCall, result: MethodChannel.Result) {
if (call.argument<Boolean>(WechatPluginKeys.ANDROID) == false) { if (call.argument<Boolean?>("android") == false) {
return return
} }
if (wxApi != null) { if (wxApi != null) {
result.success(mapOf( result.success(true)
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID,
WechatPluginKeys.RESULT to true
))
return return
} }
val appId: String? = call.argument(WechatPluginKeys.APP_ID) val appId: String? = call.argument("appId")
if (appId.isNullOrBlank()) { if (appId.isNullOrBlank()) {
result.error("invalid app id", "are you sure your app id is correct ?", appId) result.error("invalid app id", "are you sure your app id is correct ?", appId)
return return
} }
val api = WXAPIFactory.createWXAPI(context?.applicationContext, appId)
val api = WXAPIFactory.createWXAPI(registrar!!.context().applicationContext, appId)
val registered = api.registerApp(appId) val registered = api.registerApp(appId)
wxApi = api wxApi = api
result.success(mapOf( result.success(registered)
WechatPluginKeys.PLATFORM to WechatPluginKeys.ANDROID,
WechatPluginKeys.RESULT to registered
))
} }
fun checkWeChatInstallation(result: MethodChannel.Result) { fun checkWeChatInstallation(result: MethodChannel.Result) {
if (wxApi == null) { if (wxApi == null) {
result.error(CallResult.RESULT_API_NULL, "please config wxapi first", null) result.error("Unassigned WxApi", "please config wxapi first", null)
return return
} else { } else {
result.success(wxApi!!.isWXAppInstalled) result.success(wxApi?.isWXAppInstalled)
} }
} }
......
package com.jarvan.fluwx.io
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okio.*
import top.zibin.luban.Luban
import java.io.*
import java.util.*
import kotlin.math.sqrt
/***
* Created by mo on 2020/3/7
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class ImagesIOIml(override val image: WeChatImage) : 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: WeChatImage
suspend fun readByteArray(): ByteArray
suspend fun compressedByteArray(context: Context, maxSize: Int): ByteArray
}
internal suspend fun ByteArray.toExternalCacheFile(context: Context, suffix: String): File? {
val byteArray = this
return withContext(Dispatchers.IO) {
var file: File? = null
var sink: BufferedSink? = null
var source: Source? = null
var outputStream: OutputStream? = null
try {
val externalFile = context.externalCacheDir ?: return@withContext file
file = File(externalFile.absolutePath + File.separator + UUID.randomUUID().toString() + suffix)
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 external files")
} finally {
sink?.close()
source?.close()
outputStream?.close()
}
file
}
}
\ No newline at end of file
package com.jarvan.fluwx.io
import android.content.Context
import android.content.res.AssetFileDescriptor
import android.net.Uri
import android.util.Log
import io.flutter.embedding.engine.plugins.FlutterPlugin
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/3/7
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class WeChatFileImage(override val source: Any, override val suffix: String) : WeChatImage {
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 WeChatAssetImage(override val source: Any, override val suffix: String) : WeChatImage {
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 WeChatNetworkImage(override val source: Any, override val suffix: String) : WeChatImage {
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 WeChatMemoryImage(override val source: Any, override val suffix: String) : WeChatImage {
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 WeChatImage {
val source: Any
val suffix: String
suspend fun readByteArray(): ByteArray
companion object {
// NETWORK,
// ASSET,
// FILE,
// BINARY,
fun createWeChatImage(params: Map<String, Any>, flutterAssets: FlutterPlugin.FlutterAssets, context: Context): WeChatImage {
// Map toMap() => {"source": source, "schema": schema.index, "suffix": suffix};
val suffix = (params["suffix"] as String?) ?: ".jpeg"
return when ((params["schema"] as? Int) ?: 0) {
0 -> WeChatNetworkImage(source = (params["source"] as? String).orEmpty(), suffix = suffix)
1 -> {
val source = (params["source"] as? String).orEmpty()
val uri = Uri.parse(source)
val packageName = uri.getQueryParameter("package")
val subPath = if (packageName.isNullOrBlank()) {
flutterAssets.getAssetFilePathBySubpath(uri.path.orEmpty())
} else {
flutterAssets.getAssetFilePathBySubpath(uri.path.orEmpty(), packageName)
}
WeChatAssetImage(source = context.assets.openFd(subPath), suffix = suffix)
}
2 -> WeChatFileImage(source = (params["source"] as? String).orEmpty(), suffix = suffix)
3 -> WeChatMemoryImage(source = (params["source"] as? ByteArray)
?: byteArrayOf(), suffix = suffix)
else -> WeChatNetworkImage(source = (params["source"] as? String).orEmpty(), suffix = suffix)
}
}
}
}
/*
* Copyright (C) 2018 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.utils;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.text.TextUtils;
import java.io.IOException;
import io.flutter.plugin.common.PluginRegistry;
public final class AssetManagerUtil {
private AssetManagerUtil() {
throw new RuntimeException("can't do this");
}
public static AssetFileDescriptor openAsset(PluginRegistry.Registrar registrar, String assetKey, String assetPackage) {
AssetFileDescriptor fd = null;
AssetManager assetManager = registrar.context().getAssets();
String key;
if (TextUtils.isEmpty(assetPackage)) {
key = registrar.lookupKeyForAsset(assetKey);
} else {
key = registrar.lookupKeyForAsset(assetKey, assetPackage);
}
try {
fd = assetManager.openFd(key);
} catch (IOException e) {
e.printStackTrace();
}
return fd;
}
}
/*
* Copyright (C) 2018 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.utils;
import android.content.res.AssetFileDescriptor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;
public class FileUtil {
public static File createTmpFile(AssetFileDescriptor fileDescriptor) {
if (fileDescriptor == null) {
return null;
}
File file = null;
BufferedSink sink = null;
Source source = null;
OutputStream outputStream = null;
try {
file = File.createTempFile(UUID.randomUUID().toString(), ".png");
outputStream = new FileOutputStream(file);
sink = Okio.buffer(Okio.sink(outputStream));
source = Okio.source(fileDescriptor.createInputStream());
sink.writeAll(source);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (sink != null) {
try {
sink.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (source != null) {
try {
source.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
}
/*
* Copyright (C) 2018 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.utils;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import com.jarvan.fluwx.constant.WeChatPluginImageSchema;
import com.jarvan.fluwx.constant.WechatPluginKeys;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import io.flutter.plugin.common.PluginRegistry;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;
public class ShareImageUtil {
final static int WX_MAX_IMAGE_BYTE_SIZE = 10485760;
public static byte[] getImageData(PluginRegistry.Registrar registrar, String path) {
byte[] result = null;
if (path.startsWith(WeChatPluginImageSchema.SCHEMA_ASSETS)) {
String key = path.substring(WeChatPluginImageSchema.SCHEMA_ASSETS.length());
AssetFileDescriptor fileDescriptor = AssetManagerUtil.openAsset(registrar, key, getPackage(key));
try {
InputStream inputStream = fileDescriptor.createInputStream();
result = streamToByteArray(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
} else if (path.startsWith(WeChatPluginImageSchema.SCHEMA_FILE)) {
String pathWithoutUri = path.substring("file://".length());
result = fileToByteArray(registrar, pathWithoutUri);
} else if (path.startsWith(WeChatPluginImageSchema.SCHEMA_CONTENT)) {
File file = getFileFromContentProvider(registrar, path);
if (file != null) {
result = fileToByteArray(registrar, file.getAbsolutePath());
}
} else {
// result = handleNetworkImage(registrar, path);
result = Util.inputStreamToByte(openStream(path));
}
return result;
}
private static byte[] streamToByteArray(InputStream inputStream) {
Bitmap bmp = null;
bmp = BitmapFactory.decodeStream(inputStream);
return Util.bmpToByteArray(bmp, true);
}
private static byte[] fileToByteArray(File file) {
Bitmap bmp = null;
bmp = BitmapFactory.decodeFile(file.getAbsolutePath());
return Util.bmpToByteArray(bmp, true);
}
private static byte[] fileToByteArray(PluginRegistry.Registrar registrar, String pathWithoutUri) {
byte[] result = null;
Bitmap bmp = null;
bmp = BitmapFactory.decodeFile(pathWithoutUri);
int byteCount;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
byteCount = bmp.getAllocationByteCount();
} else {
byteCount = bmp.getByteCount();
}
if (byteCount >= WX_MAX_IMAGE_BYTE_SIZE) {
result = Util.bmpToCompressedByteArray(bmp, Bitmap.CompressFormat.JPEG, true);
} else {
result = Util.bmpToByteArray(bmp, true);
}
return result;
}
private static String getPackage(String assetsName) {
String packageStr = null;
if (assetsName.contains(WechatPluginKeys.PACKAGE)) {
int index = assetsName.indexOf(WechatPluginKeys.PACKAGE);
packageStr = assetsName.substring(index + WechatPluginKeys.PACKAGE.length(), assetsName.length());
}
return packageStr;
}
public static File inputStreamToFile(InputStream inputStream, String suffix, Context context) {
File file = null;
BufferedSink sink = null;
Source source = null;
OutputStream outputStream = null;
try {
File externalFile = context.getExternalCacheDir();
if (externalFile == null) {
return null;
}
file = new File(externalFile.getAbsolutePath() + File.separator + UUID.randomUUID().toString() + suffix);
// file = File.createTempFile(UUID.randomUUID().toString(), suffix);
outputStream = new FileOutputStream(file);
sink = Okio.buffer(Okio.sink(outputStream));
source = Okio.source(inputStream);
sink.writeAll(source);
sink.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (sink != null) {
try {
sink.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (source != null) {
try {
source.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
private static InputStream openStream(String url) {
if (!url.startsWith("https") && !url.startsWith("http")) {
url = "http://" + url;
}
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url(url).get().build();
try {
Response response = okHttpClient.newCall(request).execute();
ResponseBody responseBody = response.body();
if (response.isSuccessful() && responseBody != null) {
return responseBody.byteStream();
} else {
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static File getFileFromContentProvider(PluginRegistry.Registrar registrar, String path) {
Source source = null;
BufferedSink sink = null;
File file = null;
try {
Context context = registrar.context().getApplicationContext();
Uri uri = Uri.parse(path);
String suffix = null;
String mimeType = context.getContentResolver().getType(uri);
if (TextUtils.equals(mimeType, "image/jpeg") || TextUtils.equals(mimeType, "image/jpg")) {
suffix = ".jpg";
} else if (TextUtils.equals(mimeType, "image/png")) {
suffix = ".png";
}
file = File.createTempFile(UUID.randomUUID().toString(), suffix);
InputStream inputStream = context.getContentResolver().openInputStream(uri);
if (inputStream == null) {
return null;
}
OutputStream outputStream = new FileOutputStream(file);
sink = Okio.buffer(Okio.sink(outputStream));
source = Okio.source(inputStream);
sink.writeAll(source);
source.close();
sink.close();
} catch (IOException e) {
Log.i("fluwx", "reading image failed:\n" + e.getMessage());
}
return file;
}
}
/*
* Copyright (C) 2018 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.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ThumbnailCompressUtil {
/*** 图片压缩比例计算**
* @param options BitmapFactory.Options* @param minSideLength 小边长,单位为像素,如果为-1,则不按照边来压缩图片*
* @param maxNumOfPixels 这张片图片最大像素值,单位为byte,如100*1024* @return 压缩比例,必须为2的次幂*/
public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
}
/*** 计算图片的压缩比例,用于图片压缩
* * @param options BitmapFactory.Options* @param minSideLength 小边长,单位为像素,如果为-1,则不按照边来压缩图片
* * @param maxNumOfPixels 这张片图片最大像素值,单位为byte,如100*1024*
* @return 压缩比例*/
private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}
public static Bitmap makeNormalBitmap(String nativeImagePath, int minSideLength, int maxNumOfPixels) {
return makeNormalBitmap(nativeImagePath, minSideLength, maxNumOfPixels, Bitmap.Config.ARGB_4444);
}
public static Bitmap makeNormalBitmap(String nativeImagePath, int minSideLength, int maxNumOfPixels, Bitmap.Config config) {
Bitmap.CompressFormat format = Bitmap.CompressFormat.JPEG;
if (nativeImagePath.toLowerCase().endsWith(".png")) {
format = Bitmap.CompressFormat.PNG;
}
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Bitmap bitmap = BitmapFactory.decodeFile(nativeImagePath);
// BitmapFactory.Options options = new BitmapFactory.Options();
// options.inPreferredConfig = config;
// options.outHeight = bitmap.getHeight();
// options.outWidth = bitmap.getWidth();
// int quality = computeSampleSize(options, minSideLength, maxNumOfPixels);
// bitmap.compress(format, quality, baos);
//
// byte[] bytes = baos.toByteArray();
// bitmap.recycle();
// Bitmap result = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
// try {
// baos.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
Bitmap bitmap = BitmapFactory.decodeFile(nativeImagePath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100; //此处可尝试用90%开始压缩,跳过100%压缩
// 循环判断如果压缩后图片是否大于100kb,大于继续压缩
int length;
while ((length = baos.toByteArray().length) / 1024 > 32) {
// 每次都减少10
options -= 10;
// 重置baos即清空baos
baos.reset();
// 这里压缩options%,把压缩后的数据存放到baos中
if (options <= 0) {
options = 0;
}
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
if (options == 0) {
break;
}
}
return bitmap;
}
public static Bitmap compress(String nativeImagePath) {
Bitmap.CompressFormat format = Bitmap.CompressFormat.JPEG;
if (nativeImagePath.toLowerCase().endsWith(".png")) {
format = Bitmap.CompressFormat.PNG;
}
Log.e("tag", nativeImagePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Bitmap bitmap = BitmapFactory.decodeFile(nativeImagePath);
bitmap.compress(format, 90, baos);
byte[] bytes = baos.toByteArray();
bitmap.recycle();
Bitmap result = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static Bitmap createScaledBitmapWithRatio(Bitmap bitmap, float thumbWidth, boolean recycle) {
Bitmap thumb;
int imagw = bitmap.getWidth();
int imagh = bitmap.getHeight();
if (imagh > imagw) {
int height = (int) (imagh * thumbWidth / imagw);
thumb = Bitmap.createScaledBitmap(bitmap, (int) thumbWidth, height, true);
} else {
int width = (int) (imagw * thumbWidth / imagh);
thumb = Bitmap.createScaledBitmap(bitmap, width, (int) thumbWidth, true);
}
if (recycle) {
bitmap.recycle();
}
return thumb;
}
public static Bitmap createScaledBitmap(Bitmap bitmap, int size, boolean recycle) {
Bitmap thumb;
thumb = Bitmap.createScaledBitmap(bitmap, size, size, true);
if (recycle) {
bitmap.recycle();
}
return thumb;
}
public static Bitmap createScaledBitmapWithRatio(Bitmap bitmap, int maxLength, boolean recycle) {
Bitmap result = bitmap;
while (true) {
double ratio = ((double) maxLength) / result.getByteCount();
double width = result.getWidth() * Math.sqrt(ratio);
double height = result.getHeight() * Math.sqrt(ratio);
Bitmap tmp = Bitmap.createScaledBitmap(result, (int) width, (int) height, true);
if (result != bitmap) {
result.recycle();
}
result = tmp;
if (result.getByteCount() < maxLength) {
break;
}
}
if (recycle) {
bitmap.recycle();
}
return result;
}
public static byte[] bmpToByteArray(final Bitmap bmp, Bitmap.CompressFormat format, final boolean needRecycle) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
bmp.compress(format, 100, output);
if (needRecycle) {
bmp.recycle();
}
byte[] result = output.toByteArray();
try {
output.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
/*
* Copyright (C) 2018 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.utils;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
class Util {
private static final String TAG = "SDK_Sample.Util";
public static byte[] bmpToByteArray(final Bitmap bmp, final boolean needRecycle) {
return bmpToByteArray(bmp, CompressFormat.PNG, needRecycle);
}
public static byte[] bmpToCompressedByteArray(final Bitmap bmp, CompressFormat format, final boolean needRecycle) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
bmp.compress(format, 25, output);
if (needRecycle) {
bmp.recycle();
}
byte[] result = output.toByteArray();
try {
output.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static byte[] bmpToByteArray(final Bitmap bmp, CompressFormat format, final boolean needRecycle) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
bmp.compress(format, 100, output);
if (needRecycle) {
bmp.recycle();
}
byte[] result = output.toByteArray();
try {
output.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static byte[] bmpToByteArray2(final Bitmap bmp, final boolean needRecycle) {
int i;
int j;
if (bmp.getHeight() > bmp.getWidth()) {
i = bmp.getWidth();
j = bmp.getWidth();
} else {
i = bmp.getHeight();
j = bmp.getHeight();
}
Bitmap localBitmap = Bitmap.createBitmap(i, j, Bitmap.Config.ARGB_4444);
Canvas localCanvas = new Canvas(localBitmap);
while (true) {
localCanvas.drawBitmap(bmp, new Rect(0, 0, i, j), new Rect(0, 0, i, j), null);
if (needRecycle)
bmp.recycle();
ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
localBitmap.compress(Bitmap.CompressFormat.JPEG, 100,
localByteArrayOutputStream);
localBitmap.recycle();
byte[] arrayOfByte = localByteArrayOutputStream.toByteArray();
try {
localByteArrayOutputStream.close();
return arrayOfByte;
} catch (Exception e) {
// F.out(e);
}
i = bmp.getHeight();
j = bmp.getHeight();
}
}
public static byte[] getHtmlByteArray(final String url) {
URL htmlUrl = null;
InputStream inStream = null;
try {
htmlUrl = new URL(url);
URLConnection connection = htmlUrl.openConnection();
HttpURLConnection httpConnection = (HttpURLConnection) connection;
int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
inStream = httpConnection.getInputStream();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
byte[] data = inputStreamToByte(inStream);
return data;
}
public static byte[] inputStreamToByte(InputStream is) {
try {
ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
int ch;
while ((ch = is.read()) != -1) {
bytestream.write(ch);
}
byte imgdata[] = bytestream.toByteArray();
bytestream.close();
return imgdata;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] readFromFile(String fileName, int offset, int len) {
if (fileName == null) {
return null;
}
File file = new File(fileName);
if (!file.exists()) {
Log.i(TAG, "readFromFile: file not found");
return null;
}
if (len == -1) {
len = (int) file.length();
}
Log.d(TAG, "readFromFile : offset = " + offset + " len = " + len + " offset + len = " + (offset + len));
if (offset < 0) {
Log.e(TAG, "readFromFile invalid offset:" + offset);
return null;
}
if (len <= 0) {
Log.e(TAG, "readFromFile invalid len:" + len);
return null;
}
if (offset + len > (int) file.length()) {
Log.e(TAG, "readFromFile invalid file len:" + file.length());
return null;
}
byte[] b = null;
try {
RandomAccessFile in = new RandomAccessFile(fileName, "r");
b = new byte[len]; // ���������ļ���С������
in.seek(offset);
in.readFully(b);
in.close();
} catch (Exception e) {
Log.e(TAG, "readFromFile : errMsg = " + e.getMessage());
e.printStackTrace();
}
return b;
}
private static final int MAX_DECODE_PICTURE_SIZE = 1920 * 1440;
public static Bitmap extractThumbNail(final String path, final int height, final int width, final boolean crop) {
// Assert.assertTrue(path != null && !path.equals("") && height > 0 && width > 0);
BitmapFactory.Options options = new BitmapFactory.Options();
try {
options.inJustDecodeBounds = true;
Bitmap tmp = BitmapFactory.decodeFile(path, options);
if (tmp != null) {
tmp.recycle();
tmp = null;
}
Log.d(TAG, "extractThumbNail: round=" + width + "x" + height + ", crop=" + crop);
final double beY = options.outHeight * 1.0 / height;
final double beX = options.outWidth * 1.0 / width;
Log.d(TAG, "extractThumbNail: extract beX = " + beX + ", beY = " + beY);
options.inSampleSize = (int) (crop ? (beY > beX ? beX : beY) : (beY < beX ? beX : beY));
if (options.inSampleSize <= 1) {
options.inSampleSize = 1;
}
// NOTE: out of memory error
while (options.outHeight * options.outWidth / options.inSampleSize > MAX_DECODE_PICTURE_SIZE) {
options.inSampleSize++;
}
int newHeight = height;
int newWidth = width;
if (crop) {
if (beY > beX) {
newHeight = (int) (newWidth * 1.0 * options.outHeight / options.outWidth);
} else {
newWidth = (int) (newHeight * 1.0 * options.outWidth / options.outHeight);
}
} else {
if (beY < beX) {
newHeight = (int) (newWidth * 1.0 * options.outHeight / options.outWidth);
} else {
newWidth = (int) (newHeight * 1.0 * options.outWidth / options.outHeight);
}
}
options.inJustDecodeBounds = false;
Log.i(TAG, "bitmap required size=" + newWidth + "x" + newHeight + ", orig=" + options.outWidth + "x" + options.outHeight + ", sample=" + options.inSampleSize);
Bitmap bm = BitmapFactory.decodeFile(path, options);
if (bm == null) {
Log.e(TAG, "bitmap decode failed");
return null;
}
Log.i(TAG, "bitmap decoded size=" + bm.getWidth() + "x" + bm.getHeight());
final Bitmap scale = Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
if (scale != null) {
bm.recycle();
bm = scale;
}
if (crop) {
final Bitmap cropped = Bitmap.createBitmap(bm, (bm.getWidth() - width) >> 1, (bm.getHeight() - height) >> 1, width, height);
if (cropped == null) {
return bm;
}
bm.recycle();
bm = cropped;
Log.i(TAG, "bitmap croped size=" + bm.getWidth() + "x" + bm.getHeight());
}
return bm;
} catch (final OutOfMemoryError e) {
Log.e(TAG, "decode bitmap failed: " + e.getMessage());
options = null;
}
return null;
}
}
/* /*
* Copyright (C) 2018 The OpenFlutter Organization * Copyright (C) 2020 The OpenFlutter Organization
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -18,9 +18,8 @@ package com.jarvan.fluwx.wxapi ...@@ -18,9 +18,8 @@ package com.jarvan.fluwx.wxapi
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import com.jarvan.fluwx.handler.FluwxRequestHandler import com.jarvan.fluwx.handlers.FluwxResponseHandler
import com.jarvan.fluwx.handler.FluwxResponseHandler import com.jarvan.fluwx.handlers.WXAPiHandler
import com.jarvan.fluwx.handler.WXAPiHandler
import com.tencent.mm.opensdk.modelbase.BaseReq import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelbase.BaseResp import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
...@@ -28,20 +27,18 @@ import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler ...@@ -28,20 +27,18 @@ import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler { open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
// IWXAPI 是第三方app和微信通信的openapi接口 // IWXAPI 是第三方app和微信通信的openapi接口
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
try { try {
WXAPiHandler.wxApi?.handleIntent(intent, this) WXAPiHandler.wxApi?.handleIntent(intent, this)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
startSpecifiedActivity()
finish() finish()
} }
} }
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
...@@ -53,6 +50,7 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler { ...@@ -53,6 +50,7 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
WXAPiHandler.wxApi?.handleIntent(intent, this) WXAPiHandler.wxApi?.handleIntent(intent, this)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
startSpecifiedActivity()
finish() finish()
} }
} }
...@@ -61,11 +59,9 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler { ...@@ -61,11 +59,9 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
override fun onReq(baseReq: BaseReq) { override fun onReq(baseReq: BaseReq) {
// FIXME: 可能是官方的Bug,从微信拉起APP的Intent类型不对,无法跳转回Flutter Activity // FIXME: 可能是官方的Bug,从微信拉起APP的Intent类型不对,无法跳转回Flutter Activity
// 稳定复现场景:微信版本为7.0.5,小程序SDK为2.7.7 // 稳定复现场景:微信版本为7.0.5,小程序SDK为2.7.7
val activity = FluwxRequestHandler.getRegistrar()?.activity() if (baseReq.type == 4) {
if (baseReq.type == 4 && activity is Activity) {
// com.tencent.mm.opensdk.constants.ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX = 4 // com.tencent.mm.opensdk.constants.ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX = 4
startActivity(Intent(this, activity::class.java)) startSpecifiedActivity()
finish()
} }
} }
...@@ -75,5 +71,10 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler { ...@@ -75,5 +71,10 @@ open class FluwxWXEntryActivity : Activity(), IWXAPIEventHandler {
finish() finish()
} }
private fun startSpecifiedActivity() {
Intent("$packageName.FlutterActivity").run {
startActivity(this)
}
finish()
}
} }
\ No newline at end of file
/*
* Copyright (C) 2018 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
open class WXEntryActivity : FluwxWXEntryActivity()
\ No newline at end of file
/*
* Copyright (C) 2018 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
open class WXPayEntryActivity : FluwxWXEntryActivity()
\ No newline at end of file
package com.jarvan.fluwx
class FluwxTest {
fun test(){}
}
\ No newline at end of file
## AUTH BY QR CODE
When WeChat not installed on devices, user can login with WeChat-QRCode.
### Flutter
Request QRCode by the following codes:
```dart
fluwx.authByQRCode(
appId: "wxd930ea5d5a258f4f",
scope: "noncestr",
nonceStr: "nonceStr",
timeStamp: "1417508194",
signature: "429eaaa13fd71efbc3fd344d0a9a9126835e7303");
}
```
[What do the params mean?](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=215238808828h4XN&token=&lang=zh_CN)
You can receive the data from WeChat through these ways:
```dart
//listening your auth status
fluwx.onAuthByQRCodeFinished.listen((data){
setState(() {
_status =
"errorCode=>${data.errorCode}\nauthCode=>${data.authCode}";
});
});
//show your qrcode after this
fluwx.onAuthGotQRCode.listen((image) {
setState(() {
_image = image;
});
});
//qrcode was scanned
fluwx.onQRCodeScanned.listen((scanned) {
setState(() {
_status = "scanned";
});
});
```
### Android
WeChatSDK will crash due to `HttpClient` when running on Android P,see this [solution](https://cloud.tencent.com/developer/ask/146536) for help .
## 二维码登录
如果用户没有安装微信,我们可以通过扫描二维码登录.
### Flutter
请求一个二维码:
```dart
fluwx.authByQRCode(
appId: "wxd930ea5d5a258f4f",
scope: "noncestr",
nonceStr: "nonceStr",
timeStamp: "1417508194",
signature: "429eaaa13fd71efbc3fd344d0a9a9126835e7303");
}
```
[这些参数都是什么意思?](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=215238808828h4XN&token=&lang=zh_CN)
通过以下方式监听扫描过程:
```dart
//当扫码完成
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";
});
});
```
### Android
在Android P上,微信SDK会闪退, 查看[该文章](https://cloud.tencent.com/developer/ask/146536)以寻求帮助.
## SIGN AUTO-DEDUCT
see [details](https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_5&index=2).
```dart
import 'package:fluwx/fluwx.dart' as fluwx;
fluwx.autoDeDuct(
appId: "",
mchId: "",
planId: "",
contractCode: "",
requestSerial: "",
contractDisplayAccount: "",
notifyUrl: "",
version: "",
sign: "",
timestamp: "",
returnApp: '3');
```
\ No newline at end of file
## 签约微信免密支付
查看 [详情](https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_5&index=2).
```dart
import 'package:fluwx/fluwx.dart' as fluwx;
fluwx.autoDeDuct(
appId: "",
mchId: "",
planId: "",
contractCode: "",
requestSerial: "",
contractDisplayAccount: "",
notifyUrl: "",
version: "",
sign: "",
timestamp: "",
returnApp: '3');
```
\ No newline at end of file
## LAUNCH MINI-PROGRAM
`Fluwx` can launch mini-program now.
```dart
import 'package:fluwx/fluwx.dart' as fluwx;
fluwx.launchMiniProgram(
username: "gh_d43f693ca31f"
);
```
\ No newline at end of file
## LAUNCH MINI-PROGRAM
`Fluwx` 现在可以拉起小程序了.
```dart
import 'package:fluwx/fluwx.dart' as fluwx;
fluwx.launchMiniProgram(
username: "gh_d43f693ca31f"
);
```
\ No newline at end of file
## 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)
#### 微信登录不能触发fluwx.responseFromAuth.listen监听回调
请先看文档:[微信调回](https://github.com/OpenFlutter/fluwx/blob/master/doc/RESPONSE_CN.md) 。Android端如果是混合开发,请注册```WXEntryActivity```and```WXPayEntryActivity```;从1.0.0以后iOS端不需要重写```AppDelegate```中的相关方法,如果需要请务必调用相应super方法。
#### 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;```
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论