更新记录

2.0.3(2026-08-28)

更新说明文档

2.0.2(2026-08-28)

更新说明文档

2.0.1(2026-08-28)

  • 移除通话模板内置接听/挂断(callControl: 'builtin')及 answerCall / endCall API
  • 接听/挂断请使用 aiko-call-recorder 插件
查看更多

平台兼容性

uni-app(4.23)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× × × ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × × ×

uni-app x(4.62)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × × × ×

aiko-float-window

Android 全局浮窗 UTS 插件:基于系统叠加层(SYSTEM_ALERT_WINDOW),可在微信、视频播放器、系统通话界面等任意 App 上层显示浮窗。支持多实例、圆球 ⇄ 面板切换、拖动贴边、通话/微信/视频三套模板、WebView 自定义面板,以及系统通话与前台包名场景事件。

重要:插件只负责「画窗 + 抛事件」。是否弹出浮窗、弹出什么内容,均由宿主 App 自己决定。插件不会因场景事件自动 show


平台支持

平台 支持
App-Android
uni-app(Vue2 / Vue3)
uni-app x
iOS
鸿蒙
小程序 / Web
  • 最低 Android API:21(Android 5.0)
  • 推荐 HBuilderX:4.25+
  • Vue 页面组件无法直接画进系统叠加层,必须通过本插件

安装与调试

  1. uni_modules/aiko-float-window 放入项目根目录
  2. HBuilderX → 运行 → 运行到手机或模拟器 → 制作自定义调试基座
  3. 使用自定义基座运行到 Android 真机
  4. 修改插件原生代码后,必须重新制作自定义基座才会生效

引入

uni-app x(UTS)

import * as floatWin from '@/uni_modules/aiko-float-window'

uni-app(JavaScript / TypeScript)

import * as floatWin from '@/uni_modules/aiko-float-window'

能力一览(v2)

