/*
* 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.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.common.api.ResultCallback;
import com.google.android.gms.drive.Drive;
import com.google.android.gms.drive.DriveApi;
import com.google.android.gms.drive.DriveContents;
import com.google.android.gms.drive.DriveFile;
import com.google.android.gms.drive.DriveFolder;
import com.google.android.gms.drive.DriveId;
import com.google.android.gms.drive.Metadata;
import com.google.android.gms.drive.MetadataChangeSet;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
class DriveSyncService extends SyncService implements
ConnectionCallbacks, OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private DriveId mDriveId;
private static final String SAVED_DATA="pb-drive-data";
private static final String LOG_TAG = "PB:DriveSyncService";
@Override
public SyncService initialize(Activity context) {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.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() {
mDriveId.asDriveFile()
.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null)
.setResultCallback(contentsResultCallback);
}
@Override
public void send(byte[] data) {
if(data !=null) {
mData = data;
if(mGoogleApiClient.isConnected()) {
if (mDriveId == null) {
Drive.DriveApi.newDriveContents(mGoogleApiClient)
.setResultCallback(driveContentsCallback);
} else {
mDriveId.asDriveFile()
.open(mGoogleApiClient, DriveFile.MODE_WRITE_ONLY, null)
.setResultCallback(contentsResultCallbackToWrite);
}
}
}
}
@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) {
Log.d(LOG_TAG, "Connected");
DriveFolder folder = Drive.DriveApi.getAppFolder(mGoogleApiClient);
folder.listChildren(mGoogleApiClient).setResultCallback(childrenRetrievedCallback);
}
@Override
public void onConnectionSuspended(int cause) { }
private ResultCallback<DriveApi.MetadataBufferResult> childrenRetrievedCallback = new
ResultCallback<DriveApi.MetadataBufferResult>() {
@Override
public void onResult(@NonNull DriveApi.MetadataBufferResult result) {
if(!result.getStatus().isSuccess()) {
Log.w(LOG_TAG, "Retrieving files received error");
}
else {
for(Metadata data : result.getMetadataBuffer()) {
String fileName = data.getTitle();
if(fileName != null && fileName.equalsIgnoreCase(SAVED_DATA)) {
mDriveId = data.getDriveId();
break;
}
}
}
if(mDriveId!=null) {
read();
}
else {
mListener.onSyncFailed(CA.NO_DATA);
}
result.release();
}
};
private ResultCallback<DriveApi.DriveContentsResult> contentsResultCallback = new
ResultCallback<DriveApi.DriveContentsResult>() {
@Override
public void onResult(@NonNull DriveApi.DriveContentsResult result) {
if(result.getStatus().isSuccess()) {
try {
FileDescriptor file = result.getDriveContents()
.getParcelFileDescriptor().getFileDescriptor();
FileInputStream fis = new FileInputStream(file);
mData = new byte[fis.available()];
int nbr = fis.read(mData);
fis.close();
if(nbr < mData.length) {
mListener.onSyncFailed(CA.DATA_RECEIVED);
return;
}
mListener.onSyncProgress(CA.DATA_RECEIVED);
}catch(IOException e) {
mListener.onSyncFailed(CA.DATA_RECEIVED);
Log.w(LOG_TAG, "Reading contents received IOException");
}
}
else {
mListener.onSyncFailed(CA.NO_DATA);
Log.w(LOG_TAG, "Retrieving contents received error");
}
}
};
private ResultCallback<DriveApi.DriveContentsResult> contentsResultCallbackToWrite = new
ResultCallback<DriveApi.DriveContentsResult>() {
@Override
public void onResult(@NonNull DriveApi.DriveContentsResult result) {
if(result.getStatus().isSuccess()) {
try {
FileDescriptor file = result.getDriveContents()
.getParcelFileDescriptor().getFileDescriptor();
FileOutputStream fos = new FileOutputStream(file);
fos.write(mData);
fos.close();
result.getDriveContents().commit(mGoogleApiClient, null);
mListener.onSyncProgress(CA.DATA_SENT);
}catch(IOException e) {
mListener.onSyncFailed(CA.DATA_SENT);
Log.w(LOG_TAG, "Writing contents received IOException");
}
}
else {
mListener.onSyncFailed(CA.DATA_SENT);
Log.w(LOG_TAG, "Writing contents received error");
}
}
};
private ResultCallback<DriveApi.DriveContentsResult> driveContentsCallback = new
ResultCallback<DriveApi.DriveContentsResult>() {
@Override
public void onResult(@NonNull DriveApi.DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
Log.w(LOG_TAG, "Error while trying to create new file contents");
return;
}
final DriveContents driveContents = result.getDriveContents();
new Thread() {
@Override
public void run() {
FileDescriptor file = driveContents
.getParcelFileDescriptor().getFileDescriptor();
FileOutputStream fos = new FileOutputStream(file);
try {
fos.write(mData);
fos.close();
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage());
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(SAVED_DATA)
.setMimeType("application/bin")
.build();
Drive.DriveApi.getAppFolder(mGoogleApiClient)
.createFile(mGoogleApiClient, changeSet, driveContents)
.setResultCallback(fileCreateCallback);
}
}.start();
}
};
private ResultCallback<DriveFolder.DriveFileResult> fileCreateCallback = new
ResultCallback<DriveFolder.DriveFileResult>() {
@Override
public void onResult(@NonNull DriveFolder.DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
Log.w(LOG_TAG, "Error while trying to create the file");
return;
}
mDriveId = result.getDriveFile().getDriveId();
}
};
}