org.odk.collect.android.activities.GoogleDriveActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.odk.collect.android.activities.GoogleDriveActivity.java

Source

/*
 * Copyright (C) 2017 Nafundi
 *
 * 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.
 */

/**
 * @author Carl Hartung (chartung@nafundi.com)
 */

package org.odk.collect.android.activities;

import android.Manifest;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewConfigurationCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
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.Drive.Files;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.FileList;

import org.odk.collect.android.R;
import org.odk.collect.android.adapters.FileArrayAdapter;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.listeners.GoogleDriveFormDownloadListener;
import org.odk.collect.android.listeners.TaskListener;
import org.odk.collect.android.logic.DriveListItem;
import org.odk.collect.android.preferences.PreferenceKeys;
import org.odk.collect.android.utilities.ToastUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

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

import static org.odk.collect.android.tasks.GoogleSheetsAbstractUploader.REQUEST_ACCOUNT_PICKER;
import static org.odk.collect.android.tasks.GoogleSheetsAbstractUploader.REQUEST_AUTHORIZATION;
import static org.odk.collect.android.tasks.GoogleSheetsAbstractUploader.REQUEST_PERMISSION_GET_ACCOUNTS;

public class GoogleDriveActivity extends AppCompatActivity
        implements GoogleApiClient.OnConnectionFailedListener, View.OnClickListener, TaskListener,
        GoogleDriveFormDownloadListener, EasyPermissions.PermissionCallbacks, AdapterView.OnItemClickListener {

    private static final int PROGRESS_DIALOG = 1;
    private static final int GOOGLE_USER_DIALOG = 3;
    private static final int RESOLVE_CONNECTION_REQUEST_CODE = 5555;
    private static final int COMPLETE_AUTHORIZATION_REQUEST_CODE = 4322;
    private static final String MY_DRIVE_KEY = "mydrive";
    private static final String PATH_KEY = "path";
    private static final String DRIVE_ITEMS_KEY = "drive_list";
    private static final String PARENT_KEY = "parent";
    private static final String ALERT_MSG_KEY = "alertmsg";
    private static final String ALERT_SHOWING_KEY = "alertshowing";
    private static final String ROOT_KEY = "root";
    private static final String FILE_LIST_KEY = "fileList";
    private static final String PARENT_ID_KEY = "parentId";
    private static final String CURRENT_ID_KEY = "currentDir";
    protected GoogleAccountCredential credential;
    private Button rootButton;
    private Button backButton;
    private Button downloadButton;
    private ImageButton searchButton;
    private EditText searchText;
    private Stack<String> currentPath = new Stack<>();
    private Stack<String> folderIdStack = new Stack<>();
    private String alertMsg;
    private boolean alertShowing;
    private String rootId = null;
    private boolean myDrive;
    private FileArrayAdapter adapter;
    private RetrieveDriveFileContentsAsyncTask retrieveDriveFileContentsAsyncTask;
    private GetFileTask getFileTask;
    private String parentId;
    private ArrayList<DriveListItem> toDownload;
    private Drive driveService;
    private ListView listView;
    private TextView emptyView;

    private void initToolbar() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        boolean hasHardwareMenu = ViewConfigurationCompat
                .hasPermanentMenuKey(ViewConfiguration.get(getApplicationContext()));

        if (hasHardwareMenu) {
            toolbar.setTitle(getString(R.string.google_drive));
        } else {
            setTitle(getString(R.string.google_drive));
        }
        if (!hasHardwareMenu) {
            setSupportActionBar(toolbar);
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

        setProgressBarVisibility(true);
        setContentView(R.layout.drive_layout);
        listView = (ListView) findViewById(android.R.id.list);
        listView.setOnItemClickListener(this);
        emptyView = (TextView) findViewById(android.R.id.empty);

        initToolbar();

        parentId = null;
        alertShowing = false;
        toDownload = new ArrayList<>();

        if (savedInstanceState != null && savedInstanceState.containsKey(MY_DRIVE_KEY)) {
            // recover state on rotate
            myDrive = savedInstanceState.getBoolean(MY_DRIVE_KEY);
            String[] patharray = savedInstanceState.getStringArray(PATH_KEY);
            currentPath = buildPath(patharray);

            parentId = savedInstanceState.getString(PARENT_KEY);
            alertMsg = savedInstanceState.getString(ALERT_MSG_KEY);
            alertShowing = savedInstanceState.getBoolean(ALERT_SHOWING_KEY);

            ArrayList<DriveListItem> dl = savedInstanceState.getParcelableArrayList(DRIVE_ITEMS_KEY);
            adapter = new FileArrayAdapter(GoogleDriveActivity.this, R.layout.two_item_image, dl);
            listView.setAdapter(adapter);
            adapter.setEnabled(true);
        } else {
            // new
            myDrive = false;

            if (!isDeviceOnline()) {
                createAlertDialog(getString(R.string.no_connection));
            }
        }

        // restore any task state
        if (getLastCustomNonConfigurationInstance() instanceof RetrieveDriveFileContentsAsyncTask) {
            retrieveDriveFileContentsAsyncTask = (RetrieveDriveFileContentsAsyncTask) getLastNonConfigurationInstance();
            setProgressBarIndeterminateVisibility(true);
        } else {
            getFileTask = (GetFileTask) getLastNonConfigurationInstance();
            if (getFileTask != null) {
                getFileTask.setGoogleDriveFormDownloadListener(this);
            }
        }
        if (getFileTask != null && getFileTask.getStatus() == AsyncTask.Status.FINISHED) {
            try {
                dismissDialog(PROGRESS_DIALOG);
            } catch (Exception e) {
                Timber.i("Exception was thrown while dismissing a dialog.");
            }
        }
        if (alertShowing) {
            try {
                dismissDialog(PROGRESS_DIALOG);
            } catch (Exception e) {
                // don't care...
                Timber.i("Exception was thrown while dismissing a dialog.");
            }
            createAlertDialog(alertMsg);
        }

        rootButton = (Button) findViewById(R.id.root_button);
        if (myDrive) {
            rootButton.setText(getString(R.string.go_shared));
        } else {
            rootButton.setText(getString(R.string.go_drive));
        }
        rootButton.setOnClickListener(this);

        backButton = (Button) findViewById(R.id.back_button);
        backButton.setEnabled(parentId != null);
        backButton.setOnClickListener(this);

        downloadButton = (Button) findViewById(R.id.download_button);
        downloadButton.setOnClickListener(this);

        searchText = (EditText) findViewById(R.id.search_text);
        searchText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                    executeSearch();
                    return true;
                }
                return false;
            }
        });
        searchButton = (ImageButton) findViewById(R.id.search_button);
        searchButton.setOnClickListener(this);

        // Initialize credentials and service object.
        credential = GoogleAccountCredential
                .usingOAuth2(getApplicationContext(), Collections.singletonList(DriveScopes.DRIVE))
                .setBackOff(new ExponentialBackOff());

        HttpTransport transport = AndroidHttp.newCompatibleTransport();
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        driveService = new com.google.api.services.drive.Drive.Builder(transport, jsonFactory, credential)
                .setApplicationName("ODK-Collect").build();

        getResultsFromApi();
    }

    /*
     * Attempt to call the API, after verifying that all the preconditions are
     * satisfied. The preconditions are: Google Play Services installed, an
     * account was selected and the device currently has online access. If any
     * of the preconditions are not satisfied, the app will prompt the user as
     * appropriate.
     *
     * Google Drive API V3
     * Please refer to the below link for reference:
     * https://developers.google.com/drive/v3/web/quickstart/android
     */
    private void getResultsFromApi() {
        if (credential.getSelectedAccountName() == null) {
            chooseAccount();
        } else if (!isDeviceOnline()) {
            ToastUtils.showShortToast("No network connection available.");
        } else {
            if (isDeviceOnline()) {
                toDownload.clear();
                rootButton.setEnabled(false);
                searchButton.setEnabled(false);
                backButton.setEnabled(false);
                downloadButton.setEnabled(false);
                listFiles(ROOT_KEY);
                myDrive = !myDrive;
            } else {
                createAlertDialog(getString(R.string.no_connection));
            }
            currentPath.clear();
            currentPath.add((String) rootButton.getText());
        }
    }

    /**
     * Checks whether the device currently has a network connection.
     *
     * @return true if the device has a network connection, false otherwise.
     */
    private boolean isDeviceOnline() {
        ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        return (networkInfo != null && networkInfo.isConnected());
    }

    /**
     * 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.
    }

    /*
     * 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
                startActivityForResult(credential.newChooseAccountIntent(), 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);
        }
    }

    void executeSearch() {
        String searchString = searchText.getText().toString();
        if (searchString.length() > 0) {
            toDownload.clear();
            searchButton.setEnabled(false);
            backButton.setEnabled(false);
            downloadButton.setEnabled(false);
            rootButton.setEnabled(false);
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(searchText.getWindowToken(), 0);
            currentPath.clear();
            listFiles(ROOT_KEY, searchText.getText().toString());
        } else {
            ToastUtils.showShortToast(R.string.no_blank_search);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putBoolean(MY_DRIVE_KEY, myDrive);
        ArrayList<DriveListItem> dl = new ArrayList<DriveListItem>();
        for (int i = 0; i < listView.getCount(); i++) {
            dl.add((DriveListItem) listView.getItemAtPosition(i));
        }
        outState.putParcelableArrayList(DRIVE_ITEMS_KEY, dl);
        outState.putStringArray(PATH_KEY, currentPath.toArray(new String[currentPath.size()]));
        outState.putString(PARENT_KEY, parentId);
        outState.putBoolean(ALERT_SHOWING_KEY, alertShowing);
        outState.putString(ALERT_MSG_KEY, alertMsg);
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        adapter.setEnabled(false);
        DriveListItem o = adapter.getItem(position);
        if (o != null && o.getType() == DriveListItem.DIR) {
            if (isDeviceOnline()) {
                toDownload.clear();
                searchText.setText(null);
                listFiles(o.getDriveId());
                folderIdStack.push(o.getDriveId());
                currentPath.push(o.getName());
            } else {
                adapter.setEnabled(true);
                createAlertDialog(getString(R.string.no_connection));
            }
        } else {
            adapter.setEnabled(true);
            // file clicked, download the file, mark checkbox.
            CheckBox cb = (CheckBox) view.findViewById(R.id.checkbox);
            cb.setChecked(!cb.isChecked());

            if (toDownload.contains(o) && !cb.isChecked()) {
                toDownload.remove(o);
            } else {
                toDownload.add(o);
            }
            downloadButton.setEnabled(toDownload.size() > 0);
        }
    }

    private void getFiles() {
        StringBuilder messageBuilder = new StringBuilder();
        for (int i = 0; i < toDownload.size(); i++) {
            DriveListItem o = toDownload.get(i);
            messageBuilder.append(o.getName());
            if (i != toDownload.size() - 1) {
                messageBuilder.append(", ");
            }
        }

        alertMsg = getString(R.string.drive_get_file, messageBuilder.toString());
        showDialog(PROGRESS_DIALOG);

        getFileTask = new GetFileTask();
        getFileTask.setGoogleDriveFormDownloadListener(this);
        getFileTask.execute(toDownload);
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        if (connectionResult.hasResolution()) {
            try {
                connectionResult.startResolutionForResult(this, RESOLVE_CONNECTION_REQUEST_CODE);
            } catch (IntentSender.SendIntentException e) {
                // Unable to resolve, message user appropriately
            }
        } else {
            GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case PROGRESS_DIALOG:
            Collect.getInstance().getActivityLogger().logAction(this, "onCreateDialog.PROGRESS_DIALOG", "show");

            ProgressDialog progressDialog = new ProgressDialog(this);
            DialogInterface.OnClickListener loadingButtonListener = new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Collect.getInstance().getActivityLogger().logAction(this, "onCreateDialog.PROGRESS_DIALOG",
                            "cancel");
                    dialog.dismiss();
                    getFileTask.cancel(true);
                    getFileTask.setGoogleDriveFormDownloadListener(null);
                }
            };
            progressDialog.setTitle(getString(R.string.downloading_data));
            progressDialog.setMessage(alertMsg);
            progressDialog.setIndeterminate(true);
            progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            progressDialog.setCancelable(false);
            progressDialog.setButton(getString(R.string.cancel), loadingButtonListener);
            return progressDialog;
        case GOOGLE_USER_DIALOG:
            AlertDialog.Builder gudBuilder = new AlertDialog.Builder(this);

            gudBuilder.setTitle(getString(R.string.no_google_account));
            gudBuilder.setMessage(getString(R.string.google_set_account));
            gudBuilder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                }
            });
            gudBuilder.setCancelable(false);
            return gudBuilder.create();
        }
        return null;
    }

    private void createAlertDialog(String message) {
        Collect.getInstance().getActivityLogger().logAction(this, "createAlertDialog", "show");

        AlertDialog alertDialog = new AlertDialog.Builder(this).create();
        alertDialog.setTitle(getString(R.string.download_forms_result));
        alertDialog.setMessage(message);
        DialogInterface.OnClickListener quitListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int i) {
                switch (i) {
                case DialogInterface.BUTTON1: // ok
                    Collect.getInstance().getActivityLogger().logAction(this, "createAlertDialog", "OK");
                    alertShowing = false;
                    finish();
                    break;
                }
            }
        };
        alertDialog.setCancelable(false);
        alertDialog.setButton(getString(R.string.ok), quitListener);
        alertDialog.setIcon(android.R.drawable.ic_dialog_info);
        alertShowing = true;
        alertMsg = message;
        alertDialog.show();
    }

    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        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();
                }
            }
            break;
        case REQUEST_AUTHORIZATION:
            if (resultCode == RESULT_OK) {
                getResultsFromApi();
            }
            break;

        case COMPLETE_AUTHORIZATION_REQUEST_CODE:
            if (resultCode == Activity.RESULT_OK) {
                if (isDeviceOnline()) {
                    listFiles(ROOT_KEY);
                } else {
                    createAlertDialog(getString(R.string.no_connection));
                }
            } else {
                // User denied access, show him the account chooser again
            }
            break;
        }
        if (resultCode == RESULT_CANCELED) {
            finish();
        }

    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        if (retrieveDriveFileContentsAsyncTask != null
                && retrieveDriveFileContentsAsyncTask.getStatus() == AsyncTask.Status.RUNNING) {
            return retrieveDriveFileContentsAsyncTask;
        }
        return getFileTask;
    }

    private Stack<String> buildPath(String[] path) {
        Stack<String> pathStack = new Stack<String>();
        for (int i = 0; i < path.length; i++) {
            pathStack.push(path[i]);
        }
        return pathStack;
    }

    @Override
    public void taskComplete(HashMap<String, Object> results) {
        rootButton.setEnabled(true);
        downloadButton.setEnabled(toDownload.size() > 0);
        searchButton.setEnabled(true);
        setProgressBarIndeterminateVisibility(false);

        if (results == null) {
            // if results was null, then got a google exception
            // requiring the user to authorize
            return;
        }

        String parentId = (String) results.get(PARENT_ID_KEY);
        String currentDir = (String) results.get(CURRENT_ID_KEY);

        if (myDrive) {
            rootButton.setText(getString(R.string.go_shared));
        } else {
            rootButton.setText(getString(R.string.go_drive));
        }

        if (folderIdStack.empty()) {
            backButton.setEnabled(false);
        } else {
            backButton.setEnabled(true);
        }
        this.parentId = parentId;

        if (currentPath.empty()) {
            if (myDrive) {
                currentPath.add(getString(R.string.go_drive));
            } else {
                currentPath.add(getString(R.string.go_shared));
            }
        }

    }

    @Override
    protected void onPause() {
        if (retrieveDriveFileContentsAsyncTask != null) {
            retrieveDriveFileContentsAsyncTask.setTaskListener(null);
        }
        if (getFileTask != null) {
            getFileTask.setGoogleDriveFormDownloadListener(null);
        }
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (retrieveDriveFileContentsAsyncTask != null) {
            retrieveDriveFileContentsAsyncTask.setTaskListener(this);
        }
        if (getFileTask != null) {
            getFileTask.setGoogleDriveFormDownloadListener(this);
        }
    }

    @Override
    public void formDownloadComplete(HashMap<String, Object> results) {
        try {
            dismissDialog(PROGRESS_DIALOG);
        } catch (Exception e) {
            // tried to close a dialog not open. don't care.
            Timber.i("Exception thrown due to closing a dialog that was not open");
        }

        StringBuilder sb = new StringBuilder();
        Iterator<String> it = results.keySet().iterator();

        while (it.hasNext()) {
            String id = it.next();
            sb.append(id + " :: " + results.get(id) + "\n\n");
        }
        if (sb.length() > 1) {
            sb.setLength(sb.length() - 1);
        }
        createAlertDialog(sb.toString());

    }

    @Override
    protected void onStart() {
        super.onStart();
        Collect.getInstance().getActivityLogger().logOnStart(this);
    }

    @Override
    protected void onStop() {
        Collect.getInstance().getActivityLogger().logOnStop(this);
        super.onStop();
    }

    public void listFiles(String dir, String query) {
        setProgressBarIndeterminateVisibility(true);
        adapter = null;
        retrieveDriveFileContentsAsyncTask = new RetrieveDriveFileContentsAsyncTask();
        retrieveDriveFileContentsAsyncTask.setTaskListener(GoogleDriveActivity.this);
        if (query != null) {
            retrieveDriveFileContentsAsyncTask.execute(dir, query);
        } else {
            retrieveDriveFileContentsAsyncTask.execute(dir);

        }
    }

    public void listFiles(String dir) {
        listFiles(dir, null);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.root_button:
            getResultsFromApi();
            break;

        case R.id.back_button:
            backButton.setEnabled(false);
            rootButton.setEnabled(false);
            downloadButton.setEnabled(false);
            toDownload.clear();
            if (isDeviceOnline()) {
                if (folderIdStack.empty()) {
                    parentId = ROOT_KEY;
                } else {
                    parentId = folderIdStack.pop();
                }
                listFiles(parentId);
                currentPath.pop();
                // }
            } else {
                createAlertDialog(getString(R.string.no_connection));
            }
            break;

        case R.id.download_button:
            getFiles();
            break;

        case R.id.search_button:
            executeSearch();
            break;
        }
    }

    private class RetrieveDriveFileContentsAsyncTask
            extends AsyncTask<String, HashMap<String, Object>, HashMap<String, Object>> {
        private TaskListener listener;

        void setTaskListener(TaskListener tl) {
            listener = tl;
        }

        @Override
        protected HashMap<String, Object> doInBackground(String... params) {
            String currentDir = params[0];
            String query;

            if (rootId == null) {
                try {
                    rootId = driveService.files().get("root").setFields("id").execute().getId();
                } catch (UserRecoverableAuthIOException e) {
                    startActivityForResult(e.getIntent(), COMPLETE_AUTHORIZATION_REQUEST_CODE);
                    return null;
                } catch (IOException e) {
                    Timber.e(e);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            createAlertDialog(getString(R.string.google_auth_io_exception_msg));
                        }
                    });
                }
                if (rootId == null) {
                    Timber.e("Unable to fetch drive contents");
                    return null;
                }
            }

            Files.List request = null;
            String parentId = "";
            try {
                if (folderIdStack.empty()) {
                    parentId = rootId;
                } else {
                    parentId = folderIdStack.peek();
                }
                query = "'" + parentId + "' in parents";

                if (params.length == 2) {
                    // TODO: *.xml or .xml or xml
                    // then search mimetype
                    query = "fullText contains '" + params[1] + "' and trashed=false";
                }

                // SharedWithMe, and root:
                if (!myDrive && currentDir.equals(ROOT_KEY)) {
                    query = "sharedWithMe=true";
                    folderIdStack.removeAllElements();
                }

                query += " and trashed=false";
                request = driveService.files().list().setQ(query);
            } catch (IOException e) {
                Timber.e(e);
            }
            request.setFields("nextPageToken, files(modifiedTime, id, name, mimeType)");

            HashMap<String, Object> results = new HashMap<>();
            results.put(PARENT_ID_KEY, parentId);
            results.put(CURRENT_ID_KEY, currentDir);
            do {
                try {
                    FileList fa = request.execute();
                    List<com.google.api.services.drive.model.File> driveFileListPage = new ArrayList<>();

                    driveFileListPage.addAll(fa.getFiles());
                    request.setPageToken(fa.getNextPageToken());
                    HashMap<String, Object> nextPage = new HashMap<>();
                    nextPage.put(PARENT_ID_KEY, parentId);
                    nextPage.put(CURRENT_ID_KEY, currentDir);
                    nextPage.put(FILE_LIST_KEY, driveFileListPage);
                    publishProgress(nextPage);
                } catch (IOException e) {
                    Timber.e(e, "Exception thrown while accessing the file list");
                }
            } while (request.getPageToken() != null && request.getPageToken().length() > 0);

            return results;

        }

        @Override
        protected void onPostExecute(HashMap<String, Object> results) {
            super.onPostExecute(results);
            if (results == null) {
                // was an auth request
                return;
            }
            if (listener != null) {
                listener.taskComplete(results);
            }
        }

        @SafeVarargs
        @Override
        protected final void onProgressUpdate(HashMap<String, Object>... values) {
            super.onProgressUpdate(values);
            List<com.google.api.services.drive.model.File> fileList = (List<com.google.api.services.drive.model.File>) values[0]
                    .get(FILE_LIST_KEY);
            String parentId = (String) values[0].get(PARENT_ID_KEY);
            String currentDir = (String) values[0].get(CURRENT_ID_KEY);

            List<DriveListItem> dirs = new ArrayList<>();
            List<DriveListItem> forms = new ArrayList<>();

            for (com.google.api.services.drive.model.File f : fileList) {
                String type = f.getMimeType();
                switch (type) {
                case "application/xml":
                case "text/xml":
                case "application/xhtml":
                case "text/xhtml":
                case "application/xhtml+xml":
                    forms.add(new DriveListItem(f.getName(), "", f.getModifiedTime(), "", "", DriveListItem.FILE,
                            f.getId(), currentDir));
                    break;
                case "application/vnd.google-apps.folder":
                    dirs.add(new DriveListItem(f.getName(), "", f.getModifiedTime(), "", "", DriveListItem.DIR,
                            f.getId(), parentId));
                    break;
                default:
                    // skip the rest of the files
                    break;
                }
            }
            Collections.sort(dirs);
            Collections.sort(forms);
            dirs.addAll(forms);

            if (dirs.size() == 0) {
                emptyView.setVisibility(View.VISIBLE);
            } else {
                emptyView.setVisibility(View.INVISIBLE);
            }

            if (adapter == null) {
                adapter = new FileArrayAdapter(GoogleDriveActivity.this, R.layout.two_item_image, dirs);
                listView.setAdapter(adapter);
            } else {
                for (DriveListItem d : dirs) {
                    adapter.add(d);
                }
                adapter.addAll(dirs);
            }
            adapter.sort(new Comparator<DriveListItem>() {
                @Override
                public int compare(DriveListItem lhs, DriveListItem rhs) {
                    if (lhs.getType() != rhs.getType()) {
                        if (lhs.getType() == DriveListItem.DIR) {
                            return -1;
                        } else {
                            return 1;
                        }
                    } else {
                        return lhs.getName().compareTo(rhs.getName());
                    }
                }
            });
            adapter.notifyDataSetChanged();
        }
    }

    private class GetFileTask extends AsyncTask<ArrayList<DriveListItem>, Boolean, HashMap<String, Object>> {

        private GoogleDriveFormDownloadListener listener;

        void setGoogleDriveFormDownloadListener(GoogleDriveFormDownloadListener gl) {
            listener = gl;
        }

        @SafeVarargs
        @Override
        protected final HashMap<String, Object> doInBackground(ArrayList<DriveListItem>... params) {
            HashMap<String, Object> results = new HashMap<>();

            ArrayList<DriveListItem> fileItems = params[0];

            for (int k = 0; k < fileItems.size(); k++) {
                DriveListItem fileItem = fileItems.get(k);

                FileOutputStream fileOutputStream = null;
                try {
                    com.google.api.services.drive.model.File df = driveService.files().get(fileItem.getDriveId())
                            .execute();

                    fileOutputStream = new FileOutputStream(
                            new File(Collect.FORMS_PATH + File.separator + fileItem.getName()));
                    downloadFile(df).writeTo(fileOutputStream);
                } catch (Exception e) {
                    Timber.e(e);
                    results.put(fileItem.getName(), e.getMessage());
                    return results;
                } finally {
                    try {
                        if (fileOutputStream != null) {
                            fileOutputStream.close();
                        }
                    } catch (IOException e) {
                        Timber.e(e, "Unable to close the file output stream");
                    }
                }

                results.put(fileItem.getName(), Collect.getInstance().getString(R.string.success));
            }
            return results;

        }

        private ByteArrayOutputStream downloadFile(com.google.api.services.drive.model.File file)
                throws IOException {

            String fileId = file.getId();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            driveService.files().get(fileId).executeMediaAndDownloadTo(outputStream);
            return outputStream;
        }

        @Override
        protected void onPostExecute(HashMap<String, Object> results) {
            listener.formDownloadComplete(results);
        }
    }
}