提交 fb1af343 authored 作者: JarvanMo's avatar JarvanMo

优化localImagePath处理,减轻非Android开发者的上手难度

上级 d0c5d772
...@@ -13,6 +13,7 @@ import io.flutter.plugin.common.MethodCall ...@@ -13,6 +13,7 @@ import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.File import java.io.File
import java.io.IOException
import java.util.* import java.util.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
...@@ -22,7 +23,10 @@ import kotlin.coroutines.CoroutineContext ...@@ -22,7 +23,10 @@ import kotlin.coroutines.CoroutineContext
* 冷风如刀,以大地为砧板,视众生为鱼肉。 * 冷风如刀,以大地为砧板,视众生为鱼肉。
* 万里飞雪,将穹苍作烘炉,熔万物为白银。 * 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/ **/
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")
...@@ -108,9 +112,36 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -108,9 +112,36 @@ internal interface FluwxShareHandler : CoroutineScope {
imageData = it imageData = it
imgDataHash = imgHash imgDataHash = imgHash
} }
}?:run { } ?: run {
WXImageObject().apply { WXImageObject().apply {
imagePath = map["localImagePath"] as? String val localImagePath = map["localImagePath"] as? String
localImagePath?.also {
if (supportFileProvider && targetHigherThanN) {
if (localImagePath.startsWith("content://")) {
imagePath = localImagePath
} else {
val tempFile = File(localImagePath)
val ecd = context.externalCacheDir ?: return@also
val desPath =
ecd.absolutePath + File.separator + cachePathName
if (tempFile.exists()) {
val target = if (isFileInDirectory(
file = tempFile,
directory = File(desPath)
)
) {
tempFile
} else {
withContext(Dispatchers.IO) {
copyFile(tempFile.absolutePath, desPath)
}
}
imagePath = getFileContentUri(target)
}
}
}
}
imgDataHash = imgHash imgDataHash = imgHash
} }
} }
...@@ -209,9 +240,19 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -209,9 +240,19 @@ internal interface FluwxShareHandler : CoroutineScope {
wxFileObject.apply { wxFileObject.apply {
if (supportFileProvider && targetHigherThanN) { if (supportFileProvider && targetHigherThanN) {
setFilePath(getFileContentUri(sourceByteArray.toCacheFile(context, sourceFile.suffix))) setFilePath(
getFileContentUri(
sourceByteArray.toCacheFile(
context,
sourceFile.suffix
)
)
)
} else { } else {
filePath = sourceByteArray.toExternalCacheFile(context, sourceFile.suffix)?.absolutePath filePath = sourceByteArray.toExternalCacheFile(
context,
sourceFile.suffix
)?.absolutePath
} }
} }
...@@ -222,14 +263,20 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -222,14 +263,20 @@ internal interface FluwxShareHandler : CoroutineScope {
} }
} }
private suspend fun sendRequestInMain(result: MethodChannel.Result, request: BaseReq) = withContext(Dispatchers.Main) { private suspend fun sendRequestInMain(result: MethodChannel.Result, request: BaseReq) =
result.success(WXAPiHandler.wxApi?.sendReq(request)) withContext(Dispatchers.Main) {
} result.success(WXAPiHandler.wxApi?.sendReq(request))
}
private suspend fun compressThumbnail(ioIml: ImagesIO, length: Int) = ioIml.compressedByteArray(context, 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("messageAction") msg.messageAction = call.argument("messageAction")
call.argument<String?>("msgSignature")?.let { call.argument<String?>("msgSignature")?.let {
msg.msgSignature = it msg.msgSignature = it
...@@ -260,19 +307,24 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -260,19 +307,24 @@ internal interface FluwxShareHandler : CoroutineScope {
if (file == null || !file.exists()) if (file == null || !file.exists())
return null return null
val contentUri = FileProvider.getUriForFile(context, val contentUri = FileProvider.getUriForFile(
"${context.packageName}.fluwxprovider", // 要与`AndroidManifest.xml`里配置的`authorities`一致,假设你的应用包名为com.example.app context,
file) "${context.packageName}.fluwxprovider", // 要与`AndroidManifest.xml`里配置的`authorities`一致,假设你的应用包名为com.example.app
file
)
// 授权给微信访问路径 // 授权给微信访问路径
context.grantUriPermission("com.tencent.mm", // 这里填微信包名 context.grantUriPermission(
contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) "com.tencent.mm", // 这里填微信包名
contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
)
return contentUri.toString() // contentUri.toString() 即是以"content://"开头的用于共享的路径 return contentUri.toString() // contentUri.toString() 即是以"content://"开头的用于共享的路径
} }
private val supportFileProvider: Boolean get() = (WXAPiHandler.wxApi?.wxAppSupportAPI ?: 0) >= 0x27000D00 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 private val targetHigherThanN: Boolean get() = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N
val context: Context val context: Context
...@@ -287,3 +339,12 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -287,3 +339,12 @@ internal interface FluwxShareHandler : CoroutineScope {
fun onDestroy() = job.cancel() fun onDestroy() = job.cancel()
} }
private fun isFileInDirectory(file: File, directory: File): Boolean {
return try {
val filePath = file.canonicalPath
val dirPath = directory.canonicalPath
filePath.startsWith(dirPath)
} catch (e: IOException) {
false
}
}
\ No newline at end of file
...@@ -15,7 +15,7 @@ import java.util.* ...@@ -15,7 +15,7 @@ import java.util.*
* 万里飞雪,将穹苍作烘炉,熔万物为白银。 * 万里飞雪,将穹苍作烘炉,熔万物为白银。
**/ **/
private const val cachePathName = "fluwxSharedData" internal const val cachePathName = "fluwxSharedData"
internal suspend fun ByteArray.toExternalCacheFile(context: Context, suffix: String): File? { internal suspend fun ByteArray.toExternalCacheFile(context: Context, suffix: String): File? {
var file: File? = null var file: File? = null
...@@ -68,3 +68,16 @@ private suspend fun saveToLocal(byteArray: ByteArray, file: File): File? { ...@@ -68,3 +68,16 @@ private suspend fun saveToLocal(byteArray: ByteArray, file: File): File? {
file file
} }
} }
@Throws(IOException::class)
internal fun copyFile(sourceFilePath: String, destinationDirPath: String):File {
val sourceFile = File(sourceFilePath)
val destinationDir = File(destinationDirPath)
if (!destinationDir.exists()) {
destinationDir.mkdirs()
}
val destinationFile = File(destinationDir, sourceFile.name)
return sourceFile.copyTo(destinationFile, overwrite = true)
}
...@@ -338,6 +338,9 @@ class WeChatImageToShare with _Argument { ...@@ -338,6 +338,9 @@ class WeChatImageToShare with _Argument {
/// [uint8List] is available on both iOS and Android /// [uint8List] is available on both iOS and Android
/// [localImagePath] only available on Android, if [uint8List] is null, [localImagePath] must not be null; /// [localImagePath] only available on Android, if [uint8List] is null, [localImagePath] must not be null;
/// if [uint8List] and [localImagePath] are both provided on android, [uint8List] will be used. /// if [uint8List] and [localImagePath] are both provided on android, [uint8List] will be used.
/// If [localImagePath] starts with content://, it will be treated as a content provider uri and please ensure
/// you have already granted the app the permission to read the content. Otherwise, please ensure the file is
/// accessible by the app.
WeChatImageToShare({this.uint8List, this.localImagePath, this.imgDataHash}) { WeChatImageToShare({this.uint8List, this.localImagePath, this.imgDataHash}) {
if (Platform.isIOS) { if (Platform.isIOS) {
assert(uint8List != null); assert(uint8List != null);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论