Compare commits

...

4 Commits

59 changed files with 9 additions and 2370 deletions

View File

@@ -107,58 +107,10 @@
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\adal-1.1.19\classes-adal.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\commons-logging-1.1.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-android-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-services-drive-v2-rev102-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-android-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-jackson-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-jackson2-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-oauth-client-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\httpclient-4.0.3.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\httpcore-4.0.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\httpmime-4.0.3.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\json_simple-1.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\jsr305-1.3.9.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-gson-1.16.0-rc.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\jackson-core-2.7.4.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okio-1.9.0.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedJar Include="Jars\dropbox-core-sdk-3.0.3.jar" />
</ItemGroup>
</Project>

View File

@@ -1,27 +0,0 @@
using Android.Content;
#if !EXCLUDE_JAVAFILESTORAGE
namespace keepass2android.Io
{
public partial class DropboxFileStorage: JavaFileStorage
{
public DropboxFileStorage(Context ctx, IKp2aApp app) :
base(new Keepass2android.Javafilestorage.DropboxV2Storage(ctx, AppKey, AppSecret), app)
{
}
}
public partial class DropboxAppFolderFileStorage: JavaFileStorage
{
public DropboxAppFolderFileStorage(Context ctx, IKp2aApp app) :
base(new Keepass2android.Javafilestorage.DropboxV2AppFolderStorage(ctx, AppKey, AppSecret), app)
{
}
}
}
#endif

View File

@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using KeePassLib.Serialization;
#if !EXCLUDE_JAVAFILESTORAGE
namespace keepass2android.Io
{
public class GoogleDriveFileStorage : JavaFileStorage
{
public GoogleDriveFileStorage(Context ctx, IKp2aApp app) :
base(new Keepass2android.Javafilestorage.GoogleDriveFileStorage(), app)
{
}
}
}
#endif

View File

@@ -75,12 +75,9 @@
<Compile Include="Io\AndroidContentStorage.cs" />
<Compile Include="Io\BuiltInFileStorage.cs" />
<Compile Include="Io\CachingFileStorage.cs" />
<Compile Include="Io\DropboxFileStorage.cs" />
<Compile Include="Io\DropboxFileStorageKeys.cs" />
<Compile Include="Io\FileDescription.cs" />
<Compile Include="Io\FileStorageSetupActivity.cs" />
<Compile Include="Io\FileStorageSetupInitiatorActivity.cs" />
<Compile Include="Io\GDriveFileStorage.cs" />
<Compile Include="Io\IFileStorage.cs" />
<Compile Include="Io\IoUtil.cs" />
<Compile Include="Io\JavaFileStorage.cs" />

View File

