diff --git a/app/src/main/java/com/orgzly/android/repos/GoogleDriveClient.java b/app/src/main/java/com/orgzly/android/repos/GoogleDriveClient.java index afa4674e4..7ea30fd6f 100644 --- a/app/src/main/java/com/orgzly/android/repos/GoogleDriveClient.java +++ b/app/src/main/java/com/orgzly/android/repos/GoogleDriveClient.java @@ -1,12 +1,14 @@ package com.orgzly.android.repos; -import android.app.Activity; -import android.content.Intent; import android.content.Context; import android.net.Uri; +import android.os.Build; + +import androidx.annotation.RequiresApi; import com.google.android.gms.auth.api.signin.GoogleSignIn; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; 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.http.FileContent; @@ -20,20 +22,16 @@ import java.io.BufferedOutputStream; // import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import android.util.Log; - public class GoogleDriveClient { private static final String TAG = GoogleDriveClient.class.getName(); @@ -48,10 +46,10 @@ public class GoogleDriveClient { private final Context mContext; private final long repoId; - private Drive mDriveService; + private static Drive mDriveService; - private Map pathIds; - { + private static Map pathIds; + static { pathIds = new HashMap<>(); pathIds.put("My Drive", "root"); pathIds.put("", "root"); @@ -61,30 +59,61 @@ public GoogleDriveClient(Context context, long id) { mContext = context; repoId = id; - } - public void setService(Drive driveService) { - mDriveService = driveService; + if (isLinked()) setDriveService(); } public boolean isLinked() { // Check for existing Google Sign In account, if the user is already signed in // the GoogleSignInAccount will be non-null. - return GoogleSignIn.getLastSignedInAccount(mContext) != null; + return getGoogleAccount() != null; + } + + private GoogleSignInAccount getGoogleAccount() { + return GoogleSignIn.getLastSignedInAccount(mContext); + } + + public Drive getDriveService(GoogleSignInAccount googleAccount) { + GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2( + mContext, Collections.singleton(DriveScopes.DRIVE)); + credential.setSelectedAccount(googleAccount.getAccount()); + return new Drive.Builder( + AndroidHttp.newCompatibleTransport(), + new GsonFactory(), + credential) + .setApplicationName("Orgzly") + .build(); + } + + public Drive getDriveService() { + if (mDriveService != null) return mDriveService; + return getDriveService(getGoogleAccount()); + } + + public void setDriveService() { + if (mDriveService == null) mDriveService = getDriveService(); + } + + public void setDriveService(GoogleSignInAccount googleAccount) { + mDriveService = getDriveService(googleAccount); } private void linkedOrThrow() throws IOException { if (! isLinked()) { throw new IOException(NOT_LINKED); + } else { + setDriveService(); } } + @RequiresApi(api = Build.VERSION_CODES.N) private String findId(String path) throws IOException { if (pathIds.containsKey(path)) { return pathIds.get(path); } String[] parts = path.split("/"); + parts = Arrays.stream(parts).filter(s -> !s.isEmpty()).toArray(String[]::new); String[] ids = new String[parts.length+1]; ids[0] = "root"; @@ -95,23 +124,21 @@ private String findId(String path) throws IOException { .setSpaces("drive") .setFields("files(id, name, mimeType)") .execute(); - List files = result.getFiles(); + List files = result.getFiles(); if (!files.isEmpty()) { File file = (File) files.get(0); ids[i+1] = file.getId(); } } - for (int i = 0; i < ids.length; ++i) { - if (ids[i] == null) { - break; - } - pathIds.put(path, ids[i]); + if (ids[ids.length-1] != null) { + pathIds.put(path, ids[ids.length-1]); } return ids[ids.length-1]; // Returns null if no file is found } + @RequiresApi(api = Build.VERSION_CODES.N) public List getBooks(Uri repoUri) throws IOException { linkedOrThrow(); @@ -129,24 +156,23 @@ public List getBooks(Uri repoUri) throws IOException { try { - String folderId = findId(path); - + String pathId = findId(path); - if (folderId != null) { + if (pathId != null) { - File folder = mDriveService.files().get(folderId) - .setFields("id, mimeType") + File folder = mDriveService.files().get(pathId) + .setFields("id, name, mimeType, version, modifiedTime") .execute(); - if (folder.getMimeType() == "application/vnd.google-apps.folder") { + if (folder.getMimeType().equals("application/vnd.google-apps.folder")) { String pageToken = null; do { FileList result = mDriveService.files().list() .setQ(String.format("mimeType != 'application/vnd.google-apps.folder' " + - "and '%s' in parents and trashed = false", folderId)) + "and '%s' in parents and trashed = false", pathId)) .setSpaces("drive") - .setFields("nextPageToken, files(id, name, mimeType)") + .setFields("nextPageToken, files(id, name, mimeType, version, modifiedTime)") .setPageToken(pageToken) .execute(); for (File file : result.getFiles()) { @@ -170,7 +196,7 @@ public List getBooks(Uri repoUri) throws IOException { throw new IOException("Not a directory: " + repoUri); } } else { - throw new IOException("Not a directory: " + repoUri); + throw new IOException("Path not found: " + repoUri); } } catch (Exception e) { @@ -187,6 +213,7 @@ public List getBooks(Uri repoUri) throws IOException { /** * Download file from Google Drive and store it to a local file. */ + @RequiresApi(api = Build.VERSION_CODES.N) public VersionedRook download(Uri repoUri, String fileName, java.io.File localFile) throws IOException { linkedOrThrow(); @@ -200,10 +227,10 @@ public VersionedRook download(Uri repoUri, String fileName, java.io.File localFi if (fileId != null) { File file = mDriveService.files().get(fileId) - .setFields("id, mimeType, version, modifiedDate") + .setFields("id, mimeType, version, modifiedTime") .execute(); - if (file.getMimeType() != "application/vnd.google-apps.folder") { + if (!file.getMimeType().equals("application/vnd.google-apps.folder")) { String rev = Long.toString(file.getVersion()); long mtime = file.getModifiedTime().getValue(); @@ -231,6 +258,7 @@ public VersionedRook download(Uri repoUri, String fileName, java.io.File localFi /** Upload file to Google Drive. */ + @RequiresApi(api = Build.VERSION_CODES.N) public VersionedRook upload(java.io.File file, Uri repoUri, String fileName) throws IOException { linkedOrThrow(); @@ -267,7 +295,7 @@ public VersionedRook upload(java.io.File file, Uri repoUri, String fileName) thr pathIds.put(filePath, fileId); } else { fileMetadata = mDriveService.files().update(fileId, fileMetadata, mediaContent) - .setFields("id") + .setFields("id, version, modifiedTime") .execute(); } @@ -285,6 +313,7 @@ public VersionedRook upload(java.io.File file, Uri repoUri, String fileName) thr return new VersionedRook(repoId, RepoType.GOOGLE_DRIVE, repoUri, bookUri, rev, mtime); } + @RequiresApi(api = Build.VERSION_CODES.N) public void delete(String path) throws IOException { linkedOrThrow(); @@ -293,7 +322,7 @@ public void delete(String path) throws IOException { if (fileId != null) { File file = mDriveService.files().get(fileId).setFields("id, mimeType").execute(); - if (file.getMimeType() != "application/vnd.google-apps.folder") { + if (!file.getMimeType().equals("application/vnd.google-apps.folder")) { File fileMetadata = new File(); fileMetadata.setTrashed(true); mDriveService.files().update(fileId, fileMetadata).execute(); @@ -313,6 +342,7 @@ public void delete(String path) throws IOException { } } + @RequiresApi(api = Build.VERSION_CODES.N) public VersionedRook move(Uri repoUri, Uri from, Uri to) throws IOException { linkedOrThrow(); @@ -324,10 +354,10 @@ public VersionedRook move(Uri repoUri, Uri from, Uri to) throws IOException { if (fileId != null) { fileMetadata = mDriveService.files().update(fileId, fileMetadata) - .setFields("id, mimeType, version, modifiedDate") + .setFields("id, mimeType, version, modifiedTime") .execute(); - if (fileMetadata.getMimeType() == "application/vnd.google-apps.folder") { + if (fileMetadata.getMimeType().equals("application/vnd.google-apps.folder")) { throw new IOException("Relocated object not a file?"); } @@ -336,7 +366,7 @@ public VersionedRook move(Uri repoUri, Uri from, Uri to) throws IOException { String rev = Long.toString(fileMetadata.getVersion()); long mtime = fileMetadata.getModifiedTime().getValue(); - return new VersionedRook(repoId, RepoType.DROPBOX, repoUri, to, rev, mtime); + return new VersionedRook(repoId, RepoType.GOOGLE_DRIVE, repoUri, to, rev, mtime); } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/com/orgzly/android/ui/repo/googledrive/GoogleDriveRepoActivity.kt b/app/src/main/java/com/orgzly/android/ui/repo/googledrive/GoogleDriveRepoActivity.kt index 13e69bfbe..63928c993 100644 --- a/app/src/main/java/com/orgzly/android/ui/repo/googledrive/GoogleDriveRepoActivity.kt +++ b/app/src/main/java/com/orgzly/android/ui/repo/googledrive/GoogleDriveRepoActivity.kt @@ -1,6 +1,5 @@ package com.orgzly.android.ui.repo.googledrive -import android.annotation.SuppressLint import android.app.Activity import android.app.AlertDialog import android.content.DialogInterface @@ -10,7 +9,6 @@ import android.os.Bundle import android.text.TextUtils import android.view.Menu import android.view.MenuItem -import android.widget.EditText import androidx.databinding.DataBindingUtil import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders @@ -39,14 +37,9 @@ import com.google.android.gms.common.api.Scope; import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.OnCompleteListener; -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.json.gson.GsonFactory; -import com.google.api.services.drive.Drive; import com.google.api.services.drive.DriveScopes; import androidx.annotation.NonNull; -import java.util.Collections; import android.util.Log; @@ -154,28 +147,17 @@ class GoogleDriveRepoActivity : CommonActivity() { handleSignInResult(requestCode, resultData) } - fun handleSignInResult(requestCode:Int, result:Intent?) { + private fun handleSignInResult(requestCode:Int, result:Intent?) { if (requestCode == REQUEST_CODE_SIGN_IN) { GoogleSignIn.getSignedInAccountFromIntent(result) - .addOnSuccessListener({ googleAccount-> - Log.d(TAG, "Signed in as " + googleAccount.getEmail()) - // Use the authenticated account to sign in to the Drive service. - val credential = GoogleAccountCredential.usingOAuth2( - this, Collections.singleton(DriveScopes.DRIVE)) - credential.setSelectedAccount(googleAccount.getAccount()) - val googleDriveService = Drive.Builder( - AndroidHttp.newCompatibleTransport(), - GsonFactory(), - credential) - .setApplicationName("Orgzly") - .build() - // The DriveServiceHelper encapsulates all REST API and SAF functionality. - // Its instantiation is required before handling any onClick actions. - client.setService(googleDriveService); - showSnackbar(R.string.message_google_drive_linked) - }) - .addOnFailureListener({ exception-> Log.d(TAG, "Unable to sign in." + exception) }) + .addOnSuccessListener { googleAccount -> + Log.d(TAG, "Signed in as " + googleAccount.getEmail()) + // Use the authenticated account to sign in to the Drive service. + client.setDriveService(googleAccount) + showSnackbar(R.string.message_google_drive_linked) + } + .addOnFailureListener { exception -> Log.d(TAG, "Unable to sign in.$exception") } } }