Following the deprecation of the
[Google Sign-In](https://android-developers.googleblog.com/2024/09/streamlining-android-authentication-credential-manager-replaces-legacy-apis.html)
API, we are removing the games v1 SDK in 2026. After February 2025, you will be unable to publish
titles that are newly integrated with games v1 SDK, on Google Play. We recommend that you use the
games v2 SDK instead.  

While existing titles with the previous games v1 integrations continue to function for a
couple of years, you are encouraged to
[migrate to v2](https://developer.android.com/games/pgs/android/migrate-to-v2)
starting June 2025.  

This guide is for using the Play Games Services v1 SDK. For information
on the latest SDK version, see the
[v2 documentation](https://developer.android.com/games/pgs/android/saved-games).

This guide shows you how to implement saved games game using the
snapshots API provided by Google Play Games Services. The APIs can be found in the
[`com.google.android.gms.games.snapshot`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/package-summary)
and [`com.google.android.gms.games`](https://developers.google.com/android/reference/com/google/android/gms/games/package-summary) packages.

## Before you begin

If you haven't already done so, you might find it helpful to review the
[Saved Games game concepts](https://developer.android.com/games/pgs/savedgames).

- Make sure to [enable saved games support](https://developer.android.com/games/pgs/console/enable-features#enable_saved_games) for your game in the Google Play Console.
- Download and review the saved games code sample in the [Android samples page](https://github.com/playgameservices/android-basic-samples).
- Familiarize yourself with the recommendations described in [Quality Checklist](https://developer.android.com/games/pgs/v1/quality).

## Get the snapshots client

To start using the snapshots API, your game must first obtain a
[`SnapshotsClient`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient) object. You can do this by calling the
[`Games.getSnapshotsClient()`](https://developers.google.com/android/reference/com/google/android/gms/games/Games.html#getSnapshotsClient(android.app.Activity,%20com.google.android.gms.auth.api.signin.GoogleSignInAccount)) method and passing in the
activity and the [`GoogleSignInAccount`](https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInAccount) for the current player. To learn how to
retrieve the player account information, see
[Sign-in in Android Games](https://developer.android.com/games/pgs/v1/android/signin).
| **Note:** The [`SnapshotsClient`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient) class makes use of the Google Play services [`Task`](https://developers.google.com/android/reference/com/google/android/gms/tasks/Task) class to return results asynchronously. To learn more about using tasks to manage threaded work, see the [Tasks API developer guide](https://developers.google.com/android/guides/tasks).

## Specify the Drive scope

The snapshots API relies on the [Google Drive API](https://developers.google.com/drive/android/) for saved games storage. To
access the Drive API, your app must specify the
[`Drive.SCOPE_APPFOLDER`](https://developers.google.com/android/reference/com/google/android/gms/drive/Drive#SCOPE_APPFOLDER)
scope when building the Google sign-in client.

Here's an example of how to do this in the
[`onResume()`](https://developer.android.com/reference/android/app/Activity#onResume())
method for your sign-in activity:  

```transact-sql
private GoogleSignInClient mGoogleSignInClient;

@Override
protected void onResume() {
  super.onResume();
  signInSilently();
}

private void signInSilently() {
  GoogleSignInOptions signInOption =
      new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
          // Add the APPFOLDER scope for Snapshot support.
          .requestScopes(Drive.SCOPE_APPFOLDER)
          .build();

  GoogleSignInClient signInClient = GoogleSignIn.getClient(this, signInOption);
  signInClient.silentSignIn().addOnCompleteListener(this,
      new OnCompleteListener<GoogleSignInAccount>() {
        @Override
        public void onComplete(@NonNull Task<GoogleSignInAccount> task) {
          if (task.isSuccessful()) {
            onConnected(task.getResult());
          } else {
            // Player will need to sign-in explicitly using via UI
          }
        }
      });
}
```

## Display saved games

You can integrate the snapshots API wherever your game provides players with
the option to save or restore their progress. Your game might display such an
option at designated save/restore points or allow players to save or restore
progress at any time.

Once players select the save/restore option in your game, your game can
optionally bring up a screen that prompts players to enter information for a new saved
game or to select an existing saved game to restore.

To simplify your development, the snapshots API provides a default saved games selection user
interface (UI) that you can use out-of-the-box. The saved games selection UI allows players to
create a new saved game, view details about existing saved games, and load previous saved games.

To launch the default Saved Games UI:

1. Call [`SnapshotsClient.getSelectSnapshotIntent()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#getSelectSnapshotIntent(java.lang.String,%20boolean,%20boolean,%20int)) to get an [`Intent`](https://developer.android.com/reference/android/content/Intent) for launching the default saved games selection UI.
2. Call [`startActivityForResult()`](https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int)) and pass in that [`Intent`](https://developer.android.com/reference/android/content/Intent). If the call is successful, the game displays the saved game selection UI, along with the options you specified.

Here's an example of how to launch the default saved games selection UI:  

```transact-sql
private static final int RC_SAVED_GAMES = 9009;

private void showSavedGamesUI() {
  SnapshotsClient snapshotsClient =
      Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this));
  int maxNumberOfSavedGamesToShow = 5;

  Task<Intent> intentTask = snapshotsClient.getSelectSnapshotIntent(
      "See My Saves", true, true, maxNumberOfSavedGamesToShow);

  intentTask.addOnSuccessListener(new OnSucce<ssList>enerIntent() {
    @Override
    public void onSuccess(Intent intent) {
      startActivityForResult(intent, RC_SAVED_GAMES);
    }
  });
}
```

If the player selects to create a new saved game or load an existing saved game,
the UI sends a request to Google Play Games Services. If the request is successful,
Google Play Games Services returns information to create or restore the saved game through
the [`onActivityResult()`](https://developer.android.com/reference/android/app/Activity#onActivityResult(int,%20int,%20android.content.Intent))
callback. Your game can override this callback to check if any errors occurred during request.

The following code snippet shows a sample implementation of
[`onActivityResult()`](https://developer.android.com/reference/android/app/Activity#onActivityResult(int,%20int,%20android.content.Intent)):  

```transact-sql
private String mCurrentSaveName = "snapshotTemp";

/**
 * This callback will be triggered after you call startActivityForResult from the
 * showSavedGamesUI method.
 */
@Override
protected void onActivityResult(int requestCode, int resultCode,
                                Intent intent) {
  if (intent != null) {
    if (intent.hasExtra(SnapshotsClient.EXTRA_SNAPSHOT_METADATA)) {
      // Load a snapshot.
      SnapshotMetadata snapshotMetadata =
          intent.getParcelableExtra(SnapshotsClient.EXTRA_SNAPSHOT_METADATA);
      mCurrentSaveName = snapshotMetadata.getUniqueName();

      // Load the game data from the Snapshot
      // ...
    } else if (intent.hasExtra(SnapshotsClient.EXTRA_SNAPSHOT_NEW)) {
      // Create a new snapshot named with a unique string
      String unique = new BigInteger(281, new Random()).toString(13);
      mCurrentSaveName = "snapshotTemp-" + unique;

      // Create the new snapshot
      // ...
    }
  }
}
```

## Write saved games

To store content to a saved game:

1. Asynchronously open a snapshot via [`SnapshotsClient.open()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#open(com.google.android.gms.games.snapshot.SnapshotMetadata)). Then, retrieve the [`Snapshot`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/Snapshot) object from the task's result by calling [`SnapshotsClient.DataOrConflict.getData()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.DataOrConflict.html#getData()).
2. Retrieve a [`SnapshotContents`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotContents) instance via [`SnapshotsClient.SnapshotConflict`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/Snapshot.html#getSnapshotContents()).
3. Call [`SnapshotContents.writeBytes()`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotContents.html#writeBytes(byte%5B%5D)) to store the player's data in byte format.
4. Once all your changes are written, call [`SnapshotsClient.commitAndClose()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#commitAndClose(com.google.android.gms.games.snapshot.Snapshot,%20com.google.android.gms.games.snapshot.SnapshotMetadataChange)) to send your changes to Google's servers. In the method call, your game can optionally provide additional information to tell Google Play Games Services how to present this saved game to players. This information is represented in a [`SnapshotMetaDataChange`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotMetadataChange) object, which your game creates using [`SnapshotMetadataChange.Builder`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotMetadataChange.Builder).

The following snippet shows how your game might commit changes to a saved game:  

```gdscript
private Task<SnapshotMetadata> writeSnapshot(Snapshot snapshot,
                                             byte[] data, Bitmap coverImage, String desc) {

  // Set the data payload for the snapshot
  snapshot.getSnapshotContents().writeBytes(data);

  // Create the change operation
  SnapshotMetadataChange metadataChange = new SnapshotMetadataChange.Builder()
      .setCoverImage(coverImage)
      .setDescription(desc)
      .build();

  SnapshotsClient snapshotsClient =
      Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this));

  // Commit the operation
  return snapshotsClient.commitAndClose(snapshot, metadataChange);
}
```

If the player's device is not connected to a network when your app calls
[`SnapshotsClient.commitAndClose()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#commitAndClose(com.google.android.gms.games.snapshot.Snapshot,%20com.google.android.gms.games.snapshot.SnapshotMetadataChange)), Google Play Games Services stores the saved game data locally on
the device. Upon device re-connection, Google Play Games Services syncs the locally cached saved game
changes to Google's servers.

## Load saved games

To retrieve saved games for the currently signed-in player:

1. Asynchronously open a snapshot via [`SnapshotsClient.open()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#open(com.google.android.gms.games.snapshot.SnapshotMetadata,%20int)). Then, retrieve the [`Snapshot`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/Snapshot) object from the task's result by calling [`SnapshotsClient.DataOrConflict.getData()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.DataOrConflict.html#getData()). Alternatively, your game can also retrieve a specific snapshot through the saved games selection UI, as described in [Display saved games](https://developer.android.com/games/pgs/v1/android/saved-games#display).
2. Retrieve the [`SnapshotContents`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotContents) instance via [`SnapshotsClient.SnapshotConflict`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/Snapshot.html#getSnapshotContents()).
3. Call [`SnapshotContents.readFully()`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotContents.html#readFully()) to read the contents of the snapshot.

The following snippet shows how you might load a specific saved game:  

```gdscript
Task<byte[]> loadSnapshot() {
  // Display a progress dialog
  // ...

  // Get the SnapshotsClient from the signed in account.
  SnapshotsClient snapshotsClient =
      Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this));

  // In the case of a conflict, the most recently modified version of this snapshot will be used.
  int conflictResolutionPolicy = SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED;

  // Open the saved game using its name.
  return snapshotsClient.open(mCurrentSaveName, true, conflictResolutionPolicy)
      .addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
          Log.e(TAG, "Error while opening Snapshot.", e);
        }
      }).continueWith(new Co<ntinuationSnapshotsClient.Data<OrConfli>ctSnapsh>ot, byte[]() {
        @Override
        public byte[] then(@No<nNull TaskSnapshotsClient.Data<OrConfli>>ctSnapshot task) throws Exception {
          Snapshot snapshot = task.getResult().getData();

          // Opening the snapshot was a success and any conflicts have been resolved.
          try {
            // Extract the raw data from the snapshot.
            return snapshot.getSnapshotContents().readFully();
          } catch (IOException e) {
            Log.e(TAG, "Error while reading Snapshot.", e);
          }

          return null;
        }
      }).addOnCompleteListener(ne<w OnCo>mpleteListenerbyte[]() {
        @Override
        public void onCo<mplete>(@NonNull Taskbyte[] task) {
          // Dismiss progress dialog and reflect the changes in the UI when complete.
          // ...
        }
      });
}
```

## Handle saved game conflicts

When using the snapshots API in your game, it is possible for multiple
devices to perform reads and writes on the same saved game. In the event that a
device temporarily loses its network connection and later reconnects, this might
cause data conflicts whereby the saved game stored on a player's local device
is out-of-sync with the remote version stored in Google's servers.

The snapshots API provides a conflict resolution mechanism that presents both
sets of conflicting saved games at read-time and lets you implement a resolution
strategy that is appropriate for your game.

When Google Play Games Services detects a data conflict, the
[`SnapshotsClient.DataOrConflict.isConflict()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.DataOrConflict.html#isConflict()) method returns a value of `true` In this event, the
[`SnapshotsClient.SnapshotConflict`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict) class provides two versions of the saved game:

- **Server version**: The most-up-to-date version known by Google Play Games Services to be accurate for the player's device; and
- **Local version**: A modified version detected on one of the player's devices that contains conflicting content or metadata. This may not be the same as the version that you tried to save.

Your game must decide how to resolve the conflict by picking one of the
provided versions or merging the data of the two saved game versions.

To detect and resolve saved game conflicts:

1. Call [`SnapshotsClient.open()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#open(com.google.android.gms.games.snapshot.SnapshotMetadata,%20int)). The task result contains a `SnapshotsClient.DataOrConflict` class.
2. Call the [`SnapshotsClient.DataOrConflict.isConflict()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.DataOrConflict.html#isConflict()) method. If the result is true, you have a conflict to resolve.
3. Call [`SnapshotsClient.DataOrConflict.getConflict()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.DataOrConflict.html#getConflict()) to retrieve a [`SnapshotsClient.snapshotConflict`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict) instance.
4. Call [`SnapshotsClient.SnapshotConflict.getConflictId()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict.html#getConflictId()) to retrieve the conflict ID that uniquely identifies the detected conflict. Your game needs this value to send a conflict resolution request later.
5. Call [`SnapshotsClient.SnapshotConflict.getConflictingSnapshot()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict.html#getConflictingSnapshot()) to get the local version.
6. Call [`SnapshotsClient.SnapshotConflict.getSnapshot()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict.html#getSnapshot()) to get the server version.
7. To resolve the saved game conflict, select a version that you want to save to the server as the final version, and pass it to the [`SnapshotsClient.resolveConflict()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#resolveConflict(java.lang.String,%20java.lang.String,%20com.google.android.gms.games.snapshot.SnapshotMetadataChange,%20com.google.android.gms.games.snapshot.SnapshotContents)) method.

The following snippet shows and example of how your game might handle a saved game conflict by
selecting the most recently modified saved game as the final version to save:  

```carbon
private static final int MAX_SNAPSHOT_RESOLVE_RETRIES = 10;

Task<Snapshot> processSnapshotOpenResult(SnapshotsClient.DataOrConflict<Snapshot> result,
                                         final int retryCount) {

  if (!result.isConflict()) {
    // There was no conflict, so return the result of the source.
    TaskCompletionSource<Snapshot> source = new TaskCompletionSource<>();
    source.setResult(result.getData());
    return source.getTask();
  }

  // There was a conflict.  Try resolving it by selecting the newest of the conflicting snapshots.
  // This is the same as using RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED as a conflict resolution
  // policy, but we are implementing it as an example of a manual resolution.
  // One option is to present a UI to the user to choose which snapshot to resolve.
  SnapshotsClient.SnapshotConflict conflict = result.getConflict();

  Snapshot snapshot = conflict.getSnapshot();
  Snapshot conflictSnapshot = conflict.getConflictingSnapshot();

  // Resolve between conflicts by selecting the newest of the conflicting snapshots.
  Snapshot resolvedSnapshot = snapshot;

  if (snapshot.getMetadata().getLastModifiedTimestamp() <
      conflictSnapshot.getMetadata().getLastModifiedTimestamp()) {
    resolvedSnapshot = conflictSnapshot;
  }

  return Games.getSnapshotsClient(theActivity, GoogleSignIn.getLastSignedInAccount(this))
      .resolveConflict(conflict.getConflictId(), resolvedSnapshot)
      .continueWithTask(
          new Continuation<
              SnapshotsClient.DataOrConflict<Snapshot>,
              Task<Snapshot>>() {
            @Override
            public Task<Snapshot> then(
                @NonNull Task<SnapshotsClient.DataOrConflict<Snapshot>> task)
                throws Exception {
              // Resolving the conflict may cause another conflict,
              // so recurse and try another resolution.
              if (retryCount < MAX_SNAPSHOT_RESOLVE_RETRIES) {
                return processSnapshotOpenResult(task.getResult(), retryCount + 1);
              } else {
                throw new Exception("Could not resolve snapshot conflicts");
              }
            }
          });
}
```

### Modify saved games for conflict resolution

If you want to merge data from multiple saved games or modify an existing [`Snapshot`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/Snapshot)
to save to the server as the resolved final version, follow these steps:

1. Call [`SnapshotsClient.open()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#open(com.google.android.gms.games.snapshot.SnapshotMetadata,%20int)) .
2. Call [`SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict.html#getResolutionSnapshotContents()) to get a new [`SnapshotContents`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotContents) object.
3. Merge the data from [`SnapshotsClient.SnapshotConflict.getConflictingSnapshot()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict.html#getConflictingSnapshot()) and [`SnapshotsClient.SnapshotConflict.getSnapshot()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict.html#getSnapshot()) into the [`SnapshotContents`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotContents) object from the previous step.
4. Optionally, create a [`SnapshotMetadataChange`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotMetadataChange) instance if there are any changes to the metadata fields.
5. Call [`SnapshotsClient.resolveConflict()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#resolveConflict(java.lang.String,%20java.lang.String,%20com.google.android.gms.games.snapshot.SnapshotMetadataChange,%20com.google.android.gms.games.snapshot.SnapshotContents)). In your method call, pass in [`SnapshotsClient.SnapshotConflict.getConflictId()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.SnapshotConflict.html#getConflictId()) as the first argument, and the [`SnapshotMetadataChange`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotMetadataChange) and [`SnapshotContents`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/SnapshotContents) objects that you modified earlier as the second and third arguments respectively.
6. If the [`SnapshotsClient.resolveConflict()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.html#resolveConflict(java.lang.String,%20java.lang.String,%20com.google.android.gms.games.snapshot.SnapshotMetadataChange,%20com.google.android.gms.games.snapshot.SnapshotContents)) call is successful, the API stores the `Snapshot` object to the server and attempts to open the Snapshot object on your local device.
   - If there is a conflict, [`SnapshotsClient.DataOrConflict.isConflict()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.DataOrConflict.html#isConflict()) returns `true`. In this case, your game should return to step 2 and repeat the steps to modify the snapshot until conflicts are resolved.
   - If there's no conflict, [`SnapshotsClient.DataOrConflict.isConflict()`](https://developers.google.com/android/reference/com/google/android/gms/games/SnapshotsClient.DataOrConflict.html#isConflict()) returns `false` and the [`Snapshot`](https://developers.google.com/android/reference/com/google/android/gms/games/snapshot/Snapshot) object is open for your game to modify.