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

support share image via FileProvider

上级 103b28f5
<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,15 @@ package com.jarvan.fluwx.handlers ...@@ -2,15 +2,15 @@ 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 android.util.Log
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 +18,17 @@ import io.flutter.plugin.common.MethodCall ...@@ -18,16 +18,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")
...@@ -135,10 +136,14 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -135,10 +136,14 @@ internal interface FluwxShareHandler : CoroutineScope {
} }
sourceByteArray.size > 512 * 1024 -> { sourceByteArray.size > 512 * 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)
...@@ -261,11 +266,11 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -261,11 +266,11 @@ internal interface FluwxShareHandler : CoroutineScope {
// 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 +281,25 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -276,6 +281,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 +312,5 @@ internal interface FluwxShareHandler : CoroutineScope { ...@@ -288,4 +312,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
}
}
...@@ -119,37 +119,3 @@ interface ImagesIO { ...@@ -119,37 +119,3 @@ interface ImagesIO {
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
<?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
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论