提交 178d85fa authored 作者: JarvanMo's avatar JarvanMo

better support for sharing files

fix bug when sharing mini program on android
上级 42995b28
......@@ -7,7 +7,6 @@ import android.content.pm.PackageManager
import android.content.res.AssetFileDescriptor
import android.net.Uri
import android.text.TextUtils
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.jarvan.fluwx.io.*
......@@ -66,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"
......@@ -114,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)
......@@ -126,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()
......@@ -134,7 +134,7 @@ 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)))
......@@ -232,15 +232,27 @@ 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 msg = WXMediaMessage()
msg.mediaObject = file
msg.description = call.argument("description")
val wxFileObject = WXFileObject()
// val filePath: String? = call.argument("filePath")
// wxFileObject.filePath = filePath
launch {
val msg = WXMediaMessage()
msg.mediaObject = wxFileObject
msg.description = call.argument("description")
val map: Map<String, Any> = call.argument("source") ?: mapOf()
val sourceFile = WeChatFile.createWeChatFile(map, assetFileDescriptor)
val sourceByteArray = sourceFile.readByteArray()
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)
......@@ -253,16 +265,16 @@ 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) {
......
......@@ -37,7 +37,6 @@ internal suspend fun ByteArray.toCacheFile(context: Context, suffix: String): Fi
}
}
file = File(dir.absolutePath + File.separator + UUID.randomUUID().toString() + suffix)
return saveToLocal(this, 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,7 +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
}
......@@ -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)
}
}
}
......
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"fluwx","path":"/Users/mo/Code/Other/fluwx/","dependencies":[]}],"android":[{"name":"fluwx","path":"/Users/mo/Code/Other/fluwx/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"fluwx","dependencies":[]}],"date_created":"2020-05-12 17:06:39.777970","version":"1.17.0"}
\ No newline at end of file
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"fluwx","path":"/Users/mo/Code/Other/fluwx/","dependencies":[]}],"android":[{"name":"fluwx","path":"/Users/mo/Code/Other/fluwx/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"fluwx","dependencies":[]}],"date_created":"2020-05-14 12:16:23.046621","version":"1.17.0"}
\ No newline at end of file
......@@ -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;
......@@ -288,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;
......@@ -300,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,37 +19,54 @@
import 'dart:io';
import 'dart:typed_data';
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() =>
......@@ -57,22 +74,22 @@ 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) {
String readSuffix(String suffix, String defaultSuffix) {
if (suffix != null && suffix.trim().isNotEmpty) {
if (suffix.startsWith(".")) {
return suffix;
......@@ -87,6 +104,6 @@ extension _ImageSuffix on String {
if (index >= 0) {
return path.substring(index);
}
return ".jpeg";
return defaultSuffix;
}
}
......@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论