moved logic of StorageSelectionActivity to testable base class

added tests for StorageSelectionActivity, fixed bugs
if database is read-only, no edit buttons are displayed
This commit is contained in:
Philipp Crocoll 2014-11-24 18:21:45 +01:00
parent 2593119dec
commit 415049af7a
14 changed files with 1290 additions and 327 deletions

View File

@ -41,8 +41,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MasterKeeWinPlugin", "Maste
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplePlugin", "SamplePlugin\SamplePlugin.csproj", "{4C1BB6F8-D2CD-49C2-9053-21705681356C}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplePlugin", "SamplePlugin\SamplePlugin.csproj", "{4C1BB6F8-D2CD-49C2-9053-21705681356C}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeNeo", "..\..\..\..\..\Dropbox\programmieren\KeepassPlugins\AutoTypeNeo\AutoTypeNeo.csproj", "{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -446,24 +444,6 @@ Global
{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU {4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU {4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU {4C1BB6F8-D2CD-49C2-9053-21705681356C}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|Win32.ActiveCfg = Debug|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Debug|x64.ActiveCfg = Debug|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Any CPU.Build.0 = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|Win32.ActiveCfg = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.Release|x64.ActiveCfg = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -474,7 +454,6 @@ Global
{9A4C5BAA-1A8A-49B4-BBC3-60D4871FB36C} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} {9A4C5BAA-1A8A-49B4-BBC3-60D4871FB36C} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3}
{BBF77830-BC7D-4F28-A255-A348B5C6A925} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} {BBF77830-BC7D-4F28-A255-A348B5C6A925} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3}
{4C1BB6F8-D2CD-49C2-9053-21705681356C} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3} {4C1BB6F8-D2CD-49C2-9053-21705681356C} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3}
{4E5181A6-5FC4-4963-87B3-BAD15E7A765B} = {CAC7DBC4-E21F-41E1-B33A-E3A04585F6A3}
EndGlobalSection EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = keepass2android\keepass2android.csproj StartupItem = keepass2android\keepass2android.csproj

View File

@ -79,8 +79,17 @@ namespace keepass2android
Handler UiThreadHandler { get; } Handler UiThreadHandler { get; }
IProgressDialog CreateProgressDialog(Context ctx); IProgressDialog CreateProgressDialog(Context ctx);
/// <summary>
/// returns the file storage for the given ioc. might be a caching file storage
/// </summary>
IFileStorage GetFileStorage(IOConnectionInfo iocInfo); IFileStorage GetFileStorage(IOConnectionInfo iocInfo);
/// <summary>
/// returns the file storage for the given ioc. if allowCache=false, no cached file storage is returned
/// </summary>
IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache);
void TriggerReload(Context context); void TriggerReload(Context context);
/// <summary> /// <summary>
@ -90,5 +99,7 @@ namespace keepass2android
//bool OnServerCertificateError(int certificateProblem); //bool OnServerCertificateError(int certificateProblem);
RemoteCertificateValidationCallback CertificateValidationCallback { get; } RemoteCertificateValidationCallback CertificateValidationCallback { get; }
} }
} }

View File

@ -80,6 +80,7 @@
<Compile Include="Io\SkyDriveFileStorage.cs" /> <Compile Include="Io\SkyDriveFileStorage.cs" />
<Compile Include="IProgressDialog.cs" /> <Compile Include="IProgressDialog.cs" />
<Compile Include="PreferenceKey.cs" /> <Compile Include="PreferenceKey.cs" />
<Compile Include="SelectStorageLocationActivityBase.cs" />
<Compile Include="UiStringKey.cs" /> <Compile Include="UiStringKey.cs" />
<Compile Include="database\Database.cs" /> <Compile Include="database\Database.cs" />
<Compile Include="database\edit\ActionOnFinish.cs" /> <Compile Include="database\edit\ActionOnFinish.cs" />

View File

