mtfyun 7d2e8fed8e 小鲸娘 v1.0:悬浮宠物完整工程
- 106 动画 + 2366 表情包 + 语义标签
- 三层记忆 / 心情系统 / 9 可配置插件 / 10 技能
- DeepSeek 对话直连 + MCP/HTTP 接入 + 接入文档
- 动画随机链 + 物理引擎 + 贴边碰撞校准
- 大资源(动画webp/表情图)由 scripts/ 再生成,不入库
2026-11-08 08:07:09 +00:00

小鲸娘 · 悬浮宠物(安卓原生 App)

一个独立的安卓「悬浮宠物」应用:把一只透明动画宠物做成 TYPE_APPLICATION_OVERLAY 悬浮窗,浮在手机所有 App 之上,并起一个本机 HTTP 控制端口(127.0.0.1:7350),让你(或 DSH 里的 Agent)用 curl 远程指挥它说话、换动作。

从 v1.0 起已演进为完整的小鲸娘:106 个动画、2366 张表情包、三层记忆、心情系统、9 个可配置插件、10 个技能、DeepSeek 对话直连、MCP/HTTP 接入、动画随机链 + 物理引擎。

定位说明:这不是对 dsh-pet npm 插件的修改——网页插件改不出原生悬浮窗。它是 dsh-pet 的原生伴生 App,复用了 dsh-pet 的控制契约(/broadcast)与动画素材,跑在安卓系统层。


1. 目录结构

dsh-pet-floating/
├── README.md
├── settings.gradle.kts
├── build.gradle.kts
├── gradle.properties
├── gradle/wrapper/gradle-wrapper.properties   # wrapper jar 未包含(二进制)
└── app/
    ├── build.gradle.kts
    └── src/main/
        ├── AndroidManifest.xml
        ├── assets/pet.html                     # 悬浮窗前端(气泡 + 轮询 /state)
        └── java/com/dsh/petfloat/
            ├── MainActivity.kt                 # 索要悬浮窗权限 + 启动服务
            ├── FloatingPetService.kt           # 前台服务:悬浮窗 + WebView + 拖拽
            └── PetServer.kt                    # 本地 HTTP 控制端口(127.0.0.1:7350)

2. 为什么动画用 WebP 而不是 dsh-pet 的 webm

这是本工程最关键的设计点:

  • dsh-pet 的动画是 VP9-alpha webm;
  • 安卓 WebView(Chromium)对「透明 webm」支持很差,大概率渲染成黑底;
  • 而 Chromium 对动态 WebP(animated WebP)+ alpha 支持良好,<img> 就能透明播放。

所以:把 dsh-pet 的 webm 转成动态 WebP,丢进 app/src/main/assets/,即可透明悬浮。默认没放素材时,App 会用一只 🐋 emoji 兜底,保证能跑。


3. 前置条件

  • 一台能跑 Android Studio 的电脑;
  • JDK 17;
  • Android Studio(会自带 AGP / Gradle);本工程锁定 AGP 8.2.2 + Kotlin 1.9.22 + Gradle 8.2,Gradle Wrapper 的 gradle-wrapper.jar 未提交,用 Android Studio 打开即可自动生成,或在本机 Gradle 下执行 gradle wrapper。

4. 构建

cd dsh-pet-floating
./gradlew :app:assembleDebug        # 或 assembleRelease(需要自己配签名)

产物:app/build/outputs/apk/debug/app-debug.apk。

release 未配签名,直接装会失败;先用 debug,或自行加 signingConfig。


5. 安装 & 授权悬浮窗

  1. 安装 APK(任选其一):

    • 电脑 adb install app-debug.apk;
    • 或让宿主的 Agent 用 pilot 的 app.install {"upload":"<文件名>"}(先把 APK 放进 /opt/pilot/run/uploads/)装。
  2. 首次启动:App 会跳到系统「显示在其他应用上层」授权页,勾选允许。

  3. 也可用 Agent 侧 appops 直接开(android 8+):

    appops.set { package: "com.dsh.petfloat", op: "android:system_alert_window", mode: "allow" }
    
  4. 授权后回到 App(或由 app.launch 拉起 com.dsh.petfloat),悬浮宠物即出现,可按住拖动位置;前台服务常驻,通知栏有一条「悬浮宠物运行中」,关闭该通知/强杀即可停。


6. 控制接口(本机 127.0.0.1:7350)

契约对齐 dsh-pet 的 /broadcast,Agent 用同一套 curl 就能指挥。

让宠物头顶冒气泡

curl -X POST 'http://127.0.0.1:7350/broadcast' \
  -H 'Content-Type: application/json' \
  -d '{"text":"搞定啦,回来看看","duration":8000}'
  • text:气泡正文(原样显示);空 → 收起。
  • duration:显示毫秒,缺省 8000。

换动画

curl -X POST 'http://127.0.0.1:7350/anim' \
  -H 'Content-Type: application/json' \
  -d '{"name":"dance"}'
  • 前端会尝试加载 /dance.webp;若 assets/dance.webp 不存在,保持当前形象。

收起气泡

curl -X POST 'http://127.0.0.1:7350/bubble/clear'

读取状态 / 探活

curl 'http://127.0.0.1:7350/state'

对照表

本 App dsh-pet 原接口 说明
POST /broadcast POST /dsh-pet-7340/broadcast 气泡接力,语义一致
POST /anim POST /dsh-pet-7340/anim 简化为「按名换 webp」
POST /bubble/clear POST /dsh-pet-7340/bubble/clear 语义一致

