* GoogleDrive: remove full path check, display only filename

* added method prepareFileUsage() without Activity parameter
This commit is contained in:
Philipp Crocoll 2014-07-20 08:32:32 +02:00
parent d93946ed05
commit 458e0a3d90
7 changed files with 182 additions and 150 deletions

View File

@ -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) {

View File

@ -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<String /*fileId*/, FileSystemEntryData> 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<parts.length;i++)
{
String part = parts[i];
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);
FileSystemEntryData thisFolder = accountData.mFolderCache.get(id);
if (thisFolder == null)
{
if (i== parts.length-1)
{
//not all files are cached
thisFolder = tryAddFileToCache(this);
}
//check if it's still null
if (thisFolder == null)
throw new FileNotFoundException("couldn't find id " + id + " being part of "+ mAccountLocalPath+" in GDrive account " + mAccount);
}
if (thisFolder.parentIds.contains(parentId) == false)
throw new FileNotFoundException("couldn't find parent id " + parentId + " as parent of "+thisFolder.displayName +" in "+ mAccountLocalPath+" in GDrive account " + mAccount);
if (thisFolder.displayName.equals(name) == false)
throw new FileNotFoundException("Name of "+id+" changed from "+name+" to "+thisFolder.displayName +" in "+ mAccountLocalPath+" in GDrive account " + mAccount);
parentId = id;
File fl;
try {
fl = getDriveService(getAccount()).files().get(getGDriveId()).execute();
} catch (Exception e) {
e.printStackTrace();
throw new FileNotFoundException("error getting file with for "+ this.getFullPath());
}
String displayName = fl.getTitle();
if (displayName.equals(name) == false)
throw new FileNotFoundException("Name of "+id+" changed from "+name+" to "+displayName +" in "+ mAccountLocalPath+" in GDrive account " + mAccount);
}
private String extractAccount(String path) throws InvalidPathException, UnsupportedEncodingException {
@ -204,25 +182,22 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
String[] parts = mAccountLocalPath.split("/");
for (int i=0;i<parts.length;i++)
String part = parts[parts.length-1];
logDebug("parsing part " + part);
int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP);
if (indexOfSeparator < 0)
{
String part = parts[i];
logDebug("parsing part " + part);
int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP);
if (indexOfSeparator < 0)
{
//seems invalid, but we're very generous here
displayName += "/"+part;
continue;
}
String name = part.substring(0, indexOfSeparator);
try {
name = decode(name);
} catch (UnsupportedEncodingException e) {
//ignore
}
displayName += "/"+name;
//seems invalid, but we're very generous here
displayName += "/"+part;
}
String name = part.substring(0, indexOfSeparator);
try {
name = decode(name);
} catch (UnsupportedEncodingException e) {
//ignore
}
displayName += "/"+name;
return displayName;
}
@ -290,34 +265,6 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
return currentVersion.equals(previousFileVersion) == false;
}
public FileSystemEntryData tryAddFileToCache(GDrivePath path) {
FileSystemEntryData thisFile = new FileSystemEntryData();
File fl;
try {
fl = getDriveService(path.getAccount()).files().get(path.getGDriveId()).execute();
} catch (Exception e) {
e.printStackTrace();
return null;
}
thisFile.id = fl.getId();
thisFile.displayName = fl.getTitle();
for (ParentReference parent: fl.getParents())
{
thisFile.parentIds.add(parent.getId());
}
mAccountData.get(path.getAccount()).mFolderCache.put(thisFile.id, thisFile);
/*try {
Log.d(TAG, "Added "+path.getFullPath()+" to cache");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}*/
return thisFile;
}
@Override
public String getCurrentFileVersionFast(String path) {
@ -425,13 +372,6 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
logDebug("created folder "+newDirName+" in "+parentPath+". id: "+file.getId());
//add to cache to avoid network traffic if this folder is accessed (which is likely to happen soon)
FileSystemEntryData newCacheEntry = new FileSystemEntryData();
newCacheEntry.displayName = newDirName;
newCacheEntry.id = file.getId();
newCacheEntry.parentIds.add(parentGdrivePath.getGDriveId());
mAccountData.get(parentGdrivePath.getAccount()).mFolderCache.put(file.getId(), newCacheEntry);
return new GDrivePath(parentPath, file).getFullPath();
}
catch (Exception e)
@ -575,7 +515,7 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
try
{
driveService.files().delete(gdrivePath.getGDriveId()).execute();
mAccountData.get(gdrivePath.getAccount()).mFolderCache.remove(gdrivePath.getGDriveId());
}
catch (Exception e)
{
@ -584,9 +524,9 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
}
private Drive createDriveService(String accountName, Activity activity) {
private Drive createDriveService(String accountName, Context appContext) {
logDebug("createDriveService "+accountName);
GoogleAccountCredential credential = createCredential(activity);
GoogleAccountCredential credential = createCredential(appContext);
credential.setSelectedAccountName(accountName);
return new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential)
@ -658,6 +598,7 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
private void initializeAccountOrPath(final JavaFileStorage.FileStorageSetupActivity setupAct, final String accountNameOrPath) {
final Activity activity = ((Activity)setupAct);
final Context appContext = activity.getApplicationContext();
String accountNameTemp;
GDrivePath gdrivePath = null;
@ -685,18 +626,7 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
try {
if (!mAccountData.containsKey(accountName))
{
AccountData newAccountData = new AccountData();
newAccountData.drive = createDriveService(accountName, activity);
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);
}
initializeAccount(appContext, accountName);
if (setupAct.getProcessName().equals(PROCESS_NAME_SELECTFILE))
setupAct.getState().putString(EXTRA_PATH, getRootPathForAccount(accountName));
@ -711,6 +641,8 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
@Override
protected void onPostExecute(AsyncTaskResult<String> 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<String,FileSystemEntryData> buildFoldersCache(String accountName) throws IOException {
HashMap<String, FileSystemEntryData> folderCache = new HashMap<String, GoogleDriveFileStorage.FileSystemEntryData>();
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<String> scopes = new ArrayList<String>();
scopes.add(DriveScopes.DRIVE);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(activity.getApplicationContext(), scopes);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(appContext, scopes);
return credential;
}

View File

@ -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();

View File

@ -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
}
}

View File

@ -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

View File

@ -0,0 +1,12 @@
package keepass2android.javafilestorage;
public class UserInteractionRequiredException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
}

View File

@ -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;
}
}