@ -0,0 +1,318 @@
using System;
using Android.App;
using Android.Content;
using Android.Widget;
using Java.Net;
using KeePassLib.Serialization;
using keepass2android.Io;
namespace keepass2android
{
/// <summary>
/// base class for SelectStorageLocationActivity containing testable (non-UI) code
/// </summary>
public abstract class SelectStorageLocationActivityBase: Activity
{
public enum WritableRequirements
{
ReadOnly = 0,
WriteDesired = 1,
WriteDemanded = 2
}
protected const int RequestCodeFileStorageSelectionForPrimarySelect = 983713;
private const int RequestCodeFileStorageSelectionForCopyToWritableLocation = 983714;
private const int RequestCodeFileFileBrowseForWritableLocation = 983715;
private const int RequestCodeFileBrowseForOpen = 983716;
protected IOConnectionInfo _selectedIoc;
private IKp2aApp _app;
public SelectStorageLocationActivityBase(IKp2aApp app)
{
_app = app;
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if ((requestCode == RequestCodeFileStorageSelectionForPrimarySelect) || ((requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation)))
{
int browseRequestCode = RequestCodeFileBrowseForOpen;
if (requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation)
{
browseRequestCode = RequestCodeFileFileBrowseForWritableLocation;
}
if (resultCode == ExitFileStorageSelectionOk)
{
string protocolId = data.GetStringExtra("protocolId");
if (protocolId == "androidget")
{
ShowAndroidBrowseDialog(RequestCodeFileBrowseForOpen, false);
}
else
{
bool isForSave = (requestCode == RequestCodeFileStorageSelectionForPrimarySelect) ?
IsStorageSelectionForSave : true;
StartSelectFile(isForSave, browseRequestCode, protocolId);
}
}
else
{
ReturnCancel();
}
}
if ((requestCode == RequestCodeFileBrowseForOpen) || (requestCode == RequestCodeFileFileBrowseForWritableLocation))
{
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
{
IOConnectionInfo ioc = new IOConnectionInfo();
SetIoConnectionFromIntent(ioc, data);
bool isForSave = (requestCode == RequestCodeFileFileBrowseForWritableLocation) ?
true : IsStorageSelectionForSave;
StartFileChooser(ioc.Path, requestCode, isForSave);
return;
}
if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE")))
{
ShowToast(data.GetStringExtra("EXTRA_ERROR_MESSAGE"));
}
if (resultCode == Result.Ok)
{
string filename = IntentToFilename(data);
if (filename != null)
{
if (filename.StartsWith("file://"))
{
filename = filename.Substring(7);
filename = URLDecoder.Decode(filename);
}
IOConnectionInfo ioc = new IOConnectionInfo
{
Path = filename
};
IocSelected(ioc, requestCode);
}
else
{
if (data.Data.Scheme == "content")
{
IocSelected(IOConnectionInfo.FromPath(data.DataString), requestCode);
}
else
{
ShowInvalidSchemeMessage(data.DataString);
ReturnCancel();
}
}
}
else
{
ReturnCancel();
}
}
}
protected abstract void ShowToast(string text);
protected abstract void ShowInvalidSchemeMessage(string dataString);
protected abstract string IntentToFilename(Intent data);
protected abstract void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data);
protected abstract Result ExitFileStorageSelectionOk { get; }
/// <summary>
/// Starts the appropriate file selection process (either manual file select or prepare filechooser + filechooser)
/// </summary>
/// <param name="isForSave"></param>
/// <param name="browseRequestCode"></param>
/// <param name="protocolId"></param>
protected abstract void StartSelectFile(bool isForSave, int browseRequestCode, string protocolId);
protected abstract void ShowAndroidBrowseDialog(int requestCode, bool isForSave);
protected abstract bool IsStorageSelectionForSave { get; }
private void IocSelected(IOConnectionInfo ioc, int requestCode)
{
if (requestCode == RequestCodeFileFileBrowseForWritableLocation)
{
IocForCopySelected(ioc);
}
else if (requestCode == RequestCodeFileBrowseForOpen)
{
PrimaryIocSelected(ioc);
}
else
{
#if DEBUG
throw new Exception("invalid request code!");
#endif
}
}
private void IocForCopySelected(IOConnectionInfo targetIoc)
{
PerformCopy(() =>
{
IOConnectionInfo sourceIoc = _selectedIoc;
try
{
CopyFile(targetIoc, sourceIoc);
}
catch (Exception e)
{
return () =>
{
ShowToast(_app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message);
ReturnCancel();
};
}
return () => { ReturnOk(targetIoc); };
});
}
protected abstract void PerformCopy(Func<Action> copyAndReturnPostExecute);
private void MoveToWritableLocation(IOConnectionInfo ioc)
{
_selectedIoc = ioc;
StartFileStorageSelection(RequestCodeFileStorageSelectionForCopyToWritableLocation, false, false);
}
protected abstract void StartFileStorageSelection(int requestCode,
bool allowThirdPartyGet, bool allowThirdPartySend);
protected bool OnReceivedSftpData(string filename, int requestCode, bool isForSave)
{
IOConnectionInfo ioc = new IOConnectionInfo { Path = filename };
#if !EXCLUDE_FILECHOOSER
StartFileChooser(ioc.Path, requestCode, isForSave);
#else
IocSelected(ioc, requestCode);
#endif
return true;
}
protected abstract void StartFileChooser(string path, int requestCode, bool isForSave);
protected bool OnOpenButton(String fileName, int requestCode)
{
IOConnectionInfo ioc = new IOConnectionInfo
{
Path = fileName
};
IocSelected(ioc, requestCode);
return true;
}
protected virtual void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc)
{
IFileStorage sourceStorage = _app.GetFileStorage(sourceIoc, false); //don't cache source. file won't be used ever again
IFileStorage targetStorage = _app.GetFileStorage(targetIoc);
using (
var writeTransaction = targetStorage.OpenWriteTransaction(targetIoc,
_app.GetBooleanPreference(
PreferenceKey.UseFileTransactions)))
{
using (var writeStream = writeTransaction.OpenFile())
{
sourceStorage.OpenFileForRead(sourceIoc).CopyTo(writeStream);
}
writeTransaction.CommitWrite();
}
}
private void PrimaryIocSelected(IOConnectionInfo ioc)
{
var filestorage = _app.GetFileStorage(ioc, false);
if (!filestorage.IsPermanentLocation(ioc))
{
string message = _app.GetResourceString(UiStringKey.FileIsTemporarilyAvailable) + " " + _app.GetResourceString(UiStringKey.CopyFileRequired) + " " + _app.GetResourceString(UiStringKey.ClickOkToSelectLocation);
EventHandler<DialogClickEventArgs> onOk = (sender, args) => { MoveToWritableLocation(ioc); };
EventHandler<DialogClickEventArgs> onCancel = (sender, args) => { ReturnCancel(); };
ShowAlertDialog(message, onOk, onCancel);
return;
}
if ((RequestedWritableRequirements != WritableRequirements.ReadOnly) && (filestorage.IsReadOnly(ioc)))
{
string readOnlyExplanation = _app.GetResourceString(UiStringKey.FileIsReadOnly);
BuiltInFileStorage builtInFileStorage = filestorage as BuiltInFileStorage;
if (builtInFileStorage != null)
{
if (builtInFileStorage.IsReadOnlyBecauseKitkatRestrictions(ioc))
readOnlyExplanation = _app.GetResourceString(UiStringKey.FileIsReadOnlyOnKitkat);
}
EventHandler<DialogClickEventArgs> onOk = (sender, args) => { MoveToWritableLocation(ioc); };
EventHandler<DialogClickEventArgs> onCancel = (sender, args) =>
{
if (RequestedWritableRequirements == WritableRequirements.WriteDemanded)
ReturnCancel();
else
ReturnOk(ioc);
};
ShowAlertDialog(readOnlyExplanation + " "
+ (RequestedWritableRequirements == WritableRequirements.WriteDemanded ?
_app.GetResourceString(UiStringKey.CopyFileRequired)
: _app.GetResourceString(UiStringKey.CopyFileRequiredForEditing))
+ " "
+ _app.GetResourceString(UiStringKey.ClickOkToSelectLocation), onOk, onCancel);
return;
}
ReturnOk(ioc);
}
protected abstract void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel);
protected abstract WritableRequirements RequestedWritableRequirements { get; }
protected abstract void ReturnOk(IOConnectionInfo ioc);
protected abstract void ReturnCancel();
}
}

