You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7.5 KiB
7.5 KiB
floating_ui_plugin
该目录是一个本地 Flutter 插件包,当前在项目中主要承担「悬浮 UI」相关的原生能力:
- Android:翻译悬浮窗(系统悬浮窗 / overlay window)+ 录音悬浮条(计时/暂停/关闭)+ 悬浮窗权限检查/跳转。
- iOS:为系统画中画(PiP)提供可渲染的原生内容 View,并支持动态更新翻译文本。
说明:Flutter 包名为 floating_ui_plugin(见本插件 pubspec.yaml),主工程通过 path 依赖引用本目录。
主要能力
1) Android 翻译悬浮窗(Translation Floating Window)
- 通道:
MethodChannel('floating_window') - 原理:Flutter 调用通道方法 → Android 启动/更新
FloatingWindowService→ service 通过系统 WindowManager 显示 overlay view。 - 交互回传:service 会反向调用同一通道,把状态/点击/关闭事件回传给 Flutter。
支持的方法(Flutter → Android):
enableFloatingWindow:启动悬浮窗服务(如果已在运行,相当于确保存在)。disableFloatingWindow:停止悬浮窗服务。showFloatingWindow:显示悬浮窗(当前实现等价于启动服务)。hideFloatingWindow:隐藏悬浮窗(stopSelf)。updateContent(Map):更新展示内容。setPosition({x,y}):设置悬浮窗位置。
回传的方法(Android → Flutter):
onFloatingWindowStateChanged({state}):hidden|minimized|expandedonFloatingWindowClosed():用户关闭。onFloatingWindowClicked():用户点击语言区域(service 内部会 bringAppToFront)。
数据协议(updateContent):
sourceText/translatedTextsourceLanguage/targetLanguagetimestamp(毫秒时间戳)isIntermediate(是否中间态)
对应实现:
- Android 插件入口:NativePlugin.kt
- 悬浮窗服务:FloatingWindowService.kt
2) Android 悬浮窗权限(Overlay Permission)
- 通道:
MethodChannel('overlay_permission') - 方法:
hasOverlayPermission:是否具备 SYSTEM_ALERT_WINDOWrequestOverlayPermission:跳转系统页面申请
对应实现:NativePlugin.kt
3) Android 录音悬浮条(Recording Floating Bar)
- 通道:
MethodChannel('native_plugin') - 原理:Flutter 下发 show/update/hide → Android 启动/更新
RecordingFloatingService→ service 以 overlay 方式显示一个小型悬浮条。
支持的方法(Flutter → Android):
showRecordingFloatingBar({duration, recordingType, isPaused})updateRecordingFloatingBar({duration, recordingType, isPaused})hideRecordingFloatingBar()
用户操作回传(Android → Flutter):
- 事件通道:
EventChannel('native_plugin/recording_floating_actions') - action 字符串:
togglePause:点击暂停/继续openMeetingRecord:点击中间区域,拉起 App 并打开录音页closeFloatingBar:关闭悬浮条
对应实现:
- 服务:RecordingFloatingService.kt
- 事件桥接:NativePlugin.emitRecordingFloatingAction
iOS:PiP 内容 View(Picture-in-Picture Content View)
- 通道:
MethodChannel('native_plugin') - 目标:创建一个用于 PiP 渲染的原生
UIView(PlayerView),并支持后续更新显示内容。
支持的方法:
createPipContentView():返回一个viewId(本质是 PlayerView 的指针值)。updatePipContentView({viewId?, lang, cn, en}):更新内容。disposePipContentView(viewId):释放。
对应实现:
- 插件入口:NativePlugin.swift
- 具体 UI:PlayerView.swift
在主工程中的调用链路(现状)
Android:翻译悬浮窗
- 入口:翻译页控制器 TranslationController
toggleFloatingWindow()→_enableFloatingWindow()
- 管理器:FloatingWindowManager
enableFloatingWindow():先通过 OverlayPermissionUtil 请求权限,再调用TranslationFloatingWindowApi.enable()_setupMethodCallHandler():接收onFloatingWindowStateChanged/onFloatingWindowClicked/onFloatingWindowClosed并更新状态/路由跳转
- 内容更新:
TranslationController._updateFloatingWindowWithTranslation()→FloatingWindowManager.updateFloatingWindowContent()→updateContent(Map)
Android:录音悬浮条
- 入口:会议录音控制器 MeetingRecordController
showBackgroundRecordingIndicator()→_startAndroidRecordingFloatingBar()→NativePlugin.showRecordingFloatingBar()- 定时器每秒
updateRecordingFloatingBar()更新 duration/isPaused _setupRecordingFloatingBarActionListener()监听recordingFloatingActionStream并处理togglePause/openMeetingRecord/closeFloatingBar
iOS:翻译悬浮显示(PiP)
- 入口同样在 TranslationController
_startPip():NativePlugin.createPipContentView()→ 将 viewId 传给第三方 PiP 组件的PipOptions.contentView_updateFloatingWindowWithTranslation():NativePlugin.updatePipContentView({viewId, cn, en, lang})
权限与 Manifest
- Android 需要:
android.permission.SYSTEM_ALERT_WINDOW - 本插件自带 AndroidManifest 声明(包含两项 service):
- android/src/main/AndroidManifest.xml
平台差异与注意事项
- Android 与 iOS 的“悬浮显示”实现不同:Android 用系统悬浮窗;iOS 走系统 PiP。
createPipContentView/updatePipContentView/disposePipContentView仅 iOS 实现;Android 侧没有实现这些 method,请务必用Platform.isIOS保护调用。floating_window/overlay_permission/native_plugin/recording_floating_actions都是固定通道名,避免在其他地方重复注册同名通道。- 工程
android/app/src/main/kotlin/com/saitong/voitrans/下存在同名通道的旧实现文件(未在 MainActivity 注册),当前实际走的是本插件实现;如后续要启用 app 侧实现,需要先解决通道名冲突与 service 声明问题。