* modified setup workflow for IFileStorage (to be compatible with Google Drive requirements)

* scheme (protocol) is always contained in path variables passed to JavaFileStorage implementors
* file chooser improvements (internal browser displayed also in file chooser list e.g. when selecting an attachments, compatible with Solid Explorer content uris, removed OI stuff)
* started GDrive support
This commit is contained in:
Philipp Crocoll 2013-10-07 06:28:06 +02:00
parent fcae4fcbb6
commit 6f22ad012e
45 changed files with 3425 additions and 2569 deletions

View File

@ -20,7 +20,7 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>False</Optimize> <Optimize>False</Optimize>
<OutputPath>bin\Debug</OutputPath> <OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;EXCLUDE_KEYTRANSFORM</DefineConstants> <DefineConstants>DEBUG;INCLUDE_TWOFISH;INCLUDE_KEYBOARD;INCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause> <ConsolePause>False</ConsolePause>
@ -155,7 +155,7 @@
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\kp2akeytransform\kp2akeytransform.csproj" Condition="!$(DefineConstants.Contains('EXCLUDE_KEYTRANSFORM'))"> <ProjectReference Include="..\kp2akeytransform\kp2akeytransform.csproj" Condition="!$(DefineConstants.Contains('EXCLUDE_KEYTRANSFORM'))">
<Project>{A57B3ACE-5634-469A-88C4-858BB409F356}</Project> <Project>{A57B3ACE-5634-469A-88C4-858BB409F356}</Project>
<Name>kp2akeytransform</Name> <Name>kp2akeytransform</Name>
</ProjectReference> </ProjectReference>

View File

@ -2,16 +2,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Text;
using Android.App;
using Android.Content; using Android.Content;
using Android.OS; using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using KeePassLib.Serialization; using KeePassLib.Serialization;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -89,8 +82,6 @@ namespace keepass2android.Io
return new BuiltInFileTransaction(ioc, useFileTransaction); return new BuiltInFileTransaction(ioc, useFileTransaction);
} }
public IFileStorageSetup RequiredSetup { get { return null; } }
public class BuiltInFileTransaction : IWriteTransaction public class BuiltInFileTransaction : IWriteTransaction
{ {
private readonly FileTransactionEx _transaction; private readonly FileTransactionEx _transaction;
@ -155,5 +146,54 @@ namespace keepass2android.Io
//TODO //TODO
throw new NotImplementedException(); throw new NotImplementedException();
} }
public bool RequiresSetup(IOConnectionInfo ioConnection)
{
return false;
}
public string IocToPath(IOConnectionInfo ioc)
{
return ioc.Path;
}
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
{
if (protocolId != "file")
activity.PerformManualFileSelect(isForSave, requestCode, protocolId);
else
{
Intent intent = new Intent();
activity.IocToIntent(intent, new IOConnectionInfo() { Path = protocolId+"://"});
activity.OnImmediateResult(requestCode, (int) FileStorageResults.FileChooserPrepared, intent);
}
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode)
{
Intent intent = new Intent();
activity.IocToIntent(intent, ioc);
activity.OnImmediateResult(requestCode, (int) FileStorageResults.FileUsagePrepared, intent);
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
{
throw new NotImplementedException();
}
public void OnResume(IFileStorageSetupActivity activity)
{
throw new NotImplementedException();
}
public void OnStart(IFileStorageSetupActivity activity)
{
throw new NotImplementedException();
}
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
{
throw new NotImplementedException();
}
} }
} }

View File