View File

@ -48,6 +48,12 @@ namespace keepass2android
SynchronizingOtpAuxFile, SynchronizingOtpAuxFile,
SavingOtpAuxFile, SavingOtpAuxFile,
CertificateFailure, CertificateFailure,
exporting_database exporting_database,
FileIsTemporarilyAvailable,
CopyFileRequired,
ClickOkToSelectLocation,
FileIsReadOnly,
FileIsReadOnlyOnKitkat,
CopyFileRequiredForEditing
} }
} }

View File

@ -119,7 +119,7 @@ namespace keepass2android
KpDatabase = pwDatabase; KpDatabase = pwDatabase;
SearchHelper = new SearchDbHelper(app); SearchHelper = new SearchDbHelper(app);
CanWrite = databaseLoader.CanWrite; CanWrite = databaseLoader.CanWrite && !fileStorage.IsReadOnly(iocInfo);
} }
/// <summary> /// <summary>

View File

@ -84,6 +84,7 @@
<Compile Include="TestCachingFileStorage.cs" /> <Compile Include="TestCachingFileStorage.cs" />
<Compile Include="TestSaveDb.cs" /> <Compile Include="TestSaveDb.cs" />
<Compile Include="TestSaveDbCached.cs" /> <Compile Include="TestSaveDbCached.cs" />
<Compile Include="TestSelectStorageLocation.cs" />
<Compile Include="TestSynchronizeCachedDatabase.cs" /> <Compile Include="TestSynchronizeCachedDatabase.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -23,7 +23,7 @@ namespace Kp2aUnitTests
//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileOnly")); //runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdb1WithKeyfileOnly"));
runner.AddTests(new List<Type> { typeof(TestBuiltInFileStorage) }); runner.AddTests(new List<Type> { typeof(TestSelectStorageLocation) });
//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)}); //runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)});
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure")); //runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure"));
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure")); //runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure"));

View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Java.IO;
using KeePassLib;
using KeePassLib.Interfaces;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using keepass2android;
using keepass2android.Io;
namespace Kp2aUnitTests
{
[TestClass]
internal class TestBuiltInFileStorage
{
[TestMethod]
public void ReadOnlyKitKat()
{
var storage = new BuiltInFileStorage(new TestKp2aApp());
var extFile = "/storage/sdcard1/file.txt";
Assert.IsTrue(storage.IsReadOnly(IOConnectionInfo.FromPath(extFile)));
Assert.IsTrue(storage.IsReadOnly(IOConnectionInfo.FromPath(extFile)));
Assert.IsFalse(storage.IsReadOnly(IOConnectionInfo.FromPath(Application.Context.GetExternalFilesDir(null).AbsolutePath+ "/file.txt")));
}
}
}

View File

@ -134,11 +134,18 @@ namespace Kp2aUnitTests
return new ProgressDialogStub(); return new ProgressDialogStub();
} }
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo) public virtual IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
{ {
return FileStorage; return FileStorage;
} }
public virtual IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache)
{
if (FileStorage is CachingFileStorage)
throw new Exception("bad test class");
return FileStorage;
}
public bool TriggerReloadCalled; public bool TriggerReloadCalled;
private TestFileStorage _testFileStorage; private TestFileStorage _testFileStorage;

View File