7. 素材转换(VP9-alpha webm → animated WebP)

在一台有 ffmpeg(含 libwebp 编码器)的机器上:

ffmpeg -i 输入.webm -c:v libwebp -q:v 80 -lossless 0 -loop 0 -pix_fmt yuva420p -an 输出.webp
  • 默认形象:转一个待机动画,命名 pet.webp,放 app/src/main/assets/pet.webp;
  • 点播动画:每个动作命名 <name>.webp,用 /anim {"name":"<name>"} 切换;
  • dsh-pet 素材源在 dsh-pet/assets/webm/(106 个、约 53MB、含中文文件名;放进 assets 的 webp 文件名建议用 [a-zA-Z0-9._-],本服务的静态路由只放行该字符集)。

验证 alpha:ffprobe -v error -show_entries stream=pix_fmt 输出.webp 应看到 yuva420p。


8. Agent 如何对接(在宿主机上)

App 的控制端口绑定 127.0.0.1:7350,与 dsh-pet 一样走本机 HTTP,因此 Agent 直接:

curl -X POST 'http://127.0.0.1:7350/broadcast' -H 'Content-Type: application/json' \
  -d '{"text":"…","duration":8000}'

即可让手机上的悬浮宠物说话;/anim、/bubble/clear 同理。


9. 已知限制 / 风险

  1. 本机没有安卓构建链(无 JDK/Gradle/SDK),本工程源码在此环境下未实际编译验证,需在 Android Studio 里首次构建时排错;
  2. 透明动画依赖「webm→动画 WebP」转码,未在此环境验证最终观感;
  3. 悬浮窗在系统设置、部分锁屏/支付界面可能被系统临时隐藏(安卓安全策略);
  4. 控制端口只绑回环地址、无鉴权,仅限本机可信环境使用,别开放公网。

10. 素材再生成(新机器还原大体积资源)

动画与表情包体积大(动画两套 537MB、表情包 114MB),不纳入 git。 新机器上按下面顺序重建即可(均不依赖本仓库之外的私有数据):

# 1) 动画 webp(assets_hq / assets_small,106 个,来自 dsh-pet 的 webm 转码)
DSH_PET=/path/to/dsh-pet ./scripts/regenerate_assets.sh

# 2) 表情包图(assets_net,约 2366 张,来自梗鲸档案馆 aigengtu.com)
python3 scripts/download_memes.py
  • scripts/anim_index.json:106 个动画的「中文名 → a000N → 时长」映射(已随仓库提交)
  • scripts/meme_meta.json:档案馆 2436 条「编号 → 台词(含情绪描述)」元数据(已提交)
  • scripts/gen_config.py:从 dsh-pet 的 config.jsonc 重建 config.json 的动画部分(保留 memes)
  • app/src/main/assets_common/(config.json、字体、pet.html、m01–m51 精选表情)随仓库提交,是可直接构建的最小集

无 dsh-pet 源或 ffmpeg 时,App 仍可用 🐋 emoji 兜底启动,只是没有正式动画。


11. 对外接入(速览)

详细文档见 INTEGRATION.md 与各端口 /docs / /docs.md / /llms.txt。

能力 入口
HTTP 控制 POST /api/task /api/notify /api/anim /api/meme /api/skill 等(127.0.0.1:7350,写请求需 X-Pet-Token)
SSE 互动事件 GET /api/events?token=…
MCP Server POST /mcp(JSON-RPC,7 个工具)
能力目录 GET /api/catalog(106 动作 + 2366 表情 + 语义标签)
插件 GET/POST /api/plugin、GET /api/plugins.js(JS,WebView 内跑)

10. 素材再生成(新机器还原)

三个大体积资源目录不纳入 git(合计约 650MB,多为可再生/可下载的二进制), 在新机器上按下面顺序重建:

# ① 动画 webp(assets_hq 640×360 / assets_small 320×180,共 106 个)
#    从 dsh-pet 的 webm 源转码,映射表在 scripts/anim_index.json
DSH_PET=/path/to/dsh-pet ./scripts/regenerate_assets.sh

# ② 表情包图(assets_net,~2300 张)
#    从「梗鲸档案馆」(aigengtu.com) 下载并转码,元数据在 scripts/meme_meta.json
python3 scripts/download_memes.py

# ③(可选)动画配置 config.json 若需从 dsh-pet 重建,运行后保留 memes 列表
DSH_PET=/path/to/dsh-pet python3 scripts/gen_config.py

依赖:ffmpeg(libvpx-vp9 解码 + libwebp_anim 编码)、requests、Pillow。 config.json、candy.ttf、pet.html、内置表情包(m01–m51)在 assets_common/,已随仓库提交。

仓库只存「源码 + 脚本 + 源数据 + 小体积源资产」,构建产物(APK)与可再生资源(hq/small/net) 始终留在本机,不被 git 追踪——这是为了让仓库能稳定推送、克隆,而不是塞进 650MB 二进制。

S
Description
小鲸娘 Android 悬浮宠物:106 动画 + 记忆 + 心情 + 插件 + MCP + DeepSeek 直连
Readme
909 MiB
0 Stars 1 Watchers 0 Forks
Languages
Kotlin 95.4%
Python 2.3%
Shell 1.2%
HTML 1.1%