@ -4,6 +4,8 @@ using System.IO;
using System.Net; using System.Net;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Android.Content;
using Android.OS;
using KeePassLib.Cryptography; using KeePassLib.Cryptography;
using KeePassLib.Serialization; using KeePassLib.Serialization;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -19,7 +21,7 @@ namespace keepass2android.Io
/// called when a save operation only updated the cache but not the remote file /// called when a save operation only updated the cache but not the remote file
/// </summary> /// </summary>
/// <param name="ioc">The file which we tried to write</param> /// <param name="ioc">The file which we tried to write</param>
/// <param name="e">The exception why the remote file couldn't be updated</param> /// <param name="ex">The exception why the remote file couldn't be updated</param>
void CouldntSaveToRemote(IOConnectionInfo ioc, Exception ex); void CouldntSaveToRemote(IOConnectionInfo ioc, Exception ex);
/// <summary> /// <summary>
@ -398,8 +400,6 @@ namespace keepass2android.Io
return new CachedWriteTransaction(ioc, useFileTransaction, this); return new CachedWriteTransaction(ioc, useFileTransaction, this);
} }
public IFileStorageSetup RequiredSetup { get { return _cachedStorage.RequiredSetup; } }
public bool CompleteIoId() public bool CompleteIoId()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -436,6 +436,46 @@ namespace keepass2android.Io
return _cachedStorage.GetFileDescription(ioc); return _cachedStorage.GetFileDescription(ioc);
} }
public bool RequiresSetup(IOConnectionInfo ioConnection)
{
return _cachedStorage.RequiresSetup(ioConnection);
}
public string IocToPath(IOConnectionInfo ioc)
{
return _cachedStorage.IocToPath(ioc);
}
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
{
_cachedStorage.StartSelectFile(activity, isForSave, requestCode, protocolId);
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode)
{
_cachedStorage.PrepareFileUsage(activity, ioc, requestCode);
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
{
_cachedStorage.OnCreate(activity, savedInstanceState);
}
public void OnResume(IFileStorageSetupActivity activity)
{
_cachedStorage.OnResume(activity);
}
public void OnStart(IFileStorageSetupActivity activity)
{
_cachedStorage.OnStart(activity);
}
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
{
_cachedStorage.OnActivityResult(activity, requestCode, resultCode, data);
}
public string GetBaseVersionHash(IOConnectionInfo ioc) public string GetBaseVersionHash(IOConnectionInfo ioc)
{ {

View File

@ -16,17 +16,14 @@ using Keepass2android.Javafilestorage;
namespace keepass2android.Io namespace keepass2android.Io
{ {
public class DropboxFileStorage: JavaFileStorage public partial class DropboxFileStorage: JavaFileStorage
{ {
public DropboxFileStorage(Context ctx, IKp2aApp app) : public DropboxFileStorage(Context ctx, IKp2aApp app) :
base(new Keepass2android.Javafilestorage.DropboxFileStorage(ctx), app) base(new Keepass2android.Javafilestorage.DropboxFileStorage(ctx, AppKey, AppSecret), app)
{ {
} }
protected override string Protocol
{
get { return "dropbox"; }
}
} }
} }
#endif #endif

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
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;
namespace keepass2android.Io
{
public interface IFileStorageSetupActivity
{
IOConnectionInfo Ioc { get; }
String ProcessName { get; }
bool IsForSave { get; }
Bundle State { get; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using Android.App;
using Android.Content;
using Android.OS;
using KeePassLib.Serialization;
namespace keepass2android.Io
{
public interface IFileStorageSetupInitiatorActivity
{
void StartSelectFileProcess(IOConnectionInfo ioc, bool isForSave, int requestCode);
void StartFileUsageProcess(IOConnectionInfo ioc, int requestCode);
void OnImmediateResult(int requestCode, int result, Intent intent);
Activity Activity { get; }
void IocToIntent(Intent intent, IOConnectionInfo ioc);
void PerformManualFileSelect(bool isForSave, int requestCode, string protocolId);
}
}

View File

@ -14,7 +14,7 @@ using KeePassLib.Serialization;
namespace keepass2android.Io namespace keepass2android.Io
{ {
public class GDriveFileStorage: IFileStorage /*public class GDriveFileStorage: IFileStorage
{ {
public IEnumerable<string> SupportedProtocols { get { yield return "gdrive"; } } public IEnumerable<string> SupportedProtocols { get { yield return "gdrive"; } }
public void Delete(IOConnectionInfo ioc) public void Delete(IOConnectionInfo ioc)
@ -42,7 +42,6 @@ namespace keepass2android.Io
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IFileStorageSetup RequiredSetup { get; private set; }
public bool CompleteIoId() public bool CompleteIoId()
{ {
@ -78,5 +77,5 @@ namespace keepass2android.Io
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }*/
} }

View File

@ -1,20 +1,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.OS; using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using KeePassLib.Keys;
using KeePassLib.Serialization; using KeePassLib.Serialization;
namespace keepass2android.Io namespace keepass2android.Io
{ {
public enum FileStorageResults
{
FullFilenameSelected = 874345 + 1,
FileChooserPrepared = FullFilenameSelected + 1,
FileUsagePrepared = FileChooserPrepared + 1
}
public static class FileStorageSetupDefs
{
public static String ProcessNameSelectfile = "SELECT_FILE";
public static String ProcessNameFileUsageSetup = "FILE_USAGE_SETUP";
public static String ExtraProcessName = "EXTRA_PROCESS_NAME";
public static String ExtraPath = "PATH";
public static String ExtraIsForSave = "IS_FOR_SAVE";
public static String ExtraErrorMessage = "EXTRA_ERROR_MESSAGE";
}
/// <summary> /// <summary>
/// Called as a callback from CheckForFileChangeAsync. /// Called as a callback from CheckForFileChangeAsync.
/// </summary> /// </summary>
@ -69,14 +82,6 @@ namespace keepass2android.Io
/// <param name="useFileTransaction">if true, force to use file system level transaction. This might be ignored if the file storage has built in transaction support</param> /// <param name="useFileTransaction">if true, force to use file system level transaction. This might be ignored if the file storage has built in transaction support</param>
IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction); IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction);
/// <summary>
/// Returns an instance of an implementation of IFileStorageSetup or one of the more complex interfaces.
/// Depending on the type returned, the caller should try to follow the interface as close as possible.
/// Returns null if the file storage is setup or doesn't require anything to work.
/// </summary>
/// This is due to different storage types requiring different workflows for authentication processes
IFileStorageSetup RequiredSetup { get; }
/// <summary> /// <summary>
/// brings up a dialog to query credentials or something like this. /// brings up a dialog to query credentials or something like this.
/// </summary> /// </summary>
@ -111,32 +116,36 @@ namespace keepass2android.Io
/// returns the description of the given file /// returns the description of the given file
/// </summary> /// </summary>
FileDescription GetFileDescription(IOConnectionInfo ioc); FileDescription GetFileDescription(IOConnectionInfo ioc);
}
/// <summary>
/// Base interface for required setup code
/// </summary>
public interface IFileStorageSetup
{
/// <summary> /// <summary>
/// call this when the user explicitly wants to use this file storage. Might require user interaction. /// returns true if everything is ok with connecting to the given file.
/// May throw if the setup failed permanentaly. /// Returns False if PrepareFileUsage must be called first.
/// </summary> /// </summary>
/// <returns>true if the setup was succesful immediately (without UI). Returns false if setup was not successful but no error occured or can be displayed.</returns> bool RequiresSetup(IOConnectionInfo ioConnection);
bool TrySetup(Activity activity);
}
/// <summary>
/// Interface which can be used additionally for an IFileStorageSetup to indicate that setup must be completed in OnResume()
/// </summary>
public interface IFileStorageSetupOnResume
{
/// <summary> /// <summary>
/// call this after TrySetup() returned false in the next OnResume() /// converts the ioc to a path which may contain the credentials
/// May throw if the setup failed permanentaly.
/// </summary> /// </summary>
/// <returns>true if setup was succesful</returns> string IocToPath(IOConnectionInfo ioc);
bool TrySetupOnResume(Activity activity);
/// <summary>
/// Initiates the process for choosing a file in the given file storage.
/// The file storage should either call OnImmediateResult or StartSelectFileProcess
/// </summary>
void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId);
/// <summary>
/// Initiates the process for choosing a file in the given file storage.
/// The file storage should either call OnImmediateResult or StartFileUsageProcess
/// </summary>
void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode);
//Setup methods: these are called from the setup activity so the file storage can handle UI events for authorization etc.
void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState);
void OnResume(IFileStorageSetupActivity activity);
void OnStart(IFileStorageSetupActivity activity);
void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data);
} }
public interface IWriteTransaction: IDisposable public interface IWriteTransaction: IDisposable

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Android.App; using Android.App;
using Android.Content;
using Android.OS;
using KeePassLib.Serialization; using KeePassLib.Serialization;
using KeePassLib.Utility; using KeePassLib.Utility;
#if !EXCLUDE_JAVAFILESTORAGE #if !EXCLUDE_JAVAFILESTORAGE
@ -16,6 +18,8 @@ namespace keepass2android.Io
#if !EXCLUDE_JAVAFILESTORAGE #if !EXCLUDE_JAVAFILESTORAGE
public abstract class JavaFileStorage: IFileStorage public abstract class JavaFileStorage: IFileStorage
{ {
protected string Protocol { get { return _jfs.ProtocolId; } }
public IEnumerable<string> SupportedProtocols { get { yield return Protocol; } } public IEnumerable<string> SupportedProtocols { get { yield return Protocol; } }
@ -64,7 +68,7 @@ namespace keepass2android.Io
{ {
try try
{ {
return Jfs.GetCurrentFileVersionFast(ioc.Path); return Jfs.GetCurrentFileVersionFast(IocToPath(ioc));
} }
catch (Java.Lang.Exception e) catch (Java.Lang.Exception e)
{ {
@ -103,55 +107,11 @@ namespace keepass2android.Io
return new JavaFileStorageWriteTransaction(IocToPath(ioc), useFileTransaction, this); return new JavaFileStorageWriteTransaction(IocToPath(ioc), useFileTransaction, this);
} }
public IFileStorageSetup RequiredSetup
{
get
{
if (Jfs.IsConnected)
return null;
return new JavaFileStorageSetup(this);
}
}
internal IJavaFileStorage Jfs internal IJavaFileStorage Jfs
{ {
get { return _jfs; } get { return _jfs; }
} }
public class JavaFileStorageSetup : IFileStorageSetup, IFileStorageSetupOnResume
{
private readonly JavaFileStorage _javaFileStorage;
public JavaFileStorageSetup(JavaFileStorage javaFileStorage)
{
_javaFileStorage = javaFileStorage;
}
public bool TrySetup(Activity activity)
{
try
{
return _javaFileStorage.Jfs.TryConnect(activity);
}
catch (Java.Lang.Exception e)
{
throw _javaFileStorage.LogAndConvertJavaException(e);
}
}
public bool TrySetupOnResume(Activity activity)
{
try
{
_javaFileStorage.Jfs.OnResume();
return _javaFileStorage.Jfs.IsConnected;
}
catch (Java.Lang.Exception e)
{
throw _javaFileStorage.LogAndConvertJavaException(e);
}
}
}
class JavaFileStorageWriteTransaction: IWriteTransaction class JavaFileStorageWriteTransaction: IWriteTransaction
{ {
@ -255,13 +215,14 @@ namespace keepass2android.Io
CanWrite = e.CanWrite, CanWrite = e.CanWrite,
IsDirectory = e.IsDirectory, IsDirectory = e.IsDirectory,
LastModified = JavaTimeToCSharp(e.LastModifiedTime), LastModified = JavaTimeToCSharp(e.LastModifiedTime),
Path = Protocol + "://" + e.Path, Path = e.Path,
SizeInBytes = e.SizeInBytes SizeInBytes = e.SizeInBytes
}; };
} }
public FileDescription GetFileDescription(IOConnectionInfo ioc) public FileDescription GetFileDescription(IOConnectionInfo ioc)
{ {
Kp2aLog.Log("GetFileDescription "+ioc.Path);
try try
{ {
return ConvertToFileDescription(Jfs.GetFileEntry(IocToPath(ioc))); return ConvertToFileDescription(Jfs.GetFileEntry(IocToPath(ioc)));
@ -276,24 +237,54 @@ namespace keepass2android.Io
} }
} }
public bool RequiresSetup(IOConnectionInfo ioConnection)
{
return _jfs.RequiresSetup(IocToPath(ioConnection));
}
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
{
Kp2aLog.Log("StartSelectFile " + protocolId);
_jfs.StartSelectFile((IJavaFileStorageFileStorageSetupInitiatorActivity) activity, isForSave, requestCode);
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode)
{
_jfs.PrepareFileUsage((IJavaFileStorageFileStorageSetupInitiatorActivity)activity, IocToPath(ioc), requestCode);
}
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
{
_jfs.OnCreate(((IJavaFileStorageFileStorageSetupActivity)activity), savedInstanceState);
}
public void OnResume(IFileStorageSetupActivity activity)
{
Kp2aLog.Log("JFS/OnResume Ioc.Path=" +activity.Ioc.Path+". Path="+((IJavaFileStorageFileStorageSetupActivity)activity).Path);
_jfs.OnResume(((IJavaFileStorageFileStorageSetupActivity) activity));
}
public void OnStart(IFileStorageSetupActivity activity)
{
_jfs.OnStart(((IJavaFileStorageFileStorageSetupActivity) activity));
}
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
{
_jfs.OnActivityResult(((IJavaFileStorageFileStorageSetupActivity) activity), requestCode, resultCode, data);
}
private DateTime JavaTimeToCSharp(long javatime) private DateTime JavaTimeToCSharp(long javatime)
{ {
//todo test
return new DateTime(1970, 1, 1).AddMilliseconds(javatime); return new DateTime(1970, 1, 1).AddMilliseconds(javatime);
} }
private string IocToPath(IOConnectionInfo ioc) public string IocToPath(IOConnectionInfo ioc)
{ {
if (ioc.Path.StartsWith(Protocol + "://")) return ioc.Path;
return ioc.Path.Substring(Protocol.Length + 3);
else
{
return ioc.Path;
}
} }
protected abstract string Protocol { get; }
} }
#endif #endif
} }

View File

@ -20,7 +20,7 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;EXCLUDE_JAVAFILESTORAGE</DefineConstants> <DefineConstants>TRACE;DEBUG;INCLUDE_TWOFISH;INCLUDE_KEYBOARD;INCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
@ -58,7 +58,10 @@
<Compile Include="Io\BuiltInFileStorage.cs" /> <Compile Include="Io\BuiltInFileStorage.cs" />
<Compile Include="Io\CachingFileStorage.cs" /> <Compile Include="Io\CachingFileStorage.cs" />
<Compile Include="Io\DropboxFileStorage.cs" /> <Compile Include="Io\DropboxFileStorage.cs" />
<Compile Include="Io\DropboxFileStorageKeys.cs" />
<Compile Include="Io\FileDescription.cs" /> <Compile Include="Io\FileDescription.cs" />
<Compile Include="Io\FileStorageSetupActivity.cs" />
<Compile Include="Io\FileStorageSetupInitiatorActivity.cs" />
<Compile Include="Io\GDriveFileStorage.cs" /> <Compile Include="Io\GDriveFileStorage.cs" />
<Compile Include="Io\IFileStorage.cs" /> <Compile Include="Io\IFileStorage.cs" />
<Compile Include="Io\IoUtil.cs" /> <Compile Include="Io\IoUtil.cs" />
@ -93,7 +96,7 @@
<Compile Include="ProgressDialogStatusLogger.cs" /> <Compile Include="ProgressDialogStatusLogger.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj" Condition="!$(DefineConstants.Contains('EXCLUDE_JAVAFILESTORAGE'))"> <ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project> <Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
<Name>JavaFileStorageBindings</Name> <Name>JavaFileStorageBindings</Name>
</ProjectReference> </ProjectReference>

View File

@ -5,5 +5,7 @@
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/> <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="con" path="com.google.gdt.eclipse.managedapis.MANAGED_API_CONTAINER/drive-v2r102lv1.16.0-rc"/>
<classpathentry exported="true" kind="lib" path="C:/Users/Philipp/AppData/Local/Android/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar"/>
<classpathentry kind="output" path="bin/classes"/> <classpathentry kind="output" path="bin/classes"/>
</classpath> </classpath>

View File

@ -18,5 +18,6 @@
</application> </application>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
</manifest> </manifest>

View File

@ -8,8 +8,9 @@
# project structure. # project structure.
# #
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt:proguard-google-api-client.txt
# Project target. # Project target.
target=android-17 target=android-17
android.library=true android.library=true
android.library.reference.1=../../../../../../../AppData/Local/Android/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib

View File

