/*
* Copyright 2015 Qianqian Zhu <zhuqianqian.299@gmail.com> All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.z299studio.pb;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.drive.Drive;
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.SnapshotMetadataChange;
import com.google.android.gms.games.snapshot.Snapshots;
import java.io.IOException;
class GameSyncService extends SyncService implements
ConnectionCallbacks, OnConnectionFailedListener {
private static final String SAVED_DATA="Passbook-Saved-Data";
private static final int MAX_SNAPSHOT_RESOLVE_RETRIES = 3;
private GoogleApiClient mGoogleApiClient;
@Override
public SyncService initialize(Activity context) {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(Games.API)
.addScope(Games.SCOPE_GAMES)
.addApi(Drive.API)
.addScope(Drive.SCOPE_APPFOLDER)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
return this;
}
@Override
public SyncService connect(int localVersion) {
mLocalVersion = localVersion;
mGoogleApiClient.connect();
return this;
}
@Override
public void disconnect() {
mGoogleApiClient.disconnect();
}
@Override
public void read() {
AsyncTask<Void, Void, Integer> task = new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... params) {
int status = 0;
try{
Snapshots.OpenSnapshotResult result = Games.Snapshots.open(
mGoogleApiClient, SAVED_DATA, true).await();
status = result.getStatus().getStatusCode();
if (status == GamesStatusCodes.STATUS_OK) {
Snapshot snapshot = result.getSnapshot();
try {
mData = snapshot.getSnapshotContents().readFully();
if (mData != null && mData.length > Application.FileHeader.HEADER_SIZE) {
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onSyncProgress(CA.DATA_RECEIVED);
}
});
} else {
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onSyncFailed(CA.NO_DATA);
}
});
}
} catch(IOException e) {
e.printStackTrace();
}
}
else{
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onSyncFailed(CA.DATA_RECEIVED);
}});
}
}
catch(IllegalStateException e) {
Log.w("Pb:GameSyncService", "IllegalStateException during read()");
}
return status;
}
@Override
protected void onPostExecute(Integer status){
}
};
task.execute();
}
@Override
public void send(final byte[] data) {
if(data==null) {
return;
}
if(mGoogleApiClient.isConnected()) {
AsyncTask<Void, Void, Snapshots.OpenSnapshotResult> task =
new AsyncTask<Void, Void, Snapshots.OpenSnapshotResult>() {
@Override
protected Snapshots.OpenSnapshotResult doInBackground(Void... params) {
return Games.Snapshots.open(
mGoogleApiClient, SAVED_DATA, true).await();
}
@Override
protected void onPostExecute(Snapshots.OpenSnapshotResult result) {
try{
Snapshot toWrite = processSnapshotOpenResult(result, 0);
if(toWrite!=null) {
toWrite.getSnapshotContents().writeBytes(data);
SnapshotMetadataChange metadataChange = new SnapshotMetadataChange.Builder()
.setDescription(SAVED_DATA)
.build();
Games.Snapshots.commitAndClose(mGoogleApiClient, toWrite, metadataChange);
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onSyncProgress(CA.DATA_SENT);
}});
}
} catch(IllegalStateException e) {
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onSyncFailed(CA.DATA_SENT);
}
});
}
}
};
task.execute();
}
}
private Snapshot processSnapshotOpenResult(Snapshots.OpenSnapshotResult result, int retryCount) {
Snapshot mResolvedSnapshot;
retryCount++;
int status = result.getStatus().getStatusCode();
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();
mResolvedSnapshot = snapshot;
if (snapshot.getMetadata().getLastModifiedTimestamp() < conflictSnapshot
.getMetadata().getLastModifiedTimestamp()) {
mResolvedSnapshot = conflictSnapshot;
}
Snapshots.OpenSnapshotResult resolveResult = Games.Snapshots
.resolveConflict(mGoogleApiClient, result.getConflictId(),
mResolvedSnapshot).await();
if (retryCount < MAX_SNAPSHOT_RESOLVE_RETRIES) {
return processSnapshotOpenResult(resolveResult, retryCount);
}
}
return null;
}
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQ_RESOLUTION) {
if (resultCode == Activity.RESULT_OK) {
mGoogleApiClient.connect();
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onSyncProgress(CA.AUTH);
}});
}
else {
mHandler.post(new Runnable(){
@Override
public void run() {
mListener.onSyncFailed(CA.AUTH);
}});
}
return true;
}
return false;
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult result) {
mListener.onConnectionFailed(result);
}
@Override
public void onConnected(Bundle connectionHint) {
read();
}
@Override
public void onConnectionSuspended(int cause) {}
}