Skip to content

Commit

Permalink
Refactors activities using google sheets and google drive (#1569)
Browse files Browse the repository at this point in the history
* Adds GoogleAccountManager class to handle google account selection and permission

Adds DriveHelper to interact with Google Drive

Adds SheetsHelper to interact with Google Sheets

* Overrides theme of account picker dialog

* Removes duplicate code in ServerPreferencesFragment

* Removes parameter from chooseAccount method and converts into a field

* Removes listener from constructor

* Breaks down methods into smaller ones in DriveHelper
Code refactor

* Moves all of the Drive calls into DriveService

* Adds dependencies for power mock
It is added to mock final methods which is not handled by Mockito

* Adds tests for GoogleAccountsManager

* Reverts the version of mockito-core to 2.8.47 to fix tests

* Adds tests for DriveHelper

* Adds sheets service

* Adds tests for sheets helper

* Fixes downloading media files from the media directory

Issue : Folder mime type was getting appended even when the folder name was null.
So fixed it by adding a condition while determining mime type

* updates the test condition for getFilesFromDrive()
if we don't provide any parent folder id or the folder name, the drive service shouldn't fetch any files from the google drive
  • Loading branch information
shobhitagarwal1612 authored and lognaturel committed Jan 11, 2018
1 parent 98624a8 commit bdecf25
Show file tree
Hide file tree
Showing 15 changed files with 1,521 additions and 814 deletions.
6 changes: 5 additions & 1 deletion collect_app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,14 @@ dependencies {

// Testing-only dependencies
testImplementation group: 'junit', name: 'junit', version: '4.12'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.12.0'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.8.47'
testImplementation group: 'org.robolectric', name: 'robolectric', version: '3.5.1'
testImplementation group: 'org.robolectric', name: 'shadows-multidex', version: '3.5.1'

// power mock (for mocking final methods which is not handled by mockito)
testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: '1.6.4'
testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: '1.7.3'

androidTestImplementation group: 'org.mockito', name: 'mockito-android', version: '2.11.0'
androidTestImplementation(group: 'com.android.support.test', name: 'runner', version: '1.0.1') {
exclude group: 'com.android.support', module: 'support-annotations'
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

package org.odk.collect.android.activities;

import android.Manifest;
import android.accounts.AccountManager;
import android.app.AlertDialog;
import android.app.Dialog;
Expand All @@ -38,49 +37,42 @@
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;

import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.drive.DriveScopes;

import org.odk.collect.android.R;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.dao.InstancesDao;
import org.odk.collect.android.utilities.gdrive.GoogleAccountsManager;
import org.odk.collect.android.listeners.InstanceUploaderListener;
import org.odk.collect.android.preferences.PreferenceKeys;
import org.odk.collect.android.provider.InstanceProviderAPI.InstanceColumns;
import org.odk.collect.android.tasks.InstanceGoogleSheetsUploader;
import org.odk.collect.android.utilities.ArrayUtils;
import org.odk.collect.android.utilities.ToastUtils;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
import timber.log.Timber;

import static org.odk.collect.android.tasks.InstanceGoogleSheetsUploader.REQUEST_ACCOUNT_PICKER;
import static org.odk.collect.android.utilities.gdrive.GoogleAccountsManager.REQUEST_ACCOUNT_PICKER;
import static org.odk.collect.android.tasks.InstanceGoogleSheetsUploader.REQUEST_AUTHORIZATION;
import static org.odk.collect.android.tasks.InstanceGoogleSheetsUploader.REQUEST_PERMISSION_GET_ACCOUNTS;


public class GoogleSheetsUploaderActivity extends AppCompatActivity implements InstanceUploaderListener,
EasyPermissions.PermissionCallbacks {
GoogleAccountsManager.GoogleAccountSelectionListener {
private static final int PROGRESS_DIALOG = 1;
private static final int GOOGLE_USER_DIALOG = 3;
private static final String ALERT_MSG = "alertmsg";
private static final String ALERT_SHOWING = "alertshowing";
private GoogleAccountCredential credential;
private ProgressDialog progressDialog;
private AlertDialog alertDialog;
private String alertMsg;
private boolean alertShowing;
private Long[] instancesToSend;
private InstanceGoogleSheetsUploader instanceGoogleSheetsUploader;

private GoogleAccountsManager accountsManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand Down Expand Up @@ -123,18 +115,16 @@ protected void onCreate(Bundle savedInstanceState) {
Timber.i("onCreate: Beginning upload of %d instances!", instancesToSend.length);
}

// Initialize credentials and service object.
credential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Collections.singleton(DriveScopes.DRIVE))
.setBackOff(new ExponentialBackOff());
accountsManager = new GoogleAccountsManager(this);
accountsManager.setListener(this);

getResultsFromApi();
}

private void runTask() {
instanceGoogleSheetsUploader = (InstanceGoogleSheetsUploader) getLastCustomNonConfigurationInstance();
if (instanceGoogleSheetsUploader == null) {
instanceGoogleSheetsUploader = new InstanceGoogleSheetsUploader(credential, this);
instanceGoogleSheetsUploader = new InstanceGoogleSheetsUploader(accountsManager);

// ensure we have a google account selected
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Expand Down Expand Up @@ -163,54 +153,22 @@ private void runTask() {
* appropriate.
*/
private void getResultsFromApi() {
if (credential.getSelectedAccountName() == null) {
chooseAccount();
if (!accountsManager.isGoogleAccountSelected()) {
accountsManager.chooseAccount();
} else if (!isDeviceOnline()) {
ToastUtils.showShortToast("No network connection available.");
} else {
runTask();
}
}

/*
* Attempts to set the account used with the API credentials. If an account
* name was previously saved it will use that one; otherwise an account
* picker dialog will be shown to the user. Note that the setting the
* account to use with the credentials object requires the app to have the
* GET_ACCOUNTS permission, which is requested here if it is not already
* present. The AfterPermissionGranted annotation indicates that this
* function will be rerun automatically whenever the GET_ACCOUNTS permission
* is granted.
*/
@AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS)
private void chooseAccount() {
if (EasyPermissions.hasPermissions(
this, Manifest.permission.GET_ACCOUNTS)) {
// ensure we have a google account selected
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String googleUsername = prefs.getString(
PreferenceKeys.KEY_SELECTED_GOOGLE_ACCOUNT, "");
if (!googleUsername.equals("")) {
credential.setSelectedAccountName(googleUsername);
getResultsFromApi();
} else {
// Start a dialog from which the user can choose an account
Intent intentChooseAccount = credential.newChooseAccountIntent();
intentChooseAccount.putExtra("overrideTheme", 1);
intentChooseAccount.putExtra("overrideCustomTheme", 0);
startActivityForResult(intentChooseAccount,REQUEST_ACCOUNT_PICKER);
}
} else {
// Request the GET_ACCOUNTS permission via a user dialog
EasyPermissions.requestPermissions(
this,
getString(R.string.request_permissions_google_account),
REQUEST_PERMISSION_GET_ACCOUNTS,
Manifest.permission.GET_ACCOUNTS);
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
accountsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
}


/**
* Called when an activity launched here (specifically, AccountPicker
* and authorization) exits, giving you the requestCode you started it with,
Expand All @@ -226,78 +184,28 @@ private void chooseAccount() {
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_CANCELED) {
Timber.d("AUTHORIZE_DRIVE_ACCESS failed, asking to choose new account:");
finish();
}

switch (requestCode) {
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null && data.getExtras() != null) {
String accountName =
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PreferenceKeys.KEY_SELECTED_GOOGLE_ACCOUNT, accountName);
editor.apply();
credential.setSelectedAccountName(accountName);
getResultsFromApi();
}
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
accountsManager.setSelectedAccountName(accountName);
}
break;
case REQUEST_AUTHORIZATION:
dismissDialog(PROGRESS_DIALOG);
if (resultCode == RESULT_OK) {
getResultsFromApi();
} else {
Timber.d("AUTHORIZE_DRIVE_ACCESS failed, asking to choose new account:");
finish();
}
break;
}
}

/**
* Respond to requests for permissions at runtime for API 23 and above.
*
* @param requestCode The request code passed in
* requestPermissions(android.app.Activity, String, int, String[])
* @param permissions The requested permissions. Never null.
* @param grantResults The grant results for the corresponding permissions
* which is either PERMISSION_GRANTED or PERMISSION_DENIED. Never null.
*/
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(
requestCode, permissions, grantResults, this);
}

/**
* Callback for when a permission is granted using the EasyPermissions
* library.
*
* @param requestCode The request code associated with the requested
* permission
* @param list The requested permission list. Never null.
*/
@Override
public void onPermissionsGranted(int requestCode, List<String> list) {
// Do nothing.
}

/**
* Callback for when a permission is denied using the EasyPermissions
* library.
*
* @param requestCode The request code associated with the requested
* permission
* @param list The requested permission list. Never null.
*/
@Override
public void onPermissionsDenied(int requestCode, List<String> list) {
// Do nothing.
}

/**
* Checks whether the device currently has a network connection.
*
Expand Down Expand Up @@ -384,7 +292,7 @@ public void uploadingComplete(HashMap<String, String> result) {
if (keys.size() == 0) {
if (instanceGoogleSheetsUploader.isAuthFailed()) {
message.append(getString(R.string.google_auth_io_exception_msg));
instanceGoogleSheetsUploader.setAuthFailed(false);
instanceGoogleSheetsUploader.setAuthFailed();
} else {
message.append(getString(R.string.no_forms_uploaded));
}
Expand Down Expand Up @@ -416,7 +324,7 @@ public void uploadingComplete(HashMap<String, String> result) {
} else {
if (instanceGoogleSheetsUploader.isAuthFailed()) {
message.append(getString(R.string.google_auth_io_exception_msg));
instanceGoogleSheetsUploader.setAuthFailed(false);
instanceGoogleSheetsUploader.setAuthFailed();
} else {
message.append(getString(R.string.no_forms_uploaded));
}
Expand Down Expand Up @@ -513,4 +421,9 @@ public void onClick(DialogInterface dialog, int i) {
public void authRequest(Uri url, HashMap<String, String> doneSoFar) {
// in interface, but not needed
}

@Override
public void onGoogleAccountSelected(String accountName) {
getResultsFromApi();
}
}
Loading

0 comments on commit bdecf25

Please sign in to comment.