|
|
|
@ -25,10 +25,15 @@ class MainActivity: FlutterActivity() { |
|
|
|
private var pendingDeepLink: Intent? = null |
|
|
|
private var pendingNavigationIntent: Intent? = null |
|
|
|
private var pendingShareIntent: Intent? = null |
|
|
|
private var pendingSharedFilePath: String? = null |
|
|
|
private var pendingDeepLinkUrl: String? = null |
|
|
|
|
|
|
|
private val EXTRA_NAVIGATE_TO = "com.saitong.voitrans.extra.NAVIGATE_TO" |
|
|
|
private val NAVIGATE_TO_MEETING_RECORD = "meetingRecord" |
|
|
|
private val ROUTE_MEETING_RECORD = "/meeting/record" |
|
|
|
private val DEEPLINK_CHANNEL = "com.saitong.voitrans/deeplink" |
|
|
|
private val DELIVERY_RETRY_DELAY_MS = 300L |
|
|
|
private val MAX_DELIVERY_RETRIES = 12 |
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) { |
|
|
|
Log.d(TAG, "🚀 MainActivity onCreate 开始") |
|
|
|
@ -78,8 +83,28 @@ class MainActivity: FlutterActivity() { |
|
|
|
Log.d(TAG, "New Intent data: ${intent.data}") |
|
|
|
Log.d(TAG, "New Intent extras: ${intent.extras}") |
|
|
|
|
|
|
|
// 处理深链接 |
|
|
|
handleNavigationIntent(intent) |
|
|
|
intent.let { |
|
|
|
if (it.action == Intent.ACTION_SEND) { |
|
|
|
pendingShareIntent = it |
|
|
|
Log.d(TAG, "📎 暂存分享文件意图,等待Flutter引擎准备完成") |
|
|
|
} else if (it.action == Intent.ACTION_VIEW && it.data != null) { |
|
|
|
val uri = it.data |
|
|
|
if (uri != null && isLikelyAudio(it, uri)) { |
|
|
|
pendingShareIntent = it |
|
|
|
Log.d(TAG, "📎 暂存打开文件意图,等待Flutter引擎准备完成") |
|
|
|
} else { |
|
|
|
pendingDeepLink = it |
|
|
|
Log.d(TAG, "🔗 暂存深链接,等待Flutter引擎准备完成") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (intent.hasExtra(EXTRA_NAVIGATE_TO)) { |
|
|
|
pendingNavigationIntent = intent |
|
|
|
Log.d(TAG, "🧭 暂存导航意图,等待Flutter引擎准备完成") |
|
|
|
} |
|
|
|
|
|
|
|
deliverOrQueueIntent(intent) |
|
|
|
} |
|
|
|
|
|
|
|
override fun provideFlutterEngine(context: Context): FlutterEngine? { |
|
|
|
@ -127,19 +152,19 @@ class MainActivity: FlutterActivity() { |
|
|
|
Handler(Looper.getMainLooper()).postDelayed({ |
|
|
|
pendingShareIntent?.let { intent -> |
|
|
|
Log.d(TAG, "📎 处理待处理的分享/打开文件意图") |
|
|
|
handleNavigationIntent(intent) |
|
|
|
deliverOrQueueIntent(intent) |
|
|
|
pendingShareIntent = null |
|
|
|
} |
|
|
|
|
|
|
|
pendingDeepLink?.let { intent -> |
|
|
|
Log.d(TAG, "🔗 处理待处理的深链接") |
|
|
|
handleNavigationIntent(intent) |
|
|
|
deliverOrQueueIntent(intent) |
|
|
|
pendingDeepLink = null |
|
|
|
} |
|
|
|
|
|
|
|
pendingNavigationIntent?.let { intent -> |
|
|
|
Log.d(TAG, "🧭 处理待处理的导航意图") |
|
|
|
handleNavigationIntent(intent) |
|
|
|
deliverOrQueueIntent(intent) |
|
|
|
pendingNavigationIntent = null |
|
|
|
} |
|
|
|
}, 1000) // 延迟1秒确保Flutter完全准备好 |
|
|
|
@ -147,67 +172,143 @@ class MainActivity: FlutterActivity() { |
|
|
|
Log.d(TAG, "✅ configureFlutterEngine 完成") |
|
|
|
} |
|
|
|
|
|
|
|
// 处理深链接的统一方法 |
|
|
|
private fun handleNavigationIntent(intent: Intent?) { |
|
|
|
intent?.let { |
|
|
|
val navigateTo = it.getStringExtra(EXTRA_NAVIGATE_TO) |
|
|
|
if (navigateTo == NAVIGATE_TO_MEETING_RECORD) { |
|
|
|
pushRouteToFlutter(ROUTE_MEETING_RECORD) |
|
|
|
return |
|
|
|
} |
|
|
|
private fun deliverOrQueueIntent(intent: Intent?) { |
|
|
|
intent ?: return |
|
|
|
|
|
|
|
if (it.action == Intent.ACTION_SEND || it.action == Intent.ACTION_VIEW) { |
|
|
|
try { |
|
|
|
val path = extractAudioPathFromIntent(it) |
|
|
|
if (path != null) { |
|
|
|
flutterEngine?.dartExecutor?.binaryMessenger?.let { messenger -> |
|
|
|
val channel = MethodChannel(messenger, "com.saitong.voitrans/deeplink") |
|
|
|
channel.invokeMethod("handleSharedFile", mapOf("filePath" to path)) |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "❌ 处理分享/打开文件失败", e) |
|
|
|
val navigateTo = intent.getStringExtra(EXTRA_NAVIGATE_TO) |
|
|
|
if (navigateTo == NAVIGATE_TO_MEETING_RECORD) { |
|
|
|
pushRouteToFlutter(ROUTE_MEETING_RECORD) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if (intent.action == Intent.ACTION_SEND || |
|
|
|
intent.action == Intent.ACTION_SEND_MULTIPLE || |
|
|
|
intent.action == Intent.ACTION_VIEW |
|
|
|
) { |
|
|
|
try { |
|
|
|
val path = extractAudioPathFromIntent(intent) |
|
|
|
if (path != null) { |
|
|
|
pendingSharedFilePath = path |
|
|
|
deliverPendingSharedFile() |
|
|
|
return |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "❌ 处理分享/打开文件失败", e) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (it.action == Intent.ACTION_VIEW && it.data != null) { |
|
|
|
val uri = it.data |
|
|
|
Log.d(TAG, "🔗 处理深链接: $uri") |
|
|
|
|
|
|
|
try { |
|
|
|
Log.d(TAG, "🔗 开始发送深链接到 Flutter") |
|
|
|
|
|
|
|
// 通过 MethodChannel 将深链接信息传递给 Flutter |
|
|
|
flutterEngine?.dartExecutor?.binaryMessenger?.let { messenger -> |
|
|
|
Log.d(TAG, "🔗 获取到 BinaryMessenger") |
|
|
|
|
|
|
|
val channel = MethodChannel( |
|
|
|
messenger, |
|
|
|
"com.saitong.voitrans/deeplink" |
|
|
|
if (intent.action == Intent.ACTION_VIEW && intent.data != null) { |
|
|
|
val uri = intent.data |
|
|
|
Log.d(TAG, "🔗 处理深链接: $uri") |
|
|
|
pendingDeepLinkUrl = uri.toString() |
|
|
|
deliverPendingDeepLink() |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
Log.d(TAG, "🔗 Intent 不是深链接或数据为空") |
|
|
|
} |
|
|
|
|
|
|
|
private fun getEngineForMessaging(): FlutterEngine? { |
|
|
|
return flutterEngine ?: FlutterEngineCache.getInstance().get("main_engine") |
|
|
|
} |
|
|
|
|
|
|
|
private fun deliverPendingSharedFile(attempt: Int = 0) { |
|
|
|
val filePath = pendingSharedFilePath ?: return |
|
|
|
val engine = getEngineForMessaging() |
|
|
|
val messenger = engine?.dartExecutor?.binaryMessenger |
|
|
|
if (messenger == null) { |
|
|
|
if (attempt < MAX_DELIVERY_RETRIES) { |
|
|
|
Handler(Looper.getMainLooper()).postDelayed( |
|
|
|
{ deliverPendingSharedFile(attempt + 1) }, |
|
|
|
DELIVERY_RETRY_DELAY_MS |
|
|
|
) |
|
|
|
} else { |
|
|
|
Log.w(TAG, "❌ FlutterEngine 未就绪,放弃发送分享文件:$filePath") |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
val channel = MethodChannel(messenger, DEEPLINK_CHANNEL) |
|
|
|
channel.invokeMethod( |
|
|
|
"handleSharedFile", |
|
|
|
mapOf("filePath" to filePath), |
|
|
|
object : MethodChannel.Result { |
|
|
|
override fun success(result: Any?) { |
|
|
|
pendingSharedFilePath = null |
|
|
|
} |
|
|
|
|
|
|
|
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { |
|
|
|
Log.e(TAG, "❌ Flutter 处理分享文件返回错误:$errorCode, $errorMessage") |
|
|
|
} |
|
|
|
|
|
|
|
override fun notImplemented() { |
|
|
|
if (attempt < MAX_DELIVERY_RETRIES) { |
|
|
|
Handler(Looper.getMainLooper()).postDelayed( |
|
|
|
{ deliverPendingSharedFile(attempt + 1) }, |
|
|
|
DELIVERY_RETRY_DELAY_MS |
|
|
|
) |
|
|
|
|
|
|
|
val arguments = buildUrlArguments(uri.toString()) |
|
|
|
|
|
|
|
Log.d(TAG, "🔗 准备发送参数: $arguments") |
|
|
|
|
|
|
|
channel.invokeMethod("handleDeepLink", arguments) |
|
|
|
Log.d(TAG, "✅ 深链接信息已发送到 Flutter") |
|
|
|
} ?: run { |
|
|
|
Log.w(TAG, "❌ BinaryMessenger 为空,无法发送深链接") |
|
|
|
} else { |
|
|
|
Log.w(TAG, "❌ Flutter 未注册分享处理器,放弃发送分享文件:$filePath") |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "❌ 处理深链接时出错", e) |
|
|
|
} |
|
|
|
} |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
private fun deliverPendingDeepLink(attempt: Int = 0) { |
|
|
|
val url = pendingDeepLinkUrl ?: return |
|
|
|
val engine = getEngineForMessaging() |
|
|
|
val messenger = engine?.dartExecutor?.binaryMessenger |
|
|
|
if (messenger == null) { |
|
|
|
if (attempt < MAX_DELIVERY_RETRIES) { |
|
|
|
Handler(Looper.getMainLooper()).postDelayed( |
|
|
|
{ deliverPendingDeepLink(attempt + 1) }, |
|
|
|
DELIVERY_RETRY_DELAY_MS |
|
|
|
) |
|
|
|
} else { |
|
|
|
Log.d(TAG, "🔗 Intent 不是深链接或数据为空") |
|
|
|
Log.w(TAG, "❌ FlutterEngine 未就绪,放弃发送深链接:$url") |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
val channel = MethodChannel(messenger, DEEPLINK_CHANNEL) |
|
|
|
val arguments = buildUrlArguments(url) |
|
|
|
channel.invokeMethod( |
|
|
|
"handleDeepLink", |
|
|
|
arguments, |
|
|
|
object : MethodChannel.Result { |
|
|
|
override fun success(result: Any?) { |
|
|
|
pendingDeepLinkUrl = null |
|
|
|
} |
|
|
|
|
|
|
|
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { |
|
|
|
Log.e(TAG, "❌ Flutter 处理深链接返回错误:$errorCode, $errorMessage") |
|
|
|
} |
|
|
|
|
|
|
|
override fun notImplemented() { |
|
|
|
if (attempt < MAX_DELIVERY_RETRIES) { |
|
|
|
Handler(Looper.getMainLooper()).postDelayed( |
|
|
|
{ deliverPendingDeepLink(attempt + 1) }, |
|
|
|
DELIVERY_RETRY_DELAY_MS |
|
|
|
) |
|
|
|
} else { |
|
|
|
Log.w(TAG, "❌ Flutter 未注册深链接处理器,放弃发送深链接:$url") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
private fun extractAudioPathFromIntent(intent: Intent): String? { |
|
|
|
val uri: Uri? = when (intent.action) { |
|
|
|
Intent.ACTION_SEND -> intent.getParcelableExtra(Intent.EXTRA_STREAM) |
|
|
|
Intent.ACTION_SEND -> { |
|
|
|
val extraStream = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) |
|
|
|
extraStream ?: intent.clipData?.takeIf { it.itemCount > 0 }?.getItemAt(0)?.uri |
|
|
|
} |
|
|
|
Intent.ACTION_SEND_MULTIPLE -> { |
|
|
|
val items = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM) |
|
|
|
items?.firstOrNull() ?: intent.clipData?.takeIf { it.itemCount > 0 }?.getItemAt(0)?.uri |
|
|
|
} |
|
|
|
Intent.ACTION_VIEW -> intent.data |
|
|
|
else -> null |
|
|
|
} |
|
|
|
@ -246,17 +347,26 @@ class MainActivity: FlutterActivity() { |
|
|
|
val sharedDir = File(applicationContext.filesDir, "shared_audio") |
|
|
|
if (!sharedDir.exists()) sharedDir.mkdirs() |
|
|
|
val outFile = File(sharedDir, "audio_${System.currentTimeMillis()}.$ext") |
|
|
|
resolver.openInputStream(uri)?.use { inStream -> |
|
|
|
val inStream = resolver.openInputStream(uri) |
|
|
|
if (inStream == null) { |
|
|
|
outFile.delete() |
|
|
|
return null |
|
|
|
} |
|
|
|
inStream.use { |
|
|
|
FileOutputStream(outFile).use { out -> |
|
|
|
val buffer = ByteArray(8192) |
|
|
|
var len: Int |
|
|
|
while (true) { |
|
|
|
len = inStream.read(buffer) |
|
|
|
len = it.read(buffer) |
|
|
|
if (len <= 0) break |
|
|
|
out.write(buffer, 0, len) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (!outFile.exists() || outFile.length() <= 0) { |
|
|
|
outFile.delete() |
|
|
|
return null |
|
|
|
} |
|
|
|
outFile.absolutePath |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "复制分享文件到缓存失败: ${e.message}", e) |
|
|
|
|