# TSGame 原生 ↔ H5 接口契约与启动流程总规范（跨平台适配版）

> **文档目的**：本规范以现有 Android 工程的**真实代码**为唯一依据，抽象出"原生容器"与"H5 游戏内容"之间的**完整接口契约**和**启动/资源加载流程**。
> 目标是：在一个全新的原生平台（HarmonyOS）上重新实现"原生容器"，使**现有 H5 一行代码都不改动**即可正常运行。
>
> **适配原则**：
> - 本文档只规定"契约"（协议、接口名、参数结构、调用时机、数据流），**不规定原生内部如何实现**——原生侧（包括 HarmonyOS）可根据平台能力自由实现，只要对外行为与本契约一致即可。
> - 文中所有接口名、参数字段、常量值均来自真实源码，并标注了来源文件与行号，便于核对。
> - 凡现有 Android 代码中"未实现/已注释/无回传"的部分，本文明确标注，作为新平台需要**补齐或决策**的事项。

---

## 目录

1. [总体架构](#1-总体架构)
2. [核心：JS 通信桥协议（WebViewJavascriptBridge）](#2-核心js-通信桥协议webviewjavascriptbridge)
3. [应用启动流程](#3-应用启动流程)
4. [配置体系](#4-配置体系)
5. [资源（大厅/子游戏）管理](#5-资源大厅子游戏管理)
6. [H5 数据注入：app_data.js](#6-h5-数据注入app_datajs)
7. [WebView 容器能力要求](#7-webview-容器能力要求)
8. [接口契约一：H5 → 原生（入站 Handler 全集）](#8-接口契约一h5--原生入站-handler-全集)
9. [接口契约二：原生 → H5（出站 Handler 全集）](#9-接口契约二原生--h5出站-handler-全集)
10. [原生能力专题](#10-原生能力专题)
11. [子游戏跳转与通用网页容器](#11-子游戏跳转与通用网页容器)
12. [数据结构汇总](#12-数据结构汇总)
13. [关键常量与第三方账号](#13-关键常量与第三方账号)
14. [HarmonyOS 适配检查清单与待决策项](#14-harmonyos-适配检查清单与待决策项)

---

## 1. 总体架构

整个 App 本质上是一个**多 WebView 容器 + 资源管理器**，所有业务逻辑（大厅、棋牌子游戏）都是 H5，原生只提供"启动引导 + 资源下载解压 + 设备能力桥接"。

```
┌──────────────────────────────────────────────────────────────┐
│                        原生容器（可在任意平台重写）              │
│                                                                │
│  [启动引导页] ──► 读本地配置 ──► 请求远程配置 ──► 下载/解压资源 │
│       │                                              │         │
│       └──────────────► 注入 app_data.js ─────────────┘         │
│                              │                                 │
│                              ▼                                 │
│   ┌────────────────────────────────────────────────────┐     │
│   │              WebView 容器（大厅 / 子游戏）            │     │
│   │   加载 file://.../index.html?Launchtype=X            │     │
│   │                                                      │     │
│   │   ◄──── JS 通信桥（WebViewJavascriptBridge） ────►   │     │
│   │        H5 ⇄ 原生：登录/分享/支付/定位/音频/震动…     │     │
│   └────────────────────────────────────────────────────┘     │
│                              │                                 │
│   设备能力：微信/QQ/抖音分享、微信支付登录、高德定位、          │
│             录音、音效、摇一摇、扫码、相机、剪贴板、震动…       │
└──────────────────────────────────────────────────────────────┘
```

### 1.1 容器角色划分（现有 Android 实现）

| 角色 | 现有 Android 类（仅作来源标注，新平台无需同名） | 说明 |
|---|---|---|
| 启动引导页 | `weclomeactivity1`（Launcher，强制横屏） | 读配置、请求远程配置、下载解压资源、注入 `app_data.js`，完成后跳大厅 |
| 大厅容器 | `webviewActivity` | 加载大厅 H5（`.../gamehall/index.html`） |
| 子游戏/内置网页容器 | `openwebActivity1` | 由大厅通过桥接口 `OpenurlTitleData` 打开任意 H5 url |
| 微信回调容器 | `NewwebviewActivity` | 与大厅容器接口集几乎完全相同，承担微信 scheme 回调 |

> **重要说明**：`webviewActivity` 与 `NewwebviewActivity` 注册的桥接口（44 个入站 handler）**名称、参数结构、回调名几乎完全一致**，差异仅 3 处（见 §8.x 标注）。新平台**只需实现一套统一的桥接口契约**即可同时覆盖大厅与子游戏容器。本文档给出的是这套统一契约。

---

## 2. 核心：JS 通信桥协议（WebViewJavascriptBridge）

> 这是整个适配的**重中之重**。H5 完全依赖此协议与原生通信，**不可更改**。新平台必须 1:1 实现此协议，包括其 URL scheme、消息格式、JS 全局对象与注入时机。
> 来源：`com/tagmae/jsbridge/`（`BridgeWebView`、`BridgeWebViewClient`、`BridgeUtil`、`Message`）。本协议是开源库 lzyzsd/JsBridge 的实现。

### 2.1 协议总览

> ⚠️ **重要范围说明**：本章描述的 `WebViewJavascriptBridge` 协议适用于**大厅容器与子游戏容器**（基于 `BridgeWebView`，即 `webviewActivity` / `NewwebviewActivity`）。
> **另有一个独立的通用网页容器 `openwebActivity1`**（由桥接口 `OpenurlTitleData` 打开，用于活动页/收银台/客服等"打开网页"场景），它**不使用本协议**，而是用传统的"**JS 接口对象注入（`@JavascriptInterface`，对象名 `settings`）+ `javascript:` 直接函数调用**"。该容器的接口契约见 [§11.3](#113-通用网页容器-openwebactivity1-的独立接口契约)，新平台**两套机制都要实现**。

在基于 `BridgeWebView` 的两个主容器里，业务通信**不使用**平台的"JS 接口注入"机制（如 Android `@JavascriptInterface`、HarmonyOS `javaScriptProxy`）作为通道——其中所有 `addJavascriptInterface(new settings(), "settings")` 均已注释失效，仅保留的崩溃监控用途与业务无关。**这两个主容器的业务通信 100% 走下述 URL scheme 拦截协议。**

> 另：源码中存在 `ShareJavascriptInterface`（注入名 `NativeShare`，方法 `shareToQQ`/`shareToDouYin`），但其注入入口 `ShareActivityPatch` 在全工程**无任何引用，是死代码**，H5 无法依赖，新平台**无需实现**。

通信靠两条单向通道拼成双向：

1. **原生 → H5**：原生执行 JS：`WebViewJavascriptBridge._handleMessageFromNative('<消息JSON>')`。
2. **H5 → 原生**：H5 改变一个隐藏 iframe 的 `src` 为 `yy://...` 触发导航；原生在"资源加载拦截/URL 跳转拦截"里识别 `yy://` 前缀并阻断真实导航，转而处理消息。

### 2.2 URL scheme 约定（必须原样实现）

来源：`BridgeUtil.java`

| 常量 | 值 | 含义 |
|---|---|---|
| 协议前缀 | `yy://` | H5 发起的所有桥调用 |
| 返回数据前缀 | `yy://return/` | H5 对原生调用的回执 / 取队列回执 |
| 取消息队列 | `yy://return/_fetchQueue/` | 原生通知 H5"把待发消息队列交出来" |

原生侧 URL 拦截逻辑（必须复刻）：
```
拦截到 url（需先 URLDecode）：
  if url 以 "yy://return/" 开头:
       → 解析出 functionName 与 data，执行对应回调，阻断导航
  else if url 以 "yy://" 开头:
       → 调用 _fetchQueue()（见下），阻断导航
  else:
       → 正常导航
```

### 2.3 原生 → H5 的两条 JS 调用（必须原样实现）

来源：`BridgeUtil.java`
- 下发单条消息：`javascript:WebViewJavascriptBridge._handleMessageFromNative('<JSON字符串>');`
- 抽取 H5 待发队列：`javascript:WebViewJavascriptBridge._fetchQueue();`
  - 该调用本身被当作一次"原生→H5"调用，其回执由 H5 通过 `yy://return/_fetchQueue/<JSON数组>` 送回原生。

### 2.4 消息体（Message）JSON 结构

来源：`Message.java`。原生与 H5 之间所有消息都序列化为如下结构（字段按需出现）：

| 字段 | 类型 | 含义 |
|---|---|---|
| `handlerName` | String | 目标 handler 名（指定调用哪个已注册的 handler；无则走 defaultHandler） |
| `data` | String | 业务数据载荷（**注意：可能是裸字符串，也可能是 JSON 字符串**，逐接口而定） |
| `callbackId` | String | 本条消息期望对端回执时带回的 id（发起方生成） |
| `responseId` | String | 回执消息：对应此前收到的 `callbackId` |
| `responseData` | String | 回执消息的返回数据载荷 |

原生侧生成 callbackId 的格式：`JAVA_CB_<自增id>_<时间戳>`（来源 `BridgeWebView.doSend`）。新平台可用任意唯一字符串格式，H5 不关心其内部格式，只负责原样回带。

### 2.5 JS 端 API（H5 使用，原生必须保证这些 API 存在且行为一致）

H5 通过全局对象 `window.WebViewJavascriptBridge` 调用以下方法（由注入的 `WebViewJavascriptBridge.js` 提供）：

| JS API | 作用 |
|---|---|
| `registerHandler(handlerName, function(data, responseCallback){...})` | H5 注册一个 handler，供**原生调用**（即接收"原生→H5"消息）。`responseCallback(retData)` 用于把结果回给原生。 |
| `callHandler(handlerName, data, function(responseData){...})` | H5 **调用原生**注册的 handler，`responseData` 回调接收原生的同步返回。 |
| `init(function(message, responseCallback){...})` | 设置默认 handler（接收未指定 handlerName 的消息）。 |
| `send(data, responseCallback)` | 向原生默认 handler 发消息。 |

H5 监听桥就绪事件后才开始调用（典型写法，原生需保证此事件/回调时序）：
```javascript
function connectBridge(cb){
  if (window.WebViewJavascriptBridge) { cb(WebViewJavascriptBridge); }
  else { document.addEventListener('WebViewJavascriptBridgeReady',
           function(){ cb(WebViewJavascriptBridge); }, false); }
}
```

> 新平台需提供与 lzyzsd/JsBridge **同名同行为**的 `WebViewJavascriptBridge.js`，确保 H5 中已有的 `registerHandler`/`callHandler` 调用全部生效。建议直接复用该库的 `WebViewJavascriptBridge.js` 原文件。

### 2.6 JS 注入时机（关键时序）

来源：`BridgeWebViewClient.onPageFinished`
- 在**每个页面加载完成（onPageFinished）时**，原生把 `WebViewJavascriptBridge.js` 内容注入页面（作为 JS 执行）。
- 注入后，原生把"启动消息队列"（页面加载完成前积压的待发消息）逐条 `_handleMessageFromNative` 下发。

新平台实现要点：
1. 监听页面加载完成事件 → 注入桥 JS。
2. 维护"启动前积压消息队列"：页面就绪前原生若调用 H5 handler，先入队，页面就绪后补发。
3. 注入的 JS 解析时会**去除以 `//` 开头的注释行**（`BridgeUtil.assetFile2Str`），新平台若直接读取 js 文件注入，需保持等价（或直接内联完整 js）。

### 2.7 一次完整调用的数据流示例

**H5 调用原生（带返回）** —— 例：H5 获取系统时间
```
H5:   bridge.callHandler('getTime', '', function(ret){ /* ret = "1700000000000" */ });
       └─ 桥生成 message {handlerName:'getTime', data:'', callbackId:'cb_1'}
          入队 → 触发 iframe.src='yy://__queue__'
原生: 拦截 yy:// → _fetchQueue() 取出该 message
      → 找到名为 'getTime' 的 handler 执行 → responseCallback("1700000000000")
      → 原生把 {responseId:'cb_1', responseData:'1700000000000'} 经
        _handleMessageFromNative 下发
H5:   桥按 responseId 找到 cb_1 回调并执行
```

**原生调用 H5（推送）** —— 例：定位结果推送
```
原生: callHandler('getlocationinfo', '<MaplocationInfo的JSON>', null)
      → message {handlerName:'getlocationinfo', data:'<json>'} 经
        _handleMessageFromNative 下发
H5:   桥查到 H5 用 registerHandler('getlocationinfo', ...) 注册的处理函数并执行
```

---

## 3. 应用启动流程

> 来源：`weclomeactivity1.java`、`initwebviewutil`、`GameupdateUtil.java`、`webviewActivity.initwebview`。
> 下述为**行为时序**，新平台按此顺序复刻即可，内部实现自由。

### 3.1 启动总时序

```
启动引导页 onCreate
  │  全屏、强制横屏、保持屏幕常亮
  ▼
读取本地配置（资源目录名编码，见 §4.1）
  │  initfile = "gamehall"（gamestart 配置）
  │  filestart = "FtJf...ziK"（gamedir 配置）
  ▼
申请运行时权限（存储 / 电话状态 / 定位）
  ▼
拼接远程配置 URL（见 §4.3）
  │  configUrl = "http://" + (gameconfig 配置去'-'换'/') + ".txt"
  ▼
首次安装？
  ├─ 是：把内置预置包（gamehall）拷贝到内部存储并解压
  └─ 否：比较内置 version.xml 与已解压 version.xml 版本，必要时重新拷贝
  ▼
检测网络 → 请求远程配置（HTTP GET，禁缓存，URL 追加 "?a=<时间戳>"）
  ▼
解析远程配置（JSON）
  └─ 真实格式：单 agentlist 树，agent→game→channel→market 逐层匹配（见 §4.3/§4.4）
  ▼
版本决策：远程 game_version > 本地 version.xml 的 version ？
  ├─ 需要更新：下载 game_zip 指向的 zip → 删旧目录 → 解压到资源目录
  └─ 无需更新：直接用本地资源
  ▼
（如有）APK 自升级：app_version 高于本地 appversion → 下载 APK 安装
  ▼
注入 app_data.js（把配置写进大厅资源目录，见 §6）
  ▼
跳转大厅容器（不带 Intent extra；配置经 app_data.js 与桥接口获取）
  ▼
大厅容器加载 file://.../gamehall/index.html?Launchtype=0
```

### 3.2 阻断式提示

远程配置 JSON 顶层若含非空 `showmessage` 字段，启动流程会**弹窗提示并阻断**（用于公告/停服）。配置内容长度过短（< 30 字符）时视为错误文案，直接弹窗。新平台需复刻此"全局公告阻断"行为。

---

## 4. 配置体系

### 4.1 本地配置编码机制（资源目录名编码）

> 来源：`getAllFilename(String name)`（`weclomeactivity1` / `webviewActivity` 均有同名实现）。

现有 Android 把每个配置项做成一个**资源目录**，目录下放一个**以"配置值"命名的子文件夹**（外加一个占位文件 `BoolTest.java` 需被跳过）。读取逻辑：
```
列出 assets/<配置名>/ 下的条目
  跳过名为 "BoolTest.java" 的占位文件
  剩下那个"子文件夹的名字"即为配置值
返回该名字（找不到则为空字符串）
```

> **HarmonyOS 适配建议**：这种"用目录名编码配置"的做法是历史包袱。新平台**推荐改为正规的键值配置文件**（如随包内置一个 JSON），只要能提供下表中的同名配置值即可，H5 完全无感知。

### 4.2 配置项清单（本仓库实测值）

| 配置键 | 本仓库实际值 | 含义 / 用途 |
|---|---|---|
| `agent` | `veRa0qrBf0df2K1G4de2tgfmVxB2jxpv` | 代理商 ID（远程配置分层匹配 key） |
| `channel` | `FtJf073aa0d6rI1xD8J1Y42fINTm0ziK` | 渠道 ID（分层匹配 key） |
| `gamedir` | `FtJf073aa0d6rI1xD8J1Y42fINTm0ziK` | 资源解压**父目录名** |
| `gamestart` | `gamehall` | 资源解压后的**游戏目录名**（大厅入口所在目录） |
| `appversion` | `49` | 当前 App 版本号（数字，与远程 `app_version` 比较决定是否升级 APK） |
| `market` | `3` | 市场 ID（分层匹配 key；也经桥 `getmarketname` 暴露给 H5） |
| `gameid` | （空，仅占位） | 游戏 ID（空时回退用 version.xml 中的 game id） |
| `weburl` | （空） | 大厅 H5 远程地址；**空 → 本地 file:// 加载**（本仓库走本地） |
| `gameconfig` | `tsgames.daoqi88.cn-config-update_jsonv2` | 远程配置地址（`-`→`/` 后拼 `.txt`）。生产端点；测试端点为 `...-config_test-update_jsonv2_test` |
| `other` | （空） | 业务自定义值，经桥 `getOther`/`getothername` 暴露给 H5 |
| `tuiguang` | （目录不存在 → 空） | 推广/邀请码，写入 app_data.js 的 `app_invitationcode` |
| `servertype` | （空，**无代码读取**） | 历史预留，启动流程不使用，可不实现 |
| `gameserver` | （空，**无代码读取**） | 历史预留，启动流程不使用，可不实现 |

### 4.3 远程配置请求

**URL 拼装**（来源 `weclomeactivity1.init`）：
```
gameconfig 值 = "tsgames.daoqi88.cn-config-update_jsonv2"    // 生产端点
→ 把 '-' 替换为 '/'  = "tsgames.daoqi88.cn/config/update_jsonv2"
→ configUrl = "http://" + 上一步 + ".txt"
         = "http://tsgames.daoqi88.cn/config/update_jsonv2.txt"
请求时再追加防缓存参数： configUrl + "?a=" + 当前毫秒时间戳
请求方式：HTTP GET，强制不走缓存
（测试端点：把 config / update_jsonv2 换成 config_test / update_jsonv2_test）
```

**返回体**：一个 `.txt` 文件，内容为 **JSON**（解析前用 JSON 解析校验；长度 < 30 视为错误提示文案直接弹窗）。

> ⚠ **以下为线上真实格式**（实测 `http://tsgames.daoqi88.cn/config/update_jsonv2.txt`，对应 Android `Bean(Txt)` + `weclomeactivity1.chuliversion_1`）。本节曾误录为另一套废弃格式（`Bean1`/`GameupdateUtil`：层级 `agent→channel→market→game`、字段 `game_download`），**已纠正**。鸿蒙侧 `RemoteConfig`/`VersionResolver` 按本节真实格式实现。

#### 真实配置格式（顶层仅 `agentlist`，层级 agent → game → channel → market）
```jsonc
{
  "showmessage": "",                 // 非空 → 弹窗阻断（公告/停服）
  "agentlist": [
    {
      "agentid": "veRa...", "agentname": "进贤", "showmessage": "",
      "gamelist": [                   // ← game 在 channel/market 之上
        {
          "gameid": "G2hw...", "gamename": "进贤聚友",
          "game_version": 261,        // 数字！资源版本号
          "game_zip": "http://tsgames.daoqi88.cn/zip2/gamehall.zip",  // ← 资源下载字段是 game_zip（非 game_download）
          "game_size": "4.61M", "game_hall_dir": "gamehall",
          "channellist": [
            {
              "channelid": "FtJf...", "game_version": 0, "game_zip": "",
              "marketlist": [
                { "marketid": 3,       // 数字！
                  "app_version": 49,   // 数字！apk 版本
                  "app_download": "http://daoqi.daoqi88.cn/apk/gamehall.apk",
                  "app_size": "6.61M"
                  /* 此层也可携带 game_version / game_zip 覆盖上层 */
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

要点：
- **顶层只有 `agentlist`**（无顶层 `gamelist`、无两棵树并存、无二级 `url` 二次请求）。
- **资源下载字段名是 `game_zip`**（不是 `game_download`）；APK 升级字段 `app_download`/`app_version` 落在 market 层。
- `marketid` / `app_version` / `game_version` 在 JSON 里都是**数字**，匹配/比较需 string↔number 归一。

> Android 还存在一套老的 1.0 兼容分支（`mJsonRootBean.getGamelist()!=null` 时走 `chulisuoyin` + `Bean1`，字段 `game_download`、层级 `agent→channel→market→game`）。**线上真实配置顶层无 `gamelist`，恒走上面的真实格式**；1.0 分支本期不需实现。

### 4.4 分层匹配与覆盖逻辑（agent → game → channel → market）

> 来源 `weclomeactivity1.chuliversion_1`（已核实，线上真实路径）。匹配 key `{agentid, gameid, channelid, marketid}` 来自本地配置；**`gameid` 为空（大厅）时回退取 `version.xml` 的 `<game id>`**（`versd.getGameid()`）。

- 遍历 `agentlist`(agentid 命中) → `gamelist`(gameid 命中) → `channellist`(channelid 命中) → `marketlist`(marketid 命中)。
- **资源升级 `game_zip`/`game_version`**：在 game、channel、market 三层累积，**越深越后、覆盖前层**；`game_version` 为 `"0"`/空视为未设置不覆盖。
- **APK 升级 `app_download`/`app_version`**：取 market 层值（下载地址与版本均非空才采纳）。
- **`showmessage`**：任一命中层非空即生效（弹窗阻断）。
- 最终：资源版本与本地 `version.xml` 的 `<version value>` 比较，远程更高则下载 `game_zip`；APK 版本与本地 `appversion` 比较（HarmonyOS 不安装 apk，此项仅记录/比较）。

每层可携带的可覆盖字段：`app_version`、`app_download`、`app_size`、`game_version`、`game_zip`、`game_size`、`showmessage`。

> **与旧参考文档的差异提示**：旧版 `TSGame_应用启动流程详解.md` 及本规范早期版本给出的配置 JSON（两棵树并存、二级 `url` 二次请求、`game_download` 字段、`agent→channel→market→game` 层级）**与线上真实字段不符**，对应的是 Android 内已废弃的 `Bean1`/`GameupdateUtil` 路径。一律以本节真实格式（`game_zip`、单 `agentlist` 树、`agent→game→channel→market`、数字 id/版本、gameid 回退 version.xml）为准。

---

## 5. 资源（大厅/子游戏）管理

### 5.1 真实文件系统路径

> 来源 `initwebviewutil.getdate`。

```
内部存储根 = <App内部files目录>            // 例如 Android: /data/data/<包名>/files
父目录(upurlpath) = <files>/tsgames/<包名>/<启动毫秒时间戳>
解压根(urlpath)   = <父目录>/<gamedir值>    // = .../<时间戳>/FtJf073aa0d6rI1xD8J1Y42fINTm0ziK
游戏内容目录      = <解压根>/<gamestart值>  // = .../FtJf...ziK/gamehall
大厅入口          = <解压根>/gamehall/index.html
```

两个路径以键值对持久化（供容器读取）：`urlpath`（解压根）、`upurlpath`（父目录）。
> 注意：解压根含**启动时间戳**，意味着每次全新解压会落到新目录。新平台可沿用或简化为固定目录，对 H5 无影响（H5 只看到自己被以 `file://` 加载）。

### 5.2 内置预置包（首次安装）

- 现有工程随包内置 `gamehall` 目录，首启时整体拷贝到解压根，并对其中的 `.zip` / `.so` 解压，然后删除压缩包。
- **本仓库内置包 `gamehall_42_0527.zip` 仅 553 字节，里面只有一个 `version.xml`，是占位/演示包**——真正可运行的大厅 H5（index.html 等）由远程 zip 下载提供。新平台需注意：**不能假设内置包里有完整大厅页面**。

### 5.3 版本文件 version.xml

> 资源目录与内置包内均含 `version.xml`，结构如下（实测）：
```xml
<game>
  <agent   id="..." name="天盛网络"/>
  <game    id="..." name="友乐游戏"/>
  <channel id="..." name="友乐互动游戏"/>
  <version value="42" name="1.42"/>
</game>
```
- `version value` 为整数，用于与远程配置 `game_version` 比较（远程更高则下载更新）。
- `agent/game/channel` 的 id 作为资源自带的归属标识。

### 5.4 zip 下载与解压

```
下载 URL = 远程配置 game_zip 字段值 + "?a=<时间戳>"
下载到   = <App外部files目录>/dowlod_zip/<时间戳>Projects.zip   （目录名拼写即代码原样 "dowlod_zip"）
解压步骤：
  1. 删除旧的 <解压根>/gamehall 内容
  2. 解压 zip 到 <解压根>/    （zip 内顶层即含 gamehall/...）
  3. 删除 zip 文件
  4. 进入"注入 app_data.js → 跳大厅"收尾
```

### 5.5 APK 自升级

- 远程 `app_version` 高于本地 `appversion` 时，下载 `app_download` 指向的安装包并触发系统安装。
- HarmonyOS 适配：改为对应平台的应用更新方式即可，与 H5 契约无关。

---

## 6. H5 数据注入：app_data.js

> 来源 `weclomeactivity1.inith5data`。**这是把原生配置传给 H5 的关键机制之一，H5 直接读取这些全局变量，必须复刻。**

跳转大厅前，原生在大厅资源目录生成/改写 `app_data.js`：
```
路径：<解压根>/gamehall/app_data.js
```
文件内容为一组全局 JS 变量声明（H5 直接引用），字段如下：

| 全局变量 | 来源 | 含义 |
|---|---|---|
| `app_version` | version.xml 的 version | 资源版本号 |
| `app_gameconfig` | `gameconfig` 配置 | 远程配置地址标识 |
| `app_gamedir` | `gamedir` 配置 | 资源父目录名 |
| `app_gamestart` | `gamestart` 配置 | 游戏目录名（gamehall） |
| `app_agent` | `agent` 配置 | 代理商 ID |
| `app_appversion` | `appversion` 配置 | App 版本号 |
| `app_market` | `market` 配置 | 市场 ID |
| `app_channel` | `channel` 配置 | 渠道 ID |
| `app_invitationcode` | `tuiguang` 配置 | 推广/邀请码 |
| `app_Launchtype` | 固定 `'0'`（大厅） | 启动类型 |
| `app_gamename` | 资源/配置 | 游戏名 |
| `app_getwifisignalLevel` | 运行时 | WiFi 信号等级初值 |

> 新平台只需在加载大厅页面前，保证大厅目录下存在等价的 `app_data.js`（同名全局变量、同含义取值）。内部如何生成文件不限。

---

## 7. WebView 容器能力要求

> 来源 `webviewActivity.initWebViewSettings` / `NewwebviewActivity.initWebViewSettings`。下列为 H5 正常运行所需的**能力要求**，新平台用等价配置满足即可。

| 能力 | 要求 | 说明 |
|---|---|---|
| JavaScript | 必须启用 | — |
| DOM Storage / 本地数据库 | 必须启用 | H5 用 localStorage 等持久化 |
| 本地文件访问 | 必须允许 | 加载 `file://` 本地页面 |
| `file://` 跨源访问 | 必须允许（universal access from file URLs） | H5 从 file 页面请求本地/远程资源 |
| 混合内容（http+https） | 允许（always allow） | 本地页面内嵌 http 资源 |
| 明文 HTTP 流量 | 允许 | 远程配置/资源走 http |
| 缓存 | 不使用缓存（每次走网络/本地最新） | — |
| 定位 | 启用（H5 geolocation + 桥定位） | — |
| 缩放 | 关闭（禁缩放、禁内置缩放控件、禁 wide viewport） | 固定布局游戏 |
| 多窗口 | 关闭 | — |
| UserAgent | **未自定义**（用平台默认） | H5 不依赖特定 UA |
| Cookie | 未特殊配置 | — |
| 远程调试 | 启用（开发期） | — |
| 屏幕方向 | **横屏**，在横屏两朝向间跟随传感器自动旋转，不进入竖屏 | 横屏项目；H5 仍可经方向接口显式切换（见下） |

> **🔴 屏幕方向约束（横屏项目）**：本项目为**横屏项目**，应用**固定横屏**——在横屏的两个朝向（正向 / 反向 180°）之间**跟随设备重力传感器自动旋转**，但**不进入竖屏**（HarmonyOS `auto_rotation_landscape`）。
> 但**遵守 H5 零改动铁律**：H5 经方向接口发起的显式切换**行为完全不变**，与原 Android 一致——
> - `orientation` handler（§8.4）：`"1"` → 横屏，**非 `"1"` → 竖屏**（原生仍真实切竖屏，对齐 Android `setRequestedOrientation`）；
> - 子游戏 `SwitchOverGameData` 的 `webtype`（§11.1）：`"3"` 横屏 / `"2"` 竖屏；
> - 通用网页 `OpenurlTitleData` 的 `orientation`（§11.2）：`"1"` 竖屏 / 其他 横屏。
>
> 即"不支持竖屏"指 **默认横屏、不随传感器自动转屏**，**不是**禁止 H5 控制方向。原生以"默认横屏 + 按 H5 请求动态切换方向"落地（见框架文档 §7.4），从而既是横屏项目，又不破坏 H5 零改动。

容器还需提供：
- **本机 HTTP 服务**：用于"截图分享上传"。容器启动一个本机 HTTP server，把地址（`http://<本机IP>:<端口>/testurl`）通过桥 `setPostUrl` 推给 H5（见 §9）。新平台需提供等价的本机上传端点能力，否则截图分享链路不通。

---

## 8. 接口契约一：H5 → 原生（入站 Handler 全集）

> H5 通过 `bridge.callHandler('<名称>', '<data>', responseCallback)` 调用。
> **data 列**标注是裸字符串还是 JSON 字符串；**同步返回列**标注是否通过 `responseCallback` 同步返回（裸字符串/JSON）；多数能力的结果是**异步**通过 §9 的出站 handler 推回。
> 来源：`webviewActivity.initwebjh`（行 2185–3012）/ `NewwebviewActivity.initwebjh`（行 2164–3129）。两容器一致，差异在表后标注。

### 8.1 账号 / 社交

| Handler | data | 同步返回 | 异步回传 | 功能 |
|---|---|---|---|---|
| `accreditlogin` | 裸字符串（`"1"`=QQ，其他=微信；QQ 实为空实现） | 无 | `sharelogin` | 触发授权登录（实际仅微信） |
| `friendsSharetypeUrlToptitleDescript` | JSON（`sharetypeBean`，见 §12） | 无 | `sharesuccess`（仅微信有结果） | 分享（微信/QQ/抖音） |
| `getphoto` | 裸字符串（图片下载 JSON 数组） | 无 | `getphoto` | 批量下载图片/头像到本地 |

### 8.2 支付

| Handler | data | 同步返回 | 异步回传 | 功能 |
|---|---|---|---|---|
| `paybrowser` | 裸字符串（收银台 URL） | 无 | 无（H5 收银台页自理） | 浏览器方式支付（**当前生效**） |
| `getGameplay` | JSON（`price/body/typeplay/type/user`） | 无 | `PayuserPaytypePaystate` | App 内微信支付（**仅旧版 `webviewActivity` 生效**；`NewwebviewActivity` 已注释） |

### 8.3 设备 / 系统信息

| Handler | data | 同步返回 | 异步回传 | 功能 |
|---|---|---|---|---|
| `getTime` | 忽略 | 裸字符串（毫秒时间戳） | — | 获取系统时间 |
| `getphonestate` | 忽略 | 裸字符串 int（0 挂断/1 接起/2…） | — | 获取电话状态 |
| `getbattery` | 忽略 | 无 | `getBattery` | 主动获取电量 |
| `getwifiLevel` | 忽略 | 无 | `getwifiLevel` | 主动获取 WiFi 信号 |
| `getnetwork` | 忽略 | 裸字符串（`1`无网/`2`WiFi/`3`移动） | — | 主动获取网络状态 |
| `getcompareCode` | 忽略 | 裸字符串 int（1=本地版本>网络版本，0=否） | — | App 版本比较结果 |
| `getphoneInfo` | 忽略 | 无 | `getphoneinfo` | 获取手机配置信息（型号/IMEI 等） |
| `getmarketname` | 忽略 | 裸字符串（market 值） | — | 获取市场 ID |
| `getothername` | 裸字符串（配置名） | 裸字符串（该配置值） | — | 获取任意本地配置值 |
| `getOther` | 忽略 | 裸字符串（other 值） | — | 获取 other 配置值 |

### 8.4 交互 / 反馈

| Handler | data | 同步返回 | 异步回传 | 功能 |
|---|---|---|---|---|
| `orientation` | 裸字符串（`"1"`横屏，其他竖屏） | 无 | — | 切换屏幕方向 |
| `vibrator` | 裸字符串 long（毫秒） | 无 | — | 震动 |
| `repeatvibrator` | 裸字符串 int（`-1`否/`1`重复） | 无 | — | 重复震动 |
| `canclevibrator` | 忽略 | 无 | — | 取消震动 |
| `gameCopytext` | 裸字符串（文本） | 无 | — | 写入剪贴板 |
| `gamepastetext` | 忽略 | 裸字符串（剪贴板内容） | — | 读取剪贴板 |
| `notification` | 裸字符串 | 无 | — | 发送通知（**空实现，未落地**） |

### 8.5 摇一摇

| Handler | data | 异步回传 | 功能 |
|---|---|---|---|
| `startshake` | 忽略 | `shakeEnd` | 开始监听摇一摇 |
| `SwitchShake` | 裸字符串（`"1"`开声音震动/其他关） | — | 摇一摇声音开关 |
| `stopshake` | 忽略 | — | 停止监听 |

### 8.6 定位

| Handler | data | 同步返回 | 异步回传 | 功能 |
|---|---|---|---|---|
| `startlocation` | 裸字符串（`"1"`连续/其他单次） | 无 | `getlocationinfo` | 开启定位 |
| `getlocationinfo` | 忽略 | JSON（`MaplocationInfo`；未就绪返回错误码 JSON） | — | 主动取最近一次定位 |

### 8.7 音频

| Handler | data | 异步回传 | 功能 |
|---|---|---|---|
| `prepareaudio` | 忽略 | — | 准备/开始录音（长按） |
| `mediaTypeAudio` | JSON（`audiourl/type/user`） | `gameui_play_voice` / `gameui_stop_voice` | 播放语音消息 |
| `srcIsloop` | JSON（`src/isloop`） | 无 | 播放游戏音效/背景乐 |
| `voicePlaying` | 裸字符串（`"1"`开/其他静音） | — | 语音播放总开关 |
| `getaudiourl` 录音结果 | （由 `prepareaudio` 触发） | `getaudiourl` | 录音上传后回传地址 |

### 8.8 扫码 / 相机 / 浏览器 / 网页

| Handler | data | 同步返回 | 异步回传 | 功能 |
|---|---|---|---|---|
| `opensaoma` | 忽略 | 无 | `getsaomaData` | 打开扫一扫 |
| `opencamera` | 忽略 | 无 | `getcameraaAddress` | 打开相机拍照 |
| `browser` | 裸字符串（URL） | 无 | — | 系统/QQ 浏览器打开链接 |
| `OpenurlTitleData` | JSON（`url/title/data`） | 无 | `getWebdata` | 打开内置网页容器（子游戏跳转，见 §11） |
| `openApplyDownloadpath` | JSON（`packagename/downloadpath`） | 无 | — | 按包名启动 App，失败则浏览器打开下载地址 |

### 8.9 子游戏切换 / 房间（音视频）

| Handler | data | 同步返回 | 异步回传 | 功能 |
|---|---|---|---|---|
| `SwitchOverGameData` | JSON（`webtype/Gamedirectory/gamedownloadurl/data`） | 无 | — | 切换子游戏（容器内切换） |
| `getGameinstall` | 裸字符串（目录名） | 裸字符串（`1`已装/`0`未装） | — | 判断子游戏资源是否已就绪 |
| `createRoom` | 裸字符串（`videoinfobean` JSON） | 无 | — | 加入/创建音视频房间（声网） |
| `exitRoom` | 裸字符串 | 无 | — | 退出房间 |
| `getVideoinfo` | 裸字符串（`Othervideoinfo` JSON） | 无 | `getVideoinfo` | 请求/绑定远端视频窗口 |
| `DragViewvideoIsshow` | 裸字符串（`"1"`显示/其他隐藏） | 无 | — | 视频悬浮框显隐 |

### 8.10 退出 / 返回

| Handler | data | 异步回传 | 功能 | 备注 |
|---|---|---|---|---|
| `finsh` | 忽略 | — | 退出游戏 | 名字即代码原样（拼写如此） |
| `backgameData` | 裸字符串（data） | — | 子游戏带数据返回大厅 | **仅 `NewwebviewActivity` 有**；以结果码 101 回传给上层容器 |
| `getAddressBook` | 忽略 | （`getAddressbook` 已注释） | 获取通讯录 | **未实现** |

### 8.11 两容器差异小结

| 接口 | `webviewActivity`（大厅） | `NewwebviewActivity`（子游戏/微信回调） |
|---|---|---|
| `getGameplay`（App 内微信支付） | ✅ 生效 | ❌ 已注释（改用 `paybrowser`） |
| `backgameData`（带数据返回） | ❌ 无 | ✅ 有 |
| `getlocationinfo` 错误码 | 未就绪返回裸 `null` | 返回结构化错误码 JSON（`-1` 未就绪/`-2` 权限拒绝） |
| 分享内部分流 | 直连微信 SDK 多分支 | 按 `sharefriend` 分流到统一分享面板 |
| 其余 ~40 个 handler | 完全一致 | 完全一致 |

> 适配结论：实现**统一一套**入站 handler，并对差异项采用更完整的一侧（如 `getlocationinfo` 用结构化错误码、同时支持 `paybrowser` 与 `backgameData`）即可兼容两个容器。

---

## 9. 接口契约二：原生 → H5（出站 Handler 全集）

> 原生通过 `callHandler('<名称>', '<data>', null)` 调用；**H5 必须用 `registerHandler('<名称>', ...)` 注册才能收到**。
> 来源：两容器内 `callHandler` 调用点。

| Handler | data | 调用时机 |
|---|---|---|
| `appservice` | 裸字符串（`"1"`前台/`"2"`后台） | 页面首次加载到 100%、以及前后台切换（resume/pause/stop、息屏/解锁） |
| `setPostUrl` | 裸字符串（`http://<本机IP>:<端口>/testurl`） | 页面加载完成时，下发截图上传地址 |
| `getWebdata` | 裸字符串（网页回传 text / Intent 透传 data） | 页面首次加载完成；或内置网页关闭回传（结果码 101） |
| `getphoto` | JSON 数组（`[{pid, photourl}]`） | 图片/头像下载完成 |
| `sharelogin` | JSON（`openid/headimgurl/nickname/sex/city/province/unionid`） | 微信授权登录成功后 |
| `sharesuccess` | JSON（`{success:int, type:int}`，success 2 成功/3 取消） | 分享结果回调（仅微信完整） |
| `shakeEnd` | 空字符串 `""` | 摇一摇触发后约 1 秒 |
| `getBattery` | 裸字符串 float（0~1） | H5 主动取电量时；或电量变化广播 |
| `getwifiLevel` | JSON（`{ssidname:String, signalLevel:int(0~4)}`） | H5 主动取时；或 WiFi 变化广播 |
| `getnetwork` | 裸字符串 int（`1`断网/`2`WiFi/`3`移动） | 网络状态变化广播 |
| `getaudiourl` | JSON（`{audiourl:String, time:float}`；旧容器 `webviewActivity` 录音成功时**额外携带 `filepath:String`**，值与 `audiourl` 相同；取消时 audiourl="" time=0） | 录音上传完成 / 录音取消 |
| `gameui_play_voice` | 裸字符串（`user` 透传） | 语音开始播放 |
| `gameui_stop_voice` | 裸字符串（`user` 透传） | 语音播放完成 / 被打断 |
| `getphoneinfo` | JSON（`phoneInfoBean`，见 §12） | H5 调 `getphoneInfo` 后 |
| `getVideoinfo` | 裸字符串 int（远端 uid） | 音视频房间其他用户加入时 |
| `PayuserPaytypePaystate` | JSON（`{user, state, typeplay, type, data}`，state 1 成功/0 取消） | App 内微信支付结果（仅旧版容器） |
| `getlocationinfo` | JSON（`MaplocationInfo`；或错误码 JSON） | 定位成功（单次 1 次/连续每 5s）；或权限被拒 |
| `phonestate` | 裸字符串 int（来电状态） | 来电状态广播 |
| `getsaomaData` | 裸字符串（扫码结果） | 扫码返回 |
| `getcameraaAddress` | 裸字符串（照片路径） | 拍照返回 |

> 名称陷阱：`NewwebviewActivity` 中有一处出站调用名为 `backgameData-`（**末尾带连字符**，data 为空字符串），用于返回键确认对话框流程；与入站的 `backgameData`（无连字符）是两个不同名字。适配时按代码原样区分。

---

## 10. 原生能力专题

> 本节给出各设备能力的"H5 触发 → 入参 → 回传时机/结构"，原生内部如何对接第三方 SDK 不限。

### 10.1 分享（微信 / QQ / 抖音）
- **触发**：`friendsSharetypeUrlToptitleDescript`，入参 `sharetypeBean`（§12）。
  - `sharefriend=="2"` → 微信朋友圈直发；`=="1"` → 弹分享面板由用户选目标（微信好友/QQ/抖音）。
  - `type`：`"1"`网页/文本，`"2"`Canvas 截图（原生实时截当前 WebView 的 canvas 转 base64），`"3"`图片链接，抖音另有 `"4"`视频。
- **回传**：仅微信完整 → `sharesuccess`（`{success,type}`，2 成功/3 取消）。
- **现状/待补**：QQ、抖音当前**无结果回传**（QQ 回调被注释，抖音仅 Toast / 走外部 App scheme）。若 H5 依赖分享回调，新平台需为三端补齐统一 `sharesuccess` 回传。

### 10.2 微信登录
- **触发**：`accreditlogin`（非 `"1"` 即微信），scope=`snsapi_userinfo`。
- **回传**：`sharelogin`，回传**用户资料**（openid/unionid/nickname/headimgurl/sex/city/province），非 code。
- **安全提示**：现有实现用客户端硬编码 AppSecret 换 token（见 §13），新平台**建议改为回传 code、由服务端换取**。

### 10.3 支付
- **当前生效**：`paybrowser`（data 为收银台 URL，原生开浏览器，无回传，H5 收银台自理）。
- **旧版 App 内支付**：`getGameplay` → 微信 SDK → `PayuserPaytypePaystate` 回传结果（仅旧版容器）。`PayReq` 字段：`appId/partnerId/prepayId/packageValue("Sign=WXPay")/nonceStr/timeStamp/sign(MD5大写)`。

### 10.4 抖音 / 快手登录
- **未实现**：现有 `sgapi` 实为"闲聊（Xianliao）SDK"且整文件注释，无桥接入。新平台如需，须全新对接抖音开放平台/快手 SDK，建议回传 code 给 H5。

### 10.5 截图 / Canvas
- 截图当前**耦合在分享流程内部**，无独立 handler。原生注入 JS 调 H5 `canvas.toDataURL`（默认 image/jpeg 720×405；指定 id 则 png），失败回退原生绘制；结果直接交分享，不回传 H5。
- **待补建议**：若 H5 需主动截图，新增 handler（如 `getCanvasBase64`，入参 canvasId/格式/尺寸，返回纯 base64 或 dataURL）。

### 10.6 音频
- 远程语音消息：`mediaTypeAudio` 播放 → `gameui_play_voice` / `gameui_stop_voice` 回传（透传 `user`）。
- 游戏音效/背景乐：`srcIsloop`，`src` 为 wav 文件名（非 URL），路径 `<解压根>/<game>/assets/wav/<src>`；`isloop`：`0` 播一次 / `>0` 循环 / `<0` 停止。无回传。
- 静音总开关：`voicePlaying`。
- 录音：`prepareaudio` 录音 → 上传 → `getaudiourl` 回传 `{audiourl,time}`。

### 10.7 摇一摇
- `startshake` 开始 → 触发后约 1 秒 `callHandler('shakeEnd','')` → 自动重新监听。`SwitchShake` 控声音，`stopshake` 停止。
- 灵敏度等参数原生内部决定（现有实现加速度阈值约 3500）。

### 10.8 定位（高德）
- `startlocation`（`"1"`连续/5s 间隔，其他单次）→ `getlocationinfo` 推 `MaplocationInfo`。
- `getlocationinfo` 也可同步主动取（一名两用）。
- 错误码约定：`0` 成功 / 高德原始码 / `-1` 未就绪 / `-2` 权限被拒。
- 新平台用本平台定位服务实现，**只要回传 `MaplocationInfo` 同结构同字段即可**。

---

## 11. 子游戏跳转与通用网页容器

> H5 从大厅进入"另一个页面"有**两条独立路径**，机制完全不同，新平台都要实现：
> - **路径 A（容器内切换子游戏）**：`SwitchOverGameData` → 在**同一个 BridgeWebView 容器**内重载到另一个游戏目录，仍走 §2 Bridge 协议。
> - **路径 B（打开通用网页）**：`OpenurlTitleData` → 打开**独立的通用网页容器 `openwebActivity1`**，该容器用 §11.3 的**另一套接口机制**（`settings` 对象注入 + `javascript:` 直调）。

### 11.1 路径 A：容器内切换子游戏（SwitchOverGameData）

> 来源：`webviewActivity`/`NewwebviewActivity` 的 `SwitchOverGameData` → `initgame()`。

- H5 调用：`bridge.callHandler('SwitchOverGameData', JSON.stringify({webtype, Gamedirectory, gamedownloadurl, data}))`
  - `webtype`：`"2"`竖屏 / `"3"`横屏
  - `Gamedirectory`：目标游戏目录名
  - `gamedownloadurl`：游戏 id（用于判断/下载）
  - `data`：交换数据
- 原生在**当前 BridgeWebView 容器内**重载到 `file://<解压根>/<Gamedirectory>/index.html?Launchtype=1`（必要时先下载解压该游戏 zip），**桥协议与全部 handler 不变**，对 H5 透明。

**关键结论**：本路径下子游戏 H5 通常已包含在 §5 下载解压的整包内；原生按 `Gamedirectory` 切换目录加载，仍复用同一套 Bridge 接口。

### 11.2 路径 B：打开通用网页（OpenurlTitleData）

> 来源：`webviewActivity`/`NewwebviewActivity` 的 `OpenurlTitleData` → 打开 `openwebActivity1`。

```
大厅/游戏 H5 拼好目标地址（本地 file:// 或远程 http://）
  ▼
bridge.callHandler('OpenurlTitleData', JSON.stringify({url, title, data}))
  ▼
原生打开通用网页容器 openwebActivity1，加载该 url（Intent extra：url/title/data/orientation）
  │   orientation："1"=竖屏，其他=横屏
  ▼
页面加载到 100% → 原生用【javascript: 直调】getWebdata('<data>') 把交互数据交给页面
  ▼
页面内 H5 调 settings.backgameData('<data>')（或返回键确认）→ 原生 setResult(101, data) 退出
  ▼
回到上层 Bridge 容器 → 上层收到出站 getWebdata（携带回传 data）
```

> 注意：`openwebActivity1` **不是 BridgeWebView**，页面里**无 `window.WebViewJavascriptBridge`**，H5 在该容器内只能用 §11.3 的 `settings` 接口和被原生 `javascript:` 直调的全局函数。

### 11.3 通用网页容器 openwebActivity1 的独立接口契约

> 来源：`openwebActivity1.java`。机制 = **普通 WebView + 注入对象 `settings`（`@JavascriptInterface`）+ 原生 `javascript:` 直接调用全局函数**。新平台需为该容器单独实现这套契约（等价于 HarmonyOS 的 `javaScriptProxy` + `runJavaScript`）。

#### (1) H5 → 原生：全局注入对象 `settings`（对象名固定为 `settings`）

H5 直接调用 `window.settings.<方法>(...)`：

| 方法签名 | 参数 | 功能 |
|---|---|---|
| `settings.backgameData(data)` | `data`:String | 带数据返回上层容器（原生 `setResult(101,{data})` 并关闭本页） |
| `settings.loadurl(urls)` | `urls`:String | 在本容器加载新 url |
| `settings.browser(browserurl)` | `browserurl`:String | 用系统/QQ 浏览器打开链接 |
| `settings.finishweb()` | 无 | 直接关闭本页 |
| `settings.isexitdialogeshow()` | 无 | 开启"返回时弹退出确认框"行为 |
| `settings.isbackfinishweb()` | 无 | 设置返回键改为交给 H5（触发下方 `gamebackkeydown()`）而非直接关闭 |

#### (2) 原生 → H5：`javascript:` 直接调用的全局函数（H5 需在 window 上定义同名函数）

| 全局函数（H5 须实现） | 参数 | 调用时机 |
|---|---|---|
| `getWebdata('<data>')` | 字符串 data（来自打开时传入的 `data`） | 页面首次加载到 100% 时 |
| `gamebackkeydown()` | 无 | 按下返回键、且 H5 已通过 `settings.isbackfinishweb()` 接管返回键、且 WebView 可后退时 |
| `backgameData()` | 无（**注意：与 H5→原生的 `settings.backgameData(data)` 同名但无参、方向相反**） | "返回退出确认框"点确认时，原生回调通知页面即将退出 |

#### (3) 该容器的 WebView 设置差异（相对 §7 主容器）

- **不注入** `WebViewJavascriptBridge.js`；无 `yy://` 拦截。
- 缓存策略不同：有网时用默认缓存（`LOAD_DEFAULT`），无网时用缓存优先（`LOAD_CACHE_ELSE_NETWORK`）；启用 AppCache。
- 同样启用：JavaScript、DOMStorage、文件访问、file:// 跨源访问、定位；禁缩放/禁多窗口。
- WebViewClient 自行拦截 `weixin:` / `alipayqr:` / `alipays:` / `tel:` 等 scheme 跳转到系统处理。

> 适配要点：新平台需提供**两类容器**——
> ① BridgeWebView 容器（大厅+子游戏，§2 协议 + §8/§9 全部 handler）；
> ② 通用网页容器（§11.3 的 `settings` 注入对象 6 方法 + `getWebdata`/`gamebackkeydown`/`backgameData` 三个 `javascript:` 直调）。
> 两类容器都实现到位，现有 H5（含其打开的活动页/收银台/客服等）才能**零改动**运行。

---

## 12. 数据结构汇总

> 字段名严格对应代码，新平台序列化必须保持**同名同语义**。

### sharetypeBean（分享入参，全 String）
```jsonc
{
  "sharefriend": "1",      // "1" 好友(弹面板) / "2" 朋友圈(直发微信)
  "type": "1",             // "1" 网页/文本 / "2" Canvas截图 / "3" 图片链接 / "4" 视频(抖音)
  "sharetype": "",         // 分享子类型
  "webpageUrl": "https://...", // 分享链接 或 图片地址
  "title": "标题",
  "description": "描述"
}
```

### MaplocationInfo（定位回传）
```jsonc
{
  "latitude": 0.0, "longitude": 0.0,    // double
  "address": "", "country": "", "province": "", "city": "",
  "district": "", "street": "", "cityCode": "", "streetNum": "",
  "adCode": "", "aoiName": "",          // 以上 String
  "accuracy": 0.0,                       // float
  "locationType": 0,                     // int
  "errorCode": 0,                        // int：0 成功 / -1 未就绪 / -2 权限拒绝 / 其他=底层错误码
  "errorMsg": ""                         // String
}
```

### phoneInfoBean（手机信息回传，全 String）
```jsonc
{
  "PhoneVersion": "", "PhoneAdresseMAC": "", "PhoneModel": "",
  "PhoneDeviceBrand": "", "PhoneProvidersName": "",
  "PhoneIMEI": "", "PhoneIMSI": ""
}
```

### savephotoURLBean（图片下载，getphoto 回传数组元素）
```jsonc
{ "pid": "", "photourl": "" }
```

### videoinfobean（createRoom 入参，全 String）
```jsonc
{ "playerid":"", "roomid":"", "agentid":"", "gameid":"",
  "left":"", "top":"", "pmw":"", "pmh":"", "width":"", "height":"" }
```

### Othervideoinfo（getVideoinfo 入参）
```jsonc
{ "playerid":"", "left":"", "top":"", "width":"", "height":"", "pmw":"", "pmh":"" }
```

### 支付相关
- `getGameplay` 入参：`{ "price":"", "body":"", "typeplay":int, "type":"", "user":int }`
- `PayuserPaytypePaystate` 回传：`{ "user":"", "state":int(1成功/0取消), "typeplay":"", "type":"", "data":"" }`

### sharelogin（微信登录回传，全 String）
```jsonc
{ "openid":"", "headimgurl":"", "nickname":"", "sex":"", "city":"", "province":"", "unionid":"" }
```

### sharesuccess（分享回传）
```jsonc
{ "success": 2, "type": 1 }   // success: 2成功/3取消；type: 1好友/2朋友圈
```

---

## 13. 关键常量与第三方账号

> 来源：`simcpux/Constants.java`、`AndroidManifest.xml`。新平台需用自己的应用账号重新申请，下列仅说明依赖项。

| 项 | 值 | 说明 |
|---|---|---|
| 微信 AppID | `wxd2bd650e06bdfe58` | 分享/登录/支付 |
| 微信商户号 MCH_ID | `1448669802` | App 内支付 |
| 微信 API_KEY | `ClMrQsAcidRa7uJT4TgHgVAOHbzQjdPa` | 支付签名（**应下沉服务端**） |
| 微信 AppSecret | `1934a281c82ad1a059130fe51341b74b` | 登录换 token（**应下沉服务端**） |
| 高德定位 API Key | `4f92bacc16cace69a6045a544d6e3c7d` | 定位 |
| 微信回调 scheme | `wxd2bd650e06bdfe58` | 第三方回调 |
| 自定义 scheme | `gamepaywelcome`、`gamepaywxd2bd650e06bdfe58` | H5/外部唤起原生页 |

**关键权限**：存储读写、读电话状态、定位（精确+粗略）、安装应用、网络、相机、录音、允许明文 HTTP。

> ⚠️ 安全提示：现有工程把微信 AppSecret / API_KEY 明文硬编码在客户端，存在风险。HarmonyOS 新版应将签名与换 token 逻辑下沉到服务端，登录改为回传 code。

---

## 14. HarmonyOS 适配检查清单与待决策项

### 14.1 必做（保证 H5 零改动运行）

- [ ] **实现 WebViewJavascriptBridge 协议**：URL scheme `yy://` 拦截、`_handleMessageFromNative` / `_fetchQueue`、Message JSON 结构、`onPageFinished` 注入 `WebViewJavascriptBridge.js`、启动消息队列补发（§2）。直接复用该库的 `WebViewJavascriptBridge.js`。
- [ ] **实现全部入站 handler（§8）**：44 个，名称/参数结构/同步返回 1:1 对齐；差异项取更完整一侧。
- [ ] **实现全部出站 handler 调用（§9）**：在对应时机以同名 `callHandler` 推送同结构 data。
- [ ] **配置体系（§4）**：提供 §4.2 全部配置键取值（建议改用 KV 配置文件，无需沿用目录名编码）。
- [ ] **远程配置请求与分层覆盖（§4.3/4.4）**：URL 拼装、单 `agentlist` 树 agent→game→channel→market 后层覆盖（`game_zip` / 数字 id 归一 / gameid 回退 version.xml）、`showmessage` 阻断。
- [ ] **资源管理（§5）**：内置包拷贝、version.xml 版本比较、远程 zip 下载/删旧/解压到资源目录。
- [ ] **app_data.js 注入（§6）**：加载大厅前在大厅目录生成同名全局变量。
- [ ] **大厅入口（§3）**：`weburl` 空 → `file://.../gamehall/index.html?Launchtype=0`；非空 → `http://<weburl去-换/>?Launchtype=0`。
- [ ] **WebView 能力（§7）**：JS、DOMStorage、file/universal-file 访问、混合内容、明文 HTTP、禁缓存、禁缩放、定位。
- [ ] **路径 A 子游戏切换（§11.1）**：`SwitchOverGameData` 在 BridgeWebView 容器内按目录重载、横竖屏切换。
- [ ] **路径 B + 通用网页容器（§11.2/§11.3）**：`OpenurlTitleData` 打开独立网页容器；该容器实现 **`settings` 注入对象 6 方法**（`backgameData(data)`/`loadurl(urls)`/`browser(url)`/`finishweb()`/`isexitdialogeshow()`/`isbackfinishweb()`）+ **3 个 `javascript:` 直调全局函数**（`getWebdata(data)`/`gamebackkeydown()`/`backgameData()`）+ 结果码 101 回传上层 → 上层出站 `getWebdata`。**这是与主容器不同的第二套机制，勿遗漏。**
- [ ] **本机 HTTP 上传端点 + `setPostUrl`（§7/§9）**：支撑截图分享上传。
- [ ] **设备能力（§10）**：分享、微信登录/支付、定位、录音/音效、摇一摇、扫码、相机、震动、剪贴板、网络/电量/WiFi/电话状态。

### 14.2 待决策 / 需补齐（现有 Android 即缺失，新平台应主动完善）

| 事项 | 现状 | 建议 |
|---|---|---|
| QQ / 抖音分享结果回传 | 无回传 | 补齐统一 `sharesuccess` 回传 |
| 抖音 / 快手登录 | 完全未实现（sgapi 是闲聊且注释） | 按需全新对接，回传 code |
| 主动截图能力 | 仅耦合在分享内 | 新增独立 `getCanvasBase64` handler |
| 通讯录 `getAddressBook` | 空实现 | 按需实现或保持空 |
| `notification` 通知 | 空实现 | 按需实现 |
| 微信 AppSecret/API_KEY 明文 | 客户端硬编码 | 下沉服务端，登录改 code 模式 |
| `servertype` / `gameserver` 配置 | 有目录无代码读取 | 可不实现 |
| App 内微信支付 vs 浏览器支付 | 新旧容器不一致 | 统一用 `paybrowser`，按需保留 App 内支付 |

### 14.3 接口名"原样保留"陷阱清单（拼写以代码为准，不可纠正）

- `finsh`（退出，非 finish）
- `accreditlogin`、`friendsSharetypeUrlToptitleDescript`、`SwitchOverGameData`、`DragViewvideoIsshow`
- `getcameraaAddress`（双 a）、`getsaomaData`、`opensaoma`
- `PayuserPaytypePaystate`、`gameui_play_voice` / `gameui_stop_voice`
- `canclevibrator`（非 cancel）、`repeatvibrator`
- 出站 `backgameData-`（末尾连字符）≠ 入站 `backgameData`
- 下载目录名 `dowlod_zip`（拼写如此）

---

> **结论**：H5 与原生之间的契约 = §2 桥协议 + §8 入站 44 handler + §9 出站约 21 handler + §6 `app_data.js` 注入 + §3~§5 启动与资源流程。新平台（HarmonyOS）只要让这套契约对外表现完全一致，现有 H5 即可**零改动**运行；原生内部实现方式不受任何限制。