@@ -33,18 +33,9 @@ dependencies {
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.burgstaller:okhttp-digest:1.7'
// compile files('libs/dropbox-android-sdk-1.6.2.jar')
compile 'com.google.android.gms:play-services:4.0.30'
/* compile('com.google.api-client:google-api-client-xml:1.16.0-rc') {
exclude group: 'com.google.android.google-play-services'
}*/
compile 'com.google.http-client:google-http-client-gson:1.16.0-rc'
compile('com.google.api-client:google-api-client-android:1.16.0-rc') {
exclude group: 'com.google.android.google-play-services'
}
compile 'com.google.apis:google-api-services-drive:v2-rev102-1.16.0-rc'
//compile 'com.dropbox.core:dropbox-core-sdk:2.0.1'
//compile group: 'com.dropbox.core', name: 'dropbox-core-sdk', version: '0-SNAPSHOT', changing: true
compile 'com.dropbox.core:dropbox-core-sdk:3.0.3'
//onedrive:
compile('com.onedrive.sdk:onedrive-sdk-android:1.2+') {
transitive = false

View File

@@ -1,30 +0,0 @@
package keepass2android.javafilestorage;
import android.content.Context;
/**
* Created by Philipp on 22.11.2016.
*/
public class DropboxV2AppFolderStorage extends DropboxV2Storage{
public DropboxV2AppFolderStorage(Context ctx, String _appKey,
String _appSecret) {
super(ctx, _appKey, _appSecret, false, AccessType.AppFolder);
}
public DropboxV2AppFolderStorage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart)
{
super(ctx, _appKey, _appSecret, clearKeysOnStart, AccessType.AppFolder);
}
@Override
public String getProtocolId() {
return "dropboxKP2A";
}
}

View File

@@ -1,612 +0,0 @@
package keepass2android.javafilestorage;
import com.dropbox.core.DbxAppInfo;
import com.dropbox.core.DbxException;
import com.dropbox.core.DbxOAuth1AccessToken;
import com.dropbox.core.DbxOAuth1Upgrader;
import com.dropbox.core.DbxRequestConfig;
import com.dropbox.core.InvalidAccessTokenException;
import com.dropbox.core.android.Auth;
import com.dropbox.core.v2.DbxClientV2;
import com.dropbox.core.http.OkHttp3Requestor;
import com.dropbox.core.v2.files.DeleteErrorException;
import com.dropbox.core.v2.files.DeletedMetadata;
import com.dropbox.core.v2.files.DownloadErrorException;
import com.dropbox.core.v2.files.FileMetadata;
import com.dropbox.core.v2.files.FolderMetadata;
import com.dropbox.core.v2.files.GetMetadataErrorException;
import com.dropbox.core.v2.files.ListFolderErrorException;
import com.dropbox.core.v2.files.ListFolderResult;
import com.dropbox.core.v2.files.Metadata;
import com.dropbox.core.v2.files.WriteMode;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
/**
* Created by Philipp on 18.11.2016.
*/
public class DropboxV2Storage extends JavaFileStorageBase
{
private DbxAppInfo appInfo;
public void bla()
{
}
DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("kp2a")
.withHttpRequestor(new OkHttp3Requestor(OkHttp3Requestor.defaultOkHttpClient()))
.build();
final static private String TAG = "KP2AJ";
final static private String ACCOUNT_PREFS_NAME = "prefs";
final static private String ACCESS_KEY_V1_NAME = "ACCESS_KEY";
final static private String ACCESS_SECRET_V1_NAME = "ACCESS_SECRET";
final static private String ACCESS_TOKEN_NAME = "ACCESS_TOKEN_V2";
private boolean mLoggedIn = false;
private Context mContext;
public FileEntry getRootFileEntry() {
FileEntry rootEntry = new FileEntry();
rootEntry.displayName = "";
rootEntry.isDirectory = true;
rootEntry.lastModifiedTime = -1;
rootEntry.canRead = rootEntry.canWrite = true;
rootEntry.path = getProtocolId()+":///";
rootEntry.sizeInBytes = -1;
return rootEntry;
}
public enum AccessType { Full, AppFolder};
protected AccessType mAccessType = AccessType.Full;
DbxClientV2 dbxClient;
public DropboxV2Storage(Context ctx, String _appKey, String _appSecret)
{
initialize(ctx, _appKey, _appSecret, false, mAccessType);
}
public DropboxV2Storage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart)
{
initialize(ctx, _appKey, _appSecret, clearKeysOnStart, mAccessType);
}
public DropboxV2Storage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart, AccessType accessType)
{
initialize(ctx, _appKey, _appSecret, clearKeysOnStart, accessType);
}
private void initialize(Context ctx, String _appKey, String _appSecret,
boolean clearKeysOnStart, AccessType accessType) {
appInfo = new DbxAppInfo(_appKey,_appSecret);
mContext = ctx;
if (clearKeysOnStart)
clearKeys();
this.mAccessType = accessType;
buildSession();
}
public boolean tryConnect(Activity activity)
{
if (!mLoggedIn)
Auth.startOAuth2Authentication(activity, appInfo.getKey());
return mLoggedIn;
}
private void setLoggedIn(boolean b) {
mLoggedIn = b;
}
public boolean isConnected()
{
return mLoggedIn;
}
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception
{
if ((previousFileVersion == null) || (previousFileVersion.equals("")))
return false;
path = removeProtocol(path);
try {
Metadata entry = dbxClient.files().getMetadata(path);
return !String.valueOf(entry.hashCode()) .equals(previousFileVersion);
} catch (DbxException e) {
throw convertException(e);
}
}
public String getCurrentFileVersionFast(String path)
{
try {
path = removeProtocol(path);
Metadata entry = dbxClient.files().getMetadata(path);
return String.valueOf(entry.hashCode());
} catch (DbxException e) {
Log.d(TAG, e.toString());
return "";
}
}
public InputStream openFileForRead(String path) throws Exception
{
try {
path = removeProtocol(path);
return dbxClient.files().download(path).getInputStream();
} catch (DbxException e) {
//System.out.println("Something went wrong: " + e);
throw convertException(e);
}
}
public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception
{
ByteArrayInputStream bis = new ByteArrayInputStream(data);
try {
path = removeProtocol(path);
dbxClient.files().uploadBuilder(path).withMode(WriteMode.OVERWRITE).uploadAndFinish(bis);
} catch (DbxException e) {
throw convertException(e);
}
}
private Exception convertException(DbxException e) {
Log.d(TAG, "Exception of type " +e.getClass().getName()+":" + e.getMessage());
if (InvalidAccessTokenException.class.isAssignableFrom(e.getClass()) ) {
Log.d(TAG, "LoggedIn=false (due to InvalidAccessTokenException)");
setLoggedIn(false);
clearKeys();
return new UserInteractionRequiredException("Unlinked from Dropbox! User must re-link.", e);
}
if (ListFolderErrorException.class.isAssignableFrom(e.getClass()) ) {
ListFolderErrorException listFolderErrorException = (ListFolderErrorException)e;
if (listFolderErrorException.errorValue.getPathValue().isNotFound())
return new FileNotFoundException(e.toString());
}
if (DownloadErrorException.class.isAssignableFrom(e.getClass()) ) {
DownloadErrorException downloadErrorException = (DownloadErrorException)e;
if (downloadErrorException.errorValue.getPathValue().isNotFound())
return new FileNotFoundException(e.toString());
}
if (GetMetadataErrorException.class.isAssignableFrom(e.getClass()) ) {
GetMetadataErrorException getMetadataErrorException = (GetMetadataErrorException)e;
if (getMetadataErrorException.errorValue.getPathValue().isNotFound())
return new FileNotFoundException(e.toString());
}
if (DeleteErrorException.class.isAssignableFrom(e.getClass()) ) {
DeleteErrorException deleteErrorException = (DeleteErrorException)e;
if (deleteErrorException.errorValue.getPathLookupValue().isNotFound())
return new FileNotFoundException(e.toString());
}
return e;
}
private void showToast(String msg) {
Toast error = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
error.show();
}
/**
* Keep the access keys returned from Trusted Authenticator in a local
* store, rather than storing user name & password, and re-authenticating each
* time (which is not to be done, ever).
*
* @return Array of [access_key, access_secret], or null if none stored
*/
private String[] getKeysV1() {
SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
String key = prefs.getString(ACCESS_KEY_V1_NAME, null);
String secret = prefs.getString(ACCESS_SECRET_V1_NAME, null);
if (key != null && secret != null) {
String[] ret = new String[2];
ret[0] = key;
ret[1] = secret;
return ret;
} else {
return null;
}
}
private String getKeyV2() {
SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
return prefs.getString(ACCESS_TOKEN_NAME, null);
}
private void storeKey(String v2token) {
Log.d(TAG, "Storing Dropbox accessToken");
// Save the access key for later
SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
Editor edit = prefs.edit();
edit.putString(ACCESS_TOKEN_NAME, v2token);
edit.commit();
}
private void clearKeys() {
SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
Editor edit = prefs.edit();
edit.clear();
edit.commit();
}
private void buildSession() {
String v2Token = getKeyV2();
if (v2Token != null)
{
dbxClient = new DbxClientV2(requestConfig, v2Token);
setLoggedIn(true);
Log.d(TAG, "Creating Dropbox Session with accessToken");
} else {
setLoggedIn(false);
Log.d(TAG, "Creating Dropbox Session without accessToken");
}
}
@Override
public String createFolder(String parentPath, String newDirName) throws Exception {
try
{
String path = parentPath;
if (!path.endsWith("/"))
path = path + "/";
path = path + newDirName;
String pathWithoutProtocol = removeProtocol(path);
dbxClient.files().createFolder(pathWithoutProtocol);
return path;
}
catch (DbxException e) {
throw convertException(e);
}
}
@Override
public String createFilePath(String parentPath, String newFileName) throws Exception {
String path = parentPath;
if (!path.endsWith("/"))
path = path + "/";
path = path + newFileName;
return path;
}
@Override
public List<FileEntry> listFiles(String parentPath) throws Exception {
try
{
parentPath = removeProtocol(parentPath);
if (parentPath.equals("/"))
parentPath = ""; //Dropbox is a bit picky here
ListFolderResult dirEntry = dbxClient.files().listFolder(parentPath);
List<FileEntry> result = new ArrayList<FileEntry>();
while (true)
{
for (Metadata e: dirEntry.getEntries())
{
FileEntry fileEntry = convertToFileEntry(e);
result.add(fileEntry);
}
if (!dirEntry.getHasMore()) {
break;
}
dirEntry = dbxClient.files().listFolderContinue(dirEntry.getCursor());
}
return result;
} catch (DbxException e) {
throw convertException(e);
}
}
private FileEntry convertToFileEntry(Metadata e) throws Exception {
//Log.d("JFS","e="+e);
FileEntry fileEntry = new FileEntry();
fileEntry.canRead = true;
fileEntry.canWrite = true;
if (e instanceof FolderMetadata)
{
FolderMetadata fm = (FolderMetadata)e;
fileEntry.isDirectory = true;
fileEntry.sizeInBytes = 0;
fileEntry.lastModifiedTime = 0;
}
else if (e instanceof FileMetadata)
{
FileMetadata fm = (FileMetadata)e;
fileEntry.sizeInBytes = fm.getSize();
fileEntry.isDirectory = false;
fileEntry.lastModifiedTime = fm.getServerModified().getTime();
}
else if (e instanceof DeletedMetadata)
{
throw new FileNotFoundException();
}
else
{
throw new Exception("unexpected metadata " + e.getClass().getName() );
}
fileEntry.path = getProtocolId()+"://"+ e.getPathLower();
fileEntry.displayName = e.getName();
//Log.d("JFS","fileEntry="+fileEntry);
//Log.d("JFS","Ok. Dir="+fileEntry.isDirectory);
return fileEntry;
}
@Override
public void delete(String path) throws Exception {
try
{
path = removeProtocol(path);
dbxClient.files().delete(path);
} catch (DbxException e) {
throw convertException(e);
}
}
@Override
public FileEntry getFileEntry(String filename) throws Exception {
try
{
filename = removeProtocol(filename);
Log.d("KP2AJ", "getFileEntry(), " +filename);
//querying root is not supported
if ((filename.equals("")) || (filename.equals("/")))
return getRootFileEntry();
if (filename.endsWith("/"))
filename = filename.substring(0,filename.length()-1);
Metadata dbEntry = dbxClient.files().getMetadata(filename);
return convertToFileEntry(dbEntry);
} catch (DbxException e) {
throw convertException(e);
}
}
@Override
public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave,
int requestCode)
{
String path = getProtocolId()+":///";
Log.d("KP2AJ", "startSelectFile "+path+", connected: "+path);
/*if (isConnected())
{
Intent intent = new Intent();
intent.putExtra(EXTRA_IS_FOR_SAVE, isForSave);
intent.putExtra(EXTRA_PATH, path);
activity.onImmediateResult(requestCode, RESULT_FILECHOOSER_PREPARED, intent);
}
else*/
{
activity.startSelectFileProcess(path, isForSave, requestCode);
}
}
@Override
public String getProtocolId() {
return "dropbox";
}
public boolean requiresSetup(String path)
{
return !isConnected();
}
@Override
public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) {
if (isConnected())
{
Intent intent = new Intent();
intent.putExtra(EXTRA_PATH, path);
activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent);
}
else
{
activity.startFileUsageProcess(path, requestCode, alwaysReturnSuccess);
}
}
@Override
public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException {
if (!isConnected())
{
throw new UserInteractionRequiredException();
}
}
@Override
public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) {
Log.d("KP2AJ", "OnCreate");
}
@Override
public void onResume(final FileStorageSetupActivity activity) {
if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE))
activity.getState().putString(EXTRA_PATH, activity.getPath());
Log.d("KP2AJ", "OnResume (3). LoggedIn="+mLoggedIn);
/*if (mLoggedIn)
{
finishActivityWithSuccess(activity);
return;
}*/
final String[] storedV1Keys = getKeysV1();
if (storedV1Keys != null) {
new AsyncTask<Object, Object, Object>()
{
@Override
protected Object doInBackground(Object... objects) {
DbxOAuth1AccessToken v1Token = new DbxOAuth1AccessToken(storedV1Keys[0], storedV1Keys[1]);
DbxOAuth1Upgrader upgrader = new DbxOAuth1Upgrader(requestConfig, appInfo);
try {
String v2Token = upgrader.createOAuth2AccessToken(v1Token);
upgrader.disableOAuth1AccessToken(v1Token);
storeKey(v2Token);
return v2Token;
} catch (Exception e) {
e.printStackTrace();
clearKeys();
return null;
}
}
@Override
protected void onPostExecute(Object o) {
if (o != null) {
buildSession();
finishActivityWithSuccess(activity);
}
else
resumeGetAuthToken(activity);
}
}.execute();
}
else {
resumeGetAuthToken(activity);
}
}
private void resumeGetAuthToken(FileStorageSetupActivity activity) {
FileStorageSetupActivity storageSetupAct = activity;
if (storageSetupAct.getState().containsKey("hasStartedAuth")) {
Log.d("KP2AJ", "auth started");
String v2Token = Auth.getOAuth2Token();
if (v2Token != null) {
Log.d("KP2AJ", "auth successful");
try {
storeKey(v2Token);
buildSession();
finishActivityWithSuccess(activity);
return;
} catch (Exception e) {
Log.d("KP2AJ", "finish with error: " + e.toString());
finishWithError(activity, e);
return;
}
}
Log.i(TAG, "authenticating not succesful");
Intent data = new Intent();
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not succesful");
((Activity) activity).setResult(Activity.RESULT_CANCELED, data);
((Activity) activity).finish();
} else {
Log.d("KP2AJ", "Starting auth");
Auth.startOAuth2Authentication((Activity) activity, appInfo.getKey());
storageSetupAct.getState().putBoolean("hasStartedAuth", true);
}
}
@Override
public void onStart(FileStorageSetupActivity activity) {
}
@Override
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) {
//nothing to do here
}
String removeProtocol(String path)
{
if (path == null)
return null;
return path.substring(getProtocolId().length()+3);
}
@Override
public String getDisplayName(String path) {
return path;
}
@Override
public String getFilename(String path) throws Exception {
return path.substring(path.lastIndexOf("/")+1);
}
}

