diff --git a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/DropboxFileStorage.java b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/DropboxFileStorage.java index df6932f6..dcb22052 100644 --- a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/DropboxFileStorage.java +++ b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/DropboxFileStorage.java @@ -417,6 +417,15 @@ public class DropboxFileStorage extends JavaFileStorageBase { } } + + @Override + public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException { + if (!isConnected()) + { + throw new UserInteractionRequiredException(); + } + + } @Override public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) { diff --git a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/GoogleDriveFileStorage.java b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/GoogleDriveFileStorage.java index 64abb544..21f4bebf 100644 --- a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/GoogleDriveFileStorage.java +++ b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/GoogleDriveFileStorage.java @@ -29,6 +29,7 @@ import com.google.api.services.drive.model.ParentReference; import android.accounts.AccountManager; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; @@ -57,9 +58,6 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { //guaranteed to be set if AccountData is in HashMap Drive drive; - //may be null if first initialization failed - HashMap mFolderCache; - //may be null if first initialization failed protected String mRootFolderId; }; @@ -89,7 +87,7 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { public void setPath(String path) throws InvalidPathException, IOException { setPathWithoutVerify(path); - verifyWithRetry(); + verify(); } public void setPathWithoutVerify(String path) throws UnsupportedEncodingException, InvalidPathException @@ -109,24 +107,14 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { mAccountLocalPath = mAccountLocalPath + "/"; mAccountLocalPath += encode(fileToAppend.getTitle())+NAME_ID_SEP+fileToAppend.getId(); } - - private void verifyWithRetry() throws IOException, - FileNotFoundException { - try - { - verify(); - } - catch (FileNotFoundException e) - { - //the folders cache might be out of date -> rebuild and try again: - AccountData accountData = mAccountData.get(mAccount); - accountData.mFolderCache = buildFoldersCache(mAccount); - - verify(); - } - } //make sure the path exists + /*Note: in earlier versions, this method checked the full path. This was causing trouble + * for some users, it seems like the IDs of parent folders can behave unexpectedly. + * Now the display name does no longer contain the parent folders, which is why it is no longer + * necessary to check if they were renamed. + * (The path still contains the parents for file browsing, but this is only required temporarily + * during setup where everything seems fine.)*/ private void verify() throws IOException { if (mAccountLocalPath.equals("")) @@ -143,38 +131,28 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { //if initialization failed, try to repeat: finishInitialization(accountData, mAccount); - String parentId = accountData.mRootFolderId; + String part = parts[parts.length-1]; + logDebug("parsing part " + part); + int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP); + if (indexOfSeparator < 0) + throw new FileNotFoundException("invalid path " + mAccountLocalPath); + String id = part.substring(indexOfSeparator+NAME_ID_SEP.length()); + String name = decode(part.substring(0, indexOfSeparator)); + logDebug(" name=" + name); - for (int i=0;i result) { Exception error = result.getError(); @@ -747,14 +679,26 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { } + private void initializeAccount(final Context appContext, + final String accountName) throws IOException { + if (!mAccountData.containsKey(accountName)) + { + AccountData newAccountData = new AccountData(); + newAccountData.drive = createDriveService(accountName, appContext); + mAccountData.put(accountName, newAccountData); + logDebug("Added account data for " + accountName); + //try to finish the initialization. If this fails, we throw. + //in case of "Always return true" (inside CachingFileStorage) this means + //we have a partially uninitialized AccountData object. + //We'll try to initialize later in verify() if (e.g.) network is available again. + finishInitialization(newAccountData, accountName); + } + } + + private void finishInitialization(AccountData newAccountData, String accountName) throws IOException { - if (newAccountData.mFolderCache == null) - { - newAccountData.mFolderCache = buildFoldersCache(accountName); - } - if (TextUtils.isEmpty(newAccountData.mRootFolderId)) { About about = newAccountData.drive.about().get().execute(); @@ -762,32 +706,6 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { } } - private HashMap buildFoldersCache(String accountName) throws IOException { - - HashMap folderCache = new HashMap(); - logDebug("buildFoldersCache"); - FileList folders=getDriveService(accountName).files().list().setQ("mimeType='"+FOLDER_MIME_TYPE+"' and trashed=false") - .setFields("items(id,title,parents),nextPageToken") - .execute(); - for(File fl: folders.getItems()){ - logDebug("buildFoldersCache: " + fl.getTitle()); - FileSystemEntryData thisFolder = new FileSystemEntryData(); - thisFolder.id = fl.getId(); - thisFolder.displayName = fl.getTitle(); - - for (ParentReference parent: fl.getParents()) - { - thisFolder.parentIds.add(parent.getId()); - } - folderCache.put(thisFolder.id, thisFolder); - } - - logDebug("that's it!"); - return folderCache; - - } - - @Override public void startSelectFile(JavaFileStorage.FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode) { @@ -800,6 +718,27 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { ((JavaFileStorage.FileStorageSetupInitiatorActivity)(activity)).startFileUsageProcess(path, requestCode, alwaysReturnSuccess); } + + @Override + + public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException, Throwable + { + String accountName; + GDrivePath gdrivePath = null; + if (path.startsWith(getProtocolPrefix())) + { + gdrivePath = new GDrivePath(); + //don't verify yet, we're not yet initialized: + gdrivePath.setPathWithoutVerify(path); + + accountName = gdrivePath.getAccount(); + } + else + accountName = path; + + initializeAccount(appContext, accountName); + + } @Override public String getProtocolId() { @@ -820,7 +759,7 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { if (PROCESS_NAME_SELECTFILE.equals(setupAct.getProcessName())) { - GoogleAccountCredential credential = createCredential(activity); + GoogleAccountCredential credential = createCredential(activity.getApplicationContext()); logDebug("starting REQUEST_ACCOUNT_PICKER"); activity.startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER); @@ -832,10 +771,10 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase { } } - private GoogleAccountCredential createCredential(Activity activity) { + private GoogleAccountCredential createCredential(Context appContext) { List scopes = new ArrayList(); scopes.add(DriveScopes.DRIVE); - GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(activity.getApplicationContext(), scopes); + GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(appContext, scopes); return credential; } diff --git a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/JavaFileStorage.java b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/JavaFileStorage.java index e129ba68..0bf332fc 100644 --- a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/JavaFileStorage.java +++ b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/JavaFileStorage.java @@ -4,6 +4,7 @@ import java.io.InputStream; import java.util.List; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -115,6 +116,11 @@ public class FileEntry { public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode); + //prepare the file usage. if not possible, throw an exception. Must throw UserInteractionRequiredException if the + // problem can be resolved by the user. Caller should then retry with prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) + public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException, Throwable; + + //prepare the file usage. if necessary, use the activity to interact with the user, e.g. to grant access. public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess); public String getProtocolId(); diff --git a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/SftpStorage.java b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/SftpStorage.java index bcdd1393..b568890c 100644 --- a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/SftpStorage.java +++ b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/SftpStorage.java @@ -20,6 +20,7 @@ import com.jcraft.jsch.SftpATTRS; import com.jcraft.jsch.SftpException; import com.jcraft.jsch.UserInfo; +import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -424,4 +425,10 @@ public class SftpStorage extends JavaFileStorageBase { return getProtocolPrefix()+encode(username)+":"+encode(password)+"@"+host+localPath; } + + @Override + public void prepareFileUsage(Context appContext, String path) { + //nothing to do + + } } diff --git a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/SkyDriveFileStorage.java b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/SkyDriveFileStorage.java index c8a0e87b..c2a70e4b 100644 --- a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/SkyDriveFileStorage.java +++ b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/SkyDriveFileStorage.java @@ -12,6 +12,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; +import keepass2android.javafilestorage.skydrive.PrepareFileUsageListener; import keepass2android.javafilestorage.skydrive.SkyDriveException; import keepass2android.javafilestorage.skydrive.SkyDriveFile; import keepass2android.javafilestorage.skydrive.SkyDriveFolder; @@ -456,9 +457,41 @@ public class SkyDriveFileStorage extends JavaFileStorageBase { @Override public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) { + + //tell the activity which requests the file usage that it must launch the FileStorageSetupActivity + // which will then go through the onCreate/onStart/onResume process which is used by our FileStorage ((JavaFileStorage.FileStorageSetupInitiatorActivity) (activity)) .startFileUsageProcess(path, requestCode, alwaysReturnSuccess); + } + @Override + public void prepareFileUsage(Context appContext, String path) throws Exception + { + PrepareFileUsageListener listener = new PrepareFileUsageListener(); + mAuthClient.initialize(Arrays.asList(SCOPES), listener); + + if (listener.exception != null) + throw listener.exception; + + if (listener.status == LiveStatus.CONNECTED) { + if (mFolderCache.isEmpty()) + { + initializeFoldersCache(); + } + + } else { + if (listener.status == LiveStatus.NOT_CONNECTED) + logDebug( "not connected"); + else if (listener.status == LiveStatus.UNKNOWN) + logDebug( "unknown"); + else + logDebug( "unexpected status " + listener.status); + + throw new UserInteractionRequiredException(); + + } + + } @Override diff --git a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/UserInteractionRequiredException.java b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/UserInteractionRequiredException.java new file mode 100644 index 00000000..2158f98d --- /dev/null +++ b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/UserInteractionRequiredException.java @@ -0,0 +1,12 @@ +package keepass2android.javafilestorage; + +public class UserInteractionRequiredException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + + +} diff --git a/src/java/JavaFileStorage/src/keepass2android/javafilestorage/skydrive/PrepareFileUsageListener.java b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/skydrive/PrepareFileUsageListener.java new file mode 100644 index 00000000..f16577d1 --- /dev/null +++ b/src/java/JavaFileStorage/src/keepass2android/javafilestorage/skydrive/PrepareFileUsageListener.java @@ -0,0 +1,26 @@ +package keepass2android.javafilestorage.skydrive; + +import com.microsoft.live.LiveAuthException; +import com.microsoft.live.LiveAuthListener; +import com.microsoft.live.LiveConnectSession; +import com.microsoft.live.LiveStatus; + +public class PrepareFileUsageListener implements LiveAuthListener { + + public Exception exception; + public LiveStatus status; + + @Override + public void onAuthError(LiveAuthException _exception, + Object userState) { + exception = _exception; + } + + @Override + public void onAuthComplete(LiveStatus _status, + LiveConnectSession session, Object userState) + { + status = _status; + } + +}