H5 对 SwitchOverGameData 的 data 字段直接传 JSON 对象(如 {webtype:3,...})。
Android org.json getString 会把嵌套对象强制转为 JSON 字符串再交 handler JSON.parse;
而 ArkTS JSON.parse 保留为对象,handler 再 JSON.parse(对象) 得到 "[object Object]"
→ Invalid Token、解析失败、不发 SWITCH_GAME、不跳转。
- MessageCodec.toArray:新增 coerceToString,对 data/responseData 复刻 org.json
getString 语义(对象/数组→紧凑 JSON 串,数字/布尔→字符串)。
- NavProvider:webtype 统一 String() 化,避免下游 `webtype !== '2'` 对数字恒真
把竖屏子游戏错误强制横屏。
对应契约 §11.1 路径A、§2.4 Message。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
80 lines
3.5 KiB
Plaintext
80 lines
3.5 KiB
Plaintext
/**
|
||
* 导航能力(契约 §8.8/§8.9/§8.10/§11,T-M3-06)。
|
||
* - OpenurlTitleData:打开通用网页容器(路径 B,§11.2),关闭回传 → 上层 getWebdata。
|
||
* - SwitchOverGameData:容器内切换子游戏(路径 A,§11.1),同容器 loadUrl。
|
||
* - getGameinstall:同步返回子游戏资源是否就绪("1"/"0")。
|
||
* - backgameData:子游戏带数据返回大厅。
|
||
*
|
||
* 容器持 pathStack/WebviewController,本 Provider 经 EventBus 发导航事件,由 BridgeGameContainer 执行。
|
||
*/
|
||
import { common } from '@kit.AbilityKit';
|
||
import { FileSystem } from 'platform';
|
||
import { ResourceManager, ConfigManager } from 'domain_resource';
|
||
import { BridgeController } from 'feature_bridge';
|
||
import { InboundHandlers, OpenUrlTitleDataReq, SwitchOverGameReq } from 'contracts';
|
||
import { EventBus, NavEvents, OpenGenericWebPayload, SwitchGamePayload, BackGamePayload, Logger } from 'common';
|
||
import { CapabilityContext, CapabilityProvider } from '../core/CapabilityProvider';
|
||
|
||
export class NavProvider implements CapabilityProvider {
|
||
readonly name: string = 'nav';
|
||
private readonly log: Logger = Logger.tag('NavProvider');
|
||
private context: common.UIAbilityContext | undefined = undefined;
|
||
private config: ConfigManager | undefined = undefined;
|
||
|
||
register(bridge: BridgeController, ctx: CapabilityContext): void {
|
||
this.context = ctx.uiAbilityContext;
|
||
this.config = ctx.config;
|
||
|
||
bridge.registerHandler(InboundHandlers.OpenUrlTitleData, (data: string, _cb: (resp: string) => void) => {
|
||
this.openUrl(data);
|
||
});
|
||
bridge.registerHandler(InboundHandlers.SwitchOverGameData, (data: string, _cb: (resp: string) => void) => {
|
||
this.switchGame(data);
|
||
});
|
||
bridge.registerHandler(InboundHandlers.GetGameInstall, (data: string, cb: (resp: string) => void) => {
|
||
cb(this.isGameInstalled(data) ? '1' : '0');
|
||
});
|
||
bridge.registerHandler(InboundHandlers.BackGameData, (data: string, _cb: (resp: string) => void) => {
|
||
const payload: BackGamePayload = { data };
|
||
EventBus.emit(NavEvents.BACK_GAME, payload);
|
||
});
|
||
}
|
||
|
||
private openUrl(data: string): void {
|
||
let req: OpenUrlTitleDataReq;
|
||
try {
|
||
req = JSON.parse(data) as OpenUrlTitleDataReq;
|
||
} catch (e) {
|
||
this.log.w(`OpenurlTitleData bad json: ${(e as Error).message}`);
|
||
return;
|
||
}
|
||
const payload: OpenGenericWebPayload = { url: req.url, title: req.title, data: req.data, orientation: '' };
|
||
EventBus.emit(NavEvents.OPEN_GENERIC_WEB, payload);
|
||
}
|
||
|
||
private switchGame(data: string): void {
|
||
let req: SwitchOverGameReq;
|
||
try {
|
||
req = JSON.parse(data) as SwitchOverGameReq;
|
||
} catch (e) {
|
||
this.log.w(`SwitchOverGameData bad json: ${(e as Error).message}`);
|
||
return;
|
||
}
|
||
// webtype 契约为 "2"竖/"3"横,但 H5 可能发数字(如 3)。统一转字符串,
|
||
// 否则下游 `webtype !== '2'` 的严格比较对数字恒为真,会把竖屏子游戏错误强制横屏。
|
||
const payload: SwitchGamePayload = { webtype: String(req.webtype), dir: req.Gamedirectory, data: req.data };
|
||
EventBus.emit(NavEvents.SWITCH_GAME, payload);
|
||
}
|
||
|
||
/** 子游戏资源是否就绪:<解压根>/<dir>/index.html 存在。 */
|
||
private isGameInstalled(dir: string): boolean {
|
||
const ctx = this.context;
|
||
const cfg = this.config;
|
||
if (ctx === undefined || cfg === undefined || dir === '') {
|
||
return false;
|
||
}
|
||
const urlpath: string = `${ResourceManager.resourceRoot(ctx)}/${cfg.getLocal().gamedir}`;
|
||
return FileSystem.exists(`${urlpath}/${dir}/index.html`);
|
||
}
|
||
}
|