@ -0,0 +1,790 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using KeePassLib.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using keepass2android;
using keepass2android.Io;
namespace Kp2aUnitTests
{
class TemporaryFileStorage: IFileStorage
{
public IEnumerable<string> SupportedProtocols
{
get {
yield return "content";
yield return "readonly";
}
}
public void Delete(IOConnectionInfo ioc)
{
}
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
{
return false;
}
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
{
return null;
}
public Stream OpenFileForRead(IOConnectionInfo ioc)
{
throw new NotImplementedException();
}
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
{
throw new NotImplementedException();
}
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
{
throw new NotImplementedException();
}
public bool RequiresCredentials(IOConnectionInfo ioc)
{
throw new NotImplementedException();
}
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
{
throw new NotImplementedException();
}
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
{
throw new NotImplementedException();
}
public FileDescription GetFileDescription(IOConnectionInfo ioc)
{
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)
{
throw new NotImplementedException();
}
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
bool alwaysReturnSuccess)
{
throw new NotImplementedException();
}
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();
}
public string GetDisplayName(IOConnectionInfo ioc)
{
throw new NotImplementedException();
}
public string CreateFilePath(string parent, string newFilename)
{
throw new NotImplementedException();
}
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
{
throw new NotImplementedException();
}
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
{
throw new NotImplementedException();
}
public bool IsPermanentLocation(IOConnectionInfo ioc)
{
return ioc.Path.StartsWith("content") == false;
}
public bool IsReadOnly(IOConnectionInfo ioc)
{
return true;
}
}
class TestKp2aAppForSelectStorageLocation: TestKp2aApp
{
public override IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache)
{
if ((iocInfo.Path.StartsWith("content://")) || (iocInfo.Path.StartsWith("readonly://")))
{
return new TemporaryFileStorage();
}
return base.GetFileStorage(iocInfo);
}
}
sealed class TestControllableSelectStorageLocationActivity: SelectStorageLocationActivityBase
{
public List<string> toasts = new List<string>();
public WritableRequirements requestedWritableRequirements;
public bool? _result;
public IOConnectionInfo _resultIoc;
public object _userAction;
private IKp2aApp _app;
public TestControllableSelectStorageLocationActivity(IKp2aApp app) : base(app)
{
_app = app;
StartFileStorageSelection(RequestCodeFileStorageSelectionForPrimarySelect, true, false);
}
protected override void ShowToast(string text)
{
toasts.Add(text);
}
protected override void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc)
{
if (CopyFileShouldFail)
{
throw new Exception("CopyFile failed in test.");
}
}
public bool CopyFileShouldFail { get; set; }
protected override void ShowInvalidSchemeMessage(string dataString)
{
toasts.Add("invalid scheme: " + dataString);
}
protected override string IntentToFilename(Intent data)
{
return data.GetStringExtra("path");
}
protected override void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data)
{
ioc.Path = data.GetStringExtra("path");
}
protected override Result ExitFileStorageSelectionOk
{
get { return Result.FirstUser + 825; }
}
protected override void StartSelectFile(bool isForSave, int browseRequestCode, string protocolId)
{
_userAction = new SelectFileAction(isForSave, browseRequestCode, protocolId, this);
}
public void HandleActivityResult(int requestCode, Result resultCode, Intent data)
{
OnActivityResult(requestCode, resultCode, data);
}
internal class SelectFileAction
{
private readonly bool _isForSave;
private readonly int _browseRequestCode;
private readonly string _protocolId;
private readonly TestControllableSelectStorageLocationActivity _testControllableSelectStorageLocationActivity;
public SelectFileAction(bool isForSave, int browseRequestCode, string protocolId, TestControllableSelectStorageLocationActivity testControllableSelectStorageLocationActivity)
{
_isForSave = isForSave;
_browseRequestCode = browseRequestCode;
_protocolId = protocolId;
_testControllableSelectStorageLocationActivity = testControllableSelectStorageLocationActivity;
}
public bool IsForSave {
get { return _isForSave; }
}
public int BrowseRequestCode
{
get { return _browseRequestCode; }
}
public string ProtocolId
{
get { return _protocolId; }
}
public void PerformManualFileSelect(string path)
{
_testControllableSelectStorageLocationActivity.PressOpenButton(path, _browseRequestCode);
}
public void Cancel()
{
_testControllableSelectStorageLocationActivity.ReturnCancel();
}
public void PrepareFileChooser(string protocolId)
{
Intent data = new Intent();
data.PutExtra("path", protocolId+"://");
_testControllableSelectStorageLocationActivity.HandleActivityResult(_browseRequestCode, (Result) FileStorageResults.FileChooserPrepared, data);
}
}
private void PressOpenButton(string path, int browseRequestCode)
{
OnOpenButton(path, browseRequestCode);
}
internal class FileStorageSelectionAction
{
private readonly int _requestCode;
private readonly bool _allowThirdPartyGet;
private readonly bool _allowThirdPartySend;
private readonly TestControllableSelectStorageLocationActivity _testControllableSelectStorageLocationActivity;
public FileStorageSelectionAction(int requestCode, bool allowThirdPartyGet, bool allowThirdPartySend, TestControllableSelectStorageLocationActivity testControllableSelectStorageLocationActivity)
{
_requestCode = requestCode;
_allowThirdPartyGet = allowThirdPartyGet;
_allowThirdPartySend = allowThirdPartySend;
_testControllableSelectStorageLocationActivity = testControllableSelectStorageLocationActivity;
}
public int RequestCode
{
get { return _requestCode; }
}
public bool AllowThirdPartyGet
{
get { return _allowThirdPartyGet; }
}
public bool AllowThirdPartySend
{
get { return _allowThirdPartySend; }
}
public void ReturnProtocol(string protocolId)
{
Intent intent = new Intent();
intent.PutExtra("protocolId", protocolId);
_testControllableSelectStorageLocationActivity.HandleActivityResult(_requestCode, Result.FirstUser + 825 /*fs select ok*/, intent);
}
public void Cancel()
{
_testControllableSelectStorageLocationActivity.HandleActivityResult(_requestCode, Result.Canceled, null);
}
}
protected override void ShowAndroidBrowseDialog(int requestCode, bool isForSave)
{
_userAction = new AndroidBrowseDialogAction(requestCode, isForSave, this);
}
internal class AndroidBrowseDialogAction
{
private readonly int _requestCode;
private readonly bool _isForSave;
private readonly TestControllableSelectStorageLocationActivity _activity;
public AndroidBrowseDialogAction(int requestCode, bool isForSave, TestControllableSelectStorageLocationActivity activity)
{
_requestCode = requestCode;
_isForSave = isForSave;
_activity = activity;
}
public int RequestCode
{
get { return _requestCode; }
}
public void ReturnSelectedFile(string selectedUri)
{
Intent data = new Intent();
data.PutExtra("path", selectedUri);
_activity.HandleActivityResult(_requestCode, Result.Ok, data);
}
public void Cancel()
{
_activity.HandleActivityResult(_requestCode, Result.Canceled, null);
}
}
protected override bool IsStorageSelectionForSave { get { return SelectLocationForSave; } }
private bool SelectLocationForSave { get; set; }
protected override void PerformCopy(Func<Action> copyAndReturnPostExecute)
{
Action postExec = copyAndReturnPostExecute();
postExec();
}
protected override void StartFileStorageSelection(int requestCode, bool allowThirdPartyGet, bool allowThirdPartySend)
{
_userAction = new FileStorageSelectionAction(requestCode, allowThirdPartyGet, allowThirdPartySend, this);
}
protected override void StartFileChooser(string path, int requestCode, bool isForSave)
{
_userAction = new FileChooserAction(path, requestCode, isForSave, this);
}
internal class FileChooserAction
{
private readonly string _path;
private readonly int _requestCode;
private readonly bool _isForSave;
private readonly TestControllableSelectStorageLocationActivity _activity;
public FileChooserAction(string path, int requestCode, bool isForSave, TestControllableSelectStorageLocationActivity activity)
{
_path = path;
_requestCode = requestCode;
_isForSave = isForSave;
_activity = activity;
}
public string Path
{
get { return _path; }
}
public int RequestCode
{
get { return _requestCode; }
}
public bool IsForSave
{
get { return _isForSave; }
}
public void ReturnChosenFile(string path)
{
Intent data = new Intent();
data.PutExtra("path", path);
_activity.HandleActivityResult(_requestCode, Result.Ok, data);
}
public void Cancel()
{
_activity.HandleActivityResult(_requestCode, Result.Canceled, null);
}
}
protected override void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel)
{
_userAction = new ShowAlertDialogAction(message, onOk, onCancel);
}
internal class ShowAlertDialogAction
{
public string Message { get; set; }
public EventHandler<DialogClickEventArgs> OnOk { get; set; }
public EventHandler<DialogClickEventArgs> OnCancel { get; set; }
public ShowAlertDialogAction(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel)
{
Message = message;
OnOk = onOk;
OnCancel = onCancel;
}
public void Cancel()
{
OnCancel(this, null);
}
public void Ok()
{
OnOk(this, null);
}
}
protected override WritableRequirements RequestedWritableRequirements
{
get { return requestedWritableRequirements; }
}
public IKp2aApp App
{
get { return _app; }
}
protected override void ReturnOk(IOConnectionInfo ioc)
{
_result = true;
_resultIoc = ioc;
}
protected override void ReturnCancel()
{
_result = false;
}
}
[TestClass]
class TestSelectStorageLocation
{
[TestInitialize]
public void Init()
{
try
{
Looper.Prepare();
}
catch (Exception)
{
}
}
[TestMethod]
public void TestCancelFileStorageSelection()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action.Cancel();
Assert.IsFalse((bool) testee._result);
}
[TestMethod]
public void TestSimpleManualSelect()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction;
action.ReturnProtocol("ftp");
Assert.IsNull(testee._result); //no result yet
var action2 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
string path = "ftp://crocoll.net/test.kdbx";
action2.PerformManualFileSelect(path);
Assert.IsTrue((bool) testee._result);
Assert.AreEqual(testee._resultIoc.Path, path);
}
[TestMethod]
public void TestCancelManualSelect()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action.ReturnProtocol("ftp");
Assert.IsNull(testee._result); //no result yet
var action2 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
action2.Cancel();
Assert.IsFalse((bool)testee._result);
}
[TestMethod]
public void TestCancelAndroidBrowseDialog()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action.ReturnProtocol("androidget");
Assert.IsNull(testee._result); //no result yet
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
action2.Cancel();
Assert.IsFalse((bool)testee._result);
}
[TestMethod]
public void TestCancelCopyTemporaryLocation()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action.ReturnProtocol("androidget");
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
action2.ReturnSelectedFile("content://abc.kdbx");
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
Assert.IsNull(testee._result); //no result yet
action3.Cancel();
Assert.IsFalse((bool)testee._result);
}
[TestMethod]
public void TestCopyTemporaryLocation()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action.ReturnProtocol("androidget");
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
action2.ReturnSelectedFile("content://abc.kdbx");
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
Assert.IsNull(testee._result); //no result yet
action3.Ok();
var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action4.ReturnProtocol("ftp");
Assert.IsNull(testee._result);
var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
Assert.IsTrue(action5.IsForSave);
string path = "ftp://crocoll.net/testtarget.kdbx";
action5.PerformManualFileSelect(path);
Assert.IsTrue((bool)testee._result);
Assert.AreEqual(path, testee._resultIoc.Path);
}
[TestMethod]
public void TestCopyTemporaryLocationWithFileBrowser()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action.ReturnProtocol("androidget");
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
action2.ReturnSelectedFile("content://abc.kdbx");
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
Assert.IsNull(testee._result); //no result yet
action3.Ok();
var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action4.ReturnProtocol("file");
var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
Assert.IsTrue(action5.IsForSave);
action5.PrepareFileChooser("file");
Assert.IsNull(testee._result);
var action6 = (TestControllableSelectStorageLocationActivity.FileChooserAction)testee._userAction;
Assert.IsTrue(action5.IsForSave);
string path = "file:///mnt/sdcard/testtarget.kdbx";
action6.ReturnChosenFile(path);
string expectedpath = "/mnt/sdcard/testtarget.kdbx";
Assert.IsTrue((bool)testee._result);
Assert.AreEqual(expectedpath, testee._resultIoc.Path);
}
[TestMethod]
public void TestCopyTemporaryLocationWithCancelFileBrowser()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action.ReturnProtocol("androidget");
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
action2.ReturnSelectedFile("content://abc.kdbx");
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
Assert.IsNull(testee._result); //no result yet
action3.Ok();
var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action4.ReturnProtocol("file");
var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
Assert.IsTrue(action5.IsForSave);
action5.PrepareFileChooser("file");
Assert.IsNull(testee._result);
var action6 = (TestControllableSelectStorageLocationActivity.FileChooserAction)testee._userAction;
Assert.IsTrue(action5.IsForSave);
string path = "file:///mnt/sdcard/testtarget.kdbx";
action6.Cancel();
Assert.IsFalse((bool)testee._result);
}
[TestMethod]
public void TestCancelCopyReadOnlyLocation()
{
SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.WriteDesired;
string path;
var testee = PrepareTesteeForCancelCopyReadOnly(requestedWritableRequirements, out path);
Assert.IsTrue((bool)testee._result);
Assert.AreEqual(path, testee._resultIoc.Path);
}
[TestMethod]
public void TestCancelCopyReadOnlyLocationWriteRequired()
{
SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.WriteDemanded;
string path;
var testee = PrepareTesteeForCancelCopyReadOnly(requestedWritableRequirements, out path);
Assert.IsFalse((bool)testee._result);
}
private static TestControllableSelectStorageLocationActivity PrepareTesteeForCancelCopyReadOnly(
SelectStorageLocationActivityBase.WritableRequirements requestedWritableRequirements, out string path)
{
var testee = CreateTestee();
testee.requestedWritableRequirements = requestedWritableRequirements;
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction;
action.ReturnProtocol("androidget");
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction) testee._userAction;
path = "readonly://abc.kdbx";
action2.ReturnSelectedFile(path);
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction) testee._userAction;
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsReadOnly)));
Assert.IsNull(testee._result); //no result yet
action3.Cancel();
return testee;
}
[TestMethod]
public void TestOpenReadOnly()
{
var testee = CreateTestee();
testee.requestedWritableRequirements = SelectStorageLocationActivityBase.WritableRequirements.ReadOnly;
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction) testee._userAction;
action.ReturnProtocol("androidget");
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction) testee._userAction;
var path = "readonly://abc.kdbx";
action2.ReturnSelectedFile(path);
Assert.IsTrue((bool)testee._result);
Assert.AreEqual(path, testee._resultIoc.Path);
}
[TestMethod]
public void TestCopyTemporaryLocationFails()
{
var testee = CreateTestee();
var action = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action.ReturnProtocol("androidget");
var action2 = (TestControllableSelectStorageLocationActivity.AndroidBrowseDialogAction)testee._userAction;
action2.ReturnSelectedFile("content://abc.kdbx");
var action3 = (TestControllableSelectStorageLocationActivity.ShowAlertDialogAction)testee._userAction;
Assert.IsTrue(action3.Message.StartsWith(testee.App.GetResourceString(UiStringKey.FileIsTemporarilyAvailable)));
Assert.IsNull(testee._result); //no result yet
action3.Ok();
var action4 = (TestControllableSelectStorageLocationActivity.FileStorageSelectionAction)testee._userAction;
action4.ReturnProtocol("ftp");
Assert.IsNull(testee._result);
var action5 = (TestControllableSelectStorageLocationActivity.SelectFileAction)testee._userAction;
Assert.IsTrue(action5.IsForSave);
string path = "ftp://crocoll.net/testtarget.kdbx";
testee.CopyFileShouldFail = true;
action5.PerformManualFileSelect(path);
Assert.IsFalse((bool)testee._result);
}
private static TestControllableSelectStorageLocationActivity CreateTestee()
{
return new TestControllableSelectStorageLocationActivity(new TestKp2aAppForSelectStorageLocation());
}
}
}

