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.
 
 
 
 
 
 
Rodger-Wang 2d1ee0b783 apps/client 首次入库(Flutter 移动端) 1 month ago
..
android apps/client 首次入库(Flutter 移动端) 1 month ago
ios apps/client 首次入库(Flutter 移动端) 1 month ago
lib apps/client 首次入库(Flutter 移动端) 1 month ago
.gitignore apps/client 首次入库(Flutter 移动端) 1 month ago
.metadata apps/client 首次入库(Flutter 移动端) 1 month ago
CHANGELOG.md apps/client 首次入库(Flutter 移动端) 1 month ago
LICENSE apps/client 首次入库(Flutter 移动端) 1 month ago
README.md apps/client 首次入库(Flutter 移动端) 1 month ago
analysis_options.yaml apps/client 首次入库(Flutter 移动端) 1 month ago
pubspec.yaml apps/client 首次入库(Flutter 移动端) 1 month ago

README.md

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|expanded
  • onFloatingWindowClosed():用户关闭。
  • onFloatingWindowClicked():用户点击语言区域(service 内部会 bringAppToFront)。

数据协议(updateContent):

  • sourceText / translatedText
  • sourceLanguage / targetLanguage
  • timestamp(毫秒时间戳)
  • isIntermediate(是否中间态)

对应实现:

  • Android 插件入口:NativePlugin.kt
  • 悬浮窗服务:FloatingWindowService.kt

2) Android 悬浮窗权限(Overlay Permission)

  • 通道:MethodChannel('overlay_permission')
  • 方法:
    • hasOverlayPermission:是否具备 SYSTEM_ALERT_WINDOW
    • requestOverlayPermission:跳转系统页面申请

对应实现: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/yimai/eaimar/ 下存在同名通道的旧实现文件(未在 MainActivity 注册),当前实际走的是本插件实现;如后续要启用 app 侧实现,需要先解决通道名冲突与 service 声明问题。