| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- 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<T = Record<string, never>> {
- 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<any>) => void;
- reject: (error: Error) => void;
- timer: ReturnType<typeof setTimeout>;
- }
- export class GoogleAndroidImpl extends GoogleImpl {
- private _requestSeed = 0;
- private _pendingRequests = new Map<string, PendingRequest>();
- private _initPromise: Promise<boolean> | 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<IGoogleLoginRes> {
- if (!(await this.ensureInitialized())) {
- return this.loginFailure("Google Android 登录尚未初始化。");
- }
- try {
- const response = await this.request<GoogleLoginData>(
- "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<boolean> {
- 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<boolean> {
- if (!this._initPromise) this.init();
- return this._initPromise!;
- }
- private request<T = Record<string, never>>(
- method: string,
- params: Record<string, unknown> = {},
- timeoutMs = DEFAULT_TIMEOUT_MS,
- ): Promise<NativeResponse<T>> {
- const requestId = `${Date.now()}-${++this._requestSeed}`;
- return new Promise<NativeResponse<T>>((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<any>;
- try {
- response = JSON.parse(payload) as NativeResponse<any>;
- } 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);
- }
- }
|