mirror of https://github.com/tasks/tasks
Add option to copy backup files to Google Drive
parent
f471b47b12
commit
31fb7e4397
@ -0,0 +1,104 @@
|
|||||||
|
package org.tasks.drive;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
|
||||||
|
import com.google.api.client.http.InputStreamContent;
|
||||||
|
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||||
|
import com.google.api.client.json.GenericJson;
|
||||||
|
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||||
|
import com.google.api.client.util.ExponentialBackOff;
|
||||||
|
import com.google.api.services.drive.Drive;
|
||||||
|
import com.google.api.services.drive.DriveRequest;
|
||||||
|
import com.google.api.services.drive.DriveScopes;
|
||||||
|
import com.google.api.services.drive.model.File;
|
||||||
|
|
||||||
|
import org.tasks.BuildConfig;
|
||||||
|
import org.tasks.R;
|
||||||
|
import org.tasks.injection.ForApplication;
|
||||||
|
import org.tasks.preferences.Preferences;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class DriveInvoker {
|
||||||
|
|
||||||
|
private final Drive service;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DriveInvoker(@ForApplication Context context, Preferences preferences) {
|
||||||
|
this.context = context;
|
||||||
|
if (preferences.getBoolean(R.string.p_google_drive_backup, false)) {
|
||||||
|
GoogleAccountCredential credential =
|
||||||
|
GoogleAccountCredential.usingOAuth2(
|
||||||
|
context, Collections.singletonList(DriveScopes.DRIVE_FILE))
|
||||||
|
.setBackOff(new ExponentialBackOff.Builder().build())
|
||||||
|
.setSelectedAccountName(
|
||||||
|
preferences.getStringValue(R.string.p_google_drive_backup_account));
|
||||||
|
service =
|
||||||
|
new Drive.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
|
||||||
|
.setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME))
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
service = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<File> findFolder(String name) throws IOException {
|
||||||
|
String query = String.format("name='%s'", name);
|
||||||
|
return execute(service.files().list().setQ(query).setSpaces("drive")).getFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File createFolder(String name) throws IOException {
|
||||||
|
File folder = new File()
|
||||||
|
.setName(name)
|
||||||
|
.setMimeType("application/vnd.google-apps.folder");
|
||||||
|
|
||||||
|
return execute(service.files().create(folder).setFields("id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createFile(String mime, String parent, String name, Uri uri) throws IOException {
|
||||||
|
File metadata = new File()
|
||||||
|
.setParents(Collections.singletonList(parent))
|
||||||
|
.setMimeType(mime)
|
||||||
|
.setName(name);
|
||||||
|
InputStreamContent content =
|
||||||
|
new InputStreamContent(mime, context.getContentResolver().openInputStream(uri));
|
||||||
|
execute(service.files().create(metadata, content));
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized <T> T execute(DriveRequest<T> request) throws IOException {
|
||||||
|
String caller = getCaller();
|
||||||
|
Timber.d("%s request: %s", caller, request);
|
||||||
|
T response = request.execute();
|
||||||
|
Timber.d("%s response: %s", caller, prettyPrint(response));
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> Object prettyPrint(T object) throws IOException {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
if (object instanceof GenericJson) {
|
||||||
|
return ((GenericJson) object).toPrettyString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCaller() {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
try {
|
||||||
|
return Thread.currentThread().getStackTrace()[4].getMethodName();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Timber.e(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2012 Todoroo Inc
|
||||||
|
*
|
||||||
|
* <p>See the file "LICENSE" for the full license governing this code.
|
||||||
|
*/
|
||||||
|
package org.tasks.drive;
|
||||||
|
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.todoroo.andlib.utility.DialogUtilities;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.tasks.R;
|
||||||
|
import org.tasks.dialogs.DialogBuilder;
|
||||||
|
import org.tasks.gtasks.GoogleAccountManager;
|
||||||
|
import org.tasks.gtasks.PlayServices;
|
||||||
|
import org.tasks.injection.ActivityComponent;
|
||||||
|
import org.tasks.injection.InjectingAppCompatActivity;
|
||||||
|
import org.tasks.play.AuthResultHandler;
|
||||||
|
import org.tasks.preferences.Preferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This activity allows users to sign in or log in to Google Tasks through the Android account
|
||||||
|
* manager
|
||||||
|
*
|
||||||
|
* @author Sam Bosley
|
||||||
|
*/
|
||||||
|
public class DriveLoginActivity extends InjectingAppCompatActivity {
|
||||||
|
|
||||||
|
public static final int RC_REQUEST_OAUTH = 10987;
|
||||||
|
private static final int RC_CHOOSE_ACCOUNT = 10988;
|
||||||
|
@Inject DialogBuilder dialogBuilder;
|
||||||
|
@Inject GoogleAccountManager accountManager;
|
||||||
|
@Inject PlayServices playServices;
|
||||||
|
@Inject Preferences preferences;
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Intent chooseAccountIntent =
|
||||||
|
android.accounts.AccountManager.newChooseAccountIntent(
|
||||||
|
null, null, new String[] {"com.google"}, false, null, null, null, null);
|
||||||
|
startActivityForResult(chooseAccountIntent, RC_CHOOSE_ACCOUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void inject(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getAuthToken(String account) {
|
||||||
|
final ProgressDialog pd = dialogBuilder.newProgressDialog(R.string.gtasks_GLA_authenticating);
|
||||||
|
pd.show();
|
||||||
|
accountName = account;
|
||||||
|
getAuthToken(account, pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getAuthToken(String a, final ProgressDialog pd) {
|
||||||
|
playServices.getDriveAuthToken(
|
||||||
|
this,
|
||||||
|
a,
|
||||||
|
new AuthResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void authenticationSuccessful(String accountName) {
|
||||||
|
preferences.setString(R.string.p_google_drive_backup_account, accountName);
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
finish();
|
||||||
|
DialogUtilities.dismissDialog(DriveLoginActivity.this, pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void authenticationFailed(final String message) {
|
||||||
|
runOnUiThread(
|
||||||
|
() -> Toast.makeText(DriveLoginActivity.this, message, Toast.LENGTH_LONG).show());
|
||||||
|
DialogUtilities.dismissDialog(DriveLoginActivity.this, pd);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (requestCode == RC_CHOOSE_ACCOUNT && resultCode == RESULT_OK) {
|
||||||
|
String account = data.getStringExtra(android.accounts.AccountManager.KEY_ACCOUNT_NAME);
|
||||||
|
getAuthToken(account);
|
||||||
|
} else if (requestCode == RC_REQUEST_OAUTH && resultCode == RESULT_OK) {
|
||||||
|
final ProgressDialog pd = dialogBuilder.newProgressDialog(R.string.gtasks_GLA_authenticating);
|
||||||
|
pd.show();
|
||||||
|
getAuthToken(accountName, pd);
|
||||||
|
} else {
|
||||||
|
// User didn't give permission--cancel
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,54 +0,0 @@
|
|||||||
package org.tasks.gtasks;
|
|
||||||
|
|
||||||
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
|
|
||||||
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler;
|
|
||||||
import com.google.api.client.http.HttpRequest;
|
|
||||||
import com.google.api.client.http.HttpResponse;
|
|
||||||
import com.google.api.client.http.HttpResponseException;
|
|
||||||
import com.google.api.client.http.HttpStatusCodes;
|
|
||||||
import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
|
|
||||||
import com.google.api.client.util.BackOff;
|
|
||||||
import com.google.api.client.util.ExponentialBackOff;
|
|
||||||
import com.todoroo.astrid.gtasks.api.HttpNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class GoogleTasksUnsuccessfulResponseHandler implements HttpUnsuccessfulResponseHandler {
|
|
||||||
|
|
||||||
private static final BackOff BACKOFF = new ExponentialBackOff.Builder().build();
|
|
||||||
|
|
||||||
private final PlayServices playServices;
|
|
||||||
private final GoogleAccountCredential googleAccountCredential;
|
|
||||||
private final HttpBackOffUnsuccessfulResponseHandler backoffHandler =
|
|
||||||
new HttpBackOffUnsuccessfulResponseHandler(BACKOFF);
|
|
||||||
|
|
||||||
public GoogleTasksUnsuccessfulResponseHandler(
|
|
||||||
PlayServices playServices, GoogleAccountCredential googleAccountCredential) {
|
|
||||||
this.playServices = playServices;
|
|
||||||
this.googleAccountCredential = googleAccountCredential;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handleResponse(HttpRequest request, HttpResponse response, boolean supportsRetry)
|
|
||||||
throws IOException {
|
|
||||||
HttpResponseException httpResponseException = new HttpResponseException(response);
|
|
||||||
Timber.e(httpResponseException);
|
|
||||||
if (!supportsRetry) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int statusCode = response.getStatusCode();
|
|
||||||
if ((statusCode == HttpStatusCodes.STATUS_CODE_UNAUTHORIZED
|
|
||||||
|| statusCode == HttpStatusCodes.STATUS_CODE_FORBIDDEN)) {
|
|
||||||
boolean shouldRetry = playServices.clearToken(googleAccountCredential);
|
|
||||||
if (!shouldRetry) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (statusCode == 400) { // bad request
|
|
||||||
throw httpResponseException;
|
|
||||||
} else if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) {
|
|
||||||
throw new HttpNotFoundException(httpResponseException);
|
|
||||||
}
|
|
||||||
|
|
||||||
return backoffHandler.handleResponse(request, response, supportsRetry);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.tasks.play;
|
||||||
|
|
||||||
|
public interface AuthResultHandler {
|
||||||
|
void authenticationSuccessful(String accountName);
|
||||||
|
|
||||||
|
void authenticationFailed(String message);
|
||||||
|
}
|
Loading…
Reference in New Issue