import { native } from "cc"; import { GoogleImpl, IGoogleLoginRes } from "./GoogleImpl"; import AppConfig from "@scripts/app/AppConfig"; const REQUEST_EVENT = "google.request"; const RESPONSE_EVENT = "google.response"; const DEFAULT_TIMEOUT_MS = 15_000; const LOGIN_TIMEOUT_MS = 180_000; // const GOOGLE_CLIENTID = "984811369314-tgai3uj7lb3dmvpuisehm7fb98o60vsq.apps.googleusercontent.com"; interface NativeResponse> { requestId: string; method: string; success: boolean; cancelled?: boolean; data?: T; error?: { code?: string; message?: string; }; } interface GoogleLoginData { idToken?: string; email?: string; displayName?: string; givenName?: string; familyName?: string; profilePictureUrl?: string; } interface PendingRequest { method: string; resolve: (response: NativeResponse) => void; reject: (error: Error) => void; timer: ReturnType; } export class GoogleAndroidImpl extends GoogleImpl { private _requestSeed = 0; private _pendingRequests = new Map(); private _initPromise: Promise | null = null; private readonly _responseListener = (payload: string) => this.onNativeResponse(payload); init(): void { if (this._initPromise) return; native.jsbBridgeWrapper.addNativeEventListener(RESPONSE_EVENT, this._responseListener); this._initPromise = this.request("init", { webClientId: AppConfig.GoogleID }) .then(() => { this.onInited(); return true; }) .catch((error) => { console.error("[Google][Android] 初始化失败:", error); return false; }); } protected async onLogin(): Promise { if (!(await this.ensureInitialized())) { return this.loginFailure("Google Android 登录尚未初始化。"); } try { const response = await this.request( "login", {}, LOGIN_TIMEOUT_MS, ); if (response.cancelled) { return { accessToken: "", isCancel: true, status: "cancelled", }; } const idToken = response.data?.idToken || ""; if (!idToken) { return this.loginFailure("Google 登录未返回 ID Token。"); } return { // 与 H5 保持一致:accessToken 字段承载需要交给服务端验证的 Google ID Token。 accessToken: idToken, isCancel: false, status: "connected", }; } catch (error) { return this.loginFailure(this.errorMessage(error)); } } async logOut(): Promise { if (!(await this.ensureInitialized())) return false; try { await this.request("logout"); return true; } catch (error) { console.error("[Google][Android] 登出失败:", error); return false; } } private async ensureInitialized(): Promise { if (!this._initPromise) this.init(); return this._initPromise!; } private request>( method: string, params: Record = {}, timeoutMs = DEFAULT_TIMEOUT_MS, ): Promise> { const requestId = `${Date.now()}-${++this._requestSeed}`; return new Promise>((resolve, reject) => { const timer = setTimeout(() => { this._pendingRequests.delete(requestId); reject(new Error(`Google ${method} 请求超时。`)); }, timeoutMs); this._pendingRequests.set(requestId, { method, resolve, reject, timer, }); try { native.jsbBridgeWrapper.dispatchEventToNative( REQUEST_EVENT, JSON.stringify({ requestId, method, params }), ); } catch (error) { clearTimeout(timer); this._pendingRequests.delete(requestId); reject(error instanceof Error ? error : new Error(String(error))); } }); } private onNativeResponse(payload: string): void { let response: NativeResponse; try { response = JSON.parse(payload) as NativeResponse; } catch (error) { console.error("[Google][Android] 无法解析原生响应:", error); return; } const pending = this._pendingRequests.get(response.requestId); if (!pending) return; clearTimeout(pending.timer); this._pendingRequests.delete(response.requestId); if (response.success) { pending.resolve(response); return; } const code = response.error?.code ? `[${response.error.code}] ` : ""; pending.reject(new Error(`${code}${response.error?.message || `${pending.method} 执行失败。`}`)); } private loginFailure(reason: string): IGoogleLoginRes { return { accessToken: "", isCancel: false, status: "error", reason, }; } private errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } }