Files
keepass2android/src/keepass2android-app/FileSelectHelper.cs
2025-01-07 11:20:08 +01:00

816 lines
31 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
#if !NoNet
using FluentFTP;
#endif
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Google.Android.Material.Dialog;
using Java.IO;
using keepass2android.Io;
using keepass2android;
#if !EXCLUDE_JAVAFILESTORAGE
using Keepass2android.Javafilestorage;
#endif
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace keepass2android
{
public class FileSelectHelper
{
public static string ConvertFilenameToIocPath(string filename)
{
if ((filename != null) && (filename.StartsWith("file://")))
{
filename = filename.Substring(7);
filename = Java.Net.URLDecoder.Decode(filename);
}
return filename;
}
private readonly Activity _activity;
private readonly bool _isForSave;
private readonly int _requestCode;
private readonly string _schemeSeparator = "://";
private bool _tryGetPermanentAccess;
private const int SftpModeSpinnerPasswd = 0;
private const int SftpModeSpinnerPubKey = 1;
private const int SftpModeSpinnerCustomKey = 2;
private const int SftpKeySpinnerCreateNewIdx = 0;
public string DefaultExtension { get; set; }
public FileSelectHelper(Activity activity, bool isForSave, bool tryGetPermanentAccess, int requestCode)
{
_activity = activity;
_isForSave = isForSave;
_requestCode = requestCode;
_tryGetPermanentAccess = tryGetPermanentAccess;
}
private void ShowSftpDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel, string defaultPath)
{
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
View dlgContents = activity.LayoutInflater.Inflate(Resource.Layout.sftpcredentials, null);
var ctx = activity.ApplicationContext;
var fileStorage = new Keepass2android.Javafilestorage.SftpStorage(ctx);
LinearLayout addNewBtn = dlgContents.FindViewById<LinearLayout>(Resource.Id.sftp_add_key_group);
Button deleteBtn = dlgContents.FindViewById<Button>(Resource.Id.sftp_delete_key_button);
EditText keyNameTxt = dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_name);
EditText keyContentTxt = dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_content);
var keySpinner = dlgContents.FindViewById<Spinner>(Resource.Id.sftp_key_names);
var keyNamesAdapter = new ArrayAdapter(ctx, Android.Resource.Layout.SimpleSpinnerDropDownItem, new List<string>());
UpdatePrivateKeyNames(keyNamesAdapter, fileStorage, ctx);
keyNamesAdapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
keySpinner.Adapter = keyNamesAdapter;
keySpinner.SetSelection(SftpKeySpinnerCreateNewIdx);
keySpinner.ItemSelected += (sender, args) =>
{
if (keySpinner.SelectedItemPosition == SftpKeySpinnerCreateNewIdx)
{
keyNameTxt.Text = "";
keyContentTxt.Text = "";
addNewBtn.Visibility = ViewStates.Visible;
deleteBtn.Visibility = ViewStates.Gone;
}
else
{
addNewBtn.Visibility = ViewStates.Gone;
deleteBtn.Visibility = ViewStates.Visible;
}
};
var authModeSpinner = dlgContents.FindViewById<Spinner>(Resource.Id.sftp_auth_mode_spinner);
dlgContents.FindViewById<Button>(Resource.Id.send_public_key_button).Click += (sender, args) =>
{
string pub_filename = fileStorage.CreateKeyPair();
Intent sendIntent = new Intent();
sendIntent.SetAction(Intent.ActionSend);
sendIntent.PutExtra(Intent.ExtraText, System.IO.File.ReadAllText(pub_filename));
sendIntent.PutExtra(Intent.ExtraSubject, "Keepass2Android sftp public key");
sendIntent.SetType("text/plain");
activity.StartActivity(Intent.CreateChooser(sendIntent, "Send public key to..."));
};
dlgContents.FindViewById<Button>(Resource.Id.sftp_save_key_button).Click += (sender, args) =>
{
string keyName = keyNameTxt.Text;
string keyContent = keyContentTxt.Text;
string toastMsg = null;
if (!string.IsNullOrEmpty(keyName) && !string.IsNullOrEmpty(keyContent))
{
try
{
fileStorage.SavePrivateKeyContent(keyName, keyContent);
keyNameTxt.Text = "";
keyContentTxt.Text = "";
toastMsg = ctx.GetString(Resource.String.private_key_saved);
}
catch (Exception e)
{
toastMsg = ctx.GetString(Resource.String.private_key_save_failed,
new Java.Lang.Object[] { e.Message });
}
}
else
{
toastMsg = ctx.GetString(Resource.String.private_key_info);
}
if (toastMsg!= null) {
Toast.MakeText(_activity, toastMsg, ToastLength.Long).Show();
}
UpdatePrivateKeyNames(keyNamesAdapter, fileStorage, ctx);
keySpinner.SetSelection(ResolveKeySpinnerSelection(keyNamesAdapter, keyName));
};
dlgContents.FindViewById<Button>(Resource.Id.sftp_delete_key_button).Click += (sender, args) =>
{
int selectedKey = dlgContents.FindViewById<Spinner>(Resource.Id.sftp_key_names).SelectedItemPosition;
string keyName = ResolveSelectedKeyName(keyNamesAdapter, selectedKey);
if (!string.IsNullOrEmpty(keyName))
{
bool deleted = fileStorage.DeleteCustomKey(keyName);
int msgId = deleted ? Resource.String.private_key_delete : Resource.String.private_key_delete_failed;
string msg = ctx.GetString(msgId, new Java.Lang.Object[] { keyName });
Toast.MakeText(_activity, msg, ToastLength.Long).Show();
UpdatePrivateKeyNames(keyNamesAdapter, fileStorage, ctx);
keySpinner.SetSelection(SftpKeySpinnerCreateNewIdx);
}
};
authModeSpinner.ItemSelected += (sender, args) =>
{
var passwordBox = dlgContents.FindViewById<EditText>(Resource.Id.sftp_password);
var publicKeyButton = dlgContents.FindViewById<Button>(Resource.Id.send_public_key_button);
var keyfileGroup = dlgContents.FindViewById<LinearLayout>(Resource.Id.sftp_keyfile_group);
switch (authModeSpinner.SelectedItemPosition)
{
case SftpModeSpinnerPasswd:
passwordBox.Visibility = ViewStates.Visible;
publicKeyButton.Visibility = ViewStates.Gone;
keyfileGroup.Visibility = ViewStates.Gone;
break;
case SftpModeSpinnerPubKey:
passwordBox.Visibility = ViewStates.Gone;
publicKeyButton.Visibility = ViewStates.Visible;
keyfileGroup.Visibility = ViewStates.Gone;
break;
case SftpModeSpinnerCustomKey:
passwordBox.Visibility = ViewStates.Gone;
publicKeyButton.Visibility = ViewStates.Gone;
keyfileGroup.Visibility = ViewStates.Visible;
break;
}
};
if (!defaultPath.EndsWith(_schemeSeparator))
{
SftpStorage.ConnectionInfo ci = fileStorage.SplitStringToConnectionInfo(defaultPath);
dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text = ci.Host;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text = ci.Port.ToString();
dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text = ci.Username;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Text = ci.Password;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_name).Text = ci.KeyName;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_passphrase).Text = ci.KeyPassphrase;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text = ci.LocalPath;
if (ci.ConnectTimeoutSec != SftpStorage.UnsetSftpConnectTimeout)
{
dlgContents.FindViewById<EditText>(Resource.Id.sftp_connect_timeout).Text = ci.ConnectTimeoutSec.ToString();
}
if (ci.ConfigOpts.Contains(SftpStorage.SshCfgKex))
{
dlgContents.FindViewById<EditText>(Resource.Id.sftp_kex).Text = ci.ConfigOpts[SftpStorage.SshCfgKex].ToString();
}
if (ci.ConfigOpts.Contains(SftpStorage.SshCfgServerHostKey))
{
dlgContents.FindViewById<EditText>(Resource.Id.sftp_shk).Text = ci.ConfigOpts[SftpStorage.SshCfgServerHostKey].ToString();
}
if (!string.IsNullOrEmpty(ci.Password))
{
authModeSpinner.SetSelection(SftpModeSpinnerPasswd);
} else if (!string.IsNullOrEmpty(ci.KeyName))
{
authModeSpinner.SetSelection(SftpModeSpinnerCustomKey);
keySpinner.SetSelection(ResolveKeySpinnerSelection(keyNamesAdapter, ci.KeyName));
} else
{
authModeSpinner.SetSelection(SftpModeSpinnerPubKey);
}
}
builder.SetView(dlgContents);
builder.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => {
int idx = 0;
string host = dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text;
string portText = dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text;
int port = SftpStorage.DefaultSftpPort;
if (!string.IsNullOrEmpty(portText))
int.TryParse(portText, out port);
string user = dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text;
string password = null;
string keyName = null, keyPassphrase = null;
switch (dlgContents.FindViewById<Spinner>(Resource.Id.sftp_auth_mode_spinner).SelectedItemPosition)
{
case SftpModeSpinnerPasswd:
password = dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Text;
break;
case SftpModeSpinnerPubKey:
break;
case SftpModeSpinnerCustomKey:
keyName = ResolveSelectedKeyName(keyNamesAdapter, keySpinner.SelectedItemPosition);
keyPassphrase = dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_passphrase).Text;
break;
}
string initialPath = dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text;
if (string.IsNullOrEmpty(initialPath))
initialPath = "/";
string connectTimeoutText = dlgContents.FindViewById<EditText>(Resource.Id.sftp_connect_timeout).Text;
int connectTimeout = SftpStorage.UnsetSftpConnectTimeout;
if (!string.IsNullOrEmpty(connectTimeoutText))
{
int.TryParse(connectTimeoutText, out connectTimeout);
}
string kexAlgorithms = dlgContents.FindViewById<EditText>(Resource.Id.sftp_kex).Text;
string shkAlgorithms = dlgContents.FindViewById<EditText>(Resource.Id.sftp_shk).Text;
string sftpPath = fileStorage.BuildFullPath(
host, port, initialPath, user, password, connectTimeout, keyName, keyPassphrase,
kexAlgorithms, shkAlgorithms);
onStartBrowse(sftpPath);
});
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((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
}
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
private void UpdatePrivateKeyNames(ArrayAdapter dataView, SftpStorage storage, Context ctx)
{
dataView.Clear();
dataView.Add(ctx.GetString(Resource.String.private_key_create_new));
foreach (string keyName in storage.GetCustomKeyNames())
dataView.Add(keyName);
}
private int ResolveKeySpinnerSelection(ArrayAdapter dataView, string keyName)
{
int idx = -1;
for (int i = 0; i < dataView.Count; i++)
{
string itemName = dataView.GetItem(i).ToString();
if (string.Equals(keyName, itemName)) {
idx = i;
break;
}
}
return idx < 0 ? SftpKeySpinnerCreateNewIdx : idx;
}
private string ResolveSelectedKeyName(ArrayAdapter dataView, int selectedItem)
{
if (selectedItem != SftpKeySpinnerCreateNewIdx && selectedItem > 0 && selectedItem < dataView.Count)
return dataView.GetItem(selectedItem).ToString();
else
return null;
}
#endif
private void ShowHttpDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel, string defaultPath)
{
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
View dlgContents = activity.LayoutInflater.Inflate(Resource.Layout.httpcredentials, null);
if (!defaultPath.EndsWith(_schemeSeparator))
{
var webdavStorage = new Keepass2android.Javafilestorage.WebDavStorage(App.Kp2a.CertificateErrorHandler);
var connInfo = webdavStorage.SplitStringToConnectionInfo(defaultPath);
dlgContents.FindViewById<EditText>(Resource.Id.http_url).Text = connInfo.Url;
dlgContents.FindViewById<EditText>(Resource.Id.http_user).Text = connInfo.Username;
dlgContents.FindViewById<EditText>(Resource.Id.http_password).Text = connInfo.Password;
}
builder.SetView(dlgContents);
builder.SetPositiveButton(Android.Resource.String.Ok,
(sender, args) =>
{
string host = dlgContents.FindViewById<EditText>(Resource.Id.http_url).Text;
string user = dlgContents.FindViewById<EditText>(Resource.Id.http_user).Text;
string password = dlgContents.FindViewById<EditText>(Resource.Id.http_password).Text;
string scheme = defaultPath.Substring(0, defaultPath.IndexOf(_schemeSeparator, StringComparison.Ordinal));
if (host.Contains(_schemeSeparator) == false)
host = scheme + _schemeSeparator + host;
string httpPath = new Keepass2android.Javafilestorage.WebDavStorage(null).BuildFullPath(host, user,
password);
onStartBrowse(httpPath);
});
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());
builder.SetNegativeButton(Android.Resource.String.Cancel, evtH);
builder.SetTitle(activity.GetString(Resource.String.enter_http_login_title));
Dialog dialog = builder.Create();
dialog.Show();
#endif
}
private void ShowFtpDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel, string defaultPath)
{
#if !NoNet
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
View dlgContents = activity.LayoutInflater.Inflate(Resource.Layout.ftpcredentials, null);
if (!defaultPath.EndsWith(_schemeSeparator))
{
var connection = NetFtpFileStorage.ConnectionSettings.FromIoc(IOConnectionInfo.FromPath(defaultPath));
dlgContents.FindViewById<EditText>(Resource.Id.ftp_user).Text = connection.Username;
dlgContents.FindViewById<EditText>(Resource.Id.ftp_password).Text = connection.Password;
dlgContents.FindViewById<Spinner>(Resource.Id.ftp_encryption).SetSelection((int)connection.EncryptionMode);
var uri = NetFtpFileStorage.IocToUri(IOConnectionInfo.FromPath(defaultPath));
string pathAndQuery = uri.PathAndQuery;
var host = uri.Host;
var localPath = WebUtility.UrlDecode(pathAndQuery);
if (!uri.IsDefaultPort)
{
dlgContents.FindViewById<EditText>(Resource.Id.ftp_port).Text = uri.Port.ToString();
}
dlgContents.FindViewById<EditText>(Resource.Id.ftp_host).Text = host;
dlgContents.FindViewById<EditText>(Resource.Id.ftp_initial_dir).Text = localPath;
}
builder.SetView(dlgContents);
builder.SetPositiveButton(Android.Resource.String.Ok,
(sender, args) =>
{
string host = dlgContents.FindViewById<EditText>(Resource.Id.ftp_host).Text;
string portText = dlgContents.FindViewById<EditText>(Resource.Id.ftp_port).Text;
FtpEncryptionMode encryption =
(FtpEncryptionMode) dlgContents.FindViewById<Spinner>(Resource.Id.ftp_encryption).SelectedItemPosition;
int port = NetFtpFileStorage.GetDefaultPort(encryption);
if (!string.IsNullOrEmpty(portText))
int.TryParse(portText, out port);
string user = dlgContents.FindViewById<EditText>(Resource.Id.ftp_user).Text;
string password = dlgContents.FindViewById<EditText>(Resource.Id.ftp_password).Text;
string initialPath = dlgContents.FindViewById<EditText>(Resource.Id.ftp_initial_dir).Text;
string ftpPath = new NetFtpFileStorage(_activity, App.Kp2a, () => false)
.BuildFullPath(host, port, initialPath, user, password, encryption);
onStartBrowse(ftpPath);
});
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());
builder.SetNegativeButton(Android.Resource.String.Cancel, evtH);
builder.SetTitle(activity.GetString(Resource.String.enter_ftp_login_title));
Dialog dialog = builder.Create();
dialog.Show();
#endif
}
private void ShowMegaDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel, string defaultPath)
{
#if !NoNet
var settings = MegaFileStorage.GetAccountSettings(activity);
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
View dlgContents = activity.LayoutInflater.Inflate(Resource.Layout.megacredentials, null);
if (!defaultPath.EndsWith(_schemeSeparator))
{
string user = "";
string password = "";
if (!defaultPath.EndsWith(_schemeSeparator))
{
user = MegaFileStorage.GetAccount(defaultPath);
if (!settings.PasswordByUsername.TryGetValue(user, out password))
password = "";
dlgContents.FindViewById<EditText>(Resource.Id.mega_user).Enabled = false;
}
dlgContents.FindViewById<EditText>(Resource.Id.mega_user).Text = user;
dlgContents.FindViewById<EditText>(Resource.Id.mega_password).Text = password;
}
var userView = ((AutoCompleteTextView)dlgContents.FindViewById(Resource.Id.mega_user));
userView.Adapter = new ArrayAdapter(activity, Android.Resource.Layout.SimpleListItem1, Android.Resource.Id.Text1, settings.PasswordByUsername.Keys.ToArray());
userView.TextChanged += (sender, args) =>
{
if (userView.Text != null && settings.PasswordByUsername.TryGetValue(userView.Text, out string pwd))
{
dlgContents.FindViewById<EditText>(Resource.Id.mega_password).Text = pwd;
}
};
builder.SetCancelable(false);
builder.SetView(dlgContents);
builder.SetPositiveButton(Android.Resource.String.Ok,
(sender, args) =>
{
string user = dlgContents.FindViewById<EditText>(Resource.Id.mega_user).Text;
string password = dlgContents.FindViewById<EditText>(Resource.Id.mega_password).Text;
//store the credentials in the mega credentials store:
settings.PasswordByUsername[user] = password;
MegaFileStorage.UpdateAccountSettings(settings, activity);
onStartBrowse(MegaFileStorage.ProtocolId + "://" + user);
});
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());
builder.SetNegativeButton(Android.Resource.String.Cancel, evtH);
builder.SetTitle(activity.GetString(Resource.String.enter_mega_login_title));
Dialog dialog = builder.Create();
dialog.Show();
#endif
}
public void PerformManualFileSelect(string defaultPath)
{
if (defaultPath.StartsWith("sftp://"))
ShowSftpDialog(_activity, ReturnFileOrStartFileChooser, ReturnCancel, defaultPath);
else if ((defaultPath.StartsWith("ftp://")) || (defaultPath.StartsWith("ftps://")))
ShowFtpDialog(_activity, ReturnFileOrStartFileChooser, ReturnCancel, defaultPath);
else if ((defaultPath.StartsWith("http://")) || (defaultPath.StartsWith("https://")))
ShowHttpDialog(_activity, ReturnFileOrStartFileChooser, ReturnCancel, defaultPath);
else if (defaultPath.StartsWith("owncloud://"))
ShowOwncloudDialog(_activity, ReturnFileOrStartFileChooser, ReturnCancel, defaultPath, "owncloud");
else if (defaultPath.StartsWith("nextcloud://"))
ShowOwncloudDialog(_activity, ReturnFileOrStartFileChooser, ReturnCancel, defaultPath, "nextcloud");
else if (defaultPath.StartsWith("mega://"))
ShowMegaDialog(_activity, ReturnFileOrStartFileChooser, ReturnCancel, defaultPath);
else
{
Func<string, Dialog, bool> onOpen = OnOpenButton;
Util.ShowFilenameDialog(_activity,
!_isForSave ? onOpen : null,
_isForSave ? onOpen : null,
ReturnCancel,
false,
defaultPath,
_activity.GetString(Resource.String.enter_filename_details_url),
_requestCode)
;
}
}
private void ShowOwncloudDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel, string defaultPath, string subtype)
{
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
View dlgContents = activity.LayoutInflater.Inflate(Resource.Layout.owncloudcredentials, null);
builder.SetView(dlgContents);
builder.SetPositiveButton(Android.Resource.String.Ok,
(sender, args) =>
{
string host = dlgContents.FindViewById<EditText>(Resource.Id.owncloud_url).Text;
string user = dlgContents.FindViewById<EditText>(Resource.Id.http_user).Text;
string password = dlgContents.FindViewById<EditText>(Resource.Id.http_password).Text;
string scheme = defaultPath.Substring(0,defaultPath.IndexOf(_schemeSeparator, StringComparison.Ordinal));
if (host.Contains(_schemeSeparator) == false)
host = scheme + _schemeSeparator + host;
string httpPath = new Keepass2android.Javafilestorage.WebDavStorage(null).BuildFullPath(WebDavFileStorage.Owncloud2Webdav(host, subtype == "owncloud" ? WebDavFileStorage.owncloudPrefix : WebDavFileStorage.nextcloudPrefix), user,
password);
onStartBrowse(httpPath);
});
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());
builder.SetNegativeButton(Android.Resource.String.Cancel, evtH);
builder.SetTitle(activity.GetString(subtype == "owncloud" ? Resource.String.enter_owncloud_login_title : Resource.String.enter_nextcloud_login_title));
Dialog dialog = builder.Create();
dlgContents.FindViewById<EditText>(Resource.Id.owncloud_url).SetHint(subtype == "owncloud" ? Resource.String.hint_owncloud_url : Resource.String.hint_nextcloud_url);
dialog.Show();
#endif
}
private bool ReturnFileOrStartFileChooser(string filename)
{
string filenameWithoutProt = filename;
if (filenameWithoutProt.Contains(_schemeSeparator))
{
filenameWithoutProt =
filenameWithoutProt.Substring(filenameWithoutProt.IndexOf(_schemeSeparator, StringComparison.Ordinal) + 3);
}
int lastSlashPos = filenameWithoutProt.LastIndexOf('/');
int lastDotPos = filenameWithoutProt.LastIndexOf('.');
if ((lastSlashPos < 0 ) //no slash, probably only a server address (my.server.com)
|| (lastSlashPos >= lastDotPos)) //no dot after last slash or == in case neither / nor .
{
//looks like a folder.
return StartFileChooser(filename);
}
//looks like a file
IocSelected(null, IOConnectionInfo.FromPath(filename));
return true;
}
private void ReturnCancel()
{
if (OnCancel != null)
OnCancel(this, null);
}
protected void ShowFilenameWarning(string fileName, Action onUserWantsToContinue, Action onUserWantsToCorrect)
{
new MaterialAlertDialogBuilder(_activity)
.SetPositiveButton(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(null, 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
Java.IO.File file = new Java.IO.File(filename);
try
{
Java.IO.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).Dispose();
}
catch (Java.IO.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, iocResult => IocSelected(null, iocResult), _activity);
}
else
{
IocSelected(null, ioc);
}
return true;
}
private void IocSelected(Activity activity, IOConnectionInfo ioc)
{
if (OnOpen != null)
OnOpen(activity, ioc);
}
public bool StartFileChooser(string defaultPath)
{
#if !EXCLUDE_FILECHOOSER
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(LocaleManager.LocalizedAppContext, "File chooser is excluded!", ToastLength.Long).Show();
#endif
return true;
}
public event EventHandler OnCancel;
public event EventHandler<IOConnectionInfo> OnOpen;
public static bool CanEditIoc(IOConnectionInfo ioc)
{
return ioc.Path.StartsWith("http")
|| ioc.Path.StartsWith("ftp")
|| ioc.Path.StartsWith("sftp")
|| ioc.Path.StartsWith("mega");
}
public bool HandleActivityResult(Activity activity, int requestCode, Result resultCode, Intent data)
{
if (requestCode != _requestCode)
return false;
if (resultCode == KeePass.ExitFileStorageSelectionOk)
{
string protocolId = data.GetStringExtra("protocolId");
if (protocolId == "content")
{
Util.ShowBrowseDialog(activity, _requestCode, _isForSave, _tryGetPermanentAccess);
}
else
{
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(
new FileStorageSetupInitiatorActivity(activity,(i, result, arg3) =>HandleActivityResult(activity, i, result, arg3),s => PerformManualFileSelect(s)),
_isForSave,
_requestCode,
protocolId);
}
}
if (resultCode == Result.Ok)
{
if (data.Data.Scheme == "content")
{
if ((int)Build.VERSION.SdkInt >= 19)
{
//try to take persistable permissions
try
{
Kp2aLog.Log("TakePersistableUriPermission");
var takeFlags = data.Flags
& (ActivityFlags.GrantReadUriPermission
| ActivityFlags.GrantWriteUriPermission);
activity.ContentResolver.TakePersistableUriPermission(data.Data, takeFlags);
}
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
}
}
}
string filename = Util.IntentToFilename(data, activity);
if (filename == null)
filename = data.DataString;
bool fileExists = data.GetBooleanExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.result_file_exists", true);
if (fileExists)
{
var ioc = new IOConnectionInfo { Path = ConvertFilenameToIocPath(filename) };
IocSelected(activity,ioc);
}
else
{
var task = new CreateNewFilename(activity, new ActionOnFinish(activity, (success, messageOrFilename, newActivity) =>
{
if (!success)
{
Toast.MakeText(newActivity, messageOrFilename, ToastLength.Long).Show();
return;
}
var ioc = new IOConnectionInfo { Path = ConvertFilenameToIocPath(messageOrFilename) };
IocSelected(newActivity, ioc);
}), filename);
new ProgressTask(App.Kp2a, activity, task).Run();
}
}
if (resultCode == (Result)FileStorageResults.FileUsagePrepared)
{
var ioc = new IOConnectionInfo();
Util.SetIoConnectionFromIntent(ioc, data);
IocSelected(null, ioc);
}
if (resultCode == (Result)FileStorageResults.FileChooserPrepared )
{
IOConnectionInfo ioc = new IOConnectionInfo();
Util.SetIoConnectionFromIntent(ioc, data);
StartFileChooser(ioc.Path);
}
return true;
}
}
}