From 131df57dfe57423c3a670ba4eccae9bf15a89a47 Mon Sep 17 00:00:00 2001 From: Booker Date: Fri, 29 May 2026 14:05:34 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=85=A7=E7=89=87?= =?UTF-8?q?=E5=8F=91=E9=80=81=E7=A1=AE=E8=AE=A4=E5=AF=B9=E8=AF=9D=E6=A1=86?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E7=9B=B8=E6=9C=BA=E6=8D=95=E8=8E=B7?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/openim/flutter/openim/MainActivity.kt | 106 +++++++++++++++++- 1 file changed, 102 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/kotlin/io/openim/flutter/openim/MainActivity.kt b/android/app/src/main/kotlin/io/openim/flutter/openim/MainActivity.kt index 817ac74..99dbc09 100644 --- a/android/app/src/main/kotlin/io/openim/flutter/openim/MainActivity.kt +++ b/android/app/src/main/kotlin/io/openim/flutter/openim/MainActivity.kt @@ -1,7 +1,8 @@ package io.openim.flutter.openim -import android.content.Intent import android.app.Activity +import android.app.AlertDialog +import android.content.Intent import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.graphics.Bitmap @@ -15,6 +16,8 @@ import android.util.Base64 import android.util.Log import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView import androidx.core.content.FileProvider import io.flutter.embedding.android.FlutterActivity import org.conscrypt.Conscrypt @@ -33,6 +36,7 @@ class MainActivity : FlutterActivity() { private val FILE_PICKER_REQUEST_CODE = 4201 private var pendingFilePickerResult: MethodChannel.Result? = null private var pendingCameraCaptureUri: Uri? = null + private var pendingCaptureConfirmationDialog: AlertDialog? = null override fun onCreate(savedInstanceState: android.os.Bundle?) { // 华为/荣耀/OPPO 等国产设备:在任意网络请求之前同步安装 Conscrypt,修复 SSL 握手失败(无 GMS 时系统 SSL 实现不完整) @@ -51,6 +55,12 @@ class MainActivity : FlutterActivity() { disableAutofillForCurrentWindow() } + override fun onDestroy() { + pendingCaptureConfirmationDialog?.dismiss() + pendingCaptureConfirmationDialog = null + super.onDestroy() + } + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> @@ -153,8 +163,7 @@ class MainActivity : FlutterActivity() { pendingCameraCaptureUri?.let { uri -> pendingCameraCaptureUri = null - grantPickedFileReadPermission(uri) - result.success(listOf(uri.toString())) + showCaptureConfirmation(uri, result) return } @@ -191,7 +200,7 @@ class MainActivity : FlutterActivity() { capture: Boolean, result: MethodChannel.Result ) { - if (pendingFilePickerResult != null) { + if (pendingFilePickerResult != null || pendingCaptureConfirmationDialog != null) { result.error("FILE_PICKER_BUSY", "A file picker request is already active", null) return } @@ -258,6 +267,95 @@ class MainActivity : FlutterActivity() { } } + private fun showCaptureConfirmation(uri: Uri, result: MethodChannel.Result) { + if (isFinishing || + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && isDestroyed) + ) { + deleteCaptureUri(uri) + result.success(emptyList()) + return + } + + val preview = ImageView(this).apply { + setImageURI(uri) + adjustViewBounds = true + scaleType = ImageView.ScaleType.FIT_CENTER + setBackgroundColor(0xFFF2F5F8.toInt()) + layoutParams = FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + dp(360) + ) + } + val previewContainer = FrameLayout(this).apply { + setPadding(dp(20), dp(12), dp(20), 0) + addView(preview) + } + + var completed = false + fun complete(uris: List) { + if (completed) { + return + } + completed = true + if (uris.isEmpty()) { + deleteCaptureUri(uri) + } + result.success(uris) + } + + val dialog = AlertDialog.Builder(this) + .setTitle("确认发送照片") + .setMessage("确认后才会发送给好友。") + .setView(previewContainer) + .setNegativeButton("取消") { _, _ -> } + .setPositiveButton("发送") { _, _ -> } + .create() + + dialog.setOnCancelListener { + complete(emptyList()) + } + dialog.setOnDismissListener { + if (pendingCaptureConfirmationDialog == dialog) { + pendingCaptureConfirmationDialog = null + } + complete(emptyList()) + } + dialog.setOnShowListener { + val sendButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) + val cancelButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE) + sendButton.setOnClickListener { + sendButton.isEnabled = false + cancelButton.isEnabled = false + grantPickedFileReadPermission(uri) + complete(listOf(uri.toString())) + dialog.dismiss() + } + cancelButton.setOnClickListener { + complete(emptyList()) + dialog.dismiss() + } + } + + pendingCaptureConfirmationDialog = dialog + try { + dialog.show() + } catch (e: Exception) { + pendingCaptureConfirmationDialog = null + complete(emptyList()) + } + } + + private fun dp(value: Int): Int { + return (value * resources.displayMetrics.density).toInt() + } + + private fun deleteCaptureUri(uri: Uri) { + try { + contentResolver.delete(uri, null, null) + } catch (_: Exception) { + } + } + private fun createCameraCaptureUri(extension: String): Uri { val directory = externalCacheDir ?: cacheDir val file = File.createTempFile(