@ -15,6 +15,7 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.os.DropBoxManager.Entry; import android.os.DropBoxManager.Entry;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
@ -35,7 +36,7 @@ import com.dropbox.client2.session.Session.AccessType;
public class DropboxFileStorage implements JavaFileStorage { public class DropboxFileStorage implements JavaFileStorage {
//NOTE: also adjust secret! //NOTE: also adjust secret!
final static private String APP_KEY = "i8shu7v1hgh7ynt"; //KP2A //final static private String APP_KEY = "i8shu7v1hgh7ynt"; //KP2A
//final static private String APP_KEY = "4ybka4p4a1027n6"; //FileStorageTest //final static private String APP_KEY = "4ybka4p4a1027n6"; //FileStorageTest
// If you'd like to change the access type to the full Dropbox instead of // If you'd like to change the access type to the full Dropbox instead of
@ -52,8 +53,13 @@ public class DropboxFileStorage implements JavaFileStorage {
private boolean mLoggedIn = false; private boolean mLoggedIn = false;
private Context mContext; private Context mContext;
public DropboxFileStorage(Context ctx) private String appKey;
private String appSecret;
public DropboxFileStorage(Context ctx, String _appKey, String _appSecret)
{ {
appKey = _appKey;
appSecret = _appSecret;
mContext = ctx; mContext = ctx;
// We create a new AuthSession so that we can use the Dropbox API. // We create a new AuthSession so that we can use the Dropbox API.
AndroidAuthSession session = buildSession(); AndroidAuthSession session = buildSession();
@ -62,6 +68,23 @@ public class DropboxFileStorage implements JavaFileStorage {
checkAppKeySetup(); checkAppKeySetup();
} }
public DropboxFileStorage(Context ctx, String _appKey, String _appSecret, boolean clearKeysOnStart)
{
appKey = _appKey;
appSecret = _appSecret;
mContext = ctx;
if (clearKeysOnStart)
clearKeys();
// We create a new AuthSession so that we can use the Dropbox API.
AndroidAuthSession session = buildSession();
mApi = new DropboxAPI<AndroidAuthSession>(session);
checkAppKeySetup();
}
public boolean tryConnect(Activity activity) public boolean tryConnect(Activity activity)
{ {
if (!mLoggedIn) if (!mLoggedIn)
@ -69,29 +92,6 @@ public class DropboxFileStorage implements JavaFileStorage {
return mLoggedIn; return mLoggedIn;
} }
public void onResume()
{
AndroidAuthSession session = mApi.getSession();
// The next part must be inserted in the onResume() method of the
// activity from which session.startAuthentication() was called, so
// that Dropbox authentication completes properly.
if (session.authenticationSuccessful()) {
try {
// Mandatory call to complete the auth
session.finishAuthentication();
// Store it locally in our app for later use
TokenPair tokens = session.getAccessTokenPair();
storeKeys(tokens.key, tokens.secret);
setLoggedIn(true);
} catch (IllegalStateException e) {
Log.i(TAG, "Error authenticating", e);
throw e;
}
}
}
private void setLoggedIn(boolean b) { private void setLoggedIn(boolean b) {
mLoggedIn = b; mLoggedIn = b;
@ -101,7 +101,7 @@ public class DropboxFileStorage implements JavaFileStorage {
// Check if the app has set up its manifest properly. // Check if the app has set up its manifest properly.
Intent testIntent = new Intent(Intent.ACTION_VIEW); Intent testIntent = new Intent(Intent.ACTION_VIEW);
String scheme = "db-" + APP_KEY; String scheme = "db-" + appKey;
String uri = scheme + "://" + AuthActivity.AUTH_VERSION + "/test"; String uri = scheme + "://" + AuthActivity.AUTH_VERSION + "/test";
testIntent.setData(Uri.parse(uri)); testIntent.setData(Uri.parse(uri));
PackageManager pm = mContext.getPackageManager(); PackageManager pm = mContext.getPackageManager();
@ -123,9 +123,10 @@ public class DropboxFileStorage implements JavaFileStorage {
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception
{ {
if ((previousFileVersion == null) || (previousFileVersion == "")) if ((previousFileVersion == null) || (previousFileVersion.equals("")))
return false; return false;
try { try {
path = removeProtocol(path);
com.dropbox.client2.DropboxAPI.Entry entry = mApi.metadata(path, 1, null, false, null); com.dropbox.client2.DropboxAPI.Entry entry = mApi.metadata(path, 1, null, false, null);
return entry.hash != previousFileVersion; return entry.hash != previousFileVersion;
} catch (DropboxException e) { } catch (DropboxException e) {
@ -136,6 +137,7 @@ public class DropboxFileStorage implements JavaFileStorage {
public String getCurrentFileVersionFast(String path) public String getCurrentFileVersionFast(String path)
{ {
try { try {
path = removeProtocol(path);
com.dropbox.client2.DropboxAPI.Entry entry = mApi.metadata(path, 1, null, false, null); com.dropbox.client2.DropboxAPI.Entry entry = mApi.metadata(path, 1, null, false, null);
return entry.rev; return entry.rev;
} catch (DropboxException e) { } catch (DropboxException e) {
@ -147,6 +149,7 @@ public class DropboxFileStorage implements JavaFileStorage {
public InputStream openFileForRead(String path) throws Exception public InputStream openFileForRead(String path) throws Exception
{ {
try { try {
path = removeProtocol(path);
return mApi.getFileStream(path, null); return mApi.getFileStream(path, null);
} catch (DropboxException e) { } catch (DropboxException e) {
//System.out.println("Something went wrong: " + e); //System.out.println("Something went wrong: " + e);
@ -158,6 +161,7 @@ public class DropboxFileStorage implements JavaFileStorage {
{ {
ByteArrayInputStream bis = new ByteArrayInputStream(data); ByteArrayInputStream bis = new ByteArrayInputStream(data);
try { try {
path = removeProtocol(path);
//TODO: it would be nice to be able to use the parent version with putFile() //TODO: it would be nice to be able to use the parent version with putFile()
mApi.putFileOverwrite(path, bis, data.length, null); mApi.putFileOverwrite(path, bis, data.length, null);
} catch (DropboxException e) { } catch (DropboxException e) {
@ -231,7 +235,6 @@ public class DropboxFileStorage implements JavaFileStorage {
edit.commit(); edit.commit();
} }
//TODO: call when Unlinked Exception
private void clearKeys() { private void clearKeys() {
SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0); SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
Editor edit = prefs.edit(); Editor edit = prefs.edit();
@ -240,8 +243,7 @@ public class DropboxFileStorage implements JavaFileStorage {
} }
private AndroidAuthSession buildSession() { private AndroidAuthSession buildSession() {
//note: the SecretKeys class is not public because the App-Secret must be secret! AppKeyPair appKeyPair = new AppKeyPair(appKey, appSecret);
AppKeyPair appKeyPair = new AppKeyPair(APP_KEY, SecretKeys.DROPBOX_APP_SECRET);
AndroidAuthSession session; AndroidAuthSession session;
String[] stored = getKeys(); String[] stored = getKeys();
@ -263,6 +265,7 @@ public class DropboxFileStorage implements JavaFileStorage {
public void createFolder(String path) throws Exception { public void createFolder(String path) throws Exception {
try try
{ {
path = removeProtocol(path);
mApi.createFolder(path); mApi.createFolder(path);
} }
catch (DropboxException e) { catch (DropboxException e) {
@ -274,6 +277,7 @@ public class DropboxFileStorage implements JavaFileStorage {
public List<FileEntry> listFiles(String dirName) throws Exception { public List<FileEntry> listFiles(String dirName) throws Exception {
try try
{ {
dirName = removeProtocol(dirName);
com.dropbox.client2.DropboxAPI.Entry dirEntry = mApi.metadata(dirName, 0, null, true, null); com.dropbox.client2.DropboxAPI.Entry dirEntry = mApi.metadata(dirName, 0, null, true, null);
if (dirEntry.isDeleted) if (dirEntry.isDeleted)
@ -305,7 +309,7 @@ public class DropboxFileStorage implements JavaFileStorage {
fileEntry.canWrite = true; fileEntry.canWrite = true;
fileEntry.isDirectory = e.isDir; fileEntry.isDirectory = e.isDir;
fileEntry.sizeInBytes = e.bytes; fileEntry.sizeInBytes = e.bytes;
fileEntry.path = e.path; fileEntry.path = getProtocolId()+"://"+ e.path;
//Log.d("JFS","fileEntry="+fileEntry); //Log.d("JFS","fileEntry="+fileEntry);
Date lastModifiedDate = null; Date lastModifiedDate = null;
if (e.modified != null) if (e.modified != null)
@ -322,6 +326,7 @@ public class DropboxFileStorage implements JavaFileStorage {
public void delete(String path) throws Exception { public void delete(String path) throws Exception {
try try
{ {
path = removeProtocol(path);
mApi.delete(path); mApi.delete(path);
} catch (DropboxException e) { } catch (DropboxException e) {
throw convertException(e); throw convertException(e);
@ -334,8 +339,8 @@ public class DropboxFileStorage implements JavaFileStorage {
public FileEntry getFileEntry(String filename) throws Exception { public FileEntry getFileEntry(String filename) throws Exception {
try try
{ {
Log.d("JFS", "Hi!"); filename = removeProtocol(filename);
Log.d("JFS", "mApi = "+mApi); Log.d("KP2AJ", "getFileEntry(), mApi = "+mApi+" filename="+filename);
com.dropbox.client2.DropboxAPI.Entry dbEntry = mApi.metadata(filename, 0, null, false, null); com.dropbox.client2.DropboxAPI.Entry dbEntry = mApi.metadata(filename, 0, null, false, null);
Log.d("JFS", "dbEntry = "+dbEntry); Log.d("JFS", "dbEntry = "+dbEntry);
@ -350,4 +355,164 @@ public class DropboxFileStorage implements JavaFileStorage {
} }
} }
@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) {
if (isConnected())
{
Intent intent = new Intent();
intent.putExtra(EXTRA_PATH, path);
activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent);
}
else
{
activity.startFileUsageProcess(path, requestCode);
}
}
@Override
public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) {
Log.d("KP2AJ", "OnCreate");
}
@Override
public void onResume(FileStorageSetupActivity activity) {
Log.d("KP2AJ", "OnResume. LoggedIn="+mLoggedIn);
if (mLoggedIn)
{
finishActivityWithSuccess(activity);
return;
}
AndroidAuthSession session = mApi.getSession();
// The next part must be inserted in the onResume() method of the
// activity from which session.startAuthentication() was called, so
// that Dropbox authentication completes properly.
if (session.authenticationSuccessful()) {
try {
// Mandatory call to complete the auth
session.finishAuthentication();
// Store it locally in our app for later use
TokenPair tokens = session.getAccessTokenPair();
storeKeys(tokens.key, tokens.secret);
setLoggedIn(true);
finishActivityWithSuccess(activity);
return;
} catch (Throwable t) {
Log.i(TAG, "Error authenticating", t);
Intent data = new Intent();
data.putExtra(EXTRA_ERROR_MESSAGE, t.getMessage());
((Activity)activity).setResult(Activity.RESULT_CANCELED, data);
((Activity)activity).finish();
return;
}
}
JavaFileStorage.FileStorageSetupActivity storageSetupAct = (JavaFileStorage.FileStorageSetupActivity)activity;
if (storageSetupAct.getState().containsKey("hasStartedAuth"))
{
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");
mApi.getSession().startAuthentication(((Activity)activity));
storageSetupAct.getState().putBoolean("hasStartedAuth", true);
}
}
private void finishActivityWithSuccess(FileStorageSetupActivity setupActivity) {
Log.d("KP2AJ", "Success with authentcating!");
Activity activity = (Activity)setupActivity;
if (setupActivity.getProcessName().equals(PROCESS_NAME_FILE_USAGE_SETUP))
{
Intent data = new Intent();
data.putExtra(EXTRA_IS_FOR_SAVE, setupActivity.isForSave());
data.putExtra(EXTRA_PATH, setupActivity.getPath());
activity.setResult(RESULT_FILEUSAGE_PREPARED, data);
activity.finish();
return;
}
if (setupActivity.getProcessName().equals(PROCESS_NAME_SELECTFILE))
{
Intent data = new Intent();
data.putExtra(EXTRA_PATH, setupActivity.getPath());
activity.setResult(RESULT_FILECHOOSER_PREPARED, data);
activity.finish();
return;
}
Log.w("KP2AJ", "Unknown process: " + setupActivity.getProcessName());
}
@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);
}
} }

View File

@ -0,0 +1,262 @@
package keepass2android.javafilestorage;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
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.json.gson.GsonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.Drive.Children;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.ChildList;
import com.google.api.services.drive.model.ChildReference;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
public class GoogleDriveFileStorage
{};
/*/
public class GoogleDriveFileStorage implements JavaFileStorage {
private static Drive service;
//private GoogleAccountCredential credential;
static final int MAGIC_GDRIVE=2082334;
static final int REQUEST_ACCOUNT_PICKER = MAGIC_GDRIVE+1;
static final int REQUEST_AUTHORIZATION = MAGIC_GDRIVE+2;
class TestConnectionTask extends AsyncTask<Object, Void, Void>
{
@Override
protected Void doInBackground(Object... params) {
Activity activity = (Activity) params[0];
//try to list files:
//todo: is there a simpler way to test if the user is authorized?
try
{
return true;
}
catch (UserRecoverableAuthIOException e) {
activity.startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
}
catch (Throwable t)
{
Intent data = new Intent();
data.putExtra(EXTRA_ERROR_MESSAGE, t.getMessage());
activity.setResult(Activity.RESULT_CANCELED, data);
activity.finish();
}
}
}
private static void printFilesInFolder(Drive service, String folderId)
throws IOException {
Children.List request = service.files().list();
do {
try {
ChildList children = request.execute();
for (ChildReference child : children.getItems()) {
System.out.println("File Id: " + child.getId());
}
request.setPageToken(children.getNextPageToken());
} catch (IOException e) {
System.out.println("An error occurred: " + e);
request.setPageToken(null);
}
} while (request.getPageToken() != null &&
request.getPageToken().length() > 0);
}
public boolean tryConnect(Activity activity) {
List<String> scopes = new ArrayList<String>();
scopes.add(DriveScopes.DRIVE);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(activity, scopes);
String storedAccountName = PreferenceManager.getDefaultSharedPreferences(activity).getString("GDRIVE_ACCOUNT_NAME", null);
if (storedAccountName != null)
{
credential.setSelectedAccountName(storedAccountName);
//try to list files:
//todo: is there a simpler way to test if the user is authorized?
try
{
return true;
}
catch (UserRecoverableAuthIOException e) {
activity.startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
}
catch (Throwable t)
{
return false;
}
}
else
{
activity.startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
return false;
}
}
@Override
public boolean checkForFileChangeFast(String path,
String previousFileVersion) throws Exception {
// TODO Auto-generated method stub
return false;
}
@Override
public String getCurrentFileVersionFast(String path) {
// TODO Auto-generated method stub
return null;
}
@Override
public InputStream openFileForRead(String path) throws Exception {
// TODO Auto-generated method stub
return null;
}
@Override
public void uploadFile(String path, byte[] data, boolean writeTransactional)
throws Exception {
// TODO Auto-generated method stub
}
@Override
public void createFolder(String path) throws Exception {
// TODO Auto-generated method stub
}
@Override
public List<FileEntry> listFiles(String dirName) throws Exception {
// TODO Auto-generated method stub
return null;
}
@Override
public FileEntry getFileEntry(String filename) throws Exception {
// TODO Auto-generated method stub
return null;
}
@Override
public void delete(String path) throws Exception {
// TODO Auto-generated method stub
}
private Drive getDriveService(GoogleAccountCredential credential) {
return new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential)
.build();
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_ACCOUNT_PICKER:
if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) {
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
//credential.setSelectedAccountName(accountName);
todo
return;
}
}
todo
case REQUEST_AUTHORIZATION:
if (resultCode == Activity.RESULT_OK) {
// App is authorized
todo
} else {
// User denied access, show him the account chooser again
activity.startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
}
}
@Override
public void startSelectFile(Activity activity, boolean isForSave,
int requestCode) {
((JavaFileStorage.FileStorageSetupInitiatorActivity)(activity)).startSelectFileProcess(getProtocolId()+"://", isForSave, requestCode);
}
@Override
public void prepareFileUsage(Activity activity, String path, int requestCode) {
((JavaFileStorage.FileStorageSetupInitiatorActivity)(activity)).startFileUsageProcess(path, requestCode);
}
@Override
public String getProtocolId() {
return "gdrive";
}
@Override
public void onResume(Activity activity) {
JavaFileStorage.FileStorageSetupActivity setupAct = (FileStorageSetupActivity) activity;
if (activity.isFinishing())
return;
if (!hasAccount(setupAct))
{
List<String> scopes = new ArrayList<String>();
scopes.add(DriveScopes.DRIVE);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(activity, scopes);
activity.startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
else
{
}
}
@Override
public void onStart(Activity activity) {
// TODO Auto-generated method stub
}
}
*/

View File

@ -4,9 +4,37 @@ import java.io.InputStream;
import java.util.List; import java.util.List;
import android.app.Activity; import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public interface JavaFileStorage { public interface JavaFileStorage {
public static final String PROCESS_NAME_SELECTFILE = "SELECT_FILE";
public static final String PROCESS_NAME_FILE_USAGE_SETUP = "FILE_USAGE_SETUP";
public static final String EXTRA_PROCESS_NAME = "EXTRA_PROCESS_NAME";
public static final String EXTRA_PATH = "fileName"; //match KP2A PasswordActivity Ioc-Path Extra key
public static final String EXTRA_IS_FOR_SAVE = "IS_FOR_SAVE";
public static final String EXTRA_ERROR_MESSAGE = "EXTRA_ERROR_MESSAGE";
public interface FileStorageSetupInitiatorActivity
{
void startSelectFileProcess(String path, boolean isForSave, int requestCode);
void startFileUsageProcess(String path, int requestCode);
void onImmediateResult(int requestCode, int result, Intent intent);
Activity getActivity();
}
public interface FileStorageSetupActivity
{
String getPath();
String getProcessName();
//int getRequestCode();
boolean isForSave();
Bundle getState();
}
public class FileEntry { public class FileEntry {
public String path; public String path;
@ -66,11 +94,26 @@ public class FileEntry {
} }
public boolean tryConnect(Activity activity); //public boolean tryConnect(Activity activity);
public void onResume(); //public void onResume();
public boolean isConnected(); //public void onActivityResult(Activity activity, final int requestCode, final int resultCode, final Intent data);
//public boolean isConnected();
public static int MAGIC_NUMBER_JFS = 874345;
public static int RESULT_FULL_FILENAME_SELECTED = MAGIC_NUMBER_JFS+1;
public static int RESULT_FILECHOOSER_PREPARED = MAGIC_NUMBER_JFS+2;
public static int RESULT_FILEUSAGE_PREPARED = MAGIC_NUMBER_JFS+3;
public boolean requiresSetup(String path);
public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode);
public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode);
public String getProtocolId();
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception; public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception;
@ -88,4 +131,9 @@ public class FileEntry {
public void delete(String path) throws Exception; public void delete(String path) throws Exception;
public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState);
public void onResume(FileStorageSetupActivity activity);
public void onStart(FileStorageSetupActivity activity);
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data);
} }

View File

@ -980,11 +980,14 @@ public class FragmentFiles extends Fragment implements
if (path == null if (path == null
|| !BaseFileProviderUtils.isDirectory(getActivity(), path)) || !BaseFileProviderUtils.isDirectory(getActivity(), path))
{
Log.d(CLASSNAME, "load default path");
path = BaseFileProviderUtils path = BaseFileProviderUtils
.getDefaultPath( .getDefaultPath(
getActivity(), getActivity(),
path == null ? mFileProviderAuthority : path path == null ? mFileProviderAuthority : path
.getAuthority()); .getAuthority());
}
if (path == null) { if (path == null) {
showCannotConnectToServiceAndFinish(); showCannotConnectToServiceAndFinish();

View File

@ -213,10 +213,7 @@ public class DisplayPrefs extends Prefs {
* @since v4.7 beta * @since v4.7 beta
*/ */
public static boolean isRememberLastLocation(Context c) { public static boolean isRememberLastLocation(Context c) {
return p(c).getBoolean( return false; //KP2A: don't allow to remember because of different protocols
c.getString(R.string.afc_pkey_display_remember_last_location),
c.getResources().getBoolean(
R.bool.afc_pkey_display_remember_last_location_def));
} }
/** /**

View File

@ -13,7 +13,7 @@ public class Kp2aFileChooserBridge {
Class<?> cls = FileChooserActivity.class; Class<?> cls = FileChooserActivity.class;
Intent intent = new Intent(ctx, cls); Intent intent = new Intent(ctx, cls);
intent.putExtra(FileChooserActivity.EXTRA_FILE_PROVIDER_AUTHORITY, authority);
intent.putExtra(FileChooserActivity.EXTRA_ROOTPATH, intent.putExtra(FileChooserActivity.EXTRA_ROOTPATH,
BaseFile.genContentIdUriBase(authority) BaseFile.genContentIdUriBase(authority)
.buildUpon() .buildUpon()

View File

@ -230,7 +230,11 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
private MatrixCursor doAnswerApiCommand(Uri uri) { private MatrixCursor doAnswerApiCommand(Uri uri) {
MatrixCursor matrixCursor = null; MatrixCursor matrixCursor = null;
if (BaseFile.CMD_CANCEL.equals(uri.getLastPathSegment())) { String lastPathSegment = uri.getLastPathSegment();
//Log.d(CLASSNAME, "lastPathSegment:" + lastPathSegment);
if (BaseFile.CMD_CANCEL.equals(lastPathSegment)) {
int taskId = ProviderUtils.getIntQueryParam(uri, int taskId = ProviderUtils.getIntQueryParam(uri,
BaseFile.PARAM_TASK_ID, 0); BaseFile.PARAM_TASK_ID, 0);
synchronized (mMapInterruption) { synchronized (mMapInterruption) {
@ -241,15 +245,14 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
mMapInterruption.put(taskId, true); mMapInterruption.put(taskId, true);
} }
return null; return null;
} else if (BaseFile.CMD_GET_DEFAULT_PATH.equals(uri } else if (BaseFile.CMD_GET_DEFAULT_PATH.equals(lastPathSegment)) {
.getLastPathSegment())) {
return null; return null;
}// get default path }// get default path
else if (BaseFile.CMD_IS_ANCESTOR_OF.equals(uri.getLastPathSegment())) { else if (BaseFile.CMD_IS_ANCESTOR_OF.equals(lastPathSegment)) {
return doCheckAncestor(uri); return doCheckAncestor(uri);
} else if (BaseFile.CMD_GET_PARENT.equals(uri.getLastPathSegment())) { } else if (BaseFile.CMD_GET_PARENT.equals(lastPathSegment)) {
{ {
String path = Uri.parse( String path = Uri.parse(
@ -292,7 +295,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
return matrixCursor; return matrixCursor;
} }
} else if (BaseFile.CMD_SHUTDOWN.equals(uri.getLastPathSegment())) { } else if (BaseFile.CMD_SHUTDOWN.equals(lastPathSegment)) {
/* /*
* TODO Stop all tasks. If the activity call this command in * TODO Stop all tasks. If the activity call this command in
* onDestroy(), it seems that this code block will be suspended and * onDestroy(), it seems that this code block will be suspended and
@ -492,7 +495,8 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
//puts the file entry in the cache for later reuse with retrieveFileInfo //puts the file entry in the cache for later reuse with retrieveFileInfo
private void updateFileEntryCache(FileEntry f) { private void updateFileEntryCache(FileEntry f) {
fileEntryMap.put(f.path, f); if (f != null)
fileEntryMap.put(f.path, f);
} }
//removes the file entry from the cache (if cached). Should be called whenever the file changes //removes the file entry from the cache (if cached). Should be called whenever the file changes
private void removeFromCache(String filename, boolean recursive) { private void removeFromCache(String filename, boolean recursive) {
@ -501,11 +505,17 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
if (recursive) if (recursive)
{ {
Set<String> keys = fileEntryMap.keySet(); Set<String> keys = fileEntryMap.keySet();
Set<String> keysToRemove = new HashSet<String>();
for (String key: keys) for (String key: keys)
{ {
if (key.startsWith(key)) if (key.startsWith(key))
fileEntryMap.remove(key); keysToRemove.add(key);
} }
for (String key: keysToRemove)
{
fileEntryMap.remove(key);
}
} }

View File

@ -22,6 +22,7 @@ namespace keepass2android
AlertDialog.Builder builder = new AlertDialog.Builder(ctx); AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.SetTitle(ctx.GetString(Resource.String.ChangeLog_title)); builder.SetTitle(ctx.GetString(Resource.String.ChangeLog_title));
String[] changeLog = { String[] changeLog = {
ctx.GetString(Resource.String.ChangeLog_0_9),
ctx.GetString(Resource.String.ChangeLog_0_8_6), ctx.GetString(Resource.String.ChangeLog_0_8_6),
ctx.GetString(Resource.String.ChangeLog_0_8_5), ctx.GetString(Resource.String.ChangeLog_0_8_5),
ctx.GetString(Resource.String.ChangeLog_0_8_4), ctx.GetString(Resource.String.ChangeLog_0_8_4),

View File

@ -1,20 +1,10 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.Content.PM; using Android.Content.PM;
using Android.Graphics.Drawables;
using Android.OS; using Android.OS;
using Android.Runtime;
using Android.Views; using Android.Views;
using Android.Widget; using Android.Widget;
using KeePassLib;
using KeePassLib.Keys;
using KeePassLib.Security;
using KeePassLib.Serialization;
using keepass2android.Io; using keepass2android.Io;
using keepass2android.view; using keepass2android.view;
using Object = Java.Lang.Object; using Object = Java.Lang.Object;
@ -24,15 +14,17 @@ namespace keepass2android
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden , Theme="@style/NoTitleBar")] [Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden , Theme="@style/NoTitleBar")]
public class FileStorageSelectionActivity : ListActivity public class FileStorageSelectionActivity : ListActivity
{ {
private string _protocolToSetup;
private FileStorageAdapter _fileStorageAdapter; private FileStorageAdapter _fileStorageAdapter;
public const string AllowThirdPartyAppGet = "AllowThirdPartyAppGet";
public const string AllowThirdPartyAppSend = "AllowThirdPartyAppSend";
class FileStorageAdapter: BaseAdapter class FileStorageAdapter: BaseAdapter
{ {
private readonly FileStorageSelectionActivity _context; private readonly FileStorageSelectionActivity _context;
private List<string> _protocolIds = new List<string>(); private readonly List<string> _protocolIds = new List<string>();
public FileStorageAdapter(FileStorageSelectionActivity context) public FileStorageAdapter(FileStorageSelectionActivity context)
{ {
@ -40,8 +32,13 @@ namespace keepass2android
//show all supported protocols: //show all supported protocols:
foreach (IFileStorage fs in App.Kp2a.FileStorages) foreach (IFileStorage fs in App.Kp2a.FileStorages)
_protocolIds.AddRange(fs.SupportedProtocols); _protocolIds.AddRange(fs.SupportedProtocols);
//except file:// //put file:// to the top
_protocolIds.Remove("file"); _protocolIds.Remove("file");
_protocolIds.Insert(0, "file");
if (context.Intent.GetBooleanExtra(AllowThirdPartyAppGet, false))
_protocolIds.Add("androidget");
if (context.Intent.GetBooleanExtra(AllowThirdPartyAppSend, false))
_protocolIds.Add("androidsend");
} }
public override Object GetItem(int position) public override Object GetItem(int position)
@ -69,24 +66,7 @@ namespace keepass2android
private void OnItemSelected(string protocolId) private void OnItemSelected(string protocolId)
{ {
var fs = App.Kp2a.GetFileStorage(protocolId); ReturnProtocol(protocolId);
IFileStorageSetup fssetup = fs.RequiredSetup;
try
{
if ((fssetup == null) || (fssetup.TrySetup(this)))
{
ReturnProtocol(protocolId);
}
else
{
//setup not yet complete
_protocolToSetup = protocolId;
}
}
catch (Exception e)
{
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
}
} }
@ -102,46 +82,16 @@ namespace keepass2android
{ {
base.OnCreate(bundle); base.OnCreate(bundle);
if (bundle != null)
_protocolToSetup = bundle.GetString("_protocolToSetup", null);
SetContentView(Resource.Layout.filestorage_selection); SetContentView(Resource.Layout.filestorage_selection);
_fileStorageAdapter = new FileStorageAdapter(this); _fileStorageAdapter = new FileStorageAdapter(this);
this.ListAdapter = _fileStorageAdapter; ListAdapter = _fileStorageAdapter;
FindViewById<ListView>(Android.Resource.Id.List).ItemClick += FindViewById<ListView>(Android.Resource.Id.List).ItemClick +=
(sender, args) => OnItemSelected((string)_fileStorageAdapter.GetItem(args.Position)); (sender, args) => OnItemSelected((string)_fileStorageAdapter.GetItem(args.Position));
} }
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutString("_protocolToSetup",_protocolToSetup);
}
protected override void OnResume()
{
base.OnResume();
if (!String.IsNullOrEmpty(_protocolToSetup))
{
try
{
string protocolToSetup = _protocolToSetup;
_protocolToSetup = null;
IFileStorageSetupOnResume fsSetup = App.Kp2a.GetFileStorage(protocolToSetup).RequiredSetup as IFileStorageSetupOnResume;
if ((fsSetup == null) || (fsSetup.TrySetupOnResume(this)))
{
ReturnProtocol(protocolToSetup);
}
}
catch (Exception e)
{
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
}
}
}
} }
} }

View File

@ -31,7 +31,7 @@ using Android.Content.PM;
using KeePassLib.Keys; using KeePassLib.Keys;
using KeePassLib.Serialization; using KeePassLib.Serialization;
using KeePassLib.Utility; using KeePassLib.Utility;
using keepass2android.Io;
using MemoryStream = System.IO.MemoryStream; using MemoryStream = System.IO.MemoryStream;
namespace keepass2android namespace keepass2android
@ -189,6 +189,9 @@ namespace keepass2android
} }
} }
break; break;
case (Result)FileStorageResults.FileUsagePrepared:
PeformLoadDatabase();
break;
} }
} }
@ -272,26 +275,11 @@ namespace keepass2android
Window.SetSoftInputMode(SoftInput.StateVisible); Window.SetSoftInputMode(SoftInput.StateVisible);
Button confirmButton = (Button)FindViewById(Resource.Id.pass_ok); Button confirmButton = (Button)FindViewById(Resource.Id.pass_ok);
confirmButton.Click += (sender, e) => { confirmButton.Click += (sender, e) =>
String pass = GetEditText(Resource.Id.password);
String key = GetEditText(Resource.Id.pass_keyfile);
if (pass.Length == 0 && key.Length == 0)
{ {
ErrorMessage(Resource.String.error_nopass); App.Kp2a.GetFileStorage(_ioConnection)
return; .PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult), _ioConnection, 0);
} };
CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock);
App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
Handler handler = new Handler();
LoadDb task = new LoadDb(App.Kp2a, _ioConnection, _loadDbTask, pass, key, new AfterLoad(handler, this));
_loadDbTask = null; // prevent accidental re-use
SetNewDefaultFile();
new ProgressTask(App.Kp2a, this, task).Run();
};
/*CheckBox checkBox = (CheckBox) FindViewById(Resource.Id.show_password); /*CheckBox checkBox = (CheckBox) FindViewById(Resource.Id.show_password);
// Show or hide password // Show or hide password
@ -334,6 +322,32 @@ namespace keepass2android
RetrieveSettings(); RetrieveSettings();
} }
private void PeformLoadDatabase()
{
String pass = GetEditText(Resource.Id.password);
String key = GetEditText(Resource.Id.pass_keyfile);
if (pass.Length == 0 && key.Length == 0)
{
ErrorMessage(Resource.String.error_nopass);
return;
}
CheckBox cbQuickUnlock = (CheckBox) FindViewById(Resource.Id.enable_quickunlock);
App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
//avoid password being visible while loading:
_showPassword = false;
MakePasswordMaskedOrVisible();
Handler handler = new Handler();
LoadDb task = new LoadDb(App.Kp2a, _ioConnection, _loadDbTask, pass, key, new AfterLoad(handler, this));
_loadDbTask = null; // prevent accidental re-use
SetNewDefaultFile();
new ProgressTask(App.Kp2a, this, task).Run();
}
private void MakePasswordMaskedOrVisible() private void MakePasswordMaskedOrVisible()
{ {
TextView password = (TextView) FindViewById(Resource.Id.password); TextView password = (TextView) FindViewById(Resource.Id.password);
@ -470,7 +484,7 @@ namespace keepass2android
//check if FileStorage setup is all done. Usually this should not occur here because the setup is //check if FileStorage setup is all done. Usually this should not occur here because the setup is
//performed in FileSelectActivity, but e.g. if the user unlinks from Dropbox saving might fail and //performed in FileSelectActivity, but e.g. if the user unlinks from Dropbox saving might fail and
//the user is returned here. //the user is returned here.
if (App.Kp2a.GetFileStorage(_ioConnection).RequiredSetup != null) if (App.Kp2a.GetFileStorage(_ioConnection).RequiresSetup(_ioConnection))
{ {
GoToFileSelectActivity(); GoToFileSelectActivity();
} }

View File

@ -1,6 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?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"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="24" android:versionName="0.9 preview" package="keepass2android.keepass2android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14" /> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14" />
<permission android:description="@string/permission_desc"
android:icon="@drawable/ic_launcher"
android:label="KP2A internal file browsing"
android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing"
android:protectionLevel="signature" />
<application android:label="keepass2android" android:icon="@drawable/ic_launcher"> <application android:label="keepass2android" android:icon="@drawable/ic_launcher">
<activity android:name="com.dropbox.client2.android.AuthActivity" android:launchMode="singleTask" android:configChanges="orientation|keyboard"> <activity android:name="com.dropbox.client2.android.AuthActivity" android:launchMode="singleTask" android:configChanges="orientation|keyboard">
<intent-filter> <intent-filter>
@ -13,7 +20,20 @@
</activity> </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.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.history.HistoryProvider" android:authorities="keepass2android.keepass2android.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" />
<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"
android:permission="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing">
<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>
<activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/Base" android:name="keepass2android.PasswordActivity"> <activity android:configChanges="keyboardHidden|orientation" android:label="@string/app_name" android:theme="@style/Base" android:name="keepass2android.PasswordActivity">
<intent-filter android:label="@string/app_name"> <intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
@ -54,4 +74,5 @@
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" />
</manifest> </manifest>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -8,13 +8,16 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"> android:layout_gravity="center_horizontal">
<ScrollView
<ScrollView
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<keepass2android.view.FileSelectButtons
android:id="@+id/file_select" <keepass2android.view.FileSelectButtons
android:layout_width="fill_parent" android:id="@+id/file_select"
android:layout_height="wrap_content" /> android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</ScrollView> </ScrollView>
</LinearLayout> </LinearLayout>
<!-- Small hack because I need to include a list since this is a list activity --> <!-- Small hack because I need to include a list since this is a list activity -->

View File

@ -10,7 +10,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Please select the cloud storage type you want to use:" android:text="@string/select_storage_type"
style="@style/PaddedElement" style="@style/PaddedElement"
android:id="@+id/textView" android:id="@+id/textView"
android:layout_gravity="left|center_vertical"/> android:layout_gravity="left|center_vertical"/>

View File

@ -27,6 +27,7 @@
<string name="issues">http://keepass2android.codeplex.com</string> <string name="issues">http://keepass2android.codeplex.com</string>
<string name="oi_filemanager_market">market://details?id=org.openintents.filemanager</string> <string name="oi_filemanager_market">market://details?id=org.openintents.filemanager</string>
<string name="oi_filemanager_web">https://openintents.googlecode.com/files/FileManager-2.0.2.apk</string> <string name="oi_filemanager_web">https://openintents.googlecode.com/files/FileManager-2.0.2.apk</string>
<string name="permission_desc">KP2A Internal File Browsing Permission</string>
<!-- Preference settings --> <!-- Preference settings -->
<string name="algorithm_key">algorithm</string> <string name="algorithm_key">algorithm</string>

View File

@ -320,15 +320,28 @@
<string name="ok_donate">Tell me more!</string> <string name="ok_donate">Tell me more!</string>
<string name="no_thanks">No, I don\'t like it that much</string> <string name="no_thanks">No, I don\'t like it that much</string>
<string name="select_storage_type">Select the storage type:</string>
<string name="filestoragename_file">Local file</string> <string name="filestoragename_file">Local file</string>
<string name="filestoragename_androidget">Get from third-party app</string>
<string name="filestoragename_androidsend">Send to third-party app</string>
<string name="filestoragename_ftp">FTP</string> <string name="filestoragename_ftp">FTP</string>
<string name="filestoragename_http">HTTP (WebDav)</string> <string name="filestoragename_http">HTTP (WebDav)</string>
<string name="filestoragename_https">HTTPS (WebDav)</string> <string name="filestoragename_https">HTTPS (WebDav)</string>
<string name="filestoragename_dropbox">Dropbox</string> <string name="filestoragename_dropbox">Dropbox</string>
<string name="filestoragename_gdrive">Google Drive</string> <string name="filestoragename_gdrive">Google Drive</string>
<string name="filestorage_setup_title">File access initialization</string>
<string name="ChangeLog_title">Change log</string>
<string name="ChangeLog_0_9">
<b>Version 0.9</b>\n
* Support for Dropbox (Keepass2Android regular edition only)\n
* Integrated custom filechooser (based on android-filechooser by HBA)
</string>
<string name="ChangeLog_title">Change log</string>
<string name="ChangeLog_0_8_6"> <string name="ChangeLog_0_8_6">
<b>Version 0.8.6</b>\n <b>Version 0.8.6</b>\n

View File

@ -20,6 +20,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.Database;
using Android.Provider;
using Android.Widget; using Android.Widget;
using Android.Content.PM; using Android.Content.PM;
using Uri = Android.Net.Uri; using Uri = Android.Net.Uri;
@ -126,35 +128,36 @@ namespace keepass2android
public static void ShowBrowseDialog(string filename, Activity act, int requestCodeBrowse, bool forSaving) public static void ShowBrowseDialog(string filename, Activity act, int requestCodeBrowse, bool forSaving)
{ {
if ((!forSaving) && (IsIntentAvailable(act, Intent.ActionGetContent, "file/*"))) { if ((!forSaving) && (IsIntentAvailable(act, Intent.ActionGetContent, "file/*")))
{
Intent i = new Intent(Intent.ActionGetContent); Intent i = new Intent(Intent.ActionGetContent);
i.SetType("file/*"); i.SetType("file/*");
act.StartActivityForResult(i, requestCodeBrowse); act.StartActivityForResult(i, requestCodeBrowse);
return;
}
if (IsIntentAvailable(act, Intents.FileBrowse, null))
{
Intent i = new Intent(Intents.FileBrowse);
if (filename != null)
i.SetData(Uri.Parse("file://" + filename));
try
{
act.StartActivityForResult(i, requestCodeBrowse);
}
catch (ActivityNotFoundException)
{
BrowserDialog diag = new BrowserDialog(act);
diag.Show();
}
} }
else else
{ {
BrowserDialog diag = new BrowserDialog(act); string defaultPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
diag.Show();
ShowInternalLocalFileChooser(act, requestCodeBrowse, forSaving, defaultPath);
} }
} }
private static void ShowInternalLocalFileChooser(Activity act, int requestCodeBrowse, bool forSaving, string defaultPath)
{
const string fileProviderAuthority = "keepass2android.keepass2android.android-filechooser.localfile";
Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(act,
fileProviderAuthority,
defaultPath);
if (forSaving)
i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true);
act.StartActivityForResult(i, requestCodeBrowse);
}
public static string IntentToFilename(Intent data, Context ctx) public static string IntentToFilename(Intent data, Context ctx)
{ {
#if !EXCLUDE_FILECHOOSER #if !EXCLUDE_FILECHOOSER
@ -165,7 +168,25 @@ namespace keepass2android
Uri uri = (Uri) uris[0]; Uri uri = (Uri) uris[0];
return Group.Pals.Android.Lib.UI.Filechooser.Providers.BaseFileProviderUtils.GetRealUri(ctx, uri).ToString(); return Group.Pals.Android.Lib.UI.Filechooser.Providers.BaseFileProviderUtils.GetRealUri(ctx, uri).ToString();
} }
#endif #endif
try
{
Uri uri = data.Data;
if ((uri != null) && (uri.Scheme == "content"))
{
String[] col = new String[] {MediaStore.MediaColumns.Data};
ICursor c1 = ctx.ContentResolver.Query(uri, col, null, null, null);
c1.MoveToFirst();
return c1.GetString(0);
}
}
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
}
String filename = data.Data.Path; String filename = data.Data.Path;
if (String.IsNullOrEmpty(filename)) if (String.IsNullOrEmpty(filename))

View File

@ -377,7 +377,7 @@ namespace keepass2android
} }
//TODO: catch! //TODO: catch!
throw new Exception("Unknown protocol " + iocInfo); throw new Exception("Unknown protocol " + iocInfo.Path);
} }
public IEnumerable<IFileStorage> FileStorages public IEnumerable<IFileStorage> FileStorages

View File

@ -1,78 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using Android.App;
using Android.Content;
using Android.OS;
using Android.Views;
using Android.Widget;
using Android.Content.PM;
namespace keepass2android
{
/// <summary>
/// Dialog to offer to install OpenIntent file manager if there's no other browser installed
/// </summary>
public class BrowserDialog : Dialog {
public BrowserDialog(Context context) : base(context)
{
}
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.browser_install);
SetTitle(Resource.String.file_browser);
Button cancel = (Button) FindViewById(Resource.Id.cancel);
cancel.Click += (sender, e) => Cancel();
Button market = (Button) FindViewById(Resource.Id.install_market);
market.Click += (sender, e) => {
Util.GotoUrl(Context, Resource.String.oi_filemanager_market);
Cancel();
}
;
if (!IsMarketInstalled()) {
market.Visibility = ViewStates.Gone;
}
Button web = (Button) FindViewById(Resource.Id.install_web);
web.Click += (sender, e) => {
Util.GotoUrl(Context, Resource.String.oi_filemanager_web);
Cancel();
}
;
}
private bool IsMarketInstalled() {
PackageManager pm = Context.PackageManager;
try {
pm.GetPackageInfo("com.android.vending", 0);
} catch (PackageManager.NameNotFoundException) {
return false;
}
return true;
}
}
}

View File

@ -67,6 +67,7 @@ namespace keepass2android
{ {
try try
{ {
Kp2aLog.Log("Provider.GetFileEntry " + filename);
return ConvertFileDescription(App.Kp2a.GetFileStorage(filename).GetFileDescription(ConvertPathToIoc(filename))); return ConvertFileDescription(App.Kp2a.GetFileStorage(filename).GetFileDescription(ConvertPathToIoc(filename)));
} }
catch (Exception e) catch (Exception e)
@ -80,6 +81,7 @@ namespace keepass2android
protected override void ListFiles(int taskId, string dirName, bool showHiddenFiles, int filterMode, int limit, string positiveRegex, protected override void ListFiles(int taskId, string dirName, bool showHiddenFiles, int filterMode, int limit, string positiveRegex,
string negativeRegex, IList<FileEntry> fileList, bool[] hasMoreFiles) string negativeRegex, IList<FileEntry> fileList, bool[] hasMoreFiles)
{ {
Kp2aLog.Log("Provider.ListFiles " + dirName);
try try
{ {
var dirContents = App.Kp2a.GetFileStorage(dirName).ListContents(ConvertPathToIoc(dirName)); var dirContents = App.Kp2a.GetFileStorage(dirName).ListContents(ConvertPathToIoc(dirName));

View File

@ -63,7 +63,6 @@ namespace keepass2android
view.FileSelectButtons _fileSelectButtons; view.FileSelectButtons _fileSelectButtons;
internal AppTask AppTask; internal AppTask AppTask;
private IOConnectionInfo _iocToLaunch;
public const string NoForwardToPasswordActivity = "NoForwardToPasswordActivity"; public const string NoForwardToPasswordActivity = "NoForwardToPasswordActivity";
@ -234,26 +233,18 @@ namespace keepass2android
EventHandler openFileButtonClick = (sender, e) => EventHandler openFileButtonClick = (sender, e) =>
{ {
string defaultFilename = Android.OS.Environment.ExternalStorageDirectory + GetString(Resource.String.default_file_path); Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
const string detailsText = ""; intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, true);
ShowFilenameDialog(true, false, true, defaultFilename, detailsText, Intents.RequestCodeFileBrowseForOpen); StartActivityForResult(intent, 0);
}; };
openFileButton.Click += openFileButtonClick; openFileButton.Click += openFileButtonClick;
//OPEN URL //OPEN URL
Button openUrlButton = (Button)FindViewById(Resource.Id.start_open_url); Button openUrlButton = (Button)FindViewById(Resource.Id.start_open_url);
#if NoNet
openUrlButton.Visibility = ViewStates.Gone; openUrlButton.Visibility = ViewStates.Gone;
#endif
//EventHandler openUrlButtonClick = (sender, e) => ShowFilenameDialog(true, false, false, "", GetString(Resource.String.enter_filename_details_url), Intents.RequestCodeFileBrowseForOpen); //EventHandler openUrlButtonClick = (sender, e) => ShowFilenameDialog(true, false, false, "", GetString(Resource.String.enter_filename_details_url), Intents.RequestCodeFileBrowseForOpen);
openUrlButton.Click += (sender, args) =>
{
Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
StartActivityForResult(intent, 0);
};
//CREATE NEW //CREATE NEW
Button createNewButton = (Button)FindViewById(Resource.Id.start_create); Button createNewButton = (Button)FindViewById(Resource.Id.start_create);
@ -282,20 +273,6 @@ namespace keepass2android
{ {
AppTask = AppTask.CreateFromBundle(savedInstanceState); AppTask = AppTask.CreateFromBundle(savedInstanceState);
_recentMode = savedInstanceState.GetBoolean(BundleKeyRecentMode, _recentMode); _recentMode = savedInstanceState.GetBoolean(BundleKeyRecentMode, _recentMode);
string filenameToLaunch = savedInstanceState.GetString(PasswordActivity.KeyFilename);
if (filenameToLaunch != null)
{
_iocToLaunch = new IOConnectionInfo()
{
Path = filenameToLaunch,
UserName = savedInstanceState.GetString(PasswordActivity.KeyServerusername),
Password = savedInstanceState.GetString(PasswordActivity.KeyServerpassword),
CredSaveMode = (IOCredSaveMode) savedInstanceState.GetInt(PasswordActivity.KeyServercredmode)
};
}
} }
} }
@ -322,13 +299,6 @@ namespace keepass2android
AppTask.ToBundle(outState); AppTask.ToBundle(outState);
outState.PutBoolean(BundleKeyRecentMode, _recentMode); outState.PutBoolean(BundleKeyRecentMode, _recentMode);
if (_iocToLaunch != null)
{
outState.PutString(PasswordActivity.KeyFilename, _iocToLaunch.Path);
outState.PutString(PasswordActivity.KeyServerusername, _iocToLaunch.UserName);
outState.PutString(PasswordActivity.KeyServerpassword, _iocToLaunch.Password);
outState.PutInt(PasswordActivity.KeyServercredmode, (int)_iocToLaunch.CredSaveMode);
}
} }
private class LaunchGroupActivity : FileOnFinish { private class LaunchGroupActivity : FileOnFinish {
@ -407,15 +377,6 @@ namespace keepass2android
void LaunchPasswordActivityForIoc(IOConnectionInfo ioc) void LaunchPasswordActivityForIoc(IOConnectionInfo ioc)
{ {
IFileStorage fileStorage = App.Kp2a.GetFileStorage(ioc); IFileStorage fileStorage = App.Kp2a.GetFileStorage(ioc);
if (fileStorage.RequiredSetup != null)
{
if (!fileStorage.RequiredSetup.TrySetup(this))
{
//store ioc to launch. TrySetup hopefully launched another activity so we can check again in OnResume
_iocToLaunch = ioc;
return;
}
}
if (fileStorage.RequiresCredentials(ioc)) if (fileStorage.RequiresCredentials(ioc))
{ {
@ -485,9 +446,19 @@ namespace keepass2android
if (resultCode == KeePass.ExitFileStorageSelectionOk) if (resultCode == KeePass.ExitFileStorageSelectionOk)
{ {
#if !EXCLUDE_FILECHOOSER #if !EXCLUDE_FILECHOOSER
Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, FileChooserFileProvider.TheAuthority, data.GetStringExtra("protocolId")+":///"); string protocolId = data.GetStringExtra("protocolId");
if (protocolId == "androidget")
{
string defaultFilename = Environment.ExternalStorageDirectory +
GetString(Resource.String.default_file_path);
Util.ShowBrowseDialog(defaultFilename, this, Intents.RequestCodeFileBrowseForOpen, false);
}
else
{
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this, OnActivityResult), false, 0, protocolId);
}
StartActivityForResult(i, Intents.RequestCodeFileBrowseForOpen);
#else #else
Toast.MakeText(this, "TODO: make this more flexible.", ToastLength.Long).Show(); Toast.MakeText(this, "TODO: make this more flexible.", ToastLength.Long).Show();
IOConnectionInfo ioc = new IOConnectionInfo IOConnectionInfo ioc = new IOConnectionInfo
@ -528,6 +499,36 @@ namespace keepass2android
} }
} }
if (resultCode == (Result) FileStorageResults.FileUsagePrepared)
{
IOConnectionInfo ioc = new IOConnectionInfo();
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
LaunchPasswordActivityForIoc(ioc);
}
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
{
IOConnectionInfo ioc = new IOConnectionInfo();
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
#if !EXCLUDE_FILECHOOSER
StartFileChooser(ioc.Path);
#endif
}
}
private void StartFileChooser(string defaultPath)
{
Kp2aLog.Log("FSA: defaultPath="+defaultPath);
string fileProviderAuthority = FileChooserFileProvider.TheAuthority;
if (defaultPath.StartsWith("file://"))
{
fileProviderAuthority = "keepass2android.keepass2android.android-filechooser.localfile";
defaultPath = Environment.ExternalStorageDirectory + GetString(Resource.String.default_file_path);
}
Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority,
defaultPath);
StartActivityForResult(i, Intents.RequestCodeFileBrowseForOpen);
} }
@ -546,26 +547,7 @@ namespace keepass2android
return; return;
} }
//check if we are resuming after setting up the file storage:
if (_iocToLaunch != null)
{
try
{
IOConnectionInfo iocToLaunch = _iocToLaunch;
_iocToLaunch = null;
IFileStorageSetupOnResume fsSetup = App.Kp2a.GetFileStorage(iocToLaunch).RequiredSetup as IFileStorageSetupOnResume;
if ((fsSetup == null) || (fsSetup.TrySetupOnResume(this)))
{
LaunchPasswordActivityForIoc(iocToLaunch);
}
}
catch (Exception e)
{
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
}
}
_fileSelectButtons.UpdateExternalStorageWarning(); _fileSelectButtons.UpdateExternalStorageWarning();
@ -591,7 +573,7 @@ namespace keepass2android
StartManagingCursor(filesCursor); StartManagingCursor(filesCursor);
filesCursor.MoveToFirst(); filesCursor.MoveToFirst();
IOConnectionInfo ioc = _DbHelper.CursorToIoc(filesCursor); IOConnectionInfo ioc = _DbHelper.CursorToIoc(filesCursor);
if (App.Kp2a.GetFileStorage(ioc).RequiredSetup == null) if (App.Kp2a.GetFileStorage(ioc).RequiresSetup(ioc) == false)
{ {
LaunchPasswordActivityForIoc(ioc); LaunchPasswordActivityForIoc(ioc);
} }
@ -684,6 +666,10 @@ namespace keepass2android
Android.Database.ICursor cursor = ca.Cursor; Android.Database.ICursor cursor = ca.Cursor;
cursor.Requery(); cursor.Requery();
} }
} }
} }

View File

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
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;
using Keepass2android.Javafilestorage;
using keepass2android.Io;
using JavaFileStorage = Keepass2android.Javafilestorage.JavaFileStorage;
namespace keepass2android.fileselect
{
[Activity(Label = "@string/filestorage_setup_title",Theme="@style/Base")]
public class FileStorageSetupActivity : Activity, IFileStorageSetupActivity
#if !EXCLUDE_JAVAFILESTORAGE
,IJavaFileStorageFileStorageSetupActivity
#endif
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Ioc = new IOConnectionInfo();
PasswordActivity.SetIoConnectionFromIntent(Ioc, Intent);
Kp2aLog.Log("FSSA.OnCreate with " + Ioc.Path);
ProcessName = Intent.GetStringExtra(FileStorageSetupDefs.ExtraProcessName);
IsForSave = Intent.GetBooleanExtra(FileStorageSetupDefs.ExtraIsForSave, false);
if (bundle == null)
State = new Bundle();
else
State = (Bundle) bundle.Clone();
App.Kp2a.GetFileStorage(Ioc).OnCreate(this, bundle);
}
protected override void OnStart()
{
base.OnStart();
App.Kp2a.GetFileStorage(Ioc).OnStart(this);
}
protected override void OnResume()
{
base.OnResume();
App.Kp2a.GetFileStorage(Ioc).OnResume(this);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
App.Kp2a.GetFileStorage(Ioc).OnActivityResult(this, requestCode, (int) resultCode, data);
}
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutAll(State);
}
public IOConnectionInfo Ioc { get; private set; }
public string Path
{
get
{
return App.Kp2a.GetFileStorage(Ioc).IocToPath(Ioc);
}
}
public string ProcessName { get; private set; }
public bool IsForSave { get; private set; }
public Bundle State { get; private set; }
}
}

View File

@ -0,0 +1,76 @@
using System;
using Android.App;
using Android.Content;
using KeePassLib.Serialization;
using Keepass2android.Javafilestorage;
using keepass2android.Io;
using keepass2android.fileselect;
namespace keepass2android
{
public class FileStorageSetupInitiatorActivity:
#if !EXCLUDE_JAVAFILESTORAGE
Java.Lang.Object
,IJavaFileStorageFileStorageSetupInitiatorActivity
#endif
, IFileStorageSetupInitiatorActivity
{
private readonly Activity _activity;
private readonly Action<int, Result, Intent> _onActivityResult;
public FileStorageSetupInitiatorActivity(Activity activity, Action<int,Result,Intent> onActivityResult)
{
_activity = activity;
_onActivityResult = onActivityResult;
}
public void StartSelectFileProcess(IOConnectionInfo ioc, bool isForSave, int requestCode)
{
Kp2aLog.Log("FSSIA: StartSelectFileProcess "+ioc.Path);
Intent fileStorageSetupIntent = new Intent(_activity, typeof(FileStorageSetupActivity));
fileStorageSetupIntent.PutExtra(FileStorageSetupDefs.ExtraProcessName, FileStorageSetupDefs.ProcessNameSelectfile);
fileStorageSetupIntent.PutExtra(FileStorageSetupDefs.ExtraIsForSave, isForSave);
PasswordActivity.PutIoConnectionToIntent(ioc, fileStorageSetupIntent);
_activity.StartActivityForResult(fileStorageSetupIntent, requestCode);
}
public void StartFileUsageProcess(IOConnectionInfo ioc, int requestCode)
{
Intent fileStorageSetupIntent = new Intent(_activity, typeof(FileStorageSetupActivity));
fileStorageSetupIntent.PutExtra(FileStorageSetupDefs.ExtraProcessName, FileStorageSetupDefs.ProcessNameFileUsageSetup);
PasswordActivity.PutIoConnectionToIntent(ioc, fileStorageSetupIntent);
_activity.StartActivityForResult(fileStorageSetupIntent, requestCode);
}
public void OnImmediateResult(int requestCode, int result, Intent intent)
{
_onActivityResult(requestCode, (Result)result, intent);
}
public Activity Activity {
get { return _activity; }
}
public void IocToIntent(Intent intent, IOConnectionInfo ioc)
{
PasswordActivity.PutIoConnectionToIntent(ioc, intent);
}
public void PerformManualFileSelect(bool isForSave, int requestCode, string protocolId)
{
throw new NotImplementedException();
}
public void StartFileUsageProcess(string p0, int p1)
{
StartFileUsageProcess(new IOConnectionInfo() { Path = p0 }, p1);
}
public void StartSelectFileProcess(string p0, bool p1, int p2)
{
StartSelectFileProcess(new IOConnectionInfo() { Path = p0 }, p1, p2);
}
}
}

View File

@ -24,7 +24,7 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>False</Optimize> <Optimize>False</Optimize>
<OutputPath>bin\Debug</OutputPath> <OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants> <DefineConstants>DEBUG;INCLUDE_TWOFISH;INCLUDE_KEYBOARD;INCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause> <ConsolePause>False</ConsolePause>
@ -79,6 +79,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="fileselect\FileChooserFileProvider.cs" /> <Compile Include="fileselect\FileChooserFileProvider.cs" />
<Compile Include="fileselect\FileStorageSetupActivity.cs" />
<Compile Include="fileselect\FileStorageSetupInitiatorActivity.cs" />
<Compile Include="FileStorageSelectionActivity.cs" /> <Compile Include="FileStorageSelectionActivity.cs" />
<Compile Include="DonateReminder.cs" /> <Compile Include="DonateReminder.cs" />
<Compile Include="app\ApplicationBroadcastReceiver.cs" /> <Compile Include="app\ApplicationBroadcastReceiver.cs" />
@ -96,7 +98,6 @@
<Compile Include="settings\DatabaseSettingsActivity.cs" /> <Compile Include="settings\DatabaseSettingsActivity.cs" />
<Compile Include="Utils\Util.cs" /> <Compile Include="Utils\Util.cs" />
<Compile Include="intents\Intents.cs" /> <Compile Include="intents\Intents.cs" />
<Compile Include="fileselect\BrowserDialog.cs" />
<Compile Include="timeout\TimeoutHelper.cs" /> <Compile Include="timeout\TimeoutHelper.cs" />
<Compile Include="GroupActivity.cs" /> <Compile Include="GroupActivity.cs" />
<Compile Include="GroupBaseActivity.cs" /> <Compile Include="GroupBaseActivity.cs" />
@ -253,7 +254,7 @@
<None Include="Resources\values-vi\strings.xml"> <None Include="Resources\values-vi\strings.xml">
<Visible>False</Visible> <Visible>False</Visible>
</None> </None>
<None Include="..\java\kp2akeytransform\libs\mips\libfinal-key.so" Condition="!$(DefineConstants.Contains('EXCLUDE_KEYTRANSFORM'))"> <None Include="..\java\kp2akeytransform\libs\mips\libfinal-key.so" Condition="!$(DefineConstants.Contains('EXCLUDE_KEYTRANSFORM'))">
<Link>libs\mips\libfinal-key.so</Link> <Link>libs\mips\libfinal-key.so</Link>
</None> </None>
<None Include="Resources\drawable-hdpi\Thumbs.db"> <None Include="Resources\drawable-hdpi\Thumbs.db">
@ -700,11 +701,11 @@
</MonoDevelop> </MonoDevelop>
</ProjectExtensions> </ProjectExtensions>
<ItemGroup> <ItemGroup>
<AndroidNativeLibrary Include="..\java\kp2akeytransform\libs\armeabi\libfinal-key.so" Condition="!$(DefineConstants.Contains('EXCLUDE_KEYTRANSFORM'))"> <AndroidNativeLibrary Include="..\java\kp2akeytransform\libs\armeabi\libfinal-key.so" Condition="!$(DefineConstants.Contains('EXCLUDE_KEYTRANSFORM'))">
<Link>libs\armeabi\libfinal-key.so</Link> <Link>libs\armeabi\libfinal-key.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</AndroidNativeLibrary> </AndroidNativeLibrary>
<AndroidNativeLibrary Include="..\java\kp2akeytransform\libs\armeabi-v7a\libfinal-key.so" Condition="!$(DefineConstants.Contains('EXCLUDE_KEYTRANSFORM'))"> <AndroidNativeLibrary Include="..\java\kp2akeytransform\libs\armeabi-v7a\libfinal-key.so" Condition="!$(DefineConstants.Contains('EXCLUDE_KEYTRANSFORM'))">
<Link>libs\armeabi-v7a\libfinal-key.so</Link> <Link>libs\armeabi-v7a\libfinal-key.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</AndroidNativeLibrary> </AndroidNativeLibrary>
@ -816,4 +817,28 @@
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\ic_storage_http.png" /> <AndroidResource Include="Resources\drawable-hdpi\ic_storage_http.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\ic_storage_androidget.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\ic_storage_androidsend.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\ic_storage_androidget.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\ic_storage_androidsend.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\ic_storage_file.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\ic_storage_file.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\ic_keepass2android.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\ic_keepass2android_nonet.png" />
</ItemGroup>
</Project> </Project>