Skip to content

Implementation of google play auto login, achievements, PGS Recall API#18416

Merged
dumganhar merged 12 commits into
cocos:v3.8.6from
qiuguohua:v3.8.6_gp_1
Mar 9, 2025
Merged

Implementation of google play auto login, achievements, PGS Recall API#18416
dumganhar merged 12 commits into
cocos:v3.8.6from
qiuguohua:v3.8.6_gp_1

Conversation

@qiuguohua

@qiuguohua qiuguohua commented Mar 6, 2025

Copy link
Copy Markdown
Contributor

Re: #

examples

        const gameSignInClient = google.play.PlayGames.getGamesSignInClient();
        gameSignInClient.isAuthenticated().addOnSuccessListener({
            onSuccess: (result: google.play.AuthenticationResult) : void => {
                console.log('Authenticated: ', result.isAuthenticated());
            },
        })
        .addOnCompleteListener({
            onComplete:(result: google.play.Task<jsb.AuthenticationResult>): void => {
                this.showAchievements();
            }
        });
        gameSignInClient.isAuthenticated().addOnCanceledListener({
            onCanceled: () => {
                console.log('cancel');
            },
        });
        gameSignInClient.isAuthenticated().addOnFailureListener({
            onFailure: (result: jsb.PlayException) => {
                console.log('Authenticated: ', result.getMessage());
            },
        });
        gameSignInClient.isAuthenticated().addOnCompleteListener({
            onComplete: (result: google.play.Task<jsb.AuthenticationResult>) => {
                console.log('Authenticated: ', result.getResult().isAuthenticated());
                result.addOnSuccessListener({
                    onSuccess: (result: jsb.AuthenticationResult) => {
                        console.log('Authenticated: ', result.isAuthenticated());
                    },
                });
            },
        });
        gameSignInClient.isAuthenticated().isCanceled();
        gameSignInClient.isAuthenticated().isSuccessful();
        gameSignInClient.isAuthenticated().isComplete();
        gameSignInClient.isAuthenticated().continueWith({
            then: (result: google.play.Task<google.play.AuthenticationResult>): google.play.AuthenticationResult => {
                const a:google.play.AuthenticationResult = new google.play.AuthenticationResult;
                return a;
            }
        });

Continuous Integration

This pull request:

  • needs automatic test cases check.

    Manual trigger with @cocos-robot run test cases afterward.

  • does not change any runtime related code or build configuration

    If any reviewer thinks the CI checks are needed, please uncheck this option, then close and reopen the issue.


Compatibility Check

This pull request:

  • changes public API, and have ensured backward compatibility with deprecated features.
  • affects platform compatibility, e.g. system version, browser version, platform sdk version, platform toolchain, language version, hardware compatibility etc.
  • affects file structure of the build package or build configuration which requires user project upgrade.
  • introduces breaking changes, please list all changes, affected features and the scope of violation.

@github-actions

github-actions Bot commented Mar 6, 2025

Copy link
Copy Markdown

Code Size Check Report

Wechat (WASM) Before After Diff
2D Empty (legacy pipeline) 1001919 bytes 1001919 bytes ✅ 0 bytes
2D All (legacy pipeline) 2409295 bytes 2409295 bytes ✅ 0 bytes
2D All (new pipeline) 2496524 bytes 2496524 bytes ✅ 0 bytes
(2D + 3D) All 5413059 bytes 5413059 bytes ✅ 0 bytes
Web (WASM + ASMJS) Before After Diff
(2D + 3D) All 16920733 bytes 16920733 bytes ✅ 0 bytes

Interface Check Report

! WARNING this pull request has changed these public interfaces:

@@ -59069,8 +59069,22 @@
             export type BillingConfig = __private.__types_jsb__jsb.BillingConfig;
             export type AlternativeBillingOnlyReportingDetails = __private.__types_jsb__jsb.AlternativeBillingOnlyReportingDetails;
             export type ExternalOfferReportingDetails = __private.__types_jsb__jsb.ExternalOfferReportingDetails;
         }