能力 说明
系统浮窗 多实例圆球 ⇄ 面板,可拖动、松手贴边,每实例独立 id
内置模板 call 通话、wechat 微信、video 视频,由宿主在 show / update 指定
WebView 自定义 mode: 'webview',加载本地 HTML 或 https,支持 JS Bridge
前台保活 有实例显示时自动拉起前台服务;全部 hide 后停止
场景事件 系统通话(高置信度)+ 前台包名(低置信度),不自动弹窗
按钮回调 自定义按钮、WebView 命令均回调宿主(接听/挂断请用 aiko-call-recorder

微信 / 视频场景说明:仅保证「相关 App 在前台」,不保证「正在微信通话」或「正在播放视频」。


推荐接入流程

1. 检查并申请悬浮窗权限(必须)
2. 检查并申请通知权限(show 后保活,强烈建议)
3. 申请运行时权限(电话状态、通知等)
4. registerListener 注册事件回调
5. (可选)开启使用情况访问 → setSceneDetector({ usage: true })
6. 宿主根据业务逻辑调用 show / update / hide

最小示例

import * as floatWin from '@/uni_modules/aiko-float-window'

// 1. 检查悬浮窗权限
floatWin.checkOverlayPermission((res) => {
  const ok = res['data']?.['status'] === true
  if (!ok) {
    floatWin.toOverlayPermissionPage(() => {})
    return
  }

  // 2. 注册监听(场景事件、按钮、WebView 命令)
  floatWin.registerListener((evt) => {
    if (evt['code'] !== 0) return
    const data = evt['data']
    if (data?.['type'] === 'scene' && data['scene'] === 'call') {
      // 宿主决定是否弹出
      floatWin.show({
        template: 'call',
        title: '系统通话',
        number: data['phoneNumber'] as string,
        state: data['callState'] as number
      }, () => {})
    }
  })

  // 3. 主动显示浮窗
  floatWin.show({
    mode: 'template',
    template: 'call',
    title: '系统通话',
    subtitle: '客户 A',
    number: '10086',
    state: 2,
    buttons: [{ id: 'note', text: '备注' }]
  }, (res) => {
    console.log(res)
  })
})

权限说明

插件已在 AndroidManifest.xml 中声明以下权限,宿主需在隐私政策中说明用途:

权限 用途 是否必须
SYSTEM_ALERT_WINDOW 显示在其他 App 上层 必须
POST_NOTIFICATIONS 前台服务通知(保活) 强烈建议
FOREGROUND_SERVICE / SPECIAL_USE 后台保持浮窗 show 时自动使用
READ_PHONE_STATE 监听系统通话状态 场景事件需要
PACKAGE_USAGE_STATS 检测前台 App 包名 微信/视频场景需要

权限相关 API

方法 说明 成功时 data
checkOverlayPermission 是否已授予悬浮窗 { status: boolean }
toOverlayPermissionPage 跳转系统悬浮窗设置 { status: boolean }
isForegroundPermission 通知是否已开启 { status: boolean }
toForegroundPage 跳转通知设置 { status: boolean }
checkUsageStatsPermission 使用情况访问是否已开 { status: boolean }
toUsageStatsPermissionPage 跳转使用情况访问设置 { status: boolean }
requestPermissions 申请运行时权限数组 见下方说明

requestPermissions 示例:

floatWin.requestPermissions([
  'android.permission.READ_PHONE_STATE',
  'android.permission.POST_NOTIFICATIONS'
], (res) => {
  // code === 0:已全部授权
  // code !== 0:仍有未授权项,data.grantedList 为待授权列表
})

统一回调格式

所有 API 均通过回调返回:

{
  "code": 0,
  "message": "",
  "data": {}
}
  • code === 0:成功
  • code !== 0:失败,message 为错误说明

错误码

code 含义
0 成功
-1 通用失败(Context 不可用、跳转失败、显示异常等)
-2 未授予悬浮窗权限
-3 WebView 地址不合法(仅允许本地 HTML / file / https)
-4 浮窗未显示(update / expand / collapse 时)
-5 未授予使用情况访问权限

v2:多实例浮窗

通过 id 区分多个独立浮窗,互不覆盖。不传 id 时默认为 default(与 v1 单实例行为兼容)。

// 实例 A:微信模板
floatWin.show({
  id: 'float-a',
  template: 'wechat',
  title: '微信跟进',
  ballText: 'A',
  y: 120
}, () => {})

// 实例 B:视频模板,置顶显示
floatWin.show({
  id: 'float-b',
  template: 'video',
  title: '视频备注',
  ballText: 'B',
  y: 220,
  bringToFront: true
}, () => {})

// 关闭指定实例
floatWin.hideById({ id: 'float-a' }, () => {})

// 关闭全部
floatWin.hideAll(() => {})

// 查询所有实例
floatWin.isShowing((res) => {
  const data = res['data']
  const list = data['instances']  // 数组
  const count = data['count']
})

update 通过 id 字段指定目标实例:

floatWin.update({ id: 'float-b', meta: '更新内容' }, () => {})
floatWin.expandById({ id: 'float-a' }, () => {})
floatWin.collapseById({ id: 'float-a' }, () => {})

关闭 / 展开 / 收起 default 实例可直接用无 id 版本:

floatWin.hide(() => {})
floatWin.expand(() => {})
floatWin.collapse(() => {})

浮窗 API

方法 说明
show(options, callback) 显示浮窗(圆球或面板)
hide(callback) 关闭浮窗并停止前台服务
update(options, callback) 更新已显示浮窗的内容(增量合并)
expand(callback) 展开为面板
collapse(callback) 收起为圆球
isShowing(callback) 查询状态

isShowing 成功时 data 字段:

{
  "showing": true,
  "expanded": false,
  "template": "call",
  "mode": "template",
  "x": 0,
  "y": 120
}

交互说明

  • 单击圆球:展开面板
  • 拖动圆球 / 面板标题区:移动位置,松手后自动贴左右边
  • 面板「收起」:收为圆球
  • 圆球文字ballText,默认按模板显示「通 / 微 / 视」
  • 角标badge 非空时显示在圆球右上角

show / update 参数(FloatShowOptions)

通用字段

字段 类型 说明
mode string template(默认)或 webview
template string call / wechat / video,由宿主指定
expanded boolean true 直接展开面板,默认 false 为圆球
title string 主标题
subtitle string 副标题
meta string 辅助信息行
avatar string 本地头像路径(远程 URL v1 忽略)
buttons Array<{id,text}> 操作按钮,最多 3 个
ballText string 圆球显示文字(取前 2 字)
badge string 圆球角标
notifyTitle string 前台服务通知标题
notifyContent string 前台服务通知内容
id string v2 实例 ID,默认 default
x / y number v2 初始坐标
bringToFront boolean v2 显示时置顶

通话模板 template: 'call'

字段 类型 说明
number string 号码
state number \| string 0 空闲 / 1 响铃 / 2 通话中
durationMs number 通话时长(毫秒),面板显示为 分:秒

微信模板 template: 'wechat'

字段 类型 说明
nickname string 昵称
kind string audio 语音 / video 视频 / unknown 未知

视频模板 template: 'video'

字段 类型 说明
appName string App 名称
contentTitle string 内容标题

WebView 模式 mode: 'webview'

字段 类型 说明
html string 内联 HTML,写入缓存后以 file 协议路径加载
url string 支持 https、file 协议 URL,或本地绝对路径

安全限制:仅允许本地 HTML、file 协议、绝对路径、https。明文 http 与 javascript 协议会被拒绝(code = -3)。

update 合并规则

  • update 只合并本次传入的非空字段,未传字段保留原值
  • 适合通话计时、状态变更等场景:
floatWin.update({
  durationMs: 60000,
  state: 2,
  meta: '已通话 60 秒'
}, () => {})

提示:可选字段请勿显式传 null,否则可能被序列化为 JSON null。插件已做过滤,但建议只传需要更新的字段。


三套模板示例

通话

floatWin.show({
  mode: 'template',
  template: 'call',
  title: '系统通话',
  subtitle: '客户 A',
  meta: '销售跟进',
  number: '10086',
  state: 2,
  durationMs: 0,
  ballText: '通',
  buttons: [
    { id: 'note', text: '备注' },
    { id: 'crm', text: '客户' }
  ],
  notifyTitle: '全局浮窗运行中',
  notifyContent: '通话中'
}, () => {})

微信

floatWin.show({
  mode: 'template',
  template: 'wechat',
  title: '微信通话',
  subtitle: '好友',
  nickname: '张三',
  kind: 'video',
  ballText: '微',
  buttons: [{ id: 'note', text: '备注' }]
}, () => {})

视频

floatWin.show({
  mode: 'template',
  template: 'video',
  title: '正在播放',
  appName: '腾讯视频',
  contentTitle: '演示影片',
  ballText: '视',
  buttons: [{ id: 'fav', text: '收藏' }]
}, () => {})

WebView 自定义与 Bridge

显示 WebView 面板

const html = `<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width,initial-scale=1"/>
  <style>
    body { font-family: sans-serif; padding: 12px; }
    button { width: 100%; padding: 8px; margin-top: 8px; }
  </style>
</head>
<body>
  <h3 id="title">自定义浮窗</h3>
  <p id="sub">Bridge 与宿主通信</p>
  <button onclick="AikoFloat.post(JSON.stringify({id:'ping',command:'ping'}))">
    向宿主发 ping
  </button>
  <pre id="meta"></pre>
  <script>
    window.__AIKO_FLOAT_PUSH = function (json) {
      document.getElementById('meta').textContent = json
      try {
        var d = JSON.parse(json)
        if (d.title) document.getElementById('title').textContent = d.title
        if (d.subtitle) document.getElementById('sub').textContent = d.subtitle
      } catch (e) {}
    }
  </script>
</body>
</html>`

floatWin.show({
  mode: 'webview',
  template: 'call',
  title: '自定义功能',
  html: html,
  expanded: true
}, () => {})

页面 → 宿主(AikoFloat.post

AikoFloat.post(JSON.stringify({
  id: 'save',       // 按钮/命令 ID
  command: 'save'   // 可选,与 id 二选一
}))

宿主在 registerListener 中收到 type: 'webview' 事件:

{
  "type": "webview",
  "command": "save",
  "buttonId": "save",
  "template": "call",
  "payload": { "id": "save", "command": "save" }
}

宿主 → 页面(update 推送)

调用 update 后,插件会执行:

window.__AIKO_FLOAT_PUSH && window.__AIKO_FLOAT_PUSH('{"title":"新标题",...}')

事件监听

方法 说明
registerListener(callback) 注册统一事件回调
unRegisterListener(callback) 取消监听并停止场景检测
isRegisterListener(callback) 是否已注册

事件类型(data.type)

type 触发时机
registered registerListener 成功
scene 通话状态或前台包名变化
button 用户点击模板按钮
webview 网页调用 AikoFloat.post
overlay 浮窗显示/隐藏/展开/收起/拖动

场景事件(type = scene)

字段 说明
scene call / wechat / video / other / idle
source telephony(系统通话)/ usage(前台包名)
confidence high(通话)/ low(包名)
callState 0 空闲 / 1 响铃 / 2 通话中(仅 telephony)
phoneNumber 来电号码(Android 12+ 可能为空)
packageName 前台包名(仅 usage)

浮窗事件(type = overlay)

action 取值:shown / hidden / expanded / collapsed / moved

按钮事件(type = button)

{
  "type": "button",
  "buttonId": "note",
  "template": "call",
  "payload": { "...": "当前 show 时的完整参数" }
}

宿主自动弹窗示例

插件不会自动 show,以下为推荐写法:

floatWin.registerListener((res) => {
  if (res['code'] !== 0) return
  const data = res['data']
  if (data?.['type'] !== 'scene') return

  const scene = data['scene'] as string

  if (scene === 'call') {
    const callState = data['callState'] as number
    if (callState === 0) {
      floatWin.hide(() => {})
      return
    }
    floatWin.show({
      template: 'call',
      title: '系统通话',
      number: data['phoneNumber'] as string,
      state: callState,
      buttons: [{ id: 'note', text: '备注' }]
    }, () => {})
  } else if (scene === 'wechat') {
    floatWin.show({
      template: 'wechat',
      title: '微信在前台',
      subtitle: '低置信度,不代表正在通话',
      kind: 'unknown'
    }, () => {})
  } else if (scene === 'video') {
    floatWin.show({
      template: 'video',
      title: '视频 App 在前台',
      appName: data['packageName'] as string,
      contentTitle: '低置信度'
    }, () => {})
  }
})

场景检测与包名规则

方法 说明
setSceneDetector(options, callback) 开关检测能力
getSceneDetector(callback) 查询当前配置
setPackageRules(options, callback) 增删微信/视频包名
getPackageRules(callback) 查询包名列表

setSceneDetector

// 默认 telephony = true,usage = false
floatWin.setSceneDetector({
  telephony: true,   // 系统通话(高置信度)
  usage: true        // 前台包名(低置信度,需先开使用情况访问)
}, (res) => {
  // 成功 data: { telephony, usage, telephonyRunning, usageRunning }
})

setPackageRules

内置微信:com.tencent.mm

内置视频(部分):爱奇艺、腾讯视频、优酷、哔哩哔哩、芒果 TV、YouTube、西瓜视频等。

// 添加自定义视频 App
floatWin.setPackageRules({
  addVideo: ['com.example.player'],
  removeVideo: ['tv.danmaku.bili'],
  addWechat: ['com.tencent.mm'],
  reset: false       // true 时清空所有自定义规则
}, () => {})

// 查询
floatWin.getPackageRules((res) => {
  // data.wechat / data.video / data.builtinWechat / data.builtinVideo
})

与 aiko-call-recorder 的关系

两个插件互不依赖,可单独使用:

插件 职责
aiko-float-window 系统浮窗 UI + 场景信号
aiko-call-recorder 通话录音、短信监听、联系人等

接听、挂断、录音请使用通话插件;本插件只负责窗口展示与场景事件。


常见问题

1. 切到微信后浮窗消失

  • 检查是否已授予通知权限(前台服务保活)
  • 部分 ROM 会激进杀后台,需在系统设置中允许 App 后台运行
  • 检查厂商是否有「悬浮窗」第二层开关

2. show 失败,code = -2

未授予悬浮窗权限。先调用 toOverlayPermissionPage 引导用户开启「显示在其他应用上层」。

3. 没有微信 / 视频场景事件

  1. 调用 toUsageStatsPermissionPage 开启「使用情况访问」
  2. setSceneDetector({ usage: true })
  3. 确认目标 App 包名在规则列表中(可用 setPackageRules 添加)

包名命中仅表示该 App 在前台,不代表正在通话或播放。

4. 面板显示 "null" 文字

请升级至最新版本。旧版在可选字段未赋值时可能显示 JSON null;新版已过滤空值。接入时建议 update 只传需要变更的字段。

5. 修改插件代码后无效果

UTS 原生插件必须重新制作自定义调试基座,普通热更新不会生效。

6. 全屏视频上看不到浮窗

部分播放器全屏时会压制叠加层,属于系统/ROM 限制,插件无法保证 100% 覆盖。

7. Android 12+ 来电号码为空

系统对 READ_PHONE_STATE 限制更严,号码字段可能为空,请结合业务做降级处理。


合规与上架提示

  • 悬浮窗会覆盖在其他 App 上层,必须在隐私政策中告知用户用途,并取得明确同意
  • 请勿用于诱导点击、未告知的监控等违规场景
  • 应用商店上架时需申报悬浮窗、电话状态、使用情况访问等敏感权限
  • 插件不采集、不上传任何用户数据

法律免责声明

购买、下载、集成或使用本插件,即视为已阅读并同意本声明。不同意请立即停止使用并卸载。

  1. 工具性质
    本插件仅为 Android 技术组件(系统叠加层浮窗、场景事件回调),按「现状(AS IS)」提供。插件作者及权利人对任何具体业务场景作出许可、背书或保证,也不因提供本插件而与最终用户形成服务关系。

  2. 使用者全责
    集成方(购买者、开发者、运营方)对其最终 App 的产品设计、权限申请、隐私政策、用户告知与同意、应用商店申报、以及实际用途承担全部法律责任。是否弹出浮窗、弹出何种内容、如何处理场景事件,均由宿主自行决定;插件不自动 show,也不内置接听、挂断或跳转。

  3. 合法合规义务
    使用本插件须遵守所在司法辖区的法律法规及监管要求(包括但不限于《中华人民共和国个人信息保护法》《网络安全法》《反不正当竞争法》及相关通信、广告、消费者保护规定),以及各应用商店、操作系统的政策。涉及覆盖其他 App、读取电话状态、使用情况访问等敏感能力时,必须向用户显著告知用途并取得明确同意,不得超范围处理个人信息。

  4. 禁止用途
    严禁将本插件用于:未经授权的监控或窃听、窃取隐私、诱导点击、骚扰、诈骗、侵犯他人合法权益,或任何违法、违规、侵害第三方权益的场景。因违规使用产生的行政处罚、民事赔偿、刑事责任及其他后果,均由使用者自行承担

  5. 能力与兼容性无担保
    场景检测(尤其是基于前台包名的微信/视频判断)不保证准确;不同 ROM、系统版本、厂商策略可能导致浮窗被压制、权限不可用或事件缺失。插件作者不保证本插件在任何设备上完全可用、持续可用或满足特定目的。

  6. 责任限制
    在法律允许的最大范围内,插件作者及权利人对因购买、集成、使用、无法使用或滥用本插件而导致的任何直接、间接、附带、惩罚性损失(包括但不限于利润损失、数据丢失、商誉损害、行政处罚、第三方索赔),不承担任何责任。即使事先知悉发生此类损失的可能性,亦同。

  7. 第三方与上架风险
    最终 App 能否通过应用商店审核、是否被系统限制悬浮窗或相关权限,取决于商店政策与设备环境,与本插件销售无关。因上架被拒、功能受限或第三方投诉引起的损失,由集成方自行承担。

  8. 声明效力
    本声明构成插件授权与使用条件的一部分。若部分条款被认定无效,不影响其余条款效力。插件作者有权更新本声明,更新后以插件文档最新版本为准。


授权与技术支持

  • DCloud 插件市场购买后:interface.uts 明文;index.uts 与 Kotlin 实现加密
  • 授权绑定购买者 appid 与 Android 包名

更新日志

changelog.md


技术支持 · 商务咨询

AI解决方案-AIKO王 · 广东 深圳

***

微信扫码添加,获取技术支持与授权咨询

隐私、权限声明

1. 本插件需要申请的系统权限列表:

SYSTEM_ALERT_WINDOW、POST_NOTIFICATIONS、FOREGROUND_SERVICE、FOREGROUND_SERVICE_SPECIAL_USE、PACKAGE_USAGE_STATS、READ_PHONE_STATE

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据,不上传服务器

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。