/** * SGBridge —— 傻瓜比价 H5 ↔ Android 原生 的桥。 * * 背景:四个主 tab(首页/福利/记录/我的)由原生 Compose 改造为 WebView 加载本工程 H5。 * H5 只负责"画 + 取后端数据";凡需要原生能力(登录态 / 跳转 / 跳外卖 App / 比价领券 / * 权限 / 定位 / Toast / 激励视频),一律经本桥调用原生。 * * 协议两个方向: * ① H5 → 原生:Android 端 WebView.addJavascriptInterface(obj, "SGBridgeNative")。 * obj 方法都是【同步】:查询类返回 String(JSON 或纯串),动作类无返回。 * ② 原生 → H5:原生执行 evaluateJavascript("window.SGBridge._emit('', '')")。 * 事件:onAuthChange(登录态变) / onBalanceChange(余额变) / onSigninChange(签到态变) / onResume(回前台刷新)。 * * 离线兜底:浏览器里(无 SGBridgeNative)走 MOCK,便于不装 App 直接在本地 server 调样式 / 渲染。 * 与原生实现对应:见 shaguabijia-app-android 的 SGBridge.kt(方法名逐个对齐本文件)。 */ (function (global) { 'use strict'; var native = global.SGBridgeNative || null; var hasNative = !!native; // ---- 离线 MOCK(仅无原生时生效,便于浏览器调试渲染;真机一律走 native) ---- var MOCK = { authState: { loggedIn: true, userId: 1, nickname: '冰', avatarUrl: '', phone: '188****8888' }, token: 'mock-token-for-browser-debug', deviceId: 'browser-debug-device', appVersion: '0.0.0-debug', }; function safeParse(s, fallback) { try { return s ? JSON.parse(s) : fallback; } catch (e) { return fallback; } } // ====== 查询类(同步返回) ====== /** 当前登录态 + 用户基本信息 → {loggedIn, userId, nickname, avatarUrl, phone}。 */ function getAuthState() { if (hasNative && native.getAuthState) return safeParse(native.getAuthState(), { loggedIn: false }); return MOCK.authState; } /** 后端鉴权用的 access token(空串=未登录)。原生持登录态,H5 调后端前取它拼 Bearer。 */ function getToken() { if (hasNative && native.getToken) return native.getToken() || ''; return MOCK.token; } /** 设备唯一标识(心跳 / 领券状态查询等用)。 */ function getDeviceId() { if (hasNative && native.getDeviceId) return native.getDeviceId() || ''; return MOCK.deviceId; } /** App 版本号。 */ function getAppVersion() { if (hasNative && native.getAppVersion) return native.getAppVersion() || ''; return MOCK.appVersion; } /** 已安装的目标电商/外卖 App 包名数组(原生 InstalledApps 探测)。H5 选平台弹窗据此判真实装机态。 */ function getInstalledApps() { if (hasNative && native.getInstalledApps) return safeParse(native.getInstalledApps(), []); // MOCK(浏览器无原生):给主流已装,便于本地预览选平台弹窗正常显示"有"。 return ['com.sankuai.meituan', 'com.taobao.taobao', 'com.jingdong.app.mall', 'me.ele']; } /** 今日是否已领券(置灰「去领取」→「去查看」)。原生读 CompareButtonState(SP 按天);无桥默认 false。 */ function getCouponClaimedToday() { if (hasNative && native.getCouponClaimedToday) return !!native.getCouponClaimedToday(); return false; } // ====== 动作类(无返回;异步结果走事件) ====== /** 跳原生页。route 取值对齐安卓 Routes(invite / settings / feedback / withdrawal / compareRecords / reportFlow / guideVideo / compareResult / coinHistory / cashHistory / welfareRules ...)。 */ function navigate(route) { if (hasNative && native.navigate) native.navigate(route); else console.log('[SGBridge mock] navigate →', route); } /** 拉起极光一键登录。结果异步经 onAuthChange 事件回来(不在此函数返回)。 */ function requestLogin() { if (hasNative && native.requestLogin) native.requestLogin(); else console.log('[SGBridge mock] requestLogin'); } /** 原生居中 Toast。 */ function toast(msg) { if (hasNative && native.toast) native.toast(String(msg)); else console.log('[SGBridge mock] toast →', msg); } /** 跳美团/外卖 App(deeplink 优先;空则原生按包名启动,未装可跳应用商店)。 */ function openMeituan(deeplink) { if (hasNative && native.openMeituan) native.openMeituan(deeplink || ''); else console.log('[SGBridge mock] openMeituan →', deeplink); } /** 触发 agent 比价流程(原生起无障碍引擎)。 */ function startCompare() { if (hasNative && native.startCompare) native.startCompare(); else console.log('[SGBridge mock] startCompare'); } /** 触发一键领券(原生先校验悬浮窗/无障碍权限,再起前台服务)。platforms: string[]。 */ function startCouponClaim(platforms) { var json = JSON.stringify(platforms || []); if (hasNative && native.startCouponClaim) native.startCouponClaim(json); else console.log('[SGBridge mock] startCouponClaim →', json); } /** 按候选包名拉起对应平台 App。照原生 HomePicker 点击跳转:原生在该平台一组候选包里取首个可 * getLaunchIntentForPackage 的拉起(NEW_TASK|CLEAR_TASK 冷启到平台首页)。packages: string[](一个平台一组候选包,任一可拉即拉)。 */ function launchApp(packages) { var json = JSON.stringify(packages || []); if (hasNative && native.launchApp) native.launchApp(json); else console.log('[SGBridge mock] launchApp →', json); } // ====== 原生 → H5 事件总线 ====== var listeners = {}; // event → [fn] /** 订阅原生事件。返回取消订阅函数。 */ function on(event, fn) { (listeners[event] || (listeners[event] = [])).push(fn); return function off() { listeners[event] = (listeners[event] || []).filter(function (f) { return f !== fn; }); }; } /** 供原生回调:window.SGBridge._emit('onAuthChange', '{...}')。payload 可为 JSON 串或对象。 */ function _emit(event, payload) { var data = typeof payload === 'string' ? safeParse(payload, payload) : payload; (listeners[event] || []).forEach(function (fn) { try { fn(data); } catch (e) { console.error('[SGBridge] listener error', event, e); } }); } global.SGBridge = { hasNative: hasNative, // 查询 getAuthState: getAuthState, getToken: getToken, getDeviceId: getDeviceId, getAppVersion: getAppVersion, getInstalledApps: getInstalledApps, getCouponClaimedToday: getCouponClaimedToday, // 动作 navigate: navigate, requestLogin: requestLogin, toast: toast, openMeituan: openMeituan, startCompare: startCompare, startCouponClaim: startCouponClaim, launchApp: launchApp, // 事件 on: on, _emit: _emit, }; })(window);