View File

@@ -1,885 +0,0 @@
package keepass2android.javafilestorage;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.Drive.Files;
import com.google.api.services.drive.model.About;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import com.google.api.services.drive.model.ParentReference;
import android.Manifest;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
public class GoogleDriveFileStorage extends JavaFileStorageBase {
private static final String GDRIVE_PROTOCOL_ID = "gdrive";
private static final String FOLDER_MIME_TYPE = "application/vnd.google-apps.folder";
static final int MAGIC_GDRIVE=2082334;
static final int REQUEST_ACCOUNT_PICKER = MAGIC_GDRIVE+1;
static final int REQUEST_AUTHORIZATION = MAGIC_GDRIVE+2;
private boolean mRequiresRuntimePermissions = false;
class FileSystemEntryData
{
String displayName;
String id;
HashSet<String> parentIds = new HashSet<String>();
};
class AccountData
{
//guaranteed to be set if AccountData is in HashMap
Drive drive;
//may be null if first initialization failed
protected String mRootFolderId;
};
HashMap<String /*accountName*/, AccountData> mAccountData = new HashMap<String, AccountData>();
public String getRootPathForAccount(String accountName) throws UnsupportedEncodingException {
return GDRIVE_PROTOCOL_ID+"://"+encode(accountName)+"/";
}
class GDrivePath
{
String mAccount;
String mAccountLocalPath; // the path after the "gdrive://account%40%0Agmail.com/"
public GDrivePath()
{
}
public GDrivePath(String path) throws InvalidPathException, IOException
{
setPath(path);
}
public void setPath(String path) throws
InvalidPathException, IOException {
setPathWithoutVerify(path);
verify();
}
public void setPathWithoutVerify(String path) throws UnsupportedEncodingException, InvalidPathException
{
logDebug("setPath: "+path);
mAccount = extractAccount(path);
mAccountLocalPath = path.substring(getProtocolPrefix().length()+encode(mAccount).length()+1);
logDebug(" mAccount=" + mAccount);
logDebug(" mAccountLocalPath=" + mAccountLocalPath);
}
public GDrivePath(String parentPath, File fileToAppend) throws UnsupportedEncodingException, FileNotFoundException, IOException, InvalidPathException
{
setPath(parentPath);
if ((!mAccountLocalPath.endsWith("/")) && (!mAccountLocalPath.equals("")))
mAccountLocalPath = mAccountLocalPath + "/";
mAccountLocalPath += encode(fileToAppend.getTitle())+NAME_ID_SEP+fileToAppend.getId();
}
//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(""))
return;
String[] parts = mAccountLocalPath.split("/");
AccountData accountData = mAccountData.get(mAccount);
if (accountData == null)
{
throw new IllegalStateException("Looks like account "+mAccount+" was not properly initialized!");
}
//if initialization failed, try to repeat:
finishInitialization(accountData, mAccount);
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);
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 {
if (!path.startsWith(getProtocolPrefix()))
throw new InvalidPathException("Invalid path: "+path);
String pathWithoutProtocol = path.substring(getProtocolPrefix().length());
int slashPos = pathWithoutProtocol.indexOf("/");
String accountNameEncoded;
if (slashPos < 0)
accountNameEncoded = pathWithoutProtocol;
else
accountNameEncoded = pathWithoutProtocol.substring(0, slashPos);
return decode(accountNameEncoded);
}
public String getDisplayName()
{
//gdrive://
String displayName = getProtocolPrefix();
//gdrive://me@google.com/
displayName += mAccount;
if (mAccountLocalPath.equals(""))
return displayName;
String[] parts = mAccountLocalPath.split("/");
String part = parts[parts.length-1];
logDebug("parsing part " + part);
int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP);
if (indexOfSeparator < 0)
{
//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;
}
public String getGDriveId() throws InvalidPathException, IOException {
String pathWithoutTrailingSlash = mAccountLocalPath;
if (pathWithoutTrailingSlash.endsWith("/"))
pathWithoutTrailingSlash = pathWithoutTrailingSlash.substring(0,pathWithoutTrailingSlash.length()-1);
if (pathWithoutTrailingSlash.equals(""))
{
AccountData accountData = mAccountData.get(mAccount);
finishInitialization(accountData, mAccount);
return accountData.mRootFolderId;
}
String lastPart = pathWithoutTrailingSlash.substring(pathWithoutTrailingSlash.lastIndexOf(NAME_ID_SEP)+NAME_ID_SEP.length());
if (lastPart.contains("/"))
throw new InvalidPathException("error extracting GDriveId from "+mAccountLocalPath);
return decode(lastPart);
}
public String getFullPath() throws UnsupportedEncodingException {
return getProtocolPrefix()+encode(mAccount)+"/"+mAccountLocalPath;
}
public String getAccount() {
return mAccount;
}
public String getFilename() throws InvalidPathException {
String[] parts = mAccountLocalPath.split("/");
String lastPart = parts[parts.length-1];
int indexOfSeparator = lastPart.lastIndexOf(NAME_ID_SEP);
if (indexOfSeparator < 0) {
throw new InvalidPathException("cannot extract filename from " + mAccountLocalPath);
}
String name = lastPart.substring(0, indexOfSeparator);
try {
name = decode(name);
} catch (UnsupportedEncodingException e) {
// ignore
}
return name;
}
};
public GoogleDriveFileStorage()
{
logDebug("Creating GDrive FileStorage.");
}
@Override
public boolean checkForFileChangeFast(String path,
String previousFileVersion) throws Exception {
String currentVersion = getCurrentFileVersionFast(path);
if (currentVersion == null)
return false;
return currentVersion.equals(previousFileVersion) == false;
}
@Override
public String getCurrentFileVersionFast(String path) {
try {
GDrivePath gdrivePath = new GDrivePath(path);
return getFileForPath(gdrivePath, getDriveService(gdrivePath.getAccount())).getMd5Checksum();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public InputStream openFileForRead(String path) throws Exception {
logDebug("openFileForRead...");
GDrivePath gdrivePath = new GDrivePath(path);
Drive driveService = getDriveService(gdrivePath.getAccount());
try
{
File file = getFileForPath(gdrivePath, driveService);
InputStream res = getFileContent(file, driveService);
logDebug("openFileForRead ok.");
return res;
}
catch (Exception e)
{
throw convertException(e);
}
}
private File getFileForPath(GDrivePath path, Drive driveService)
throws IOException, InvalidPathException {
logDebug("getFileForPath... ");
try
{
//throw new IOException("argh");
String driveId = path.getGDriveId();
logDebug("id"+driveId);
File file = driveService.files().get(driveId).execute();
logDebug("...done.");
return file;
}
catch (IOException e)
{
e.printStackTrace();
throw e;
}
catch (InvalidPathException e)
{
e.printStackTrace();
throw e;
}
}
private InputStream getFileContent(File driveFile, Drive driveService) throws IOException {
if (driveFile.getDownloadUrl() != null && driveFile.getDownloadUrl().length() > 0) {
GenericUrl downloadUrl = new GenericUrl(driveFile.getDownloadUrl());
HttpResponse resp = driveService.getRequestFactory().buildGetRequest(downloadUrl).execute();
return resp.getContent();
} else {
//return an empty input stream
return new ByteArrayInputStream("".getBytes());
}
}
@Override
public void uploadFile(String path, byte[] data, boolean writeTransactional)
throws Exception {
logDebug("upload file...");
try
{
ByteArrayContent content = new ByteArrayContent(null, data);
GDrivePath gdrivePath = new GDrivePath(path);
Drive driveService = getDriveService(gdrivePath.getAccount());
File driveFile = getFileForPath(gdrivePath, driveService);
getDriveService(gdrivePath.getAccount()).files()
.update(driveFile.getId(), driveFile, content).execute();
logDebug("upload file ok.");
}
catch (Exception e)
{
throw convertException(e);
}
}
@Override
public String createFolder(String parentPath, String newDirName) throws Exception {
File body = new File();
body.setTitle(newDirName);
body.setMimeType(FOLDER_MIME_TYPE);
GDrivePath parentGdrivePath = new GDrivePath(parentPath);
body.setParents(
Arrays.asList(new ParentReference().setId(parentGdrivePath.getGDriveId())));
try
{
File file = getDriveService(parentGdrivePath.getAccount()).files().insert(body).execute();
logDebug("created folder "+newDirName+" in "+parentPath+". id: "+file.getId());
return new GDrivePath(parentPath, file).getFullPath();
}
catch (Exception e)
{
throw convertException(e);
}
}
@Override
public String createFilePath(String parentPath, String newFileName) throws Exception {
File body = new File();
body.setTitle(newFileName);
GDrivePath parentGdrivePath = new GDrivePath(parentPath);
body.setParents(
Arrays.asList(new ParentReference().setId(parentGdrivePath.getGDriveId())));
try
{
File file = getDriveService(parentGdrivePath.getAccount()).files().insert(body).execute();
return new GDrivePath(parentPath, file).getFullPath();
}
catch (Exception e)
{
throw convertException(e);
}
}
@Override
public List<FileEntry> listFiles(String parentPath) throws Exception {
GDrivePath gdrivePath = new GDrivePath(parentPath);
String parentId = gdrivePath.getGDriveId();
List<FileEntry> result = new ArrayList<FileEntry>();
Drive driveService = getDriveService(gdrivePath.getAccount());
try
{
if (driveService.files().get(parentId).execute().getLabels().getTrashed())
throw new FileNotFoundException(parentPath + " is trashed!");
logDebug("listing files in "+parentId);
Files.List request = driveService.files().list()
.setQ("trashed=false and '" + parentId + "' in parents");
do {
try {
FileList files = request.execute();
for (File file : files.getItems()) {
String path = new GDrivePath(parentPath, file).getFullPath();
logDebug("listing file "+path);
FileEntry e = convertToFileEntry(file, path);
result.add(e);
}
request.setPageToken(files.getNextPageToken());
} catch (IOException e) {
System.out.println("An error occurred: " + e);
request.setPageToken(null);
throw e;
}
} while (request.getPageToken() != null && request.getPageToken().length() > 0);
}
catch (Exception e)
{
throw convertException(e);
}
return result;
}
private Exception convertException(Exception e) {
logDebug("Exception: " + e.toString());
e.printStackTrace();
if (UserRecoverableAuthIOException.class.isAssignableFrom(e.getClass()))
{
logDebug("clearing account data.");
//this is not really nice because it removes data from the cache which might still be valid but we don't have the account name here...
mAccountData.clear();
}
if (GoogleJsonResponseException.class.isAssignableFrom(e.getClass()) )
{
GoogleJsonResponseException jsonEx = (GoogleJsonResponseException)e;
if (jsonEx.getDetails().getCode() == 404)
return new FileNotFoundException(jsonEx.getMessage());
}
return e;
}
private FileEntry convertToFileEntry(File file, String path) {
FileEntry e = new FileEntry();
e.canRead = e.canWrite = true;
e.isDirectory = FOLDER_MIME_TYPE.equals(file.getMimeType());
e.lastModifiedTime = file.getModifiedDate().getValue();
e.path = path;
try
{
e.sizeInBytes = file.getFileSize();
}
catch (NullPointerException ex)
{
e.sizeInBytes = 0;
}
e.displayName = file.getTitle();
return e;
}
@Override
public FileEntry getFileEntry(String filename) throws Exception {
try
{
logDebug("getFileEntry "+filename);
GDrivePath gdrivePath = new GDrivePath(filename);
FileEntry res = convertToFileEntry(
getFileForPath(gdrivePath, getDriveService(gdrivePath.getAccount())),
filename);
logDebug("getFileEntry res" + res);
return res;
}
catch (Exception e)
{
logDebug("Exception in getFileEntry! "+e);
throw convertException(e);
}
}
@Override
public void delete(String path) throws Exception {
GDrivePath gdrivePath = new GDrivePath(path);
Drive driveService = getDriveService(gdrivePath.getAccount());
try
{
driveService.files().delete(gdrivePath.getGDriveId()).execute();
}
catch (Exception e)
{
throw convertException(e);
}
}
private Drive createDriveService(String accountName, Context appContext) {
logDebug("createDriveService "+accountName);
GoogleAccountCredential credential = createCredential(appContext);
credential.setSelectedAccountName(accountName);
return new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential)
.setApplicationName(getApplicationName())
.build();
}
protected String getApplicationName()
{
return "Keepass2Android";
}
private Drive getDriveService(String accountName)
{
logDebug("getDriveService "+accountName);
AccountData accountData = mAccountData.get(accountName);
logDebug("accountData " + accountData);
return accountData.drive;
}
@Override
public void onActivityResult(final JavaFileStorage.FileStorageSetupActivity setupAct, int requestCode, int resultCode, Intent data) {
logDebug("ActivityResult: " + requestCode + "/" + resultCode);
switch (requestCode) {
case REQUEST_ACCOUNT_PICKER:
logDebug("ActivityResult: REQUEST_ACCOUNT_PICKER");
if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) {
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
logDebug("Initialize Account name="+accountName);
initializeAccountOrPath(setupAct, accountName);
return;
}
}
logDebug("Error selecting account");
//Intent retData = new Intent();
//retData.putExtra(EXTRA_ERROR_MESSAGE, t.getMessage());
((Activity)setupAct).setResult(Activity.RESULT_CANCELED, data);
((Activity)setupAct).finish();
case REQUEST_AUTHORIZATION:
if (resultCode == Activity.RESULT_OK) {
//for (String k: data.getExtras().keySet())
//{
//logDebug(data.getExtras().get(k).toString());
//}
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
logDebug("Account name="+accountName);
initializeAccountOrPath(setupAct, accountName);
}
else
{
logDebug("Account name is null");
}
} else {
logDebug("Error authenticating");
//Intent retData = new Intent();
//retData.putExtra(EXTRA_ERROR_MESSAGE, t.getMessage());
((Activity)setupAct).setResult(Activity.RESULT_CANCELED, data);
((Activity)setupAct).finish();
}
}
}
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;
if (accountNameOrPath.startsWith(getProtocolPrefix()))
{
gdrivePath = new GDrivePath();
//don't verify yet, we're not yet initialized:
try {
gdrivePath.setPathWithoutVerify(accountNameOrPath);
} catch (Exception e) {
finishWithError(setupAct, e);
}
accountNameTemp = gdrivePath.getAccount();
}
else
accountNameTemp = accountNameOrPath;
final String accountName = accountNameTemp;
AsyncTask<Object, Void, AsyncTaskResult<String> > task = new AsyncTask<Object, Void, AsyncTaskResult<String>>()
{
@Override
protected AsyncTaskResult<String> doInBackground(Object... arg0) {
try {
initializeAccount(appContext, accountName);
if (setupAct.getProcessName().equals(PROCESS_NAME_SELECTFILE))
setupAct.getState().putString(EXTRA_PATH, getRootPathForAccount(accountName));
return new AsyncTaskResult<String>("ok");
} catch ( Exception anyError) {
return new AsyncTaskResult<String>(anyError);
}
}
@Override
protected void onPostExecute(AsyncTaskResult<String> result) {
Exception error = result.getError();
if (error != null ) {
if (UserRecoverableAuthIOException.class.isAssignableFrom(error.getClass()))
{
mAccountData.remove(accountName);
activity.startActivityForResult(((UserRecoverableAuthIOException)error).getIntent(), REQUEST_AUTHORIZATION);
}
else
{
finishWithError(setupAct, error);
}
} else if ( isCancelled()) {
// cancel handling here
logDebug("Async Task cancelled!");
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
} else {
//all right!
finishActivityWithSuccess(setupAct);
}
}
};
task.execute(new Object[]{});
}
private void initializeAccount(final Context appContext,
final String accountName) throws IOException {
logDebug("Init account for " + accountName);
if (!mAccountData.containsKey(accountName))
{
AccountData newAccountData = new AccountData();
newAccountData.drive = createDriveService(accountName, appContext);
mAccountData.put(accountName, newAccountData);
logDebug("Added account data for " + accountName);
}
AccountData accountData = mAccountData.get(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(accountData, accountName);
}
private void finishInitialization(AccountData newAccountData, String accountName) throws IOException
{
if (TextUtils.isEmpty(newAccountData.mRootFolderId))
{
logDebug("Finish init account for " + accountName);
About about = newAccountData.drive.about().get().execute();
newAccountData.mRootFolderId = about.getRootFolderId();
}
else
{
logDebug("Account for " + accountName + " already fully initialized.");
}
}
@Override
public void startSelectFile(JavaFileStorage.FileStorageSetupInitiatorActivity activity, boolean isForSave,
int requestCode) {
((JavaFileStorage.FileStorageSetupInitiatorActivity)(activity)).startSelectFileProcess(getProtocolPrefix(), isForSave, requestCode);
}
@Override
public void prepareFileUsage(JavaFileStorage.FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) {
((JavaFileStorage.FileStorageSetupInitiatorActivity)(activity)).startFileUsageProcess(path, requestCode, alwaysReturnSuccess);
}
@Override
public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException, Throwable
{
try
{
logDebug("prepareFileUsage " + path + "...");
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);
logDebug("prepareFileUsage ok");
}
catch (UserRecoverableAuthIOException e)
{
logDebug("prepareFileUsage: UserInteractionRequiredException");
throw new UserInteractionRequiredException(e);
}
}
@Override
public String getProtocolId() {
return GDRIVE_PROTOCOL_ID;
}
@Override
public void onRequestPermissionsResult(FileStorageSetupActivity setupAct, int requestCode, String[] permissions, int[] grantResults)
{
logDebug("onRequestPermissionsResult");
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
logDebug("granted");
initFileStorage(setupAct);
}
else
{
logDebug("denied");
finishWithError(setupAct, new Exception("You must grant the requested permissions to continue."));
}
}
private void initFileStorage(FileStorageSetupActivity setupAct) {
Activity activity = (Activity)setupAct;
if (PROCESS_NAME_SELECTFILE.equals(setupAct.getProcessName()))
{
GoogleAccountCredential credential = createCredential(activity.getApplicationContext());
logDebug("starting REQUEST_ACCOUNT_PICKER");
activity.startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
if (PROCESS_NAME_FILE_USAGE_SETUP.equals(setupAct.getProcessName()))
{
initializeAccountOrPath(setupAct, setupAct.getPath());
}
}
@Override
public void onResume(JavaFileStorage.FileStorageSetupActivity setupAct) {
}
@Override
public void onStart(final JavaFileStorage.FileStorageSetupActivity setupAct) {
logDebug("onStart "+mRequiresRuntimePermissions);
if (!mRequiresRuntimePermissions)
{
initFileStorage(setupAct);
}
}
private GoogleAccountCredential createCredential(Context appContext) {
List<String> scopes = new ArrayList<String>();
scopes.add(DriveScopes.DRIVE);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(appContext, scopes);
return credential;
}
@Override
public boolean requiresSetup(String path) {
//always send the user through the prepare file usage workflow if he needs to authorize
return true;
}
@Override
public void onCreate(FileStorageSetupActivity activity,
Bundle savedInstanceState) {
logDebug("onCreate");
mRequiresRuntimePermissions = false;
if (Build.VERSION.SDK_INT >= 23)
{
Activity act = (Activity)activity;
int permissionRes = act.checkSelfPermission(Manifest.permission.GET_ACCOUNTS);
logDebug("permissionRes="+permissionRes);
if (permissionRes == PackageManager.PERMISSION_DENIED)
{
logDebug("requestPermissions");
mRequiresRuntimePermissions = true;
act.requestPermissions(new String[] {Manifest.permission.GET_ACCOUNTS}, 0);
}
}
}
@Override
public String getDisplayName(String path) {
GDrivePath gdrivePath = new GDrivePath();
try {
gdrivePath.setPathWithoutVerify(path);
} catch (Exception e) {
e.printStackTrace();
return path;
}
return gdrivePath.getDisplayName();
}
@Override
public String getFilename(String path) throws Exception
{
GDrivePath gdrivePath = new GDrivePath();
gdrivePath.setPathWithoutVerify(path);
return gdrivePath.getFilename();
}
}