+        export namespace play {
+            export const PlayGamesSdk: typeof __private.__types_jsb__jsb.PlayGamesSdk;
+            export type PlayGamesSdk = __private.__types_jsb__jsb.PlayGamesSdk;
+            export const PlayGames: typeof __private._vendor_google_play_games__PlayGamesHelper;
+            export const GamesSignInClient: typeof __private._vendor_google_play_games__GamesSignInClientHelper;
+            export const AuthenticationResult: typeof __private.__types_jsb__jsb.AuthenticationResult;
+            export type AuthenticationResult = __private.__types_jsb__jsb.AuthenticationResult;
+            export const RecallAccess: typeof __private.__types_jsb__jsb.RecallAccess;
+            export type RecallAccess = __private.__types_jsb__jsb.RecallAccess;
+            export const PlayException: typeof __private.__types_jsb__jsb.PlayException;
+            export type PlayException = __private.__types_jsb__jsb.PlayException;
+            export type Continuation<T, K = void> = __private._vendor_google_play_task__ContinuationHelper<T, K>;
+            export type Task<T, K = void> = __private._vendor_google_play_task__TaskHelper<T, K>;
+        }
     }
     /**
      * @en
      * The video clip asset.
@@ -73609,8 +73623,65 @@
         }
         export interface __types_jsb_jsb__InAppMessageResponseListener {
             onInAppMessageResponse(inAppMessageResult: __types_jsb_jsb__InAppMessageResult): void;
         }
+        export class __types_jsb_jsb__PlayException {
+            getMessage(): string;
+            getLocalizedMessage(): string;
+            printStackTrace(): void;
+            toString(): string;
+        }
+        export interface __types_jsb_jsb__OnCanceledListener {
+            onCanceled(): void;
+        }
+        export interface __types_jsb_jsb__OnCompleteListener {
+            onComplete(task: any): void;
+        }
+        export interface __types_jsb_jsb__OnFailureListener {
+            onFailure(e: __types_jsb_jsb__PlayException): void;
+        }
+        export interface __types_jsb_jsb__OnSuccessListener {
+            onSuccess(result: any): void;
+        }
+        export interface __types_jsb_jsb__OnContinueWithListener {
+            then(result: any): void;
+        }
+        export class __types_jsb_jsb__PlayTask {
+            public addOnCanceledListener(listener: __types_jsb_jsb__OnCanceledListener): __types_jsb_jsb__PlayTask;
+            public addOnCompleteListener(listener: __types_jsb_jsb__OnCompleteListener): __types_jsb_jsb__PlayTask;
+            public addOnFailureListener(listener: __types_jsb_jsb__OnFailureListener): __types_jsb_jsb__PlayTask;
+            public addOnSuccessListener(listener: __types_jsb_jsb__OnSuccessListener): __types_jsb_jsb__PlayTask;
+            public continueWith(listener: __types_jsb_jsb__OnContinueWithListener): __types_jsb_jsb__PlayTask;
+            public getResult(listener: __types_jsb_jsb__OnSuccessListener): any;
+            public isCanceled(): boolean;
+            public isComplete(): boolean;
+            public isSuccessful(): boolean;
+        }
+        export class __types_jsb_jsb__RecallAccess {
+            public hashCode(): number;
+            public getSessionId(): string;
+            public equals(other: __types_jsb_jsb__RecallAccess): boolean;
+        }
+        export class __types_jsb_jsb__AchievementsClient {
+            public showAchievements(): void;
+            public incrementImmediate(id: string, numSteps: number): __types_jsb_jsb__PlayTask;
+            public load(forceReload: boolean): __types_jsb_jsb__PlayTask;
+            public revealImmediate(id: string): __types_jsb_jsb__PlayTask;
+            public setStepsImmediate(id: string, numSteps: number): __types_jsb_jsb__PlayTask;
+            public unlockImmediate(id: string): __types_jsb_jsb__PlayTask;
+            public increment(id: string, numSteps: number): void;
+            public reveal(id: string): void;
+            public setSteps(id: string, numSteps: number): void;
+            public unlock(id: string): void;
+        }
+        export class __types_jsb_jsb__GamesSignInClient {
+            public isAuthenticated(): __types_jsb_jsb__PlayTask;
+            public requestServerSideAccess(serverClientId: string, forceRefreshToken: boolean): __types_jsb_jsb__PlayTask;
+            public signIn(): __types_jsb_jsb__PlayTask;
+        }
+        export class __types_jsb_jsb__RecallClient {
+            public requestRecallAccess(): __types_jsb_jsb__PlayTask;
+        }
         export namespace __types_jsb__jsb {
             let window: any;
             type AccelerationXYZ = number;
             type AccelerationIncludingGravityXYZ = number;
@@ -74836,8 +74907,79 @@
                 showAlternativeBillingOnlyInformationDialog: (listener: __types_jsb_jsb__AlternativeBillingOnlyInformationDialogListener) => void;
                 showExternalOfferInformationDialog: (listener: __types_jsb_jsb__ExternalOfferInformationDialogListener) => void;
                 showInAppMessages: (params: __types_jsb_jsb__InAppMessageParams, listener: __types_jsb_jsb__InAppMessageResponseListener) => void;
             }
+            export class PlayException {
+                getMessage(): string;
+                getLocalizedMessage(): string;
+                printStackTrace(): void;
+                toString(): string;
+            }
+            export interface OnCanceledListener {
+                onCanceled(): void;
+            }
+            export interface OnCompleteListener {
+                onComplete(task: any): void;
+            }
+            export interface OnFailureListener {
+                onFailure(e: __types_jsb_jsb__PlayException): void;
+            }
+            export interface OnSuccessListener {
+                onSuccess(result: any): void;
+            }
+            export interface OnContinueWithListener {
+                then(result: any): void;
+            }
+            export interface OnContinueWithTaskListener {
+                then(result: any): void;
+            }
+            export class PlayTask {
+                public addOnCanceledListener(listener: __types_jsb_jsb__OnCanceledListener): __types_jsb_jsb__PlayTask;
+                public addOnCompleteListener(listener: __types_jsb_jsb__OnCompleteListener): __types_jsb_jsb__PlayTask;
+                public addOnFailureListener(listener: __types_jsb_jsb__OnFailureListener): __types_jsb_jsb__PlayTask;
+                public addOnSuccessListener(listener: __types_jsb_jsb__OnSuccessListener): __types_jsb_jsb__PlayTask;
+                public continueWith(listener: __types_jsb_jsb__OnContinueWithListener): __types_jsb_jsb__PlayTask;
+                public getResult(listener: __types_jsb_jsb__OnSuccessListener): any;
+                public isCanceled(): boolean;
+                public isComplete(): boolean;
+                public isSuccessful(): boolean;
+            }
+            export class AuthenticationResult {
+                public isAuthenticated(): boolean;
+            }
+            export class RecallAccess {
+                public hashCode(): number;
+                public getSessionId(): string;
+                public equals(other: __types_jsb_jsb__RecallAccess): boolean;
+            }
+            export class GamesSignInClient {
+                public isAuthenticated(): __types_jsb_jsb__PlayTask;
+                public requestServerSideAccess(serverClientId: string, forceRefreshToken: boolean): __types_jsb_jsb__PlayTask;
+                public signIn(): __types_jsb_jsb__PlayTask;
+            }
+            export class AchievementsClient {
+                public showAchievements(): void;
+                public incrementImmediate(id: string, numSteps: number): __types_jsb_jsb__PlayTask;
+                public load(forceReload: boolean): __types_jsb_jsb__PlayTask;
+                public revealImmediate(id: string): __types_jsb_jsb__PlayTask;
+                public setStepsImmediate(id: string, numSteps: number): __types_jsb_jsb__PlayTask;
+                public unlockImmediate(id: string): __types_jsb_jsb__PlayTask;
+                public increment(id: string, numSteps: number): void;
+                public reveal(id: string): void;
+                public setSteps(id: string, numSteps: number): void;
+                public unlock(id: string): void;
+            }
+            export class RecallClient {
+                public requestRecallAccess(): __types_jsb_jsb__PlayTask;
+            }
+            export class PlayGames {
+                public static getAchievementsClient(): __types_jsb_jsb__AchievementsClient;
+                public static getGamesSignInClient(): __types_jsb_jsb__GamesSignInClient;
+                public static getRecallClient(): __types_jsb_jsb__RecallClient;
+            }
+            export class PlayGamesSdk {
+                public static initialize(): void;
+            }
         }
         /**
          * @en Represents the details of a one time or subscription product.
          * @zh 代表一次性或订阅产品的详细信息。
@@ -74933,8 +75075,90 @@
             showAlternativeBillingOnlyInformationDialog: (listener: __types_jsb_jsb__AlternativeBillingOnlyInformationDialogListener) => void;
             showExternalOfferInformationDialog: (listener: __types_jsb_jsb__ExternalOfferInformationDialogListener) => void;
             showInAppMessages: (params: __types_jsb_jsb__InAppMessageParams, listener: __types_jsb_jsb__InAppMessageResponseListener) => void;
         }
+        export class __types_jsb_jsb__PlayGamesSdk {
+            public static initialize(): void;
+        }
+        /// <reference types="./@types/jsb" />
+        /****************************************************************************
+         Copyright (c) 2025 Xiamen Yaji Software Co., Ltd.
+        
+         http://www.cocos.com
+        
+         Permission is hereby granted, free of charge, to any person obtaining a copy
+         of this software and associated documentation files (the "Software"), to deal
+         in the Software without restriction, including without limitation the rights to
+         use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+         of the Software, and to permit persons to whom the Software is furnished to do so,
+         subject to the following conditions:
+        
+         The above copyright notice and this permission notice shall be included in
+         all copies or substantial portions of the Software.
+        
+         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+         THE SOFTWARE.
+         *****************************************************************************/
+        export interface _vendor_google_play_task__OnCanceledListener {
+            onCanceled(): void;
+        }
+        export interface _vendor_google_play_task__OnCompleteListener<TResult, TContinuationResult = void> {
+            onComplete(result: _vendor_google_play_task__TaskHelper<TResult, TContinuationResult>): void;
+        }
+        export interface _vendor_google_play_task__OnFailureListener {
+            onFailure(e: __types_jsb__jsb.PlayException): void;
+        }
+        export interface _vendor_google_play_task__OnSuccessListener<TResult> {
+            onSuccess(result: TResult): void;
+        }
+        export interface _vendor_google_play_task__ContinuationHelper<TResult, TContinuationResult> {
+            then(task: _vendor_google_play_task__TaskHelper<TResult, TContinuationResult>): TContinuationResult;
+        }
+        export interface _vendor_google_play_task__TaskHelper<TResult, TContinuationResult = void> {
+            addOnCanceledListener(listener: _vendor_google_play_task__OnCanceledListener): _vendor_google_play_task__TaskHelper<TResult, TContinuationResult>;
+            addOnCompleteListener(listener: _vendor_google_play_task__OnCompleteListener<TResult, TContinuationResult>): _vendor_google_play_task__TaskHelper<TResult, TContinuationResult>;
+            addOnFailureListener(listener: _vendor_google_play_task__OnFailureListener): _vendor_google_play_task__TaskHelper<TResult, TContinuationResult>;
+            addOnSuccessListener(listener: _vendor_google_play_task__OnSuccessListener<TResult>): _vendor_google_play_task__TaskHelper<TResult, TContinuationResult>;
+            continueWith(result: _vendor_google_play_task__ContinuationHelper<TResult, TContinuationResult>): _vendor_google_play_task__TaskHelper<TContinuationResult>;
+            continueWithTask(result: _vendor_google_play_task__TaskHelper<TContinuationResult, _vendor_google_play_task__TaskHelper<TContinuationResult>>): _vendor_google_play_task__TaskHelper<TContinuationResult>;
+            getResult(): TResult | null;
+            isCanceled(): boolean;
+            isComplete(): boolean;
+            isSuccessful(): boolean;
+        }
+        export class _vendor_google_play_games__AchievementsClient {
+            showAchievements(): void;
+            incrementImmediate(id: string, numSteps: number): _vendor_google_play_task__TaskHelper<boolean>;
+            load(id: boolean): _vendor_google_play_task__TaskHelper<boolean>;
+            revealImmediate(id: string): _vendor_google_play_task__TaskHelper<void>;
+            setStepsImmediate(id: string, numSteps: number): _vendor_google_play_task__TaskHelper<boolean>;
+            unlockImmediate(id: string): _vendor_google_play_task__TaskHelper<void>;
+            increment(id: string, numSteps: number): void;
+            reveal(id: string): void;
+            setSteps(id: string, numSteps: number): void;
+            unlock(id: string): void;
+        }
+        export class _vendor_google_play_games__GamesSignInClientHelper {
+            isAuthenticated(): _vendor_google_play_task__TaskHelper<__types_jsb__jsb.AuthenticationResult>;
+            requestServerSideAccess(serverClientId: string, forceRefreshToken: boolean): _vendor_google_play_task__TaskHelper<string>;
+            signIn(): _vendor_google_play_task__TaskHelper<__types_jsb__jsb.AuthenticationResult>;
+        }
+        export class _vendor_google_play_games__RecallClient {
+            requestRecallAccess(): _vendor_google_play_task__TaskHelper<__types_jsb__jsb.RecallAccess>;
+        }
+        export class _vendor_google_play_games__PlayGamesHelper {
+            static getAchievementsClient(): _vendor_google_play_games__AchievementsClient;
+            static getGamesSignInClient(): _vendor_google_play_games__GamesSignInClientHelper;
+            static getRecallClient(): _vendor_google_play_games__RecallClient;
+        }
+        export class __types_jsb_jsb__AuthenticationResult {
+            public isAuthenticated(): boolean;
+        }
         export enum _cocos_video_video_player_enums__VideoPlayerEventType {
             /**
              * @en None.
              * @zh 无。

Comment thread @types/jsb.d.ts Outdated
Comment thread native/cocos/platform/android/java/vendor/google/play/PlayGamesSdkHelper.java Outdated
Comment thread native/cocos/platform/android/java/vendor/google/play/TaskManager.java Outdated
Comment thread native/vendor/google/play/PlayTask.h Outdated
CC_LOG_WARNING("The task does not exist.");
return;
}
_tasks.erase(it);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is PlayTask deleted? Should delete it here?

@qiuguohua qiuguohua Mar 6, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PlayTask lifecycle is managed on ts.So it's called in the PlayTask destructor.

Comment thread native/vendor/google/play/PlayTaskManager.h Outdated
Comment thread native/vendor/google/play/TResult.h Outdated
Comment thread vendor/google/billing/billing.ts
Comment thread vendor/google/play/games.ts Outdated
// });
// }
// }
// }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not ready or not needed?

@qiuguohua qiuguohua requested a review from bofeng-song March 6, 2025 08:29
@qiuguohua qiuguohua requested a review from dumganhar March 6, 2025 11:42
@qiuguohua

Copy link
Copy Markdown
Contributor Author

@cocos-robot run test cases

@qiuguohua

Copy link
Copy Markdown
Contributor Author

@cocos-robot run test cases

Comment thread @types/jsb.d.ts
showInAppMessages: (params: InAppMessageParams, listener: InAppMessageResponseListener) => void;
}

export class PlayException {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Developers might misinterpret PlayException as an exception related to gameplay rather than something specific to Google Play. Should Google-specific content be encapsulated within a googleplay namespace?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is indeed under the google play namespace now. jsb's interface is not open to the public. Developers use google.play.PlayException.
image

Comment thread native/vendor/google/play/PlayTaskManager.h
@qiuguohua

Copy link
Copy Markdown
Contributor Author

@cocos-robot run test cases

Comment thread native/cocos/platform/android/java/vendor/play/google/play/TaskManager.java Outdated
latch.countDown();
}
});
latch.await();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which thread does latch.await live in ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default task is the main thread running

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so, the await may cause ANR issues. Could we custom the default task queue to sub-thread?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep,I switched to other threads for execution.#18432

@dumganhar

Copy link
Copy Markdown
Contributor

@cocos-robot run test cases

@dumganhar dumganhar merged commit f80ca2c into cocos:v3.8.6 Mar 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants