Cloud save: working implementation, no dialog to select your games yet
This commit is contained in:
@@ -39,4 +39,14 @@ class CloudSave
|
||||
|
||||
public void onActivityResult(int request, int response, Intent data) {
|
||||
}
|
||||
|
||||
public boolean save(String filename, String saveId, String dialogTitle, String description, String imageFile, long playedTimeMs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean load(String filename, String saveId, String dialogTitle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ import android.view.Display;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.app.ProgressDialog;
|
||||
|
||||
|
||||
|
||||
public class MainActivity extends Activity
|
||||
@@ -106,6 +108,8 @@ public class MainActivity extends Activity
|
||||
_layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
|
||||
_layout2 = new LinearLayout(this);
|
||||
_layout2.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
loadingDialog = new ProgressDialog(this);
|
||||
loadingDialog.setMessage(getString(R.string.accessing_network));
|
||||
|
||||
final Semaphore loadedLibraries = new Semaphore(0);
|
||||
|
||||
@@ -220,7 +224,7 @@ public class MainActivity extends Activity
|
||||
Intent intent = new Intent(this, DummyService.class);
|
||||
startService(intent);
|
||||
}
|
||||
_cloudSave = new CloudSave(this);
|
||||
cloudSave = new CloudSave(this);
|
||||
}
|
||||
|
||||
public void setUpStatusLabel()
|
||||
@@ -429,19 +433,19 @@ public class MainActivity extends Activity
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
_cloudSave.onStart();
|
||||
cloudSave.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStart();
|
||||
_cloudSave.onStop();
|
||||
cloudSave.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int request, int response, Intent data) {
|
||||
super.onActivityResult(request, response, data);
|
||||
_cloudSave.onActivityResult(request, response, data);
|
||||
cloudSave.onActivityResult(request, response, data);
|
||||
}
|
||||
|
||||
public void showScreenKeyboardWithoutTextInputField()
|
||||
@@ -1235,7 +1239,8 @@ public class MainActivity extends Activity
|
||||
private LinearLayout _layout = null;
|
||||
private LinearLayout _layout2 = null;
|
||||
private Advertisement _ad = null;
|
||||
public CloudSave _cloudSave = null;
|
||||
public CloudSave cloudSave = null;
|
||||
public ProgressDialog loadingDialog = null;
|
||||
|
||||
private FrameLayout _videoLayout = null;
|
||||
private EditText _screenKeyboard = null;
|
||||
|
||||
@@ -834,6 +834,52 @@ class DemoRenderer extends GLSurfaceView_SDL.Renderer
|
||||
context.requestNewAdvertisement();
|
||||
}
|
||||
|
||||
public boolean cloudSave(String filename, String saveId, String dialogTitle, String description, String imageFile, long playedTimeMs)
|
||||
{
|
||||
context.runOnUiThread(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
context.loadingDialog.show();
|
||||
}
|
||||
});
|
||||
|
||||
boolean ret = context.cloudSave.save(filename, saveId, dialogTitle, description, imageFile, playedTimeMs);
|
||||
|
||||
context.runOnUiThread(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
context.loadingDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public boolean cloudLoad(String filename, String saveId, String dialogTitle)
|
||||
{
|
||||
context.runOnUiThread(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
context.loadingDialog.show();
|
||||
}
|
||||
});
|
||||
|
||||
boolean ret = context.cloudSave.load(filename, saveId, dialogTitle);
|
||||
|
||||
context.runOnUiThread(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
context.loadingDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private int PowerOf2(int i)
|
||||
{
|
||||
int value = 1;
|
||||
|
||||
@@ -19,8 +19,24 @@ package com.google.example.games.basegameutils;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.ResultCallback;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
import com.google.android.gms.auth.GoogleAuthUtil;
|
||||
import com.google.android.gms.auth.UserRecoverableAuthException;
|
||||
import com.google.android.gms.common.Scopes;
|
||||
import com.google.android.gms.games.Games;
|
||||
import com.google.android.gms.games.GamesStatusCodes;
|
||||
import com.google.android.gms.games.snapshot.Snapshot;
|
||||
import com.google.android.gms.games.snapshot.SnapshotMetadata;
|
||||
import com.google.android.gms.games.snapshot.SnapshotMetadataBuffer;
|
||||
import com.google.android.gms.games.snapshot.SnapshotMetadataChange;
|
||||
import com.google.android.gms.games.snapshot.Snapshots;
|
||||
|
||||
public class CloudSave implements GameHelper.GameHelperListener {
|
||||
|
||||
@@ -40,54 +56,225 @@ public class CloudSave implements GameHelper.GameHelperListener {
|
||||
mHelper.setup(this);
|
||||
}
|
||||
|
||||
public GameHelper getGameHelper() {
|
||||
return mHelper;
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
public void onStart()
|
||||
{
|
||||
mHelper.onStart(parent);
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
public void onStop()
|
||||
{
|
||||
mHelper.onStop();
|
||||
}
|
||||
|
||||
public boolean save(String filename, String description, String imageFile)
|
||||
public void onActivityResult(int request, int response, Intent data)
|
||||
{
|
||||
mHelper.onActivityResult(request, response, data);
|
||||
}
|
||||
|
||||
public synchronized boolean save(String filename, String saveId, String dialogTitle, String description, String imageFile, long playedTimeMs)
|
||||
{
|
||||
Log.d("SDL", "CloudSave: save: file " + filename + " saveId " + saveId + " dialogTitle " + dialogTitle + " desc " + description + " imageFile " + imageFile + " playedTime " + playedTimeMs);
|
||||
|
||||
if( !signIn() )
|
||||
return false;
|
||||
|
||||
if( !filename.startsWith("/") )
|
||||
filename = Globals.DataDir + "/" + filename;
|
||||
if( imageFile.length() > 0 && !imageFile.startsWith("/") )
|
||||
imageFile = Globals.DataDir + "/" + imageFile;
|
||||
|
||||
try
|
||||
{
|
||||
if( saveId == null || saveId.length() == 0 )
|
||||
{
|
||||
Log.i("SDL", "CloudSave: save: user dialog is not supported yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
Snapshots.OpenSnapshotResult result = Games.Snapshots.open(getApiClient(), saveId, true).await();
|
||||
Snapshot crapshot = processSnapshotOpenResult(result, 0);
|
||||
if( crapshot == null )
|
||||
return false;
|
||||
|
||||
crapshot.writeBytes(readFile(filename));
|
||||
|
||||
Bitmap bmp = BitmapFactory.decodeFile(imageFile);
|
||||
while( bmp != null && bmp.getByteCount() > Games.Snapshots.getMaxCoverImageSize(getApiClient()) )
|
||||
bmp = Bitmap.createScaledBitmap(bmp, bmp.getWidth() * 3 / 4, bmp.getHeight() * 3 / 4, true);
|
||||
|
||||
SnapshotMetadataChange.Builder metadataChange = new SnapshotMetadataChange.Builder()
|
||||
.setDescription(description)
|
||||
.setPlayedTimeMillis(playedTimeMs);
|
||||
if( bmp != null )
|
||||
metadataChange.setCoverImage(bmp);
|
||||
|
||||
Games.Snapshots.commitAndClose(getApiClient(), crapshot, metadataChange.build())
|
||||
.setResultCallback(new ResultCallback<Snapshots.CommitSnapshotResult>()
|
||||
{
|
||||
public void onResult(Snapshots.CommitSnapshotResult r)
|
||||
{
|
||||
Log.i("SDL", "CloudSave: save final net sync result: " + r.getStatus().toString());
|
||||
}
|
||||
});
|
||||
|
||||
Log.i("SDL", "CloudSave: save succeeded");
|
||||
return true;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.i("SDL", "CloudSave: save failed: " + e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean load (String filename)
|
||||
public synchronized boolean load(String filename, String saveId, String dialogTitle)
|
||||
{
|
||||
Log.d("SDL", "CloudSave: load: file " + filename + " saveId " + saveId + " dialogTitle " + dialogTitle);
|
||||
|
||||
if( !signIn() )
|
||||
return false;
|
||||
|
||||
if( !filename.startsWith("/") )
|
||||
filename = Globals.DataDir + "/" + filename;
|
||||
|
||||
try
|
||||
{
|
||||
if( saveId == null || saveId.length() == 0 )
|
||||
{
|
||||
Log.i("SDL", "CloudSave: load: user dialog is not supported yet");
|
||||
return false;
|
||||
}
|
||||
|
||||
Snapshots.OpenSnapshotResult result = Games.Snapshots.open(getApiClient(), saveId, false).await();
|
||||
if (result.getStatus().getStatusCode() != GamesStatusCodes.STATUS_OK)
|
||||
{
|
||||
Log.i("SDL", "CloudSave: load: failed to load game " + saveId + ": " + result.getStatus());
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean written = writeFile(filename, result.getSnapshot().readFully());
|
||||
Log.i("SDL", "CloudSave: load: status: " + written);
|
||||
return written;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.i("SDL", "CloudSave: load failed: " + e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public class loadDialogResult
|
||||
{
|
||||
public boolean status = false;
|
||||
public String filename = "";
|
||||
}
|
||||
|
||||
public loadDialogResult loadDialog(String filename, String dialogTitle)
|
||||
{
|
||||
loadDialogResult res = new loadDialogResult();
|
||||
res.status = false;
|
||||
res.filename = "";
|
||||
return res;
|
||||
}
|
||||
|
||||
// ===== Private API =====
|
||||
|
||||
public void onActivityResult(int request, int response, Intent data) {
|
||||
mHelper.onActivityResult(request, response, data);
|
||||
boolean signInSucceeded = false;
|
||||
boolean signInFailed = false;
|
||||
public boolean signIn()
|
||||
{
|
||||
//Log.i("SDL", "CloudSave: signIn()");
|
||||
if( !isSignedIn() )
|
||||
{
|
||||
signInSucceeded = false;
|
||||
signInFailed = false;
|
||||
Log.i("SDL", "CloudSave: beginUserInitiatedSignIn()");
|
||||
beginUserInitiatedSignIn();
|
||||
Log.i("SDL", "CloudSave: beginUserInitiatedSignIn() exit");
|
||||
while (!signInSucceeded && !signInFailed)
|
||||
{
|
||||
try { Thread.sleep(300); } catch( Exception e ) {}
|
||||
}
|
||||
return signInSucceeded;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void onSignInSucceeded() {
|
||||
Log.i("SDL", "CloudSave: onSignInSucceeded()");
|
||||
signInSucceeded = true;
|
||||
}
|
||||
|
||||
public void onSignInFailed() {
|
||||
Log.i("SDL", "CloudSave: onSignInFailed()");
|
||||
signInFailed = true;
|
||||
}
|
||||
|
||||
public Snapshot processSnapshotOpenResult(Snapshots.OpenSnapshotResult result, int retryCount)
|
||||
{
|
||||
Snapshot mResolvedSnapshot = null;
|
||||
retryCount++;
|
||||
int status = result.getStatus().getStatusCode();
|
||||
|
||||
Log.i("SDL", "CloudSave: processSnapshotOpenResult status: " + result.getStatus());
|
||||
|
||||
if (status == GamesStatusCodes.STATUS_OK) {
|
||||
return result.getSnapshot();
|
||||
} else if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONTENTS_UNAVAILABLE) {
|
||||
return result.getSnapshot();
|
||||
} else if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONFLICT) {
|
||||
Snapshot snapshot = result.getSnapshot();
|
||||
Snapshot conflictSnapshot = result.getConflictingSnapshot();
|
||||
|
||||
// Resolve between conflicts by selecting the newest of the conflicting snapshots.
|
||||
mResolvedSnapshot = snapshot;
|
||||
|
||||
if (snapshot.getMetadata().getPlayedTime() == conflictSnapshot.getMetadata().getPlayedTime()) {
|
||||
if (snapshot.getMetadata().getLastModifiedTimestamp() < conflictSnapshot.getMetadata().getLastModifiedTimestamp()) {
|
||||
mResolvedSnapshot = conflictSnapshot;
|
||||
}
|
||||
} else if (snapshot.getMetadata().getPlayedTime() < conflictSnapshot.getMetadata().getPlayedTime()) {
|
||||
mResolvedSnapshot = conflictSnapshot;
|
||||
}
|
||||
|
||||
Snapshots.OpenSnapshotResult resolveResult = Games.Snapshots.resolveConflict(
|
||||
getApiClient(), result.getConflictId(), mResolvedSnapshot)
|
||||
.await();
|
||||
|
||||
if (retryCount < 3) {
|
||||
return processSnapshotOpenResult(resolveResult, retryCount);
|
||||
} else {
|
||||
Log.i("SDL", "CloudSave: could not resolve snapshot conflict");
|
||||
}
|
||||
}
|
||||
Log.i("SDL", "CloudSave: could not get savegame snapshot");
|
||||
return null;
|
||||
}
|
||||
|
||||
static public byte[] readFile(String filename)
|
||||
{
|
||||
int len = (int)(new File(filename).length());
|
||||
if( len == 0 )
|
||||
return new byte[0];
|
||||
try
|
||||
{
|
||||
byte buf[] = new byte[len];
|
||||
if( new FileInputStream(filename).read(buf, 0, len) != len )
|
||||
return new byte[0];
|
||||
return buf;
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Log.i("SDL", "CloudSave: readFile() error: " + e.toString());
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
static public boolean writeFile(String filename, byte[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(filename));
|
||||
out.write(data, 0, data.length);
|
||||
out.close();
|
||||
return true;
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Log.i("SDL", "CloudSave: writeFile() error: " + e.toString() + " file " + filename);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public GameHelper getGameHelper() {
|
||||
return mHelper;
|
||||
}
|
||||
|
||||
public GoogleApiClient getApiClient() {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
grep '<string name=' values/strings.xml | while read str; do
|
||||
|
||||
if echo "$str" | grep 'translatable="false"' ; then continue; fi
|
||||
|
||||
var=`echo $str | sed 's/<string name=["]\([^"]*\).*/\1/'`
|
||||
text=`echo $str | sed 's/<string name=["][^"]*["]>\([^<]*\).*/\1/'`
|
||||
if [ "$var" = "app_name" ]; then
|
||||
|
||||
@@ -188,4 +188,5 @@
|
||||
<string name="gamehelper_app_misconfigured">The application is incorrectly configured. Check that the package name and signing certificate match the client ID created in Developer Console. Also, if the application is not yet published, check that the account you are trying to sign in with is listed as a tester account. See logs for more information.</string>
|
||||
<string name="gamehelper_license_failed">License check failed.</string>
|
||||
<string name="gamehelper_unknown_error">Unknown error.</string>
|
||||
<string name="accessing_network">Accessing network, please wait</string>
|
||||
</resources>
|
||||
|
||||
@@ -161,4 +161,5 @@
|
||||
<string name="gamehelper_app_misconfigured">The application is incorrectly configured. Check that the package name and signing certificate match the client ID created in Developer Console. Also, if the application is not yet published, check that the account you are trying to sign in with is listed as a tester account. See logs for more information.</string>
|
||||
<string name="gamehelper_license_failed">License check failed.</string>
|
||||
<string name="gamehelper_unknown_error">Unknown error.</string>
|
||||
<string name="accessing_network">Accessing network, please wait</string>
|
||||
</resources>
|
||||
|
||||
@@ -160,4 +160,5 @@
|
||||
<string name="gamehelper_app_misconfigured">The application is incorrectly configured. Check that the package name and signing certificate match the client ID created in Developer Console. Also, if the application is not yet published, check that the account you are trying to sign in with is listed as a tester account. See logs for more information.</string>
|
||||
<string name="gamehelper_license_failed">License check failed.</string>
|
||||
<string name="gamehelper_unknown_error">Unknown error.</string>
|
||||
<string name="accessing_network">Accessing network, please wait</string>
|
||||
</resources>
|
||||
|
||||
@@ -192,6 +192,8 @@
|
||||
<string name="gamehelper_app_misconfigured">The application is incorrectly configured. Check that the package name and signing certificate match the client ID created in Developer Console. Also, if the application is not yet published, check that the account you are trying to sign in with is listed as a tester account. See logs for more information.</string>
|
||||
<string name="gamehelper_license_failed">License check failed.</string>
|
||||
<string name="gamehelper_unknown_error">Unknown error.</string>
|
||||
<string name="accessing_network">Accessing network, please wait</string>
|
||||
|
||||
<string name="google_play_game_services_app_id" translatable="false">==GOOGLEPLAYGAMESERVICES_APP_ID==</string>
|
||||
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user