import webview from '@ohos.web.webview';
import connection from '@ohos.net.connection';
/**
* `javaScriptOnDocumentStart` 脚本项。
*/
interface ScriptItemLike {
script: string;
scriptRules: string[];
}
/**
* 将设备网络状态实时同步到 H5 的 `navigator.onLine`。
*/
export class NetworkStateInjector {
private netCon: connection.NetConnection | null = null;
private controller: webview.WebviewController | null = null;
/** 在页面加载前注入的 JS 脚本,重写 `navigator.onLine` 并监听网络变化。 */
private static readonly DOCUMENT_START_SCRIPT: string =
'(function(){try{' +
'if(window.__qianOnline===undefined){window.__qianOnline=true;}' +
'try{Object.defineProperty(navigator,"onLine",{configurable:true,get:function(){return window.__qianOnline!==false;}});}catch(e){}' +
'window.__qianSetOnline=function(v){' +
'var prev=window.__qianOnline;' +
'window.__qianOnline=!!v;' +
'if(prev!==window.__qianOnline){' +
'try{window.dispatchEvent(new Event(window.__qianOnline?"online":"offline"));}catch(e){}' +
'}' +
'};' +
'}catch(e){}})();';
/** 获取 document_start 注入脚本。 */
public static getDocumentStartScript(): string {
return NetworkStateInjector.DOCUMENT_START_SCRIPT;
}
/** 绑定 WebView 控制器并启动网络监听。 */
public attach(controller: webview.WebviewController): void {
this.controller = controller;
this.installDocumentStartScript();
this.startListen();
}
/** 解绑并释放资源。 */
public detach(): void {
this.stopListen();
this.controller = null;
}
/** 注册 document_start 脚本注入。 */
private installDocumentStartScript(): void {
if (this.controller === null) {
return;
}
try {
const scriptItems: ScriptItemLike[] = [{
script: NetworkStateInjector.DOCUMENT_START_SCRIPT,
scriptRules: ['*']
}];
// 通过 ESObject cast 调用,因为老版 .d.ts 未导出 javaScriptOnDocumentStart
const ctrl: ESObject = this.controller as ESObject;
ctrl.javaScriptOnDocumentStart(scriptItems);
} catch (_e) {
// ignore
}
}
/** 同步网络状态到 H5 端。 */
private updateRuntime(online: boolean): void {
if (this.controller === null) {
return;
}
const value: string = online ? 'true' : 'false';
const script: string = 'try{window.__qianSetOnline&&window.__qianSetOnline(' + value + ');}catch(e){}';
try {
this.controller.runJavaScript(script);
} catch (_e) {
// ignore
}
}
/** 查询系统网络状态并同步。 */
private refreshFromSystem(): void {
try {
connection.getDefaultNet().then((handle: connection.NetHandle) => {
connection.getNetCapabilities(handle).then((cap: connection.NetCapabilities) => {
const hasBearer: boolean = cap.bearerTypes !== undefined && cap.bearerTypes.length > 0;
const validated: boolean = cap.networkCap !== undefined
&& cap.networkCap.indexOf(connection.NetCap.NET_CAPABILITY_VALIDATED) >= 0;
this.updateRuntime(hasBearer && validated);
}).catch((_e: Object): void => {
this.updateRuntime(true);
});
}).catch((_e: Object): void => {
this.updateRuntime(false);
});
} catch (_e) {
this.updateRuntime(true);
}
}
/** 启动网络状态订阅。 */
private startListen(): void {
try {
this.netCon = connection.createNetConnection();
this.netCon.on('netAvailable', (_handle: connection.NetHandle): void => {
this.refreshFromSystem();
});
this.netCon.on('netLost', (_handle: connection.NetHandle): void => {
this.updateRuntime(false);
});
this.netCon.on('netCapabilitiesChange',
(_info: connection.NetCapabilityInfo): void => {
this.refreshFromSystem();
});
this.netCon.register((_e: Object): void => {
// ignore register error
});
} catch (_e) {
// ignore
}
}
private stopListen(): void {
if (this.netCon !== null) {
try {
this.netCon.unregister((_e: Object): void => {
// ignore
});
} catch (_e) {
// ignore
}
this.netCon = null;
}
}
}
|