View File

@ -1,42 +1,26 @@
using System; using System;
using System.Collections.Generic;
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 Android.Widget;
using Group.Pals.Android.Lib.UI.Filechooser.Utils.UI;
using KeePassLib.Serialization; using KeePassLib.Serialization;
using KeePassLib.Utility;
using keepass2android.Io; using keepass2android.Io;
using Environment = Android.OS.Environment; using keepass2android.Utils;
namespace keepass2android namespace keepass2android
{ {
[Activity(Label = "")] [Activity(Label = "")]
public class SelectStorageLocationActivity : Activity, IDialogInterfaceOnDismissListener public class SelectStorageLocationActivity : SelectStorageLocationActivityBase, IDialogInterfaceOnDismissListener
{ {
private ActivityDesign _design; private ActivityDesign _design;
private bool _isRecreated;
private IOConnectionInfo _selectedIoc;
private const string BundleKeySelectedIoc = "BundleKeySelectedIoc";
private const int RequestCodeFileStorageSelectionForPrimarySelect = 983713;
private const int RequestCodeFileStorageSelectionForCopyToWritableLocation = 983714;
private const int RequestCodeFileFileBrowseForWritableLocation = 983715;
public enum WritableRequirements private const string BundleKeySelectedIoc = "BundleKeySelectedIoc";
{
ReadOnly = 0,
WriteDesired = 1,
WriteDemanded = 2
}
public const string ExtraKeyWritableRequirements = "EXTRA_KEY_WRITABLE_REQUIREMENTS"; public const string ExtraKeyWritableRequirements = "EXTRA_KEY_WRITABLE_REQUIREMENTS";
public SelectStorageLocationActivity() public SelectStorageLocationActivity() : base(App.Kp2a)
{ {
_design = new ActivityDesign(this); _design = new ActivityDesign(this);
} }
@ -58,6 +42,8 @@ namespace keepass2android
bool allowThirdPartyGet = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, false); bool allowThirdPartyGet = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, false);
bool allowThirdPartySend = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false); bool allowThirdPartySend = Intent.GetBooleanExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false);
bool isRecreated = false;
if (bundle == null) if (bundle == null)
State = new Bundle(); State = new Bundle();
else else
@ -66,15 +52,14 @@ namespace keepass2android
var selectedIocString = bundle.GetString(BundleKeySelectedIoc, null); var selectedIocString = bundle.GetString(BundleKeySelectedIoc, null);
if (selectedIocString != null) if (selectedIocString != null)
_selectedIoc = IOConnectionInfo.UnserializeFromString(selectedIocString); _selectedIoc = IOConnectionInfo.UnserializeFromString(selectedIocString);
_isRecreated = true; isRecreated = true;
} }
if (!_isRecreated) //todo: handle orientation change while dialog is shown
if (!isRecreated)
{ {
Intent intent = new Intent(this, typeof(FileStorageSelectionActivity)); StartFileStorageSelection(RequestCodeFileStorageSelectionForPrimarySelect, allowThirdPartyGet, allowThirdPartySend);
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, allowThirdPartyGet);
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, allowThirdPartySend);
StartActivityForResult(intent, RequestCodeFileStorageSelectionForPrimarySelect);
} }
@ -82,7 +67,58 @@ namespace keepass2android
protected Bundle State { get; set; } protected Bundle State { get; set; }
protected bool IsStorageSelectionForSave protected override void ShowToast(string text)
{
Toast.MakeText(this, text, ToastLength.Long).Show();
}
protected override void ShowInvalidSchemeMessage(string dataString)
{
Toast.MakeText(this, Resources.GetString(Resource.String.unknown_uri_scheme, new Java.Lang.Object[] { dataString }),
ToastLength.Long).Show();
}
protected override string IntentToFilename(Intent data)
{
return Util.IntentToFilename(data, this);
}
protected override void SetIoConnectionFromIntent(IOConnectionInfo ioc, Intent data)
{
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
}
protected override Result ExitFileStorageSelectionOk
{
get { return KeePass.ExitFileStorageSelectionOk; }
}
protected override void StartSelectFile( bool isForSave, int browseRequestCode, string protocolId)
{
var startManualFileSelect = new Action<string>(defaultPath =>
{
if (defaultPath.StartsWith("sftp://"))
Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel);
else
//todo oncreate nur wenn for save?
Util.ShowFilenameDialog(this, filename => OnOpenButton(filename, browseRequestCode),
filename => OnOpenButton(filename, browseRequestCode),
ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
browseRequestCode);
});
;
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this,
OnActivityResult,
startManualFileSelect
), isForSave, browseRequestCode, protocolId);
}
protected override void ShowAndroidBrowseDialog(int requestCode, bool isForSave)
{
Util.ShowBrowseDialog(this, requestCode, isForSave);
}
protected override bool IsStorageSelectionForSave
{ {
get { return Intent.GetBooleanExtra(FileStorageSetupDefs.ExtraIsForSave, false); } get { return Intent.GetBooleanExtra(FileStorageSetupDefs.ExtraIsForSave, false); }
} }
@ -103,241 +139,14 @@ namespace keepass2android
_design.ReapplyTheme(); _design.ReapplyTheme();
} }
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if ((requestCode == RequestCodeFileStorageSelectionForPrimarySelect) || ((requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation)))
{
int browseRequestCode = Intents.RequestCodeFileBrowseForOpen;
if (requestCode == RequestCodeFileStorageSelectionForCopyToWritableLocation)
{
browseRequestCode = RequestCodeFileFileBrowseForWritableLocation;
}
if (resultCode == KeePass.ExitFileStorageSelectionOk) protected override void ReturnCancel()
{
string protocolId = data.GetStringExtra("protocolId");
if (protocolId == "androidget")
{
Util.ShowBrowseDialog(this, Intents.RequestCodeFileBrowseForOpen, false);
}
else
{
bool isForSave = (requestCode == RequestCodeFileStorageSelectionForPrimarySelect) ?
IsStorageSelectionForSave : true;
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this,
OnActivityResult,
defaultPath =>
{
if (defaultPath.StartsWith("sftp://"))
Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel);
else
//todo oncreate nur wenn for save?
Util.ShowFilenameDialog(this, filename => OnOpenButton(filename, browseRequestCode),
filename => OnOpenButton(filename, browseRequestCode),
ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url),
browseRequestCode);
}
), isForSave, browseRequestCode, protocolId);
}
}
else
{
ReturnCancel();
}
}
if ((requestCode == Intents.RequestCodeFileBrowseForOpen) || (requestCode == RequestCodeFileFileBrowseForWritableLocation))
{
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
{
IOConnectionInfo ioc = new IOConnectionInfo();
PasswordActivity.SetIoConnectionFromIntent(ioc, data);
#if !EXCLUDE_FILECHOOSER
bool isForSave = (requestCode == RequestCodeFileFileBrowseForWritableLocation) ?
true : IsStorageSelectionForSave ;
StartFileChooser(ioc.Path, requestCode, isForSave);
#else
IocSelected(new IOConnectionInfo { Path = "/mnt/sdcard/keepass/yubi.kdbx" }, requestCode);
#endif
return;
}
if ((resultCode == Result.Canceled) && (data != null) && (data.HasExtra("EXTRA_ERROR_MESSAGE")))
{
Toast.MakeText(this, data.GetStringExtra("EXTRA_ERROR_MESSAGE"), ToastLength.Long).Show();
}
if (resultCode == Result.Ok)
{
string filename = Util.IntentToFilename(data, this);
if (filename != null)
{
if (filename.StartsWith("file://"))
{
filename = filename.Substring(7);
filename = Java.Net.URLDecoder.Decode(filename);
}
IOConnectionInfo ioc = new IOConnectionInfo
{
Path = filename
};
IocSelected(ioc, requestCode);
}
else
{
if (data.Data.Scheme == "content")
{
IocSelected(IOConnectionInfo.FromPath(data.DataString), requestCode);
}
else
{
Toast.MakeText(this, Resources.GetString(Resource.String.unknown_uri_scheme, new Java.Lang.Object[] {data.DataString}),
ToastLength.Long).Show();
ReturnCancel();
}
}
}
else
{
ReturnCancel();
}
}
}
private void ReturnCancel()
{ {
SetResult(Result.Canceled); SetResult(Result.Canceled);
Finish(); Finish();
} }
private void IocSelected(IOConnectionInfo ioc, int requestCode) protected override void ReturnOk(IOConnectionInfo ioc)
{
if (requestCode == RequestCodeFileFileBrowseForWritableLocation)
{
IocForCopySelected(ioc);
}
else if (requestCode == Intents.RequestCodeFileBrowseForOpen)
{
PrimaryIocSelected(ioc);
}
else
{
#if DEBUG
throw new Exception("invalid request code!");
#endif
}
}
private void IocForCopySelected(IOConnectionInfo targetIoc)
{
new keepass2android.Utils.SimpleLoadingDialog(this, GetString(Resource.String.CopyingFile), false,
() =>
{
IOConnectionInfo sourceIoc = _selectedIoc;
try
{
CopyFile(targetIoc, sourceIoc);
}
catch (Exception e)
{
return () =>
{
Toast.MakeText(this, App.Kp2a.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message, ToastLength.Long).Show();
ReturnCancel();
};
}
return () => {ReturnOk(targetIoc); };
}
).Execute(new Object[] {});
}
private static void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc)
{
IFileStorage sourceStorage = App.Kp2a.GetFileStorage(sourceIoc);
IFileStorage targetStorage = App.Kp2a.GetFileStorage(targetIoc);
using (
var writeTransaction = targetStorage.OpenWriteTransaction(targetIoc,
App.Kp2a.GetBooleanPreference(
PreferenceKey.UseFileTransactions)))
{
using (var writeStream = writeTransaction.OpenFile())
{
sourceStorage.OpenFileForRead(sourceIoc).CopyTo(writeStream);
}
writeTransaction.CommitWrite();
}
}
private void PrimaryIocSelected(IOConnectionInfo ioc)
{
if (!App.Kp2a.GetFileStorage(ioc).IsPermanentLocation(ioc))
{
new AlertDialog.Builder(this)
.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => { MoveToWritableLocation(ioc); })
.SetMessage(Resources.GetString(Resource.String.FileIsTemporarilyAvailable) + " "
+ Resources.GetString(Resource.String.CopyFileRequired) + " "
+ Resources.GetString(Resource.String.ClickOkToSelectLocation))
.SetCancelable(false)
.SetNegativeButton(Android.Resource.String.Cancel, (sender, args) => { ReturnCancel(); })
//.SetOnDismissListener(this)
.Create()
.Show();
return;
}
var filestorage = App.Kp2a.GetFileStorage(ioc);
if ((RequestedWritableRequirements != WritableRequirements.ReadOnly) && (filestorage.IsReadOnly(ioc)))
{
string readOnlyExplanation = Resources.GetString(Resource.String.FileIsReadOnly);
BuiltInFileStorage builtInFileStorage = filestorage as BuiltInFileStorage;
if (builtInFileStorage != null)
{
if (builtInFileStorage.IsReadOnlyBecauseKitkatRestrictions(ioc))
readOnlyExplanation = Resources.GetString(Resource.String.FileIsReadOnlyOnKitkat);
}
new AlertDialog.Builder(this)
.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => { MoveToWritableLocation(ioc); })
.SetCancelable(false)
.SetNegativeButton(Android.Resource.String.Cancel, (sender, args) => { ReturnCancel(); })
//.SetOnDismissListener(this)
.SetMessage(readOnlyExplanation + " "
+ (RequestedWritableRequirements == WritableRequirements.WriteDemanded ?
Resources.GetString(Resource.String.CopyFileRequired)
: Resources.GetString(Resource.String.CopyFileRequiredForEditing))
+ " "
+ Resources.GetString(Resource.String.ClickOkToSelectLocation))
.Create()
.Show();
return;
}
ReturnOk(ioc);
}
private void ReturnOk(IOConnectionInfo ioc)
{ {
Intent intent = new Intent(); Intent intent = new Intent();
PasswordActivity.PutIoConnectionToIntent(ioc, intent); PasswordActivity.PutIoConnectionToIntent(ioc, intent);
@ -345,42 +154,52 @@ namespace keepass2android
Finish(); Finish();
} }
private WritableRequirements RequestedWritableRequirements protected override void ShowAlertDialog(string message, EventHandler<DialogClickEventArgs> onOk, EventHandler<DialogClickEventArgs> onCancel)
{
new AlertDialog.Builder(this)
.SetPositiveButton(Android.Resource.String.Ok, onOk)
.SetMessage(message)
.SetCancelable(false)
.SetNegativeButton(Android.Resource.String.Cancel, onCancel)
.Create()
.Show();
}
protected override WritableRequirements RequestedWritableRequirements
{ {
get { return (WritableRequirements) Intent.GetIntExtra(ExtraKeyWritableRequirements, (int)WritableRequirements.ReadOnly); } get { return (WritableRequirements) Intent.GetIntExtra(ExtraKeyWritableRequirements, (int)WritableRequirements.ReadOnly); }
} }
private void MoveToWritableLocation(IOConnectionInfo ioc)
{
_selectedIoc = ioc;
#if !EXCLUDE_FILECHOOSER
protected override void PerformCopy(Func<Action> copyAndReturnPostExecute)
{
new SimpleLoadingDialog(this, GetString(Resource.String.CopyingFile), false,
copyAndReturnPostExecute
).Execute();
}
protected override void StartFileStorageSelection(int requestCode, bool allowThirdPartyGet,
bool allowThirdPartySend)
{
Intent intent = new Intent(this, typeof(FileStorageSelectionActivity)); Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, false); intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, allowThirdPartyGet);
intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false); intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, allowThirdPartySend);
StartActivityForResult(intent, RequestCodeFileStorageSelectionForCopyToWritableLocation);
StartActivityForResult(intent, requestCode);
} }
private bool OnReceivedSftpData(string filename, int requestCode, bool isForSave) protected override void StartFileChooser(string defaultPath, int requestCode, bool forSave)
{ {
IOConnectionInfo ioc = new IOConnectionInfo { Path = filename };
#if !EXCLUDE_FILECHOOSER #if !EXCLUDE_FILECHOOSER
StartFileChooser(ioc.Path, requestCode, isForSave); Kp2aLog.Log("FSA: defaultPath=" + defaultPath);
#else
IocSelected(ioc, requestCode);
#endif
return true;
}
#if !EXCLUDE_FILECHOOSER
private void StartFileChooser(string defaultPath, int requestCode, bool forSave)
{
Kp2aLog.Log("FSA: defaultPath="+defaultPath);
string fileProviderAuthority = FileChooserFileProvider.TheAuthority; string fileProviderAuthority = FileChooserFileProvider.TheAuthority;
if (defaultPath.StartsWith("file://")) if (defaultPath.StartsWith("file://"))
{ {
fileProviderAuthority = PackageName+".android-filechooser.localfile"; fileProviderAuthority = PackageName + ".android-filechooser.localfile";
} }
Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority, Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority,
defaultPath); defaultPath);
@ -389,31 +208,26 @@ namespace keepass2android
if (forSave) if (forSave)
{ {
i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true); i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true);
i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.default_file_ext", "kdbx"); var ext = UrlUtil.GetExtension(defaultPath);
if ((ext != String.Empty) && (ext.Contains("?")==false))
i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.default_file_ext", ext);
} }
StartActivityForResult(i, requestCode); StartActivityForResult(i, requestCode);
#else
IocSelected(new IOConnectionInfo { Path = "/mnt/sdcard/keepass/yubi.kdbx" }, requestCode);
#endif
} }
#endif #endif
private bool OnOpenButton(String fileName, int requestCode)
{
IOConnectionInfo ioc = new IOConnectionInfo
{
Path = fileName
};
IocSelected(ioc, requestCode);
return true;
}
public void OnDismiss(IDialogInterface dialog) public void OnDismiss(IDialogInterface dialog)
{ {
// ReturnCancel(); // ReturnCancel();
} }
} }

View File

@ -345,7 +345,7 @@ namespace keepass2android
{ {
string filename = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text; string filename = ((EditText) dialog.FindViewById(Resource.Id.file_filename)).Text;
ShowBrowseDialog(activity, requestCodeBrowse, onCreate != null); Util.ShowBrowseDialog(activity, requestCodeBrowse, onCreate != null);
}; };

View File

@ -392,6 +392,10 @@ namespace keepass2android
} }
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo) public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
{
return GetFileStorage(iocInfo, true);
}
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo, bool allowCache)
{ {
if (iocInfo.IsLocalFile()) if (iocInfo.IsLocalFile())
return new BuiltInFileStorage(this); return new BuiltInFileStorage(this);
@ -399,9 +403,8 @@ namespace keepass2android
{ {
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo); IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
if (DatabaseCacheEnabled) if (DatabaseCacheEnabled && allowCache)
{ {
//TODO
return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this); return new CachingFileStorage(innerFileStorage, Application.Context.CacheDir.Path, this);
} }
else else