View File

@@ -1,182 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
This project contains annotations derived from JCIP-ANNOTATIONS
Copyright (c) 2005 Brian Goetz and Tim Peierls.
See http://www.jcip.net and the Creative Commons Attribution License
(http://creativecommons.org/licenses/by/2.5)

View File

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -141,8 +141,6 @@ import java.util.ArrayList;
import java.util.List;
//import keepass2android.javafilestorage.DropboxCloudRailStorage;
import keepass2android.javafilestorage.DropboxV2Storage;
import keepass2android.javafilestorage.ICertificateErrorHandler;
import keepass2android.javafilestorage.JavaFileStorage;
import keepass2android.javafilestorage.JavaFileStorage.FileEntry;
import keepass2android.javafilestorage.OneDriveStorage;

View File

@@ -86,7 +86,6 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard-rules" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />

View File

@@ -1,144 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="46" android:versionName="0.9.3-release-3" package="keepass2android.keepass2android_debug" android:installLocation="auto">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" />
<permission android:description="@string/permission_desc" android:icon="@drawable/ic_notify_locked" android:label="KP2A internal file browsing" android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_notify_locked" android:label="KP2A entry search" android:name="keepass2android.keepass2android_debug.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<application android:label="keepass2android" android:icon="@drawable/ic_launcher">
<activity android:name="com.dropbox.core.android.AuthActivity" android:launchMode="singleTask" android:configChanges="orientation|keyboard">
<intent-filter>
<data android:scheme="db-2gormiq7iq1jls1" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<data android:scheme="db-ax0268uydp1ya57" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<provider android:name="group.pals.android.lib.ui.filechooser.providers.localfile.LocalFileProvider" android:authorities="keepass2android.keepass2android_debug.android-filechooser.localfile" android:exported="false" />
<provider android:name="group.pals.android.lib.ui.filechooser.providers.history.HistoryProvider" android:authorities="keepass2android.keepass2android_debug.android-filechooser.history" android:exported="false" />
<activity android:name="group.pals.android.lib.ui.filechooser.FileChooserActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:screenOrientation="user" android:theme="@style/Afc.Theme.Light">
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.OPENABLE" />
</intent-filter>
</activity>
<service android:name="keepass2android.autofill.AutoFillService"
android:enabled="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accserviceconfig" />
</service>
<service android:name="keepass2android.softkeyboard.KP2AKeyboard" android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter>
<action android:name="android.view.InputMethod" />
</intent-filter>
<meta-data android:name="android.view.im" android:resource="@xml/method" />
</service>
<activity android:name="keepass2android.softkeyboard.LatinIMESettings" android:label="@string/english_ime_settings">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<intent-filter>
<action android:name="keepass2android.softkeyboard.LatinIMESettings" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="keepass2android.softkeyboard.InputLanguageSelection"
android:label="@string/language_selection_title">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="keepass2android.softkeyboard.INPUT_LANGUAGE_SELECTION"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service android:name="keepass2android.autofill.AutoFillService"
android:enabled="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accserviceconfig" />
</service>
<activity android:configChanges="orientation" android:label="@string/app_name" android:theme="@style/MyTheme_Blue" android:name="keepass2android.PasswordActivity" android:windowSoftInputMode="adjustResize">
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
</intent-filter>
<intent-filter>
<action android:name="kp2a.action.PasswordActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.kdbp" />
<data android:pathPattern=".*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\.kdbx" />
<data android:pathPattern=".*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
</intent-filter>
</activity>
<uses-library required="false" name="com.sec.android.app.multiwindow" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632.0dip" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_H" android:value="598.0dip" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W" android:value="426.0dip" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H" android:value="360.0dip" />
</application>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<!-- Samsung Pass permission -->
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
</manifest>

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="23" android:versionName="0.8.6" package="keepass2android.keepass2android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14" />
<application android:label="keepass2android" android:icon="@drawable/ic_launcher">
<activity android:name="com.dropbox.client2.android.AuthActivity" android:launchMode="singleTask" android:configChanges="orientation|keyboard">
<intent-filter>
<!-- Change this to be db- followed by your app key -->
<data android:scheme="db-i8shu7v1hgh7ynt" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/MyTheme" android:name="keepass2android.PasswordActivity">
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.kdbp" />
<data android:pathPattern=".*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\.kdbx" />
<data android:pathPattern=".*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@@ -2,11 +2,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="98"
android:versionName="1.02"
package="keepass2android.keepass2android"
package="keepass2android.keepass2android_fdroid"
android:installLocation="auto">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" />
<permission android:description="@string/permission_desc" android:icon="@drawable/ic_launcher_online" android:label="KP2A internal file browsing" android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher_online" android:label="KP2A entry search" android:name="keepass2android.keepass2android.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc" android:icon="@drawable/ic_launcher_online" android:label="KP2A internal file browsing" android:name="keepass2android.keepass2android_fdroid.permission.KP2aInternalFileBrowsing" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher_online" android:label="KP2A entry search" android:name="keepass2android.keepass2android_fdroid.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<application android:label="keepass2android" android:icon="@drawable/ic_launcher_online">
<activity android:name="com.dropbox.core.android.AuthActivity" android:launchMode="singleTask" android:configChanges="orientation|keyboard">
@@ -25,8 +25,8 @@
</activity>
<provider android:name="group.pals.android.lib.ui.filechooser.providers.localfile.LocalFileProvider" android:authorities="keepass2android.keepass2android.android-filechooser.localfile" android:exported="false" />
<provider android:name="group.pals.android.lib.ui.filechooser.providers.history.HistoryProvider" android:authorities="keepass2android.keepass2android.android-filechooser.history" android:exported="false" />
<provider android:name="group.pals.android.lib.ui.filechooser.providers.localfile.LocalFileProvider" android:authorities="keepass2android.keepass2android_fdroid.android-filechooser.localfile" android:exported="false" />
<provider android:name="group.pals.android.lib.ui.filechooser.providers.history.HistoryProvider" android:authorities="keepass2android.keepass2android_fdroid.android-filechooser.history" android:exported="false" />
<activity android:name="group.pals.android.lib.ui.filechooser.FileChooserActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:screenOrientation="user" android:theme="@style/Afc.Theme.Light">
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
@@ -144,11 +144,8 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalSearch" />
<uses-permission android:name="keepass2android.keepass2android_fdroid.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="keepass2android.keepass2android_fdroid.permission.KP2aInternalSearch" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />

View File

@@ -1,134 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="93"
android:versionName="1.01-g"
package="keepass2android.keepass2android_nonet"
android:installLocation="auto">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher_online" android:label="KP2A entry search" android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<application android:label="keepass2android" android:icon="@drawable/ic_launcher_offline">
<provider android:name="group.pals.android.lib.ui.filechooser.providers.localfile.LocalFileProvider" android:authorities="keepass2android.keepass2android_nonet.android-filechooser.localfile" android:exported="false" />
<provider android:name="group.pals.android.lib.ui.filechooser.providers.history.HistoryProvider" android:authorities="keepass2android.keepass2android_nonet.android-filechooser.history" android:exported="false" />
<activity android:name="group.pals.android.lib.ui.filechooser.FileChooserActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:screenOrientation="user" android:theme="@style/Afc.Theme.Light">
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.OPENABLE" />
</intent-filter>
</activity>
<service android:name="keepass2android.autofill.AutoFillService"
android:enabled="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accserviceconfig" />
</service>
<service android:name="keepass2android.softkeyboard.KP2AKeyboard" android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter>
<action android:name="android.view.InputMethod" />
</intent-filter>
<meta-data android:name="android.view.im" android:resource="@xml/method" />
</service>
<activity android:name="keepass2android.softkeyboard.LatinIMESettings" android:label="@string/english_ime_settings">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<intent-filter>
<action android:name="keepass2android.softkeyboard.LatinIMESettings" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="keepass2android.softkeyboard.InputLanguageSelection"
android:label="@string/language_selection_title">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="keepass2android.softkeyboard.INPUT_LANGUAGE_SELECTION"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:configChanges="orientation" android:label="@string/app_name" android:theme="@style/MyTheme_Blue" android:name="keepass2android.PasswordActivity" android:windowSoftInputMode="adjustResize">
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:mimeType="application/octet-stream" />
<data android:host="*" />
</intent-filter>
<intent-filter>
<action android:name="kp2a.action.PasswordActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.kdbp" />
<data android:pathPattern=".*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbp" />
<data android:pathPattern=".*\\.kdbx" />
<data android:pathPattern=".*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
<data android:pathPattern=".*\\.kdb" />
<data android:pathPattern=".*\\..*\\.kdb" />
<data android:pathPattern=".*\\..*\\..*\\.kdb" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.kdb" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kdb" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kdb" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdb" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdb" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdb" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdb" />
</intent-filter>
</activity>
<uses-library required="false" name="com.sec.android.app.multiwindow" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632.0dip" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_H" android:value="598.0dip" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W" android:value="426.0dip" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H" android:value="360.0dip" />
</application>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalSearch" />
<!-- Samsung Pass permission -->
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -4,7 +4,7 @@
android:hint="@string/search_hint"
android:searchSettingsDescription="Search database"
android:searchMode="showSearchLabelAsBadge"
android:searchSuggestAuthority="kp2a.keepass2android.SearchProvider"
android:searchSuggestAuthority="kp2a.keepass2android_fdroid.SearchProvider"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestSelection=" ?"
android:searchSuggestThreshold="2" />

View File

@@ -40,7 +40,6 @@ using keepass2android.Io;
using keepass2android.addons.OtpKeyProv;
#if !NoNet
using Keepass2android.Javafilestorage;
using GoogleDriveFileStorage = keepass2android.Io.GoogleDriveFileStorage;
#endif
namespace keepass2android
{
@@ -80,7 +79,7 @@ namespace keepass2android
public const string PackagePart = "keepass2android_debug";
public const string Searchable = "@xml/searchable_debug";
#else
public const string PackagePart = "keepass2android";
public const string PackagePart = "keepass2android_fdroid";
public const string Searchable = "@xml/searchable";
#endif
public const int LauncherIcon = Resource.Drawable.ic_launcher_online;
@@ -511,9 +510,6 @@ namespace keepass2android
new AndroidContentStorage(Application.Context),
#if !EXCLUDE_JAVAFILESTORAGE
#if !NoNet
new DropboxFileStorage(Application.Context, this),
new DropboxAppFolderFileStorage(Application.Context, this),
new GoogleDriveFileStorage(Application.Context, this),
new OneDriveFileStorage(Application.Context, this),
new SftpFileStorage(this),
new NetFtpFileStorage(Application.Context, this),