提交 96ee83d2 authored 作者: JarvanMo's avatar JarvanMo

merge

## 2.1.0
* Specifying supported platforms
* Fix: Android分享小程序时,缩略图压缩过重的问题
* 更改分享文件的实现形式
## 2.0.9
* Android SDK 升级到6.6.4
* iOS SDK升级到1.8.7.1
......
......@@ -4,7 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>
<activity
......@@ -39,5 +39,15 @@
<data android:scheme="sdksample" />
</intent-filter>
</activity-alias>
<provider
android:name="com.jarvan.fluwx.FluwxFileProvider"
android:authorities="${applicationId}.fluwxprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/fluwx_file_provider_paths" />
</provider>
</application>
</manifest>
package com.jarvan.fluwx
import androidx.core.content.FileProvider
/***
* Created by mo on 2020/5/13
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class FluwxFileProvider: FileProvider()
\ No newline at end of file
......@@ -2,15 +2,14 @@ package com.jarvan.fluwx.handlers
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.AssetFileDescriptor
import android.net.Uri
import android.text.TextUtils
import androidx.core.content.ContextCompat
import com.jarvan.fluwx.io.ImagesIO
import com.jarvan.fluwx.io.ImagesIOIml
import com.jarvan.fluwx.io.WeChatImage
import com.jarvan.fluwx.io.toExternalCacheFile
import androidx.core.content.FileProvider
import com.jarvan.fluwx.io.*
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelmsg.*
import io.flutter.embedding.engine.plugins.FlutterPlugin
......@@ -18,16 +17,17 @@ import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry
import kotlinx.coroutines.*
import java.io.File
import java.util.*
import kotlin.coroutines.CoroutineContext
/***
* Created by mo on 2020/3/6
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
internal class FluwxShareHandlerEmbedding(private val flutterAssets: FlutterPlugin.FlutterAssets, override val context: Context) : FluwxShareHandler {
override val assetFileDescriptor: (String) -> AssetFileDescriptor = {
val uri = Uri.parse(it)
val packageName = uri.getQueryParameter("package")
......@@ -65,6 +65,7 @@ internal class FluwxShareHandlerCompat(private val registrar: PluginRegistry.Reg
internal interface FluwxShareHandler : CoroutineScope {
companion object {
const val SHARE_IMAGE_THUMB_LENGTH = 32 * 1024
const val SHARE_MINI_PROGRAM_THUMB_LENGTH = 120 * 1024
private const val keyTitle = "title"
private const val keyThumbnail = "thumbnail"
private const val keyDescription = "description"
......@@ -113,7 +114,7 @@ internal interface FluwxShareHandler : CoroutineScope {
msg.description = call.argument(keyDescription) // 小程序消息desc
launch {
msg.thumbData = readThumbnailByteArray(call)
msg.thumbData = readThumbnailByteArray(call, length = SHARE_MINI_PROGRAM_THUMB_LENGTH)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
......@@ -125,7 +126,7 @@ internal interface FluwxShareHandler : CoroutineScope {
private fun shareImage(call: MethodCall, result: MethodChannel.Result) {
launch {
val map: Map<String, Any> = call.argument("source") ?: mapOf()
val sourceImage = WeChatImage.createWeChatImage(map, assetFileDescriptor)
val sourceImage = WeChatFile.createWeChatFile(map, assetFileDescriptor)
val thumbData = readThumbnailByteArray(call)
val sourceByteArray = sourceImage.readByteArray()
......@@ -133,8 +134,11 @@ internal interface FluwxShareHandler : CoroutineScope {
sourceByteArray.isEmpty() -> {
WXImageObject()
}
sourceByteArray.size > 512 * 1024 -> {
sourceByteArray.size > 500 * 1024 -> {
WXImageObject().apply {
if (supportFileProvider && targetHigherThanN) {
setImagePath(getFileContentUri(sourceByteArray.toCacheFile(context, sourceImage.suffix)))
} else {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
setImagePath(sourceByteArray.toExternalCacheFile(context, sourceImage.suffix)?.absolutePath)
} else {
......@@ -142,6 +146,7 @@ internal interface FluwxShareHandler : CoroutineScope {
}
}
}
}
else -> {
WXImageObject(sourceByteArray)
}
......@@ -150,7 +155,7 @@ internal interface FluwxShareHandler : CoroutineScope {
msg.mediaObject = imageObject
msg.thumbData = thumbData
msg.description = call.argument<String>(keyDescription)
msg.description = call.argument(keyDescription)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
......@@ -227,15 +232,30 @@ internal interface FluwxShareHandler : CoroutineScope {
}
private fun shareFile(call: MethodCall, result: MethodChannel.Result) {
val file = WXFileObject()
val filePath: String? = call.argument("filePath")
file.filePath = filePath
launch {
val wxFileObject = WXFileObject()
// val filePath: String? = call.argument("filePath")
// wxFileObject.filePath = filePath
val msg = WXMediaMessage()
msg.mediaObject = file
msg.mediaObject = wxFileObject
msg.description = call.argument("description")
launch {
val map: Map<String, Any> = call.argument("source") ?: mapOf()
val sourceFile = WeChatFile.createWeChatFile(map, assetFileDescriptor)
val sourceByteArray = sourceFile.readByteArray()
// if (supportFileProvider && targetHigherThanN) {
// wxFileObject.filePath = getFileContentUri(sourceByteArray.toCacheFile(context, sourceFile.suffix))
// } else {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
wxFileObject.filePath = sourceByteArray.toExternalCacheFile(context, sourceFile.suffix)?.absolutePath
} else {
permissionHandler?.requestStoragePermission()
}
// }
msg.thumbData = readThumbnailByteArray(call)
val req = SendMessageToWX.Req()
setCommonArguments(call, req, msg)
......@@ -248,24 +268,24 @@ internal interface FluwxShareHandler : CoroutineScope {
result.success(WXAPiHandler.wxApi?.sendReq(request))
}
private suspend fun readThumbnailByteArray(call: MethodCall): ByteArray? {
private suspend fun readThumbnailByteArray(call: MethodCall, length: Int = SHARE_IMAGE_THUMB_LENGTH): ByteArray? {
val thumbnailMap: Map<String, Any>? = call.argument(keyThumbnail)
return thumbnailMap?.run {
val thumbnailImage = WeChatImage.createWeChatImage(thumbnailMap, assetFileDescriptor)
val thumbnailImage = WeChatFile.createWeChatFile(thumbnailMap, assetFileDescriptor)
val thumbnailImageIO = ImagesIOIml(thumbnailImage)
compressThumbnail(thumbnailImageIO)
compressThumbnail(thumbnailImageIO, length)
}
}
private suspend fun compressThumbnail(ioIml: ImagesIO) = ioIml.compressedByteArray(context, SHARE_IMAGE_THUMB_LENGTH)
private suspend fun compressThumbnail(ioIml: ImagesIO, length: Int) = ioIml.compressedByteArray(context, length)
// SESSION, TIMELINE, FAVORITE
private fun setCommonArguments(call: MethodCall, req: SendMessageToWX.Req, msg: WXMediaMessage) {
msg.messageAction = call.argument<String?>("messageAction")
msg.messageExt = call.argument<String?>("messageExt")
msg.mediaTagName = call.argument<String?>("mediaTagName")
msg.title = call.argument<String?>(keyTitle)
msg.description = call.argument<String?>(keyDescription)
msg.messageAction = call.argument("messageAction")
msg.messageExt = call.argument("messageExt")
msg.mediaTagName = call.argument("mediaTagName")
msg.title = call.argument(keyTitle)
msg.description = call.argument(keyDescription)
req.transaction = UUID.randomUUID().toString().replace("-", "")
val sceneIndex = call.argument<Int?>("scene")
req.scene = when (sceneIndex) {
......@@ -276,6 +296,25 @@ internal interface FluwxShareHandler : CoroutineScope {
}
}
private fun getFileContentUri(file: File?): String? {
if (file == null || !file.exists())
return null
val contentUri = FileProvider.getUriForFile(context,
"${context.packageName}.fluwxprovider", // 要与`AndroidManifest.xml`里配置的`authorities`一致,假设你的应用包名为com.example.app
file)
// 授权给微信访问路径
context.grantUriPermission("com.tencent.mm", // 这里填微信包名
contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
return contentUri.toString() // contentUri.toString() 即是以"content://"开头的用于共享的路径
}
private val supportFileProvider: Boolean get() = WXAPiHandler.wxApi?.wxAppSupportAPI ?: 0 >= 0x27000D00
private val targetHigherThanN: Boolean get() = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N
val context: Context
val assetFileDescriptor: (String) -> AssetFileDescriptor
......@@ -289,3 +328,4 @@ internal interface FluwxShareHandler : CoroutineScope {
fun onDestroy() = job.cancel()
}
package com.jarvan.fluwx.io
import android.content.Context
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okio.*
import java.io.*
import java.util.*
/***
* Created by mo on 2020/5/13
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
private const val cachePathName = "fluwxSharedData"
internal suspend fun ByteArray.toExternalCacheFile(context: Context, suffix: String): File? {
var file: File? = null
val externalFile = context.externalCacheDir ?: return file
val dir = File(externalFile.absolutePath + File.separator + cachePathName).apply {
if (!exists()) {
mkdirs()
}
}
file = File(dir.absolutePath + File.separator + UUID.randomUUID().toString() + suffix)
return saveToLocal(this, file)
}
internal suspend fun ByteArray.toCacheFile(context: Context, suffix: String): File? {
var file: File? = null
val externalFile = context.cacheDir ?: return file
val dir = File(externalFile.absolutePath + File.separator + cachePathName).apply {
if (!exists()) {
mkdirs()
}
}
file = File(dir.absolutePath + File.separator + UUID.randomUUID().toString() + suffix)
return saveToLocal(this, file)
}
private suspend fun saveToLocal(byteArray: ByteArray, file: File): File? {
return withContext(Dispatchers.IO) {
var sink: BufferedSink? = null
var source: Source? = null
var outputStream: OutputStream? = null
try {
outputStream = FileOutputStream(file)
sink = outputStream.sink().buffer()
source = ByteArrayInputStream(byteArray).source()
sink.writeAll(source)
sink.flush()
} catch (e: IOException) {
Log.w("Fluwx", "failed to create cache files")
} finally {
sink?.close()
source?.close()
outputStream?.close()
}
file
}
}
......@@ -4,7 +4,6 @@ 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.*
......@@ -18,7 +17,7 @@ import kotlin.math.sqrt
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class ImagesIOIml(override val image: WeChatImage) : ImagesIO {
class ImagesIOIml(override val image: WeChatFile) : ImagesIO {
override suspend fun readByteArray(): ByteArray = image.readByteArray()
......@@ -115,41 +114,7 @@ class ImagesIOIml(override val image: WeChatImage) : ImagesIO {
}
interface ImagesIO {
val image: WeChatImage
val image: WeChatFile
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
......@@ -13,14 +13,14 @@ import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
/***
* Created by mo on 2020/3/7
* Created by mo on 2020/5/13
* 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/
class WeChatFileImage(override val source: Any, override val suffix: String) : WeChatImage {
class WeChatFileFile(override val source: Any, override val suffix: String) : WeChatFile {
private var internalSource: File
init {
......@@ -46,7 +46,7 @@ class WeChatFileImage(override val source: Any, override val suffix: String) : W
}
}
private class WeChatAssetImage(override val source: Any, override val suffix: String) : WeChatImage {
private class WeChatAssetFile(override val source: Any, override val suffix: String) : WeChatFile {
private var internalSource: AssetFileDescriptor
init {
......@@ -72,7 +72,7 @@ private class WeChatAssetImage(override val source: Any, override val suffix: St
}
}
private class WeChatNetworkImage(override val source: Any, override val suffix: String) : WeChatImage {
private class WeChatNetworkFile(override val source: Any, override val suffix: String) : WeChatFile {
private var internalSource: String
init {
......@@ -100,7 +100,7 @@ private class WeChatNetworkImage(override val source: Any, override val suffix:
}
}
private class WeChatMemoryImage(override val source: Any, override val suffix: String) : WeChatImage {
private class WeChatMemoryFile(override val source: Any, override val suffix: String) : WeChatFile {
private var internalSource: ByteArray
init {
......@@ -113,7 +113,7 @@ private class WeChatMemoryImage(override val source: Any, override val suffix: S
override suspend fun readByteArray(): ByteArray = internalSource
}
interface WeChatImage {
interface WeChatFile {
val source: Any
val suffix: String
......@@ -124,16 +124,16 @@ interface WeChatImage {
// ASSET,
// FILE,
// BINARY,
fun createWeChatImage(params: Map<String, Any>, assetFileDescriptor: (String) -> AssetFileDescriptor): WeChatImage {
fun createWeChatFile(params: Map<String, Any>, assetFileDescriptor: (String) -> AssetFileDescriptor): WeChatFile {
// Map toMap() => {"source": source, "schema": schema.index, "suffix": suffix};
val suffix = (params["suffix"] as String?) ?: ".jpeg"
return when ((params["schema"] as? Int) ?: 0) {
0 -> WeChatNetworkImage(source = (params["source"] as? String).orEmpty(), suffix = suffix)
1 -> WeChatAssetImage(source = assetFileDescriptor(((params["source"] as? String).orEmpty())), suffix = suffix)
2 -> WeChatFileImage(source = File((params["source"] as? String).orEmpty()), suffix = suffix)
3 -> WeChatMemoryImage(source = (params["source"] as? ByteArray)
0 -> WeChatNetworkFile(source = (params["source"] as? String).orEmpty(), suffix = suffix)
1 -> WeChatAssetFile(source = assetFileDescriptor(((params["source"] as? String).orEmpty())), suffix = suffix)
2 -> WeChatFileFile(source = File((params["source"] as? String).orEmpty()), suffix = suffix)
3 -> WeChatMemoryFile(source = (params["source"] as? ByteArray)
?: byteArrayOf(), suffix = suffix)
else -> WeChatNetworkImage(source = (params["source"] as? String).orEmpty(), suffix = suffix)
else -> WeChatNetworkFile(source = (params["source"] as? String).orEmpty(), suffix = suffix)
}
}
}
......
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="fluwxSharedData" path="fluwxSharedData/"/>
</paths>
\ No newline at end of file
......@@ -11,7 +11,6 @@ class _ShareImagePageState extends State<ShareImagePage> {
String _imagePath =
// "http://img-download.pchome.net/download/1k1/3a/3e/ofskcd-s1a.jpg"
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1534614311230&di=b17a892b366b5d002f52abcce7c4eea0&imgtype=0&src=http%3A%2F%2Fimg.mp.sohu.com%2Fupload%2F20170516%2F51296b2673704ae2992d0a28c244274c_th.png";
String _thumbnail = "assets://images/logo.png";
String _response = "";
......
......@@ -76,7 +76,7 @@ class _ShareMiniProgramPageState extends State<ShareMiniProgramPage> {
),
new TextField(
controller:
TextEditingController(text: "assets://images/logo.png"),
TextEditingController(text: "images/logo.png"),
onChanged: (str) {
_thumbnail = str;
},
......
......@@ -12,7 +12,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
String _musicLowBandUrl = "http://www.qq.com";
String _title = "Beyond";
String _description = "A Popular Rock Band From China";
String _thumnail = "assets://images/logo.png";
String _thumnail = "images/logo.png";
WeChatScene scene = WeChatScene.SESSION;
@override
......@@ -71,7 +71,7 @@ class _ShareMusicPageState extends State<ShareMusicPage> {
),
new TextField(
controller:
TextEditingController(text: "assets://images/logo.png"),
TextEditingController(text: "images/logo.png"),
onChanged: (str) {
_thumnail = str;
},
......
......@@ -11,7 +11,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
String _videoLowBandUrl = "http://www.qq.com";
String _title = "Beyond";
String _description = "A Popular Rock Band From China";
String _thumnail = "assets://images/logo.png";
String _thumnail = "images/logo.png";
WeChatScene scene = WeChatScene.SESSION;
@override
......@@ -70,7 +70,7 @@ class _ShareMusicPageState extends State<ShareVideoPage> {
),
new TextField(
controller:
TextEditingController(text: "assets://images/logo.png"),
TextEditingController(text: "images/logo.png"),
onChanged: (str) {
_thumnail = str;
},
......
......@@ -11,7 +11,7 @@ class ShareWebPagePage extends StatefulWidget {
class ShareWebPagePageState extends State<ShareWebPagePage> {
String _url = "share text from fluwx";
String _title = "Fluwx";
String _thumnail = "assets://images/logo.png";
String _thumnail = "images/logo.png";
WeChatScene scene = WeChatScene.SESSION;
@override
......@@ -54,7 +54,7 @@ class ShareWebPagePageState extends State<ShareWebPagePage> {
),
new TextField(
controller:
TextEditingController(text: "assets://images/logo.png"),
TextEditingController(text: "images/logo.png"),
onChanged: (str) {
_thumnail = str;
},
......
......@@ -28,6 +28,7 @@ NSString *const fluwxKeySession = @"session";
NSString *const fluwxKeyFavorite = @"favorite";
NSString *const keySource = @"source";
NSString *const keySuffix = @"suffix";
CGFloat thumbnailWidth;
......@@ -75,9 +76,17 @@ NSObject <FlutterPluginRegistrar> *_fluwxRegistrar;
dispatch_async(globalQueue, ^{
NSDictionary *sourceImage = call.arguments[keySource];
NSData *sourceImageData = [self getNsDataFromWeChatImage:sourceImage];
NSData *sourceImageData = [self getNsDataFromWeChatFile:sourceImage];
UIImage *thumbnailImage = [self getCommonThumbnail:call];
UIImage *realThumbnailImage;
if (thumbnailImage == nil) {
NSString *suffix = sourceImage[@"suffix"];
BOOL isPNG = [self isPNG:suffix];
realThumbnailImage = [self getThumbnailFromNSData:sourceImageData size:defaultThumbnailSize isPNG:isPNG];
} else {
realThumbnailImage = thumbnailImage;
}
dispatch_async(dispatch_get_main_queue(), ^{
......@@ -86,7 +95,7 @@ NSObject <FlutterPluginRegistrar> *_fluwxRegistrar;
TagName:call.arguments[fluwxKeyMediaTagName]
MessageExt:call.arguments[fluwxKeyMessageExt]
Action:call.arguments[fluwxKeyMessageAction]
ThumbImage:thumbnailImage
ThumbImage:realThumbnailImage
InScene:[self intToWeChatScene:scene]
title:call.arguments[fluwxKeyTitle]
description:call.arguments[fluwxKeyDescription]
......@@ -184,15 +193,22 @@ NSObject <FlutterPluginRegistrar> *_fluwxRegistrar;
- (void)shareFile:(FlutterMethodCall *)call result:(FlutterResult)result {
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
NSDictionary *sourceFile = call.arguments[keySource];
UIImage *thumbnailImage = [self getCommonThumbnail:call];
NSString *fileExtension;
NSString *suffix = sourceFile[keySuffix];
fileExtension = suffix;
if ([suffix hasPrefix:@"."]) {
NSRange range = NSMakeRange(0, 1);
fileExtension = [suffix stringByReplacingCharactersInRange:range withString:@""];
}
NSString *filePath = call.arguments[@"filePath"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSData *data = [self getNsDataFromWeChatFile:sourceFile];
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *scene = call.arguments[fluwxKeyScene];
[WXApiRequestHandler sendFileData:data
fileExtension:call.arguments[@"fileExtension"]
fileExtension:fileExtension
Title:call.arguments[fluwxKeyTitle]
Description:call.arguments[fluwxKeyDescription]
ThumbImage:thumbnailImage
......@@ -213,8 +229,8 @@ NSObject <FlutterPluginRegistrar> *_fluwxRegistrar;
NSData *hdImageData = nil;
NSDictionary *hdImagePath = call.arguments[@"hdImagePath"];
if (hdImageData != nil && hdImagePath != (id) [NSNull null]) {
NSData *imageData = [self getNsDataFromWeChatImage:hdImagePath];
if (hdImagePath != (id) [NSNull null]) {
NSData *imageData = [self getNsDataFromWeChatFile:hdImagePath];
NSString *suffix = hdImagePath[@"suffix"];
BOOL isPNG = [self isPNG:suffix];
UIImage *uiImage = [self getThumbnailFromNSData:imageData size:120 * 1024 isPNG:isPNG];
......@@ -268,7 +284,7 @@ NSObject <FlutterPluginRegistrar> *_fluwxRegistrar;
}
NSString *suffix = thumbnail[@"suffix"];
NSData *thumbnailData = [self getNsDataFromWeChatImage:thumbnail];
NSData *thumbnailData = [self getNsDataFromWeChatFile:thumbnail];
UIImage *thumbnailImage = [self getThumbnailFromNSData:thumbnailData size:defaultThumbnailSize isPNG:[self isPNG:suffix]];
return thumbnailImage;
}
......@@ -279,22 +295,22 @@ NSObject <FlutterPluginRegistrar> *_fluwxRegistrar;
// FILE,
// BINARY,
//}
- (NSData *)getNsDataFromWeChatImage:(NSDictionary *)weChatImage {
NSNumber *schema = weChatImage[@"schema"];
- (NSData *)getNsDataFromWeChatFile:(NSDictionary *)weChatFile {
NSNumber *schema = weChatFile[@"schema"];
if ([schema isEqualToNumber:@0]) {
NSString *source = weChatImage[keySource];
NSString *source = weChatFile[keySource];
NSURL *imageURL = [NSURL URLWithString:source];
//下载图片
return [NSData dataWithContentsOfURL:imageURL];
} else if ([schema isEqualToNumber:@1]) {
NSString *source = weChatImage[keySource];
return [NSData dataWithContentsOfFile:[self readImageFromAssets:source]];
NSString *source = weChatFile[keySource];
return [NSData dataWithContentsOfFile:[self readFileFromAssets:source]];
} else if ([schema isEqualToNumber:@2]) {
NSString *source = weChatImage[keySource];
NSString *source = weChatFile[keySource];
return [NSData dataWithContentsOfFile:source];
} else if ([schema isEqualToNumber:@3]) {
FlutterStandardTypedData *imageData = weChatImage[@"source"];
FlutterStandardTypedData *imageData = weChatFile[@"source"];
return imageData.data;
} else {
return nil;
......@@ -306,7 +322,7 @@ NSObject <FlutterPluginRegistrar> *_fluwxRegistrar;
return [ThumbnailHelper compressImage:uiImage toByte:size isPNG:isPNG];
}
- (NSString *)readImageFromAssets:(NSString *)imagePath {
- (NSString *)readFileFromAssets:(NSString *)imagePath {
NSArray *array = [self formatAssets:imagePath];
NSString *key;
if ([FluwxStringUtil isBlank:array[1]]) {
......
......@@ -8,4 +8,4 @@ export 'package:fluwx/src/fluwx_iml.dart';
export 'package:fluwx/src/response/wechat_response.dart';
export 'package:fluwx/src/share/share_models.dart';
export 'package:fluwx/src/wechat_enums.dart';
export 'package:fluwx/src/wechat_image.dart' hide ImageSchema;
export 'package:fluwx/src/wechat_file.dart' hide FileSchema;
......@@ -271,7 +271,6 @@ class WeChatShareWebPageModel implements WeChatShareBaseModel {
this.messageAction,
this.messageExt})
: assert(webPage != null && webPage.isNotEmpty),
assert(thumbnail != null),
assert(scene != null),
this.description = description ?? webPage;
......@@ -289,10 +288,11 @@ class WeChatShareWebPageModel implements WeChatShareBaseModel {
}
}
/// [source] the file you want to share, [source.suffix] is necessary on iOS.
/// [scene] can't be [WeChatScene.TIMELINE], otherwise, sharing nothing.
/// send files to WeChat
class WeChatShareFileModel implements WeChatShareBaseModel {
final String filePath;
final String fileExtension;
final WeChatFile source;
final WeChatImage thumbnail;
final String title;
final String description;
......@@ -301,24 +301,22 @@ class WeChatShareFileModel implements WeChatShareBaseModel {
final String messageAction;
final String mediaTagName;
WeChatShareFileModel(this.filePath,
{this.fileExtension: "pdf",
this.title: "",
WeChatShareFileModel(this.source,
{this.title: "",
this.description: "",
this.thumbnail,
this.scene = WeChatScene.SESSION,
this.mediaTagName,
this.messageAction,
this.messageExt})
: assert(filePath != null),
: assert(source != null),
assert(scene != null);
@override
Map toMap() {
return {
_scene: scene.index,
"filePath": filePath,
"fileExtension": fileExtension,
_source: source.toMap(),
_thumbnail: thumbnail?.toMap(),
_title: title,
_description: description,
......
......@@ -19,38 +19,54 @@
import 'dart:io';
import 'dart:typed_data';
///[suffix] shall be started with .
class WeChatImage {
const String defaultSuffixJpeg = ".jpeg";
const String defaultSuffixTxt = ".txt";
class WeChatImage extends WeChatFile {
WeChatImage.network(String source, {String suffix = defaultSuffixJpeg})
: super.network(source, suffix: suffix);
WeChatImage.asset(String source, {String suffix = defaultSuffixJpeg})
: super.asset(source, suffix: suffix);
WeChatImage.file(File source, {String suffix = defaultSuffixJpeg})
: super.file(source, suffix: suffix);
WeChatImage.binary(Uint8List source, {String suffix = defaultSuffixJpeg})
: super.binary(source, suffix: suffix);
}
class WeChatFile {
final dynamic source;
final ImageSchema schema;
final FileSchema schema;
final String suffix;
/// [source] must begin with http or https
WeChatImage.network(String source, {String suffix})
WeChatFile.network(String source, {String suffix})
: assert(source != null && source.startsWith("http")),
this.source = source,
this.schema = ImageSchema.NETWORK,
this.suffix = source.readSuffix(suffix);
this.schema = FileSchema.NETWORK,
this.suffix = source.readSuffix(suffix, defaultSuffixTxt);
///[source] path of the image, like '/asset/image.jpeg?package=flutter',
///[source] path of the image, like '/asset/image.pdf?package=flutter',
///the query param package in [source] only available when you want to specify the package of image
WeChatImage.asset(String source, {String suffix})
WeChatFile.asset(String source, {String suffix})
: assert(source != null && source.trim().isNotEmpty),
this.source = source,
this.schema = ImageSchema.ASSET,
this.suffix = source.readSuffix(suffix);
this.schema = FileSchema.ASSET,
this.suffix = source.readSuffix(suffix, defaultSuffixTxt);
WeChatImage.file(File source, {String suffix = ".jpeg"})
WeChatFile.file(File source, {String suffix = defaultSuffixTxt})
: assert(source != null),
this.source = source.path,
this.schema = ImageSchema.FILE,
this.suffix = source.path.readSuffix(suffix);
this.schema = FileSchema.FILE,
this.suffix = source.path.readSuffix(suffix, defaultSuffixTxt);
WeChatImage.binary(Uint8List source, {String suffix = ".jpeg"})
WeChatFile.binary(Uint8List source, {String suffix = defaultSuffixTxt})
: assert(source != null),
assert(suffix != null && suffix.trim().isNotEmpty),
this.source = source,
this.schema = ImageSchema.BINARY,
this.schema = FileSchema.BINARY,
this.suffix = suffix;
Map toMap() =>
......@@ -58,23 +74,29 @@ class WeChatImage {
}
///Types of image, usually there are for types listed below.
///[ImageSchema.NETWORK] is online images.
///[ImageSchema.ASSET] is flutter asset image.
///[ImageSchema.BINARY] is binary image, shall be be [Uint8List]
///[ImageSchema.FILE] is local file, usually not comes from flutter asset.
enum ImageSchema {
///[FileSchema.NETWORK] is online images.
///[FileSchema.ASSET] is flutter asset image.
///[FileSchema.BINARY] is binary image, shall be be [Uint8List]
///[FileSchema.FILE] is local file, usually not comes from flutter asset.
enum FileSchema {
NETWORK,
ASSET,
FILE,
BINARY,
}
extension _ImageSuffix on String {
extension _FileSuffix on String {
/// returns [suffix] if [suffix] not blank.
/// If [suffix] is blank, then try to read from url
/// if suffix in url not found, then return jpg as default.
String readSuffix(String suffix) {
if (suffix != null && suffix.trim().isNotEmpty) return suffix;
String readSuffix(String suffix, String defaultSuffix) {
if (suffix != null && suffix.trim().isNotEmpty) {
if (suffix.startsWith(".")) {
return suffix;
} else {
return ".$suffix";
}
}
var path = Uri.parse(this).path;
var index = path.lastIndexOf(".");
......@@ -82,6 +104,6 @@ extension _ImageSuffix on String {
if (index >= 0) {
return path.substring(index);
}
return ".jpeg";
return defaultSuffix;
}
}
name: fluwx
description: The capability of implementing WeChat SDKs in Flutter. With Fluwx, developers can use WeChatSDK easily, such as sharing, payment, lanuch mini program and etc.
version: 2.0.9
version: 2.1.0
homepage: https://github.com/JarvanMo/fluwx
environment:
......@@ -24,7 +24,11 @@ flutter:
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
androidPackage: com.jarvan.fluwx
platforms:
android:
package: com.jarvan.fluwx
pluginClass: FluwxPlugin
ios:
pluginClass: FluwxPlugin
# To add assets to your plugin package, add an assets section, like this:
......
......@@ -18,7 +18,7 @@
*/
import 'package:flutter_test/flutter_test.dart';
import 'package:fluwx/fluwx.dart';
import 'package:fluwx/src/wechat_image.dart';
import 'package:fluwx/src/wechat_file.dart';
void main() {
......@@ -27,20 +27,20 @@ void main() {
WeChatImage.network("http://image.openflutter.dev/fluwx.png");
expect(withSuffixImage.source, "http://image.openflutter.dev/fluwx.png");
expect(withSuffixImage.suffix, ".png");
expect(ImageSchema.NETWORK, withSuffixImage.schema);
expect(FileSchema.NETWORK, withSuffixImage.schema);
var withNoSuffixNoUrlSuffixImage =
WeChatImage.network("http://image.openflutter.dev/fluwx");
expect("http://image.openflutter.dev/fluwx",
withNoSuffixNoUrlSuffixImage.source);
expect(withNoSuffixNoUrlSuffixImage.suffix, ".jpeg");
expect(ImageSchema.NETWORK, withSuffixImage.schema);
expect(FileSchema.NETWORK, withSuffixImage.schema);
var withSpecifiedSuffixImage = WeChatImage.network(
"http://image.openflutter.dev/fluwx.jpeg",
suffix: ".png");
expect(withSpecifiedSuffixImage.source, "http://image.openflutter.dev/fluwx.jpeg");
expect(withSpecifiedSuffixImage.suffix, ".png");
expect(withSpecifiedSuffixImage.schema, ImageSchema.NETWORK);
expect(withSpecifiedSuffixImage.schema, FileSchema.NETWORK);
});
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论