feat: 添加相机捕获功能,更新 H5ShellPage 以支持媒体播放设置

This commit is contained in:
Booker
2026-05-29 12:46:01 +07:00
parent 6b35e80d47
commit 3cb8888497
2 changed files with 69 additions and 2 deletions

View File

@@ -10,6 +10,7 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.util.Base64
import android.util.Log
import androidx.core.content.FileProvider
@@ -29,6 +30,7 @@ class MainActivity : FlutterActivity() {
private val MAX_BRANDING_ICON_SIZE = 192
private val FILE_PICKER_REQUEST_CODE = 4201
private var pendingFilePickerResult: MethodChannel.Result? = null
private var pendingCameraCaptureUri: Uri? = null
override fun onCreate(savedInstanceState: android.os.Bundle?) {
// 华为/荣耀/OPPO 等国产设备:在任意网络请求之前同步安装 Conscrypt修复 SSL 握手失败(无 GMS 时系统 SSL 实现不完整)
@@ -116,7 +118,8 @@ class MainActivity : FlutterActivity() {
"pickFiles" -> {
val acceptTypes = call.argument<List<String>>("acceptTypes") ?: emptyList()
val allowMultiple = call.argument<Boolean>("allowMultiple") ?: false
openNativeFilePicker(acceptTypes, allowMultiple, result)
val capture = call.argument<Boolean>("capture") ?: false
openNativeFilePicker(acceptTypes, allowMultiple, capture, result)
}
else -> {
result.notImplemented()
@@ -134,7 +137,20 @@ class MainActivity : FlutterActivity() {
return
}
if (resultCode != Activity.RESULT_OK || data == null) {
if (resultCode != Activity.RESULT_OK) {
pendingCameraCaptureUri = null
result.success(emptyList<String>())
return
}
pendingCameraCaptureUri?.let { uri ->
pendingCameraCaptureUri = null
grantPickedFileReadPermission(uri)
result.success(listOf(uri.toString()))
return
}
if (data == null) {
result.success(emptyList<String>())
return
}
@@ -164,6 +180,7 @@ class MainActivity : FlutterActivity() {
private fun openNativeFilePicker(
acceptTypes: List<String>,
allowMultiple: Boolean,
capture: Boolean,
result: MethodChannel.Result
) {
if (pendingFilePickerResult != null) {
@@ -172,6 +189,10 @@ class MainActivity : FlutterActivity() {
}
val mimeTypes = normalizeMimeTypes(acceptTypes)
if (capture && openNativeCapture(mimeTypes, result)) {
return
}
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = when (mimeTypes.size) {
@@ -199,6 +220,50 @@ class MainActivity : FlutterActivity() {
}
}
private fun openNativeCapture(
mimeTypes: List<String>,
result: MethodChannel.Result
): Boolean {
pendingCameraCaptureUri = null
val captureVideo = mimeTypes.isNotEmpty() &&
mimeTypes.all { it.startsWith("video/") }
val intent = if (captureVideo) {
Intent(MediaStore.ACTION_VIDEO_CAPTURE)
} else {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
val outputUri = createCameraCaptureUri(".jpg")
pendingCameraCaptureUri = outputUri
putExtra(MediaStore.EXTRA_OUTPUT, outputUri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
}
pendingFilePickerResult = result
return try {
startActivityForResult(intent, FILE_PICKER_REQUEST_CODE)
true
} catch (e: Exception) {
pendingFilePickerResult = null
pendingCameraCaptureUri = null
false
}
}
private fun createCameraCaptureUri(extension: String): Uri {
val directory = externalCacheDir ?: cacheDir
val file = File.createTempFile(
"camera-capture-${System.currentTimeMillis()}",
extension,
directory
)
return FileProvider.getUriForFile(
this,
"${packageName}.fileprovider",
file
)
}
private fun normalizeMimeTypes(acceptTypes: List<String>): List<String> {
val mimeTypes = linkedSetOf<String>()
acceptTypes

View File

@@ -204,6 +204,7 @@ class _H5ShellPageState extends State<H5ShellPage> {
unawaited(
platformController.setOnShowFileSelector(_handleAndroidFileSelection),
);
unawaited(platformController.setMediaPlaybackRequiresUserGesture(false));
}
}
@@ -220,6 +221,7 @@ class _H5ShellPageState extends State<H5ShellPage> {
{
'acceptTypes': params.acceptTypes,
'allowMultiple': params.mode == FileSelectorMode.openMultiple,
'capture': params.isCaptureEnabled,
},
);
return result ?? <String>[];