From 86bfae57c3bc27816fda41a8f0582954c945217d Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 15 Nov 2016 05:50:26 +0100 Subject: [PATCH] move duplicated file-browsing code to FileSelectHelper --- .../SelectStorageLocationActivityBase.cs | 50 +--- src/keepass2android/CreateDatabaseActivity.cs | 139 ++-------- src/keepass2android/ExportDatabaseActivity.cs | 84 ++---- src/keepass2android/FileSelectHelper.cs | 242 ++++++++++++++++++ .../SelectStorageLocationActivity.cs | 56 +--- src/keepass2android/keepass2android.csproj | 1 + 6 files changed, 293 insertions(+), 279 deletions(-) create mode 100644 src/keepass2android/FileSelectHelper.cs diff --git a/src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs b/src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs index c5ce53bc..e4fbfb25 100644 --- a/src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs +++ b/src/Kp2aBusinessLogic/SelectStorageLocationActivityBase.cs @@ -62,8 +62,8 @@ namespace keepass2android } else { - bool isForSave = (requestCode == RequestCodeFileStorageSelectionForPrimarySelect) ? - IsStorageSelectionForSave : true; + bool isForSave = (requestCode != RequestCodeFileStorageSelectionForPrimarySelect) + || IsStorageSelectionForSave; StartSelectFile(isForSave, browseRequestCode, protocolId); @@ -85,8 +85,8 @@ namespace keepass2android { IOConnectionInfo ioc = new IOConnectionInfo(); SetIoConnectionFromIntent(ioc, data); - bool isForSave = (requestCode == RequestCodeFileFileBrowseForWritableLocation) ? - true : IsStorageSelectionForSave; + bool isForSave = (requestCode == RequestCodeFileFileBrowseForWritableLocation) + || IsStorageSelectionForSave; StartFileChooser(ioc.Path, requestCode, isForSave); @@ -164,6 +164,8 @@ namespace keepass2android } + protected abstract void StartFileChooser(string path, int requestCode, bool isForSave); + protected abstract void ShowToast(string text); protected abstract void ShowInvalidSchemeMessage(string dataString); @@ -187,7 +189,7 @@ namespace keepass2android protected abstract bool IsStorageSelectionForSave { get; } - private void IocSelected(IOConnectionInfo ioc, int requestCode) + protected void IocSelected(IOConnectionInfo ioc, int requestCode) { if (requestCode == RequestCodeFileFileBrowseForWritableLocation) { @@ -245,44 +247,6 @@ namespace keepass2android 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, Action dismissDialog) - { - - IOConnectionInfo ioc = new IOConnectionInfo - { - Path = fileName - }; - - int lastSlashPos = fileName.LastIndexOf('/'); - int lastDotPos = fileName.LastIndexOf('.'); - if (lastSlashPos >= lastDotPos) //no dot after last slash or == in case neither / nor . - { - ShowFilenameWarning(fileName, () => { IocSelected(ioc, requestCode); dismissDialog(); }, () => { /* don't do anything, leave dialog open, let user try again*/ }); - //signal that the dialog should be kept open - return false; - } - - IocSelected(ioc, requestCode); - - return true; - - } - - protected abstract void ShowFilenameWarning(string fileName, Action onUserWantsToContinue, Action onUserWantsToCorrect); - protected virtual void CopyFile(IOConnectionInfo targetIoc, IOConnectionInfo sourceIoc) { diff --git a/src/keepass2android/CreateDatabaseActivity.cs b/src/keepass2android/CreateDatabaseActivity.cs index 191da39d..a0ab4bc3 100644 --- a/src/keepass2android/CreateDatabaseActivity.cs +++ b/src/keepass2android/CreateDatabaseActivity.cs @@ -111,7 +111,7 @@ namespace keepass2android defaulFilename = "file://" + defaulFilename; } - StartFileChooser(defaulFilename, RequestCodeKeyFile, false); + new FileSelectHelper(this, false, RequestCodeKeyFile).StartFileChooser(defaulFilename); } else @@ -244,28 +244,6 @@ namespace keepass2android _restoringInstanceState = false; } - private void StartFileChooser(string defaultPath, int requestCode, bool forSave) - { -#if !EXCLUDE_FILECHOOSER - Kp2aLog.Log("FSA: defaultPath=" + defaultPath); - string fileProviderAuthority = FileChooserFileProvider.TheAuthority; - if (defaultPath.StartsWith("file://")) - { - fileProviderAuthority = "keepass2android."+AppNames.PackagePart+".android-filechooser.localfile"; - } - Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority, - defaultPath); - - if (forSave) - { - 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"); - } - - StartActivityForResult(i, requestCode); -#endif - } - private void UpdateIocView() { @@ -343,17 +321,20 @@ namespace keepass2android } else { - App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this, - OnActivityResult, - defaultPath => - { - if (defaultPath.StartsWith("sftp://")) - Util.ShowSftpDialog(this, OnReceiveSftpData, () => { }); - else - Util.ShowFilenameDialog(this, OnCreateButton, null, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url), - Intents.RequestCodeFileBrowseForOpen); - } - ), true, RequestCodeDbFilename, protocolId); + FileSelectHelper fileSelectHelper = new FileSelectHelper(this, true, RequestCodeDbFilename) + { + DefaultExtension = "kdbx" + }; + fileSelectHelper.OnOpen += (sender, info) => + { + _ioc = info; + UpdateIocView(); + }; + App.Kp2a.GetFileStorage(protocolId).StartSelectFile( + new FileStorageSetupInitiatorActivity(this,OnActivityResult,s => fileSelectHelper.PerformManualFileSelect(s)), + true, + RequestCodeDbFilename, + protocolId); } } @@ -436,16 +417,14 @@ namespace keepass2android { IOConnectionInfo ioc = new IOConnectionInfo(); PasswordActivity.SetIoConnectionFromIntent(ioc, data); - StartFileChooser(ioc.Path, RequestCodeDbFilename, true); + + new FileSelectHelper(this, true, RequestCodeDbFilename) { DefaultExtension = "kdbx" } + .StartFileChooser(ioc.Path); + } } - private bool OnReceiveSftpData(string filename) - { - StartFileChooser(filename, RequestCodeDbFilename, true); - return true; - } private static string ConvertFilenameToIocPath(string filename) { @@ -457,86 +436,6 @@ namespace keepass2android return filename; } - private bool OnCreateButton(string filename, Dialog dialog) - { - // Make sure file name exists - if (filename.Length == 0) - { - Toast.MakeText(this, - Resource.String.error_filename_required, - ToastLength.Long).Show(); - return false; - } - - IOConnectionInfo ioc = new IOConnectionInfo { Path = filename }; - IFileStorage fileStorage; - try - { - fileStorage = App.Kp2a.GetFileStorage(ioc); - } - catch (NoFileStorageFoundException) - { - Toast.MakeText(this, - "Unexpected scheme in "+filename, - ToastLength.Long).Show(); - return false; - } - - if (ioc.IsLocalFile()) - { - // Try to create the file - File file = new File(filename); - try - { - File parent = file.ParentFile; - - if (parent == null || (parent.Exists() && !parent.IsDirectory)) - { - Toast.MakeText(this, - Resource.String.error_invalid_path, - ToastLength.Long).Show(); - return false; - } - - if (!parent.Exists()) - { - // Create parent dircetory - if (!parent.Mkdirs()) - { - Toast.MakeText(this, - Resource.String.error_could_not_create_parent, - ToastLength.Long).Show(); - return false; - - } - } - System.IO.File.Create(filename); - - } - catch (IOException ex) - { - Toast.MakeText( - this, - GetText(Resource.String.error_file_not_create) + " " - + ex.LocalizedMessage, - ToastLength.Long).Show(); - return false; - } - - } - if (fileStorage.RequiresCredentials(ioc)) - { - Util.QueryCredentials(ioc, AfterQueryCredentials, this); - } - else - { - _ioc = ioc; - UpdateIocView(); - } - - - return true; - } private void AfterQueryCredentials(IOConnectionInfo ioc) { diff --git a/src/keepass2android/ExportDatabaseActivity.cs b/src/keepass2android/ExportDatabaseActivity.cs index 507598bf..251ca171 100644 --- a/src/keepass2android/ExportDatabaseActivity.cs +++ b/src/keepass2android/ExportDatabaseActivity.cs @@ -62,17 +62,19 @@ namespace keepass2android } else { - App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this, - OnActivityResult, - defaultPath => - { - if (defaultPath.StartsWith("sftp://")) - Util.ShowSftpDialog(this, OnReceiveSftpData, () => { }); - else - Util.ShowFilenameDialog(this, OnCreateButton, null, null, false, defaultPath, GetString(Resource.String.enter_filename_details_url), - Intents.RequestCodeFileBrowseForOpen); - } - ), true, RequestCodeDbFilename, protocolId); + FileSelectHelper fileSelectHelper = new FileSelectHelper(this, true, RequestCodeDbFilename) + { + DefaultExtension = _ffp[_fileFormatIndex].DefaultExtension + }; + fileSelectHelper.OnOpen += (sender, ioc) => + { + ExportTo(ioc); + }; + App.Kp2a.GetFileStorage(protocolId).StartSelectFile( + new FileStorageSetupInitiatorActivity(this, OnActivityResult, s => fileSelectHelper.PerformManualFileSelect(s)), + true, + RequestCodeDbFilename, + protocolId); } return; } @@ -149,7 +151,9 @@ namespace keepass2android { IOConnectionInfo ioc = new IOConnectionInfo(); PasswordActivity.SetIoConnectionFromIntent(ioc, data); - StartFileChooser(ioc.Path, RequestCodeDbFilename, true); + new FileSelectHelper(this, true, RequestCodeDbFilename) + { DefaultExtension = _ffp[_fileFormatIndex].DefaultExtension} + .StartFileChooser(ioc.Path); return; } Finish(); @@ -176,39 +180,6 @@ namespace keepass2android get { return 0; } } - private bool OnCreateButton(string filename, Dialog dialog) - { - if (filename.Length == 0) - { - Toast.MakeText(this, - Resource.String.error_filename_required, - ToastLength.Long).Show(); - return false; - } - - IOConnectionInfo ioc = new IOConnectionInfo { Path = filename }; - try - { - App.Kp2a.GetFileStorage(ioc); - } - catch (NoFileStorageFoundException) - { - Toast.MakeText(this, - "Unexpected scheme in " + filename, - ToastLength.Long).Show(); - return false; - } - - ExportTo(new IOConnectionInfo() { Path = filename }); - return true; - } - - private bool OnReceiveSftpData(string filename) - { - StartFileChooser(filename, RequestCodeDbFilename, true); - return true; - } - private static string ConvertFilenameToIocPath(string filename) { if ((filename != null) && (filename.StartsWith("file://"))) @@ -219,29 +190,6 @@ namespace keepass2android return filename; } - private void StartFileChooser(string defaultPath, int requestCode, bool forSave) - { -#if !EXCLUDE_FILECHOOSER - Kp2aLog.Log("FSA: defaultPath=" + defaultPath); - string fileProviderAuthority = FileChooserFileProvider.TheAuthority; - if (defaultPath.StartsWith("file://")) - { - fileProviderAuthority = "keepass2android."+AppNames.PackagePart+".android-filechooser.localfile"; - } - Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority, - defaultPath); - - if (forSave) - { - i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true); - i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.default_file_ext", _ffp[_fileFormatIndex].DefaultExtension); - } - - StartActivityForResult(i, requestCode); -#endif - - } - public class ExportDb : RunnableOnFinish { private readonly IKp2aApp _app; diff --git a/src/keepass2android/FileSelectHelper.cs b/src/keepass2android/FileSelectHelper.cs new file mode 100644 index 00000000..2bfcc3c2 --- /dev/null +++ b/src/keepass2android/FileSelectHelper.cs @@ -0,0 +1,242 @@ +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 Java.IO; +using keepass2android.Io; +using KeePassLib.Serialization; +using KeePassLib.Utility; + +namespace keepass2android +{ + public class FileSelectHelper + { + private readonly Activity _activity; + private readonly bool _isForSave; + private readonly int _requestCode; + + public string DefaultExtension { get; set; } + + public FileSelectHelper(Activity activity, bool isForSave, int requestCode) + { + _activity = activity; + _isForSave = isForSave; + _requestCode = requestCode; + } + + private void ShowSftpDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel) + { +#if !EXCLUDE_JAVAFILESTORAGE && !NoNet + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + View dlgContents = activity.LayoutInflater.Inflate(Resource.Layout.sftpcredentials, null); + builder.SetView(dlgContents); + builder.SetPositiveButton(Android.Resource.String.Ok, + (sender, args) => + { + string host = dlgContents.FindViewById(Resource.Id.sftp_host).Text; + string portText = dlgContents.FindViewById(Resource.Id.sftp_port).Text; + int port = Keepass2android.Javafilestorage.SftpStorage.DefaultSftpPort; + if (!string.IsNullOrEmpty(portText)) + int.TryParse(portText, out port); + string user = dlgContents.FindViewById(Resource.Id.sftp_user).Text; + string password = dlgContents.FindViewById(Resource.Id.sftp_password).Text; + string initialPath = dlgContents.FindViewById(Resource.Id.sftp_initial_dir).Text; + string sftpPath = new Keepass2android.Javafilestorage.SftpStorage().BuildFullPath(host, port, initialPath, user, + password); + onStartBrowse(sftpPath); + }); + EventHandler evtH = new EventHandler((sender, e) => onCancel()); + + builder.SetNegativeButton(Android.Resource.String.Cancel, evtH); + builder.SetTitle(activity.GetString(Resource.String.enter_sftp_login_title)); + Dialog dialog = builder.Create(); + + dialog.Show(); +#endif + } + + public void PerformManualFileSelect(string defaultPath) + { + if (defaultPath.StartsWith("sftp://")) + ShowSftpDialog(_activity, StartFileChooser, ReturnCancel); + else + { + Func onOpen = (filename, dialog) => OnOpenButton(filename, dialog); + Util.ShowFilenameDialog(_activity, + !_isForSave ? onOpen : null, + _isForSave ? onOpen : null, + ReturnCancel, + false, + defaultPath, + _activity.GetString(Resource.String.enter_filename_details_url), + _requestCode) + ; + } + } + + private void ReturnCancel() + { + if (OnCancel != null) + OnCancel(this, null); + } + + + protected void ShowFilenameWarning(string fileName, Action onUserWantsToContinue, Action onUserWantsToCorrect) + { + new AlertDialog.Builder(_activity) + .SetPositiveButton(keepass2android.Resource.String.Continue, delegate { onUserWantsToContinue(); }) + .SetMessage(Resource.String.NoFilenameWarning) + .SetCancelable(false) + .SetNegativeButton(Android.Resource.String.Cancel, delegate { onUserWantsToCorrect(); }) + .Create() + .Show(); + + } + private bool OnOpenButton(string filename, Dialog dialog) + { + + IOConnectionInfo ioc = new IOConnectionInfo + { + Path = filename + }; + + // Make sure file name exists + if (filename.Length == 0) + { + Toast.MakeText(_activity, + Resource.String.error_filename_required, + ToastLength.Long).Show(); + return false; + } + + + int lastSlashPos = filename.LastIndexOf('/'); + int lastDotPos = filename.LastIndexOf('.'); + if (lastSlashPos >= lastDotPos) //no dot after last slash or == in case neither / nor . + { + ShowFilenameWarning(filename, () => { IocSelected(ioc); dialog.Dismiss(); }, () => { /* don't do anything, leave dialog open, let user try again*/ }); + //signal that the dialog should be kept open + return false; + } + + IFileStorage fileStorage; + try + { + fileStorage = App.Kp2a.GetFileStorage(ioc); + } + catch (NoFileStorageFoundException) + { + Toast.MakeText(_activity, + "Unexpected scheme in "+filename, + ToastLength.Long).Show(); + return false; + } + + if (_isForSave && ioc.IsLocalFile()) + { + // Try to create the file + File file = new File(filename); + try + { + File parent = file.ParentFile; + + if (parent == null || (parent.Exists() && !parent.IsDirectory)) + { + Toast.MakeText(_activity, + Resource.String.error_invalid_path, + ToastLength.Long).Show(); + return false; + } + + if (!parent.Exists()) + { + // Create parent dircetory + if (!parent.Mkdirs()) + { + Toast.MakeText(_activity, + Resource.String.error_could_not_create_parent, + ToastLength.Long).Show(); + return false; + + } + } + System.IO.File.Create(filename); + + } + catch (IOException ex) + { + Toast.MakeText( + _activity, + _activity.GetText(Resource.String.error_file_not_create) + " " + + ex.LocalizedMessage, + ToastLength.Long).Show(); + return false; + } + + } + if (fileStorage.RequiresCredentials(ioc)) + { + Util.QueryCredentials(ioc, IocSelected, _activity); + } + else + { + IocSelected(ioc); + } + + return true; + } + + private void IocSelected(IOConnectionInfo ioc) + { + if (OnOpen != null) + OnOpen(this, ioc); + } + + public bool StartFileChooser(string defaultPath) + { +#if !EXCLUDE_FILECHOOSER + Kp2aLog.Log("FSA: defaultPath=" + defaultPath); + string fileProviderAuthority = FileChooserFileProvider.TheAuthority; + if (defaultPath.StartsWith("file://")) + { + fileProviderAuthority = _activity.PackageName + ".android-filechooser.localfile"; + } + Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(_activity, fileProviderAuthority, + defaultPath); + + + if (_isForSave) + { + i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true); + string ext; + if (!string.IsNullOrEmpty(DefaultExtension)) + { + ext = DefaultExtension; + } + else + { + ext = UrlUtil.GetExtension(defaultPath); + } + if ((ext != String.Empty) && (ext.Contains("?") == false)) + i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.default_file_ext", ext); + } + _activity.StartActivityForResult(i, _requestCode); + +#else + Toast.MakeText(this, "File chooser is excluded!", ToastLength.Long).Show(); +#endif + return true; + } + + public event EventHandler OnCancel; + + public event EventHandler OnOpen; + } +} \ No newline at end of file diff --git a/src/keepass2android/SelectStorageLocationActivity.cs b/src/keepass2android/SelectStorageLocationActivity.cs index 30d23c23..c6e85123 100644 --- a/src/keepass2android/SelectStorageLocationActivity.cs +++ b/src/keepass2android/SelectStorageLocationActivity.cs @@ -96,21 +96,15 @@ namespace keepass2android protected override void StartSelectFile( bool isForSave, int browseRequestCode, string protocolId) { - var startManualFileSelect = new Action(defaultPath => - { - if (defaultPath.StartsWith("sftp://")) - Util.ShowSftpDialog(this, filename => OnReceivedSftpData(filename, browseRequestCode, isForSave), ReturnCancel); - else - Util.ShowFilenameDialog(this, - !isForSave ? delegate(string filename, Dialog dialog) { return OnOpenButton(filename, browseRequestCode, dialog.Dismiss); } : (Func) null, - isForSave ? delegate(string filename, Dialog dialog) { return OnOpenButton(filename, browseRequestCode, dialog.Dismiss); } : (Func) null, - ReturnCancel, false, defaultPath, GetString(Resource.String.enter_filename_details_url), - browseRequestCode); - }); - ; + FileSelectHelper fileSelectHelper = new FileSelectHelper(this, isForSave, browseRequestCode); + fileSelectHelper.OnOpen += (sender, ioc) => + { + IocSelected(ioc,browseRequestCode); + }; + App.Kp2a.GetFileStorage(protocolId).StartSelectFile(new FileStorageSetupInitiatorActivity(this, OnActivityResult, - startManualFileSelect + s => fileSelectHelper.PerformManualFileSelect(s) ), isForSave, browseRequestCode, protocolId); } @@ -199,43 +193,9 @@ namespace keepass2android protected override void StartFileChooser(string defaultPath, int requestCode, bool forSave) { -#if !EXCLUDE_FILECHOOSER - Kp2aLog.Log("FSA: defaultPath=" + defaultPath); - string fileProviderAuthority = FileChooserFileProvider.TheAuthority; - if (defaultPath.StartsWith("file://")) - { - fileProviderAuthority = PackageName + ".android-filechooser.localfile"; - } - Intent i = Keepass2android.Kp2afilechooser.Kp2aFileChooserBridge.GetLaunchFileChooserIntent(this, fileProviderAuthority, - defaultPath); - - - if (forSave) - { - i.PutExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.save_dialog", true); - 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); - -#else - Toast.MakeText(this, "File chooser is excluded!", ToastLength.Long).Show(); -#endif - + new FileSelectHelper(this, forSave, requestCode).StartFileChooser(defaultPath); } - protected override void ShowFilenameWarning(string fileName, Action onUserWantsToContinue, Action onUserWantsToCorrect) - { - new AlertDialog.Builder(this) - .SetPositiveButton(Resource.String.Continue, delegate { onUserWantsToContinue(); } ) - .SetMessage(Resource.String.NoFilenameWarning) - .SetCancelable(false) - .SetNegativeButton(Android.Resource.String.Cancel, delegate { onUserWantsToCorrect(); }) - .Create() - .Show(); - - } public void OnDismiss(IDialogInterface dialog) diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index be3f6109..d84db71f 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -149,6 +149,7 @@ +