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

merge

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