remove bad named folder

This commit is contained in:
Philipp Crocoll
2024-12-07 07:54:14 +01:00
parent 95d7ecd066
commit f974b0ffa0
873 changed files with 0 additions and 101203 deletions

View File

@@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_ActionBar",
Exported = true)]
[IntentFilter(new[] { "kp2a.action.AboutActivity" }, Categories = new[] { Intent.CategoryDefault })]
public class AboutActivity: Activity, IDialogInterfaceOnDismissListener
{
private AboutDialog _dialog;
protected override void OnResume()
{
if ((_dialog == null) || (_dialog.IsShowing == false))
{
if (new ActivityDesign(this).UseDarkTheme)
_dialog = new AboutDialog(this, Android.Resource.Style.ThemeHoloNoActionBarFullscreen);
else
_dialog = new AboutDialog(this, Android.Resource.Style.ThemeHoloLightNoActionBarFullscreen);
_dialog.SetOnDismissListener(this);
_dialog.Show();
}
base.OnResume();
}
public void OnDismiss(IDialogInterface dialog)
{
Finish();
}
}
}

View File

@@ -1,125 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Text.Method;
using Android.Widget;
using Android.Content.PM;
namespace keepass2android
{
public class AboutDialog : Dialog {
public AboutDialog(Context context):base (context) {
}
public AboutDialog(Context context, int theme)
: base(context, theme)
{
}
public AboutDialog(IntPtr javaRef, JniHandleOwnership transfer) : base(javaRef, transfer)
{
}
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.about);
SetTitle(Resource.String.app_name);
SetVersion();
SetContributors();
FindViewById(Resource.Id.suggest).Click += delegate
{
try
{
Util.GotoUrl(Context, Resource.String.SuggestionsURL);
}
catch (ActivityNotFoundException)
{
Toast.MakeText(Context, Resource.String.no_url_handler, ToastLength.Long).Show();
}
};
FindViewById(Resource.Id.rate).Click += delegate
{
try
{
Util.GotoMarket(Context);
}
catch (ActivityNotFoundException)
{
Toast.MakeText(Context, Resource.String.no_url_handler, ToastLength.Long).Show();
}
};
FindViewById(Resource.Id.translate).Click += delegate
{
try
{
Util.GotoUrl(Context, Resource.String.TranslationURL);
}
catch (ActivityNotFoundException)
{
Toast.MakeText(Context, Resource.String.no_url_handler, ToastLength.Long).Show();
}
}; FindViewById(Resource.Id.donate).Click += delegate
{
Util.GotoDonateUrl(Context);
};
}
private void SetContributors()
{
TextView tv = (TextView)FindViewById(Resource.Id.further_authors);
tv.Text = Context.GetString(Resource.String.further_authors, new Java.Lang.Object[] { Context.GetString(Resource.String.further_author_names) });
TextView tvdesigners = (TextView)FindViewById(Resource.Id.designers);
tvdesigners.Text = Context.GetString(Resource.String.designers, new Java.Lang.Object[] { Context.GetString(Resource.String.designer_names) });
TextView tvsupporters = (TextView)FindViewById(Resource.Id.supporters);
tvsupporters.Text = Context.GetString(Resource.String.supporters, new Java.Lang.Object[] { Context.GetString(Resource.String.supporter_names) });
}
private void SetVersion() {
Context ctx = Context;
String version;
try {
PackageInfo packageInfo = ctx.PackageManager.GetPackageInfo(ctx.PackageName, 0);
version = packageInfo.VersionName;
} catch (PackageManager.NameNotFoundException) {
version = "";
}
TextView tv = (TextView) FindViewById(Resource.Id.versionX);
tv.Text = version;
FindViewById(Resource.Id.versionB).Click += (sender, args) => ChangeLog.ShowChangeLog(ctx, () => { });
}
}
}

View File

@@ -1,50 +0,0 @@
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;
namespace keepass2android
{
public abstract class ActivityLaunchMode
{
public abstract void Launch(Activity act, Intent i);
}
public class ActivityLaunchModeForward : ActivityLaunchMode
{
public override void Launch(Activity act, Intent i)
{
i.AddFlags(ActivityFlags.ForwardResult);
act.StartActivity(i);
}
}
public class ActivityLaunchModeRequestCode : ActivityLaunchMode
{
private readonly int _reqCode;
public ActivityLaunchModeRequestCode(int reqCode)
{
_reqCode = reqCode;
}
public override void Launch(Activity act, Intent i)
{
act.StartActivityForResult(i, _reqCode);
}
}
public class ActivityLaunchModeSimple : ActivityLaunchMode
{
public override void Launch(Activity act, Intent i)
{
act.StartActivity(i);
}
}
}

View File

@@ -1,47 +0,0 @@
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;
namespace keepass2android
{
[Activity(Label = AppNames.AppName)]
public class AppKilledInfo : Activity, IDialogInterfaceOnDismissListener
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
//as we expect this to happen only rarely (having a foreground service running when unlocked),
//we don't try to handle this better
//But at least explain to the user what happened!
((NotificationManager)GetSystemService(Context.NotificationService)).CancelAll();
AlertDialog.Builder b = new AlertDialog.Builder(this);
b.SetMessage(Resource.String.killed_by_os);
b.SetPositiveButton(Android.Resource.String.Ok, delegate
{
Intent i = new Intent(this, typeof(SelectCurrentDbActivity));
i.AddFlags(ActivityFlags.ClearTask | ActivityFlags.NewTask);
StartActivity(i);
});
b.SetNegativeButton(Resource.String.cancel, delegate { });
b.SetTitle(GetString(AppNames.AppNameResource));
var dialog = b.Create();
dialog.SetOnDismissListener(this);
dialog.Show();
}
public void OnDismiss(IDialogInterface dialog)
{
Finish();
}
}
}

View File

@@ -1,21 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/

View File

@@ -1,93 +0,0 @@
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@@ -1,93 +0,0 @@
using System;
using Android.Content;
using Android.OS;
using Android.Util;
using Java.IO;
namespace keepass2android
{
/// <summary>
/// Makes attachments of PwEntries accessible when they are stored in the app cache
/// </summary>
[ContentProvider(new[]{"keepass2android."+AppNames.PackagePart+".provider"},Exported = true)]
public class AttachmentContentProvider : ContentProvider {
public const string AttachmentCacheSubDir = "AttachmentCache";
private const String ClassName = "AttachmentContentProvider";
// The authority is the symbolic name for the provider class
public const String Authority = "keepass2android."+AppNames.PackagePart+".provider";
public override bool OnCreate() {
return true;
}
public override ParcelFileDescriptor OpenFile(Android.Net.Uri uri, String mode)
{
const string logTag = ClassName + " - openFile";
Log.Verbose(logTag,
"Called with uri: '" + uri + "'." + uri.LastPathSegment);
if (uri.ToString().StartsWith("content://" + Authority))
{
// The desired file name is specified by the last segment of the
// path
// E.g.
// 'content://keepass2android.provider/Test.txt'
// Take this and build the path to the file
//Protect against path traversal with an uri like content://keepass2android.keepass2android.provider/..%2F..%2Fshared_prefs%2FKP2A.Plugin.keepass2android.plugin.qr.xml
if (uri.LastPathSegment.Contains("/"))
throw new Exception("invalid path ");
String fileLocation = Context.CacheDir + File.Separator + AttachmentCacheSubDir + File.Separator
+ uri.LastPathSegment;
// Create & return a ParcelFileDescriptor pointing to the file
// Note: I don't care what mode they ask for - they're only getting
// read only
ParcelFileDescriptor pfd = ParcelFileDescriptor.Open(new File(
fileLocation), ParcelFileMode.ReadOnly);
return pfd;
}
Log.Verbose(logTag, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: "
+ uri.ToString());
}
// //////////////////////////////////////////////////////////////
// Not supported / used / required for this example
// //////////////////////////////////////////////////////////////
public override int Update(Android.Net.Uri uri, ContentValues contentvalues, String s,
String[] strings) {
return 0;
}
public override int Delete(Android.Net.Uri uri, String s, String[] strings) {
return 0;
}
public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues contentvalues) {
return null;
}
public override String GetType(Android.Net.Uri uri) {
return null;
}
public override Android.Database.ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
{
return null;
}
}
}

View File

@@ -1,168 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Android.App;
using KeePassLib;
using KeePassLib.Security;
using KeePassLib.Serialization;
using Object = Java.Lang.Object;
namespace keepass2android
{
/// <summary>
/// Edit mode implementation for AutoOpen entries
/// </summary>
public class AutoOpenEdit : EditModeBase
{
private const string strVisible = "Visible";
private const string strEnabled = "Enabled";
private const string strUiKeyFile = "_ui_KeyFile";
private const string strUiDatabaseFile = "_ui_DatabaseFile";
private const string strUiIfDevice = "_ui_IfDevice_";
public AutoOpenEdit(PwEntry entry)
{
}
public override bool IsVisible(string fieldKey)
{
if (fieldKey == PwDefs.TitleField
|| fieldKey == PwDefs.PasswordField
|| fieldKey == strVisible
|| fieldKey == strEnabled
|| fieldKey.StartsWith("_ui_"))
{
return true;
}
return false;
}
public override IEnumerable<string> SortExtraFieldKeys(IEnumerable<string> keys)
{
return keys.OrderBy(s =>
{
if (s == strUiDatabaseFile) return 1;
if (s == strEnabled) return 2;
if (s == strUiKeyFile) return 10000;
if (s == strVisible) return 10001;
return 10;
}).ThenBy(s => s);
}
public override bool ShowAddAttachments
{
get { return false; }
}
public override bool ShowAddExtras
{
get { return false; }
}
public override string GetTitle(string key)
{
if (key == strVisible)
return LocaleManager.LocalizedAppContext.GetString(Resource.String.Visible_title);
if (key == strEnabled)
return LocaleManager.LocalizedAppContext.GetString(Resource.String.child_db_Enabled_title);
if (key == strUiKeyFile)
return LocaleManager.LocalizedAppContext.GetString(Resource.String.keyfile_heading);
if (key == strUiDatabaseFile)
return LocaleManager.LocalizedAppContext.GetString(Resource.String.database_file_heading);
if (key.StartsWith(strUiIfDevice))
{
return LocaleManager.LocalizedAppContext.GetString(Resource.String.if_device_text,new Object[]{key.Substring(strUiIfDevice.Length)});
}
return key;
}
public override string GetFieldType(string key)
{
if ((key == strEnabled)
|| key == strVisible
|| key.StartsWith(strUiIfDevice))
return "bool";
if ((key == strUiDatabaseFile)
|| (key == strUiKeyFile))
return "file";
return "";
}
public override void InitializeEntry(PwEntry entry)
{
base.InitializeEntry(entry);
if (!entry.Strings.Exists(strVisible))
{
entry.Strings.Set(strVisible, new ProtectedString(false, "true"));
}
if (!entry.Strings.Exists(strEnabled))
{
entry.Strings.Set(strEnabled, new ProtectedString(false, "true"));
}
var autoExecItem = KeeAutoExecExt.MakeAutoExecItem(App.Kp2a.CurrentDb.KpDatabase, entry, 0);
IOConnectionInfo ioc;
if (!KeeAutoExecExt.TryGetDatabaseIoc(autoExecItem, out ioc))
ioc = IOConnectionInfo.FromPath(entry.Strings.ReadSafe(PwDefs.UrlField));
string path = ioc.Path;
try
{
var filestorage = App.Kp2a.GetFileStorage(ioc);
if (filestorage != null)
{
path = filestorage.IocToPath(ioc);
}
}
catch (NoFileStorageFoundException)
{
}
entry.Strings.Set(strUiDatabaseFile, new ProtectedString(false, path));
entry.Strings.Set(strUiKeyFile,new ProtectedString(false,entry.Strings.ReadSafe(PwDefs.UserNameField)));
var devices =
KeeAutoExecExt.GetIfDevice(KeeAutoExecExt.MakeAutoExecItem(App.Kp2a.CurrentDb.KpDatabase, entry, 0));
//make sure user can enable/disable on this device explicitly:
if (!devices.ContainsKey(KeeAutoExecExt.ThisDeviceId))
devices[KeeAutoExecExt.ThisDeviceId] = false;
foreach (var ifDevice in devices)
{
entry.Strings.Set(strUiIfDevice + ifDevice.Key, new ProtectedString(false, ifDevice.Value.ToString()));
}
}
public override void PrepareForSaving(PwEntry entry)
{
entry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, entry.Strings.ReadSafe(strUiDatabaseFile)));
entry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, entry.Strings.ReadSafe(strUiKeyFile)));
entry.Strings.Remove(strUiDatabaseFile);
entry.Strings.Remove(strUiKeyFile);
Dictionary<string, bool> devices = new Dictionary<string, bool>();
foreach (string key in entry.Strings.GetKeys())
{
if (key.StartsWith(strUiIfDevice))
{
string device = key.Substring(strUiIfDevice.Length);
devices[device] = entry.Strings.ReadSafe(key).Equals("true", StringComparison.OrdinalIgnoreCase);
}
}
entry.Strings.Set(KeeAutoExecExt._ifDevice,new ProtectedString(false,KeeAutoExecExt.BuildIfDevice(devices)));
foreach (string device in devices.Keys)
{
entry.Strings.Remove(strUiIfDevice + device);
}
base.PrepareForSaving(entry);
}
}
}

View File

@@ -1,492 +0,0 @@
using System;
using Android.Content;
using Javax.Crypto;
using Java.Security;
using Java.Lang;
using Android.Views.InputMethods;
using Android.App;
using Android.OS;
using Android.Security.Keystore;
using Android.Preferences;
using Android.Util;
using Android.Widget;
using AndroidX.Biometric;
using AndroidX.Fragment.App;
using Java.IO;
using Java.Security.Cert;
using Java.Util.Concurrent;
using Javax.Crypto.Spec;
using Exception = System.Exception;
using File = System.IO.File;
namespace keepass2android
{
public interface IBiometricAuthCallback
{
void OnBiometricAuthSucceeded();
void OnBiometricError(string toString);
void OnBiometricAttemptFailed(string message);
}
public class BiometricModule
{
public AndroidX.Fragment.App.FragmentActivity Activity { get; set; }
public BiometricModule(AndroidX.Fragment.App.FragmentActivity activity)
{
Activity = activity;
}
public KeyguardManager KeyguardManager
{
get
{
return (KeyguardManager)Activity.GetSystemService("keyguard");
}
}
public KeyStore Keystore
{
get
{
try
{
return KeyStore.GetInstance("AndroidKeyStore");
}
catch (KeyStoreException e)
{
throw new RuntimeException("Failed to get an instance of KeyStore", e);
}
}
}
public KeyGenerator KeyGenerator
{
get
{
try
{
return KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, "AndroidKeyStore");
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("Failed to get an instance of KeyGenerator", e);
}
catch (NoSuchProviderException e)
{
throw new RuntimeException("Failed to get an instance of KeyGenerator", e);
}
}
}
public Cipher Cipher
{
get
{
try
{
return Cipher.GetInstance(KeyProperties.KeyAlgorithmAes + "/"
+ KeyProperties.BlockModeCbc + "/"
+ KeyProperties.EncryptionPaddingPkcs7);
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("Failed to get an instance of Cipher", e);
}
catch (NoSuchPaddingException e)
{
throw new RuntimeException("Failed to get an instance of Cipher", e);
}
}
}
public ISharedPreferences SharedPreferences
{
get { return PreferenceManager.GetDefaultSharedPreferences(Activity); }
}
public bool IsAvailable
{
get
{
return BiometricManager.From(Activity).CanAuthenticate() ==
BiometricManager.BiometricSuccess;
}
}
public bool IsHardwareAvailable
{
get
{
var result = BiometricManager.From(Activity).CanAuthenticate();
Kp2aLog.Log("BiometricHardware available = " + result);
return result == BiometricManager.BiometricSuccess
|| result == BiometricManager.BiometricErrorNoneEnrolled;
}
}
}
public abstract class BiometricCrypt : IBiometricIdentifier
{
protected const string FailedToInitCipher = "Failed to init Cipher";
protected readonly string _keyId;
protected Cipher _cipher;
private CancellationSignal _cancellationSignal;
protected BiometricPrompt.CryptoObject _cryptoObject;
protected KeyStore _keystore;
private BiometricPrompt _biometricPrompt;
private FragmentActivity _activity;
private BiometricAuthCallbackAdapter _biometricAuthCallbackAdapter;
public BiometricCrypt(BiometricModule biometric, string keyId)
{
Kp2aLog.Log("FP: Create " + this.GetType().Name);
_keyId = keyId;
_cipher = biometric.Cipher;
_keystore = biometric.Keystore;
_activity = biometric.Activity;
}
public abstract bool Init();
protected static string GetAlias(string keyId)
{
return "keepass2android." + keyId;
}
protected static string GetIvPrefKey(string prefKey)
{
return prefKey + "_iv";
}
public void StartListening(IBiometricAuthCallback callback)
{
_biometricAuthCallbackAdapter = new BiometricAuthCallbackAdapter(callback, _activity);
StartListening(_biometricAuthCallbackAdapter);
}
public void StopListening()
{
Kp2aLog.Log("Fingerprint: StopListening " + (_biometricPrompt != null ? " having prompt " : " without prompt"));
_biometricAuthCallbackAdapter?.IgnoreNextError();
_biometricPrompt?.CancelAuthentication();
}
public bool HasUserInterface
{
get { return true; }
}
public void StartListening(BiometricPrompt.AuthenticationCallback callback)
{
Kp2aLog.Log("Fingerprint: StartListening ");
var executor = Executors.NewSingleThreadExecutor();
_biometricPrompt = new BiometricPrompt(_activity, executor, callback);
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
.SetTitle(_activity.GetString(AppNames.AppNameResource))
.SetSubtitle(_activity.GetString(Resource.String.unlock_database_title))
.SetNegativeButtonText(_activity.GetString(Android.Resource.String.Cancel))
.SetConfirmationRequired(false)
.Build();
_biometricPrompt.Authenticate(promptInfo, _cryptoObject);
}
public string Encrypt(string textToEncrypt)
{
Kp2aLog.Log("FP: Encrypting");
return Base64.EncodeToString(_cipher.DoFinal(System.Text.Encoding.UTF8.GetBytes(textToEncrypt)), 0);
}
public void StoreEncrypted(string textToEncrypt, string prefKey, Context context)
{
var edit = PreferenceManager.GetDefaultSharedPreferences(context).Edit();
StoreEncrypted(textToEncrypt, prefKey, edit);
edit.Commit();
}
public void StoreEncrypted(string textToEncrypt, string prefKey, ISharedPreferencesEditor edit)
{
edit.PutString(prefKey, Encrypt(textToEncrypt));
edit.PutString(GetIvPrefKey(prefKey), Base64.EncodeToString(CipherIv, 0));
}
private byte[] CipherIv
{
get { return _cipher.GetIV(); }
}
}
public interface IBiometricIdentifier
{
bool Init();
void StartListening(IBiometricAuthCallback callback);
void StopListening();
bool HasUserInterface { get; }
}
public class BiometricDecryption : BiometricCrypt
{
private readonly Context _context;
private readonly byte[] _iv;
public BiometricDecryption(BiometricModule biometric, string keyId, byte[] iv) : base(biometric, keyId)
{
_iv = iv;
}
public BiometricDecryption(BiometricModule biometric, string keyId, Context context, string prefKey)
: base(biometric, keyId)
{
_context = context;
_iv = Base64.Decode(PreferenceManager.GetDefaultSharedPreferences(context).GetString(GetIvPrefKey(prefKey), null), 0);
}
public static bool IsSetUp(Context context, string prefKey)
{
return PreferenceManager.GetDefaultSharedPreferences(context).GetString(GetIvPrefKey(prefKey), null) != null;
}
public override bool Init()
{
Kp2aLog.Log("FP: Init for Dec");
try
{
_keystore.Load(null);
var aliases = _keystore.Aliases();
if (aliases == null)
{
Kp2aLog.Log("KS: no aliases");
}
else
{
while (aliases.HasMoreElements)
{
var o = aliases.NextElement();
Kp2aLog.Log("alias: " + o?.ToString());
}
Kp2aLog.Log("KS: end aliases");
}
var key = _keystore.GetKey(GetAlias(_keyId), null);
if (key == null)
throw new Exception("Failed to init cipher for fingerprint Init: key is null");
var ivParams = new IvParameterSpec(_iv);
_cipher.Init(CipherMode.DecryptMode, key, ivParams);
_cryptoObject = new BiometricPrompt.CryptoObject(_cipher);
return true;
}
catch (KeyPermanentlyInvalidatedException e)
{
Kp2aLog.Log("FP: KeyPermanentlyInvalidatedException." + e.ToString());
return false;
}
catch (KeyStoreException e)
{
throw new RuntimeException(FailedToInitCipher + " (keystore)", e);
}
catch (CertificateException e)
{
throw new RuntimeException(FailedToInitCipher + " (CertificateException)", e);
}
catch (UnrecoverableKeyException e)
{
throw new RuntimeException(FailedToInitCipher + " (UnrecoverableKeyException)", e);
}
catch (IOException e)
{
throw new RuntimeException(FailedToInitCipher + " (IOException)", e);
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException(FailedToInitCipher + " (NoSuchAlgorithmException)", e);
}
catch (InvalidKeyException e)
{
throw new RuntimeException(FailedToInitCipher + " (InvalidKeyException)" + e.ToString(), e);
}
}
public string Decrypt(string encryted)
{
Kp2aLog.Log("FP: Decrypting ");
byte[] encryptedBytes = Base64.Decode(encryted, 0);
return System.Text.Encoding.UTF8.GetString(_cipher.DoFinal(encryptedBytes));
}
public string DecryptStored(string prefKey)
{
string enc = PreferenceManager.GetDefaultSharedPreferences(_context).GetString(prefKey, null);
return Decrypt(enc);
}
}
public class BiometricEncryption : BiometricCrypt
{
private KeyGenerator _keyGen;
public BiometricEncryption(BiometricModule biometric, string keyId) :
base(biometric, keyId)
{
_keyGen = biometric.KeyGenerator;
Kp2aLog.Log("FP: CreateKey ");
CreateKey();
}
/// <summary>
/// Creates a symmetric key in the Android Key Store which can only be used after the user
/// has authenticated with biometry.
/// </summary>
private void CreateKey()
{
try
{
_keystore.Load(null);
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(GetAlias(_keyId),
KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
.SetBlockModes(KeyProperties.BlockModeCbc)
// Require the user to authenticate with biometry to authorize every use
// of the key
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingPkcs7)
.SetUserAuthenticationRequired(true);
if ((int)Build.VERSION.SdkInt >= 24)
builder.SetInvalidatedByBiometricEnrollment(true);
_keyGen.Init(
builder
.Build());
_keyGen.GenerateKey();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException(e);
}
catch (InvalidAlgorithmParameterException e)
{
throw new RuntimeException(e);
}
catch (CertificateException e)
{
throw new RuntimeException(e);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
catch (System.Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
}
public override bool Init()
{
Kp2aLog.Log("FP: Init for Enc ");
try
{
_keystore.Load(null);
var key = _keystore.GetKey(GetAlias(_keyId), null);
_cipher.Init(CipherMode.EncryptMode, key);
_cryptoObject = new BiometricPrompt.CryptoObject(_cipher);
return true;
}
catch (KeyPermanentlyInvalidatedException)
{
return false;
}
catch (KeyStoreException e)
{
throw new RuntimeException(FailedToInitCipher, e);
}
catch (CertificateException e)
{
throw new RuntimeException(FailedToInitCipher, e);
}
catch (UnrecoverableKeyException e)
{
throw new RuntimeException(FailedToInitCipher, e);
}
catch (IOException e)
{
throw new RuntimeException(FailedToInitCipher, e);
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException(FailedToInitCipher, e);
}
catch (InvalidKeyException e)
{
throw new RuntimeException(FailedToInitCipher, e);
}
}
}
public class BiometricAuthCallbackAdapter : BiometricPrompt.AuthenticationCallback
{
private readonly IBiometricAuthCallback _callback;
private readonly Context _context;
private bool _ignoreNextError;
public BiometricAuthCallbackAdapter(IBiometricAuthCallback callback, Context context)
{
_callback = callback;
_context = context;
}
public override void OnAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result)
{
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricAuthSucceeded());
}
public override void OnAuthenticationError(int errorCode, ICharSequence errString)
{
if (_ignoreNextError)
{
_ignoreNextError = false;
return;
}
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricError(errString.ToString()));
}
public override void OnAuthenticationFailed()
{
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricAttemptFailed(_context.Resources.GetString(Resource.String.fingerprint_not_recognized)));
}
public void IgnoreNextError()
{
_ignoreNextError = true;
}
}
}

View File

@@ -1,42 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using Android.App;
using Android.Content;
namespace keepass2android
{
public class CancelDialog : Dialog {
protected readonly Activity _activity;
public CancelDialog(Activity activity): base(activity)
{
_activity = activity;
}
public bool Canceled { get; private set; }
public override void Cancel() {
base.Cancel();
Canceled = true;
}
}
}

View File

@@ -1,217 +0,0 @@
//
// ChallengeInfo.cs
//
// Author:
// Ben.Rush <>
//
// Copyright (c) 2014 Ben.Rush
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
using System;
using System.Xml;
using System.IO;
using keepass2android;
using KeePassLib.Serialization;
using System.Xml.Serialization;
namespace KeeChallenge
{
public class ChallengeInfo
{
private bool m_LT64;
public byte[] EncryptedSecret {
get;
private set;
}
public byte[] IV {
get;
private set;
}
public byte[] Challenge {
get;
private set;
}
public byte[] Verification {
get;
private set;
}
public bool LT64
{
get { return m_LT64; }
private set { m_LT64 = value; }
}
private ChallengeInfo()
{
LT64 = false;
}
public ChallengeInfo(byte[] encryptedSecret, byte[] iv, byte[] challenge, byte[] verification, bool lt64)
{
EncryptedSecret = encryptedSecret;
IV = iv;
Challenge = challenge;
Verification = verification;
LT64 = lt64;
}
public static ChallengeInfo Load(IOConnectionInfo ioc)
{
Stream sIn = null;
ChallengeInfo inf = new ChallengeInfo();
try
{
sIn = App.Kp2a.GetOtpAuxFileStorage(ioc).OpenFileForRead(ioc);
if (!inf.LoadStream(sIn)) return null;
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
finally
{
if(sIn != null) sIn.Close();
}
return inf;
}
private bool LoadStream(Stream AuxFile)
{
//read file
XmlReader xml;
try
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.CloseInput = true;
settings.XmlResolver = null;
xml = XmlReader.Create(AuxFile,settings);
}
catch (Exception)
{
return false;
}
try
{
while (xml.Read())
{
if (xml.IsStartElement())
{
switch (xml.Name)
{
case "encrypted":
xml.Read();
EncryptedSecret = Convert.FromBase64String(xml.Value.Trim());
break;
case "iv":
xml.Read();
IV = Convert.FromBase64String(xml.Value.Trim());
break;
case "challenge":
xml.Read();
Challenge = Convert.FromBase64String(xml.Value.Trim());
break;
case "verification":
xml.Read();
Verification = Convert.FromBase64String(xml.Value.Trim());
break;
case "lt64":
xml.Read();
if (!bool.TryParse(xml.Value.Trim(), out m_LT64)) throw new Exception("Unable to parse LT64 flag");
break;
}
}
}
}
catch (Exception)
{
return false;
}
xml.Close();
//if failed, return false
return true;
}
public bool Save(IOConnectionInfo ioc)
{
Stream sOut = null;
try
{
using (var trans = App.Kp2a.GetOtpAuxFileStorage(ioc)
.OpenWriteTransaction(ioc, App.Kp2a.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
{
sOut = trans.OpenFile();
if (SaveStream(sOut))
{
trans.CommitWrite();
}
else return false;
}
return true;
}
catch(Exception) { return false; }
finally
{
if (sOut != null) sOut.Close();
}
}
private bool SaveStream(Stream file)
{
try
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.CloseOutput = true;
settings.Indent = true;
settings.IndentChars = "\t";
settings.NewLineOnAttributes = true;
XmlWriter xml = XmlWriter.Create(file,settings);
xml.WriteStartDocument();
xml.WriteStartElement("data");
xml.WriteStartElement("aes");
xml.WriteElementString("encrypted", Convert.ToBase64String(EncryptedSecret));
xml.WriteElementString("iv", Convert.ToBase64String(IV));
xml.WriteEndElement();
xml.WriteElementString("challenge", Convert.ToBase64String(Challenge));
xml.WriteElementString("verification", Convert.ToBase64String(Verification));
xml.WriteElementString("lt64", LT64.ToString());
xml.WriteEndElement();
xml.WriteEndDocument();
xml.Close();
}
catch (Exception)
{
return false;
}
return true;
}
}
}

View File

@@ -1,95 +0,0 @@
using Java.Lang;
using KeePassLib.Cryptography;
using KeePassLib.Keys;
using KeePassLib.Security;
using KeePassLib.Serialization;
using Exception = System.Exception;
namespace keepass2android
{
public class ChallengeXCKey : IUserKey, ISeedBasedUserKey
{
private readonly int _requestCode;
public ProtectedBinary KeyData
{
get
{
if (Activity == null)
throw new Exception("Need an active Keepass2Android activity to challenge Yubikey!");
Activity.RunOnUiThread(
() =>
{
byte[] challenge = _kdfSeed;
byte[] challenge64 = new byte[64];
for (int i = 0; i < 64; i++)
{
if (i < challenge.Length)
{
challenge64[i] = challenge[i];
}
else
{
challenge64[i] = (byte)(challenge64.Length - challenge.Length);
}
}
var chalIntent = Activity.TryGetYubichallengeIntentOrPrompt(challenge64, true);
if (chalIntent == null)
{
Error = Activity.GetString(Resource.String.NoChallengeApp);
}
else
{
Activity.StartActivityForResult(chalIntent, _requestCode);
}
});
while ((Response == null) && (Error == null))
{
Thread.Sleep(50);
}
if (Error != null)
{
var error = Error;
Error = null;
throw new Exception("YubiChallenge failed: " + error);
}
var result = CryptoUtil.HashSha256(Response);
Response = null;
return new ProtectedBinary(true, result);
}
}
public uint GetMinKdbxVersion()
{
return KdbxFile.FileVersion32_4;
}
private byte[] _kdfSeed;
public ChallengeXCKey(LockingActivity activity, int requestCode)
{
this.Activity = activity;
_requestCode = requestCode;
Response = null;
}
public void SetParams(byte[] masterSeed, byte[] mPbKdfSeed)
{
_kdfSeed = mPbKdfSeed;
}
public byte[] Response { get; set; }
public string Error { get; set; }
public LockingActivity Activity
{
get;
set;
}
}
}

View File

@@ -1,263 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Content.Res;
using Android.Graphics;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Text;
using Android.Text.Method;
using Android.Text.Util;
using Android.Views;
using Android.Webkit;
using Android.Widget;
namespace keepass2android
{
public static class ChangeLog
{
public static void ShowChangeLog(Context ctx, Action onDismiss)
{
AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(ctx, Android.Resource.Style.ThemeHoloLightDialog));
builder.SetTitle(ctx.GetString(Resource.String.ChangeLog_title));
List<string> changeLog = new List<string>{
BuildChangelogString(ctx, new List<int>{Resource.Array.ChangeLog_1_11,Resource.Array.ChangeLog_1_11_net}, "1.11"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_10, "1.10"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_09e, "1.09e"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_09d, "1.09d"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_09c, "1.09c"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_09b, "1.09b"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_09a, "1.09a"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08d, "1.08d"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08c, "1.08c"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08b, "1.08b"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08, "1.08"),
ctx.GetString(Resource.String.ChangeLog_1_07b),
ctx.GetString(Resource.String.ChangeLog_1_07),
ctx.GetString(Resource.String.ChangeLog_1_06),
ctx.GetString(Resource.String.ChangeLog_1_05),
ctx.GetString(Resource.String.ChangeLog_1_04b),
ctx.GetString(Resource.String.ChangeLog_1_04),
ctx.GetString(Resource.String.ChangeLog_1_03),
ctx.GetString(Resource.String.ChangeLog_1_02),
#if !NoNet
ctx.GetString(Resource.String.ChangeLog_1_01g),
ctx.GetString(Resource.String.ChangeLog_1_01d),
#endif
ctx.GetString(Resource.String.ChangeLog_1_01),
ctx.GetString(Resource.String.ChangeLog_1_0_0e),
ctx.GetString(Resource.String.ChangeLog_1_0_0),
ctx.GetString(Resource.String.ChangeLog_0_9_9c),
ctx.GetString(Resource.String.ChangeLog_0_9_9),
ctx.GetString(Resource.String.ChangeLog_0_9_8c),
ctx.GetString(Resource.String.ChangeLog_0_9_8b),
ctx.GetString(Resource.String.ChangeLog_0_9_8),
#if !NoNet
//0.9.7b fixes were already included in 0.9.7 offline
ctx.GetString(Resource.String.ChangeLog_0_9_7b),
#endif
ctx.GetString(Resource.String.ChangeLog_0_9_7),
ctx.GetString(Resource.String.ChangeLog_0_9_6),
ctx.GetString(Resource.String.ChangeLog_0_9_5),
ctx.GetString(Resource.String.ChangeLog_0_9_4),
ctx.GetString(Resource.String.ChangeLog_0_9_3_r5),
ctx.GetString(Resource.String.ChangeLog_0_9_3),
ctx.GetString(Resource.String.ChangeLog_0_9_2),
ctx.GetString(Resource.String.ChangeLog_0_9_1),
ctx.GetString(Resource.String.ChangeLog_0_9),
ctx.GetString(Resource.String.ChangeLog_0_8_6),
ctx.GetString(Resource.String.ChangeLog_0_8_5),
ctx.GetString(Resource.String.ChangeLog_0_8_4),
ctx.GetString(Resource.String.ChangeLog_0_8_3),
ctx.GetString(Resource.String.ChangeLog_0_8_2),
ctx.GetString(Resource.String.ChangeLog_0_8_1),
ctx.GetString(Resource.String.ChangeLog_0_8),
ctx.GetString(Resource.String.ChangeLog_0_7),
ctx.GetString(Resource.String.ChangeLog)
};
String version;
try {
PackageInfo packageInfo = ctx.PackageManager.GetPackageInfo(ctx.PackageName, 0);
version = packageInfo.VersionName;
} catch (PackageManager.NameNotFoundException) {
version = "";
}
string warning = "";
if (version.Contains("pre"))
{
warning = ctx.GetString(Resource.String.PreviewWarning);
}
builder.SetPositiveButton(Android.Resource.String.Ok, (dlgSender, dlgEvt) => {((AlertDialog)dlgSender).Dismiss(); });
builder.SetCancelable(false);
WebView wv = new WebView(ctx);
wv.SetBackgroundColor(Color.White);
wv.LoadDataWithBaseURL(null, GetLog(changeLog, warning, ctx), "text/html", "UTF-8", null);
//builder.SetMessage("");
builder.SetView(wv);
Dialog dialog = builder.Create();
dialog.DismissEvent += (sender, e) =>
{
onDismiss();
};
dialog.Show();
/*TextView message = (TextView)dialog.FindViewById(Android.Resource.Id.Message);
message.TextFormatted = Html.FromHtml(ConcatChangeLog(ctx, changeLog.ToArray()));
message.AutoLinkMask=MatchOptions.WebUrls;*/
}
private static string BuildChangelogString(Context ctx, int changeLogResId, string version)
{
return BuildChangelogString(ctx, new List<int>() { changeLogResId }, version);
}
private static string BuildChangelogString(Context ctx, List<int> changeLogResIds, string version)
{
string result = "Version " + version + "\n";
string previous = "";
foreach (var changeLogResId in changeLogResIds)
{
foreach (var item in ctx.Resources.GetStringArray(changeLogResId))
{
if (item == previous) //there was some trouble with crowdin translations, remove duplicates
continue;
result += " * " + item + "\n";
previous = item;
}
}
return result;
}
private const string HtmlStart = @"<html>
<head>
<style type='text/css'>
a { color:#000000 }
div.title {
color:287AA9;
font-size:1.2em;
font-weight:bold;
margin-top:1em;
margin-bottom:0.5em;
text-align:center }
div.subtitle {
color:287AA9;
font-size:0.8em;
margin-bottom:1em;
text-align:center }
div.freetext { color:#000000 }
div.list { color:#000000 }
</style>
</head>
<body>";
private const string HtmlEnd = @"</body>
</html>";
private static string GetLog(List<string> changeLog, string warning, Context ctx)
{
StringBuilder sb = new StringBuilder(HtmlStart);
if (!string.IsNullOrEmpty(warning))
{
sb.Append(warning);
}
bool inList = false;
bool isFirst = true;
foreach (string versionLog in changeLog)
{
string versionLog2 = versionLog;
bool title = true;
if (isFirst)
{
bool showDonateOption = true;
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(ctx);
if (prefs.GetBoolean(ctx.GetString(Resource.String.NoDonationReminder_key), false))
showDonateOption = false;
long usageCount = prefs.GetLong(ctx.GetString(Resource.String.UsageCount_key), 0);
if (usageCount <= 5)
showDonateOption = false;
if (showDonateOption)
{
if (versionLog2.EndsWith("\n") == false)
versionLog2 += "\n";
string donateUrl = ctx.GetString(Resource.String.donate_url,
new Java.Lang.Object[]{ctx.Resources.Configuration.Locale.Language,
ctx.PackageName
});
versionLog2 += " * <a href=\"" + donateUrl
+ "\">" +
ctx.GetString(Resource.String.ChangeLog_keptDonate)
+ "<a/>";
}
isFirst = false;
}
foreach (string line in versionLog2.Split('\n'))
{
string w = line.Trim();
if (title)
{
if (inList)
{
sb.Append("</ul></div>\n");
inList = false;
}
w = w.Replace("<b>","");
w = w.Replace("</b>", "");
w = w.Replace("\\n", "");
sb.Append("<div class='title'>"
+ w.Trim() + "</div>\n");
title = false;
}
else
{
w = w.Replace("\\n", "<br />");
if ((w.StartsWith("*") || (w.StartsWith("<22>"))))
{
if (!inList)
{
sb.Append("<div class='list'><ul>\n");
inList = true;
}
sb.Append("<li>");
sb.Append(w.Substring(1).Trim());
sb.Append("</li>\n");
}
else
{
if (inList)
{
sb.Append("</ul></div>\n");
inList = false;
}
sb.Append(w);
}
}
}
}
sb.Append(HtmlEnd);
return sb.ToString();
}
}
}

View File

@@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
namespace keepass2android
{
[Activity(Label = AppNames.AppName, Theme = "@style/MyTheme_ActionBar", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]
public class CloseImmediatelyActivity : AndroidX.AppCompat.App.AppCompatActivity
{
protected override void OnResume()
{
SetContentView(Resource.Layout.group);
base.OnResume();
SetResult(Result.Ok);
FindViewById<RelativeLayout>(Resource.Id.bottom_bar).PostDelayed(() =>
{
Finish();
OverridePendingTransition(0, 0);
}, 200);
}
}
}

View File

@@ -1,435 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Provider;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Text;
using Android.Util;
using Android.Views;
using Android.Widget;
using keepass2android.database.edit;
using KeePass.Util.Spr;
using KeePassLib;
using KeePassLib.Keys;
using KeePassLib.Security;
using KeePassLib.Serialization;
using AlertDialog = Android.App.AlertDialog;
using Object = Java.Lang.Object;
namespace keepass2android
{
[Activity(Label = "@string/child_dbs_title", MainLauncher = false, Theme = "@style/MyTheme_Blue", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden, Exported = true)]
[IntentFilter(new[] { "kp2a.action.ConfigureChildDatabasesActivity" }, Categories = new[] { Intent.CategoryDefault })]
public class ConfigureChildDatabasesActivity : LockCloseActivity
{
private ChildDatabasesAdapter _adapter;
public class ChildDatabasesAdapter : BaseAdapter
{
private readonly ConfigureChildDatabasesActivity _context;
internal List<AutoExecItem> _displayedChildDatabases;
public ChildDatabasesAdapter(ConfigureChildDatabasesActivity context)
{
_context = context;
Update();
}
public override Object GetItem(int position)
{
return position;
}
public override long GetItemId(int position)
{
return position;
}
private LayoutInflater cursorInflater;
public override View GetView(int position, View convertView, ViewGroup parent)
{
if (cursorInflater == null)
cursorInflater = (LayoutInflater)_context.GetSystemService(Context.LayoutInflaterService);
View view;
if (convertView == null)
{
// if it's not recycled, initialize some attributes
view = cursorInflater.Inflate(Resource.Layout.child_db_config_row, parent, false);
view.FindViewById<Button>(Resource.Id.child_db_enable_on_this_device).Click += (sender, args) =>
{
View sending_view = (View) sender;
_context.OnEnable(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
view.FindViewById<Button>(Resource.Id.child_db_disable_on_this_device).Click += (sender, args) =>
{
View sending_view = (View)sender;
_context.OnDisable(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
view.FindViewById<Button>(Resource.Id.child_db_edit).Click += (sender, args) =>
{
View sending_view = (View)sender;
_context.OnEdit(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
view.FindViewById<Button>(Resource.Id.child_db_open).Click += (sender, args) =>
{
View sending_view = (View)sender;
_context.OnOpen(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
view.FindViewById<Button>(Resource.Id.child_db_enable_a_copy_for_this_device).Click += (sender, args) =>
{
View sending_view = (View)sender;
_context.OnEnableCopy(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
}
else
{
view = convertView;
}
var iv = view.FindViewById<ImageView>(Resource.Id.child_db_icon);
var autoExecItem = _displayedChildDatabases[position];
var pw = autoExecItem.Entry;
SprContext ctx = new SprContext(pw, App.Kp2a.FindDatabaseForElement(pw).KpDatabase, SprCompileFlags.All);
string deviceId = KeeAutoExecExt.ThisDeviceId;
view.FindViewById<TextView>(Resource.Id.child_db_title).Text =
SprEngine.Compile(pw.Strings.GetSafe(PwDefs.TitleField).ReadString(), ctx);
view.FindViewById<TextView>(Resource.Id.child_db_url).Text =
_context.GetString(Resource.String.entry_url) + ": " + SprEngine.Compile(pw.Strings.GetSafe(PwDefs.UrlField).ReadString(),ctx);
bool deviceEnabledExplict;
bool deviceEnabled = KeeAutoExecExt.IsDeviceEnabled(autoExecItem, deviceId, out deviceEnabledExplict);
deviceEnabled &= deviceEnabledExplict;
if (!autoExecItem.Enabled)
{
view.FindViewById<TextView>(Resource.Id.child_db_enabled_here).Text =
_context.GetString(Resource.String.plugin_disabled);
}
else
{
view.FindViewById<TextView>(Resource.Id.child_db_enabled_here).Text =
_context.GetString(Resource.String.child_db_enabled_on_this_device) + ": " +
(!deviceEnabledExplict ?
_context.GetString(Resource.String.unspecified)
:
((autoExecItem.Enabled && deviceEnabled)
? _context.GetString(Resource.String.yes)
: _context.GetString(Resource.String.no)));
}
view.FindViewById(Resource.Id.child_db_enable_on_this_device).Visibility = !deviceEnabled && autoExecItem.Enabled ? ViewStates.Visible : ViewStates.Gone;
view.FindViewById(Resource.Id.child_db_disable_on_this_device).Visibility = (deviceEnabled || !deviceEnabledExplict) && autoExecItem.Enabled ? ViewStates.Visible : ViewStates.Gone;
view.FindViewById(Resource.Id.child_db_enable_a_copy_for_this_device_container).Visibility = !deviceEnabled && autoExecItem.Enabled ? ViewStates.Visible : ViewStates.Gone;
view.FindViewById(Resource.Id.child_db_edit).Visibility = deviceEnabledExplict || !autoExecItem.Enabled ? ViewStates.Visible : ViewStates.Gone;
IOConnectionInfo ioc;
if ((KeeAutoExecExt.TryGetDatabaseIoc(autoExecItem, out ioc)) && App.Kp2a.TryGetDatabase(ioc) == null)
{
view.FindViewById(Resource.Id.child_db_open).Visibility = ViewStates.Visible;
}
else view.FindViewById(Resource.Id.child_db_open).Visibility = ViewStates.Gone;
Database db = App.Kp2a.FindDatabaseForElement(pw);
bool isExpired = pw.Expires && pw.ExpiryTime < DateTime.Now;
if (isExpired)
{
db.DrawableFactory.AssignDrawableTo(iv, _context, db.KpDatabase, PwIcon.Expired, PwUuid.Zero, false);
}
else
{
db.DrawableFactory.AssignDrawableTo(iv, _context, db.KpDatabase, pw.IconId, pw.CustomIconUuid, false);
}
view.Tag = position.ToString();
return view;
}
private static int GetClickedPos(View sending_view)
{
View viewWithTag = sending_view;
while (viewWithTag.Tag == null)
viewWithTag = (View) viewWithTag.Parent;
int clicked_pos = int.Parse((string) viewWithTag.Tag);
return clicked_pos;
}
public override int Count
{
get { return _displayedChildDatabases.Count; }
}
public void Update()
{
_displayedChildDatabases = KeeAutoExecExt.GetAutoExecItems(App.Kp2a.CurrentDb.KpDatabase)
.Where(e => App.Kp2a.TryFindDatabaseForElement(e.Entry) != null) //Update() can be called while we're adding entries to the database. They may be part of the group but without saving complete
.OrderBy(e => SprEngine.Compile(e.Entry.Strings.ReadSafe(PwDefs.TitleField),new SprContext(e.Entry, App.Kp2a.FindDatabaseForElement(e.Entry).KpDatabase, SprCompileFlags.All)))
.ThenByDescending(e => e.Entry.LastModificationTime)
.ToList();
}
}
private void OnOpen(AutoExecItem item)
{
KeeAutoExecExt.AutoOpenEntry(this, item, true, new ActivityLaunchModeSimple());
}
private void OnEnableCopy(AutoExecItem item)
{
//disable this device for the cloned entry
KeeAutoExecExt.SetDeviceEnabled(item, KeeAutoExecExt.ThisDeviceId, false);
//remember the original device settings
ProtectedString ifDeviceOrig = item.Entry.Strings.GetSafe(KeeAutoExecExt._ifDevice);
//reset device settings so only the current device is enabled
item.Entry.Strings.Set(KeeAutoExecExt._ifDevice,new ProtectedString(false,""));
KeeAutoExecExt.SetDeviceEnabled(item, KeeAutoExecExt.ThisDeviceId, true);
//now clone
var newEntry = item.Entry.CloneDeep();
//reset device settings
item.Entry.Strings.Set(KeeAutoExecExt._ifDevice, ifDeviceOrig);
newEntry.SetUuid(new PwUuid(true), true); // Create new UUID
string strTitle = newEntry.Strings.ReadSafe(PwDefs.TitleField);
newEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, strTitle + " (" + Android.OS.Build.Model + ")"));
var addTask = new AddEntry(this, App.Kp2a.CurrentDb, App.Kp2a, newEntry,item.Entry.ParentGroup,new ActionOnFinish(this, (success, message, activity) => ((ConfigureChildDatabasesActivity)activity).Update()));
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
pt.Run();
}
private void Update()
{
_adapter.Update();
_adapter.NotifyDataSetChanged();
}
private void OnEdit(AutoExecItem item)
{
EntryEditActivity.Launch(this,item.Entry,new NullTask());
}
private void OnDisable(AutoExecItem item)
{
KeeAutoExecExt.SetDeviceEnabled(item,KeeAutoExecExt.ThisDeviceId, false);
Save(item);
}
private void OnEnable(AutoExecItem item)
{
KeeAutoExecExt.SetDeviceEnabled(item, KeeAutoExecExt.ThisDeviceId, true);
Save(item);
}
private void Save(AutoExecItem item)
{
var addTask = new SaveDb(this, App.Kp2a, App.Kp2a.FindDatabaseForElement(item.Entry), new ActionOnFinish(this, (success, message, activity) => ((ConfigureChildDatabasesActivity)activity).Update()));
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
pt.Run();
}
protected override void OnResume()
{
base.OnResume();
_adapter.Update();
_adapter.NotifyDataSetChanged();
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.config_child_db);
_adapter = new ChildDatabasesAdapter(this);
var listView = FindViewById<ListView>(Android.Resource.Id.List);
listView.Adapter = _adapter;
SetSupportActionBar(FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar));
FindViewById<Button>(Resource.Id.add_child_db_button).Click += (sender, args) =>
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetTitle(Resource.String.add_child_db);
List<string> items = new List<string>();
Dictionary<int, Database> indexToDb = new Dictionary<int, Database>();
foreach (var db in App.Kp2a.OpenDatabases)
{
if (db != App.Kp2a.CurrentDb)
{
indexToDb[items.Count] = db;
items.Add(App.Kp2a.GetFileStorage(db.Ioc).GetDisplayName(db.Ioc));
}
}
indexToDb[items.Count] = null;
items.Add(GetString(Resource.String.open_other_db));
builder.SetItems(items.ToArray(), (o, eventArgs) =>
{
Database db;
if (!indexToDb.TryGetValue(eventArgs.Which, out db) || (db == null))
{
StartFileSelect();
}
else
{
AddAutoOpenEntryForDatabase(db);
}
});
AlertDialog dialog = builder.Create();
dialog.Show();
};
}
private void AddAutoOpenEntryForDatabase(Database db)
{
PwGroup autoOpenGroup = null;
var rootGroup = App.Kp2a.CurrentDb.KpDatabase.RootGroup;
foreach (PwGroup pgSub in rootGroup.Groups)
{
if (pgSub.Name == "AutoOpen")
{
autoOpenGroup = pgSub;
break;
}
}
if (autoOpenGroup == null)
{
AddGroup addGroupTask = AddGroup.GetInstance(this, App.Kp2a, "AutoOpen", 1, null, rootGroup, null, true);
addGroupTask.Run();
autoOpenGroup = addGroupTask.Group;
}
PwEntry newEntry = new PwEntry(true, true);
newEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, App.Kp2a.GetFileStorage(db.Ioc).GetDisplayName(db.Ioc)));
newEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, TryMakeRelativePath(App.Kp2a.CurrentDb, db.Ioc)));
var password = db.KpDatabase.MasterKey.GetUserKey<KcpPassword>();
newEntry.Strings.Set(PwDefs.PasswordField, password == null ? new ProtectedString(true, "") : password.Password);
var keyfile = db.KpDatabase.MasterKey.GetUserKey<KcpKeyFile>();
if ((keyfile != null) && (keyfile.Ioc != null))
{
newEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, TryMakeRelativePath(App.Kp2a.CurrentDb, keyfile.Ioc)));
}
newEntry.Strings.Set(KeeAutoExecExt._ifDevice,
new ProtectedString(false,
KeeAutoExecExt.BuildIfDevice(new Dictionary<string, bool>()
{
{KeeAutoExecExt.ThisDeviceId, true}
})));
var addTask = new AddEntry(this, db, App.Kp2a, newEntry, autoOpenGroup, new ActionOnFinish(this, (success, message, activity) => (activity as ConfigureChildDatabasesActivity)?.Update()));
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
pt.Run();
}
private string TryMakeRelativePath(Database db, IOConnectionInfo ioc)
{
try
{
var fsDb = App.Kp2a.GetFileStorage(db.Ioc);
var dbParent = fsDb.GetParentPath(db.Ioc).Path + "/";
if (ioc.Path.StartsWith(dbParent))
{
return "{DB_DIR}{ENV_DIRSEP}" + ioc.Path.Substring(dbParent.Length);
}
}
catch (NoFileStorageFoundException)
{
}
return ioc.Path;
}
private void StartFileSelect(bool noForwardToPassword = false)
{
Intent intent = new Intent(this, typeof(FileSelectActivity));
intent.PutExtra(FileSelectActivity.NoForwardToPasswordActivity, noForwardToPassword);
intent.PutExtra("MakeCurrent", false);
StartActivityForResult(intent, ReqCodeOpenNewDb);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == ReqCodeOpenNewDb)
{
switch ((int)resultCode)
{
case (int)Result.Ok:
string iocString = data?.GetStringExtra("ioc");
IOConnectionInfo ioc = IOConnectionInfo.UnserializeFromString(iocString);
var db = App.Kp2a.TryGetDatabase(ioc);
if (db != null)
AddAutoOpenEntryForDatabase(db);
break;
case PasswordActivity.ResultSelectOtherFile:
StartFileSelect(true);
break;
default:
break;
}
return;
}
}
public const int ReqCodeOpenNewDb = 1;
}
}

View File

@@ -1,470 +0,0 @@
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Preferences;
using Android.Support.V7.App;
using Android.Text;
using Android.Views;
using Android.Widget;
using Java.IO;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using keepass2android.Io;
using Environment = Android.OS.Environment;
using IOException = Java.IO.IOException;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_ActionBar")]
public class CreateDatabaseActivity : LifecycleAwareActivity
{
private IOConnectionInfo _ioc;
private string _keyfileFilename;
private bool _restoringInstanceState;
private bool _showPassword;
private readonly ActivityDesign _design;
private AppTask _appTask;
private AppTask AppTask
{
get { return _appTask; }
set
{
_appTask = value;
Kp2aLog.LogTask(value, MyDebugName);
}
}
public CreateDatabaseActivity()
{
_design = new ActivityDesign(this);
}
private const int RequestCodeKeyFile = 0;
private const int RequestCodeDbFilename = 1;
private const string KeyfilefilenameBundleKey = "KeyfileFilename";
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutString(Util.KeyFilename, _ioc.Path);
outState.PutString(Util.KeyServerusername, _ioc.UserName);
outState.PutString(Util.KeyServerpassword, _ioc.Password);
outState.PutInt(Util.KeyServercredmode, (int)_ioc.CredSaveMode);
if (_keyfileFilename != null)
outState.PutString(KeyfilefilenameBundleKey, _keyfileFilename);
}
protected override void OnCreate(Bundle bundle)
{
_design.ApplyTheme();
base.OnCreate(bundle);
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetHomeButtonEnabled(true);
SetContentView(Resource.Layout.create_database);
AppTask = AppTask.GetTaskInOnCreate(bundle, Intent);
SetDefaultIoc();
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Gone;
var keyfileCheckbox = FindViewById<CheckBox>(Resource.Id.use_keyfile);
if (bundle != null)
{
_keyfileFilename = bundle.GetString(KeyfilefilenameBundleKey, null);
if (_keyfileFilename != null)
{
FindViewById<TextView>(Resource.Id.keyfile_filename).Text = FileSelectHelper.ConvertFilenameToIocPath(_keyfileFilename);
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Visible;
keyfileCheckbox.Checked = true;
}
if (bundle.GetString(Util.KeyFilename, null) != null)
{
_ioc = new IOConnectionInfo
{
Path = bundle.GetString(Util.KeyFilename),
UserName = bundle.GetString(Util.KeyServerusername),
Password = bundle.GetString(Util.KeyServerpassword),
CredSaveMode = (IOCredSaveMode) bundle.GetInt(Util.KeyServercredmode),
};
}
}
UpdateIocView();
keyfileCheckbox.CheckedChange += (sender, args) =>
{
if (keyfileCheckbox.Checked)
{
if (_restoringInstanceState)
return;
Util.ShowBrowseDialog(this, RequestCodeKeyFile, false, true);
}
else
{
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Gone;
_keyfileFilename = null;
}
};
FindViewById(Resource.Id.btn_change_location).Click += (sender, args) =>
{
Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
StartActivityForResult(intent, RequestCodeDbFilename);
};
Button generatePassword = (Button)FindViewById(Resource.Id.generate_button);
generatePassword.Click += (sender, e) =>
{
GeneratePasswordActivity.LaunchWithoutLockCheck(this);
};
FindViewById(Resource.Id.btn_create).Click += (sender, evt) =>
{
CreateDatabase(Intent.GetBooleanExtra("MakeCurrent",true));
};
ImageButton btnTogglePassword = (ImageButton)FindViewById(Resource.Id.toggle_password);
btnTogglePassword.Click += (sender, e) =>
{
_showPassword = !_showPassword;
MakePasswordMaskedOrVisible();
};
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
Android.Graphics.Color color = new Android.Graphics.Color (224, 224, 224);
btnTogglePassword.SetColorFilter (color, mMode);
Util.SetNoPersonalizedLearning(FindViewById(Resource.Id.root));
}
readonly PasswordFont _passwordFont = new PasswordFont();
private void MakePasswordMaskedOrVisible()
{
EditText password = (EditText)FindViewById(Resource.Id.entry_password);
TextView confpassword = (TextView)FindViewById(Resource.Id.entry_confpassword);
int selStart = password.SelectionStart, selEnd = password.SelectionEnd;
if (_showPassword)
{
password.InputType = InputTypes.ClassText | InputTypes.TextVariationVisiblePassword;
_passwordFont.ApplyTo(password);
confpassword.Visibility = ViewStates.Gone;
}
else
{
password.InputType = InputTypes.ClassText | InputTypes.TextVariationPassword;
confpassword.Visibility = ViewStates.Visible;
}
password.SetSelection(selStart, selEnd);
}
private void CreateDatabase(bool makeCurrent)
{
var keyfileCheckbox = FindViewById<CheckBox>(Resource.Id.use_keyfile);
string password;
if (!TryGetPassword(out password)) return;
// Verify that a password or keyfile is set
if (password.Length == 0 && !keyfileCheckbox.Checked)
{
Toast.MakeText(this, Resource.String.error_nopass, ToastLength.Long).Show();
return;
}
//create the key
CompositeKey newKey = new CompositeKey();
if (String.IsNullOrEmpty(password) == false)
{
newKey.AddUserKey(new KcpPassword(password));
}
if (String.IsNullOrEmpty(_keyfileFilename) == false)
{
try
{
var ioc = IOConnectionInfo.FromPath(_keyfileFilename);
using (var stream = App.Kp2a.GetFileStorage(ioc).OpenFileForRead(ioc))
{
byte[] keyfileData = Util.StreamToMemoryStream(stream).ToArray();
newKey.AddUserKey(new KcpKeyFile(keyfileData, ioc, true));
}
}
catch (Exception)
{
Toast.MakeText(this, Resource.String.error_adding_keyfile, ToastLength.Long).Show();
return;
}
}
// Create the new database
CreateDb create = new CreateDb(App.Kp2a, this, _ioc, new LaunchGroupActivity(_ioc, this), false, newKey, makeCurrent);
ProgressTask createTask = new ProgressTask(
App.Kp2a,
this, create);
createTask.Run();
}
private bool TryGetPassword(out string pass)
{
TextView passView = (TextView)FindViewById(Resource.Id.entry_password);
pass = passView.Text;
if (_showPassword)
return true;
TextView passConfView = (TextView) FindViewById(Resource.Id.entry_confpassword);
String confpass = passConfView.Text;
// Verify that passwords match
if (! pass.Equals(confpass))
{
// Passwords do not match
Toast.MakeText(this, Resource.String.error_pass_match, ToastLength.Long).Show();
return false;
}
return true;
}
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
}
protected override void OnRestoreInstanceState(Bundle savedInstanceState)
{
_restoringInstanceState = true;
base.OnRestoreInstanceState(savedInstanceState);
_restoringInstanceState = false;
}
private void UpdateIocView()
{
string displayPath = App.Kp2a.GetFileStorage(_ioc).GetDisplayName(_ioc);
int protocolSeparatorPos = displayPath.IndexOf("://", StringComparison.Ordinal);
string protocolId = protocolSeparatorPos < 0 ?
"file" : displayPath.Substring(0, protocolSeparatorPos);
Drawable drawable = App.Kp2a.GetStorageIcon(protocolId);
FindViewById<ImageView>(Resource.Id.filestorage_logo).SetImageDrawable(drawable);
String title = App.Kp2a.GetStorageDisplayName(protocolId);
FindViewById<TextView>(Resource.Id.filestorage_label).Text = title;
FindViewById<TextView>(Resource.Id.label_filename).Text = protocolSeparatorPos < 0 ?
displayPath :
displayPath.Substring(protocolSeparatorPos + 3);
}
private void SetDefaultIoc()
{
File directory = GetExternalFilesDir(null);
if (directory == null)
directory = FilesDir;
string strDir = directory.CanonicalPath;
if (!strDir.EndsWith(File.Separator))
strDir += File.Separator;
string filename = strDir + "keepass.kdbx";
filename = FileSelectHelper.ConvertFilenameToIocPath(filename);
int count = 2;
while (new File(filename).Exists())
{
filename = FileSelectHelper.ConvertFilenameToIocPath(strDir + "keepass" + count + ".kdbx");
count++;
}
_ioc = new IOConnectionInfo
{
Path = filename
};
}
private static string SdDir
{
get
{
string sdDir = Environment.ExternalStorageDirectory.AbsolutePath;
if (!sdDir.EndsWith("/"))
sdDir += "/";
if (!sdDir.StartsWith("file://"))
sdDir = "file://" + sdDir;
return sdDir;
}
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (resultCode == KeePass.ResultOkPasswordGenerator)
{
String generatedPassword = data.GetStringExtra("keepass2android.password.generated_password");
FindViewById<TextView>(Resource.Id.entry_password).Text = generatedPassword;
FindViewById<TextView>(Resource.Id.entry_confpassword).Text = generatedPassword;
}
FileSelectHelper fileSelectHelper = new FileSelectHelper(this, true, true, RequestCodeDbFilename)
{
DefaultExtension = "kdbx"
};
fileSelectHelper.OnOpen += (sender, info) =>
{
_ioc = info;
(sender as CreateDatabaseActivity ?? this).UpdateIocView();
};
if (fileSelectHelper.HandleActivityResult(this, requestCode, resultCode, data))
return;
if (resultCode == Result.Ok)
{
if (requestCode == RequestCodeKeyFile)
{
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);
this.ContentResolver.TakePersistableUriPermission(data.Data, takeFlags);
}
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
}
}
}
string filename = Util.IntentToFilename(data, this);
if (filename == null)
filename = data.DataString;
_keyfileFilename = FileSelectHelper.ConvertFilenameToIocPath(filename);
FindViewById<TextView>(Resource.Id.keyfile_filename).Text = _keyfileFilename;
FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Visible;
}
}
if (resultCode == (Result)FileStorageResults.FileUsagePrepared)
{
_ioc = new IOConnectionInfo();
Util.SetIoConnectionFromIntent(_ioc, data);
UpdateIocView();
}
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
{
IOConnectionInfo ioc = new IOConnectionInfo();
Util.SetIoConnectionFromIntent(ioc, data);
new FileSelectHelper(this, true, true, RequestCodeDbFilename) { DefaultExtension = "kdbx" }
.StartFileChooser(ioc.Path);
}
}
private void AfterQueryCredentials(IOConnectionInfo ioc)
{
_ioc = ioc;
UpdateIocView();
}
private class LaunchGroupActivity : FileOnFinish
{
readonly CreateDatabaseActivity _activity;
private readonly IOConnectionInfo _ioc;
public LaunchGroupActivity(IOConnectionInfo ioc, CreateDatabaseActivity activity)
: base(activity, null)
{
_activity = activity;
_ioc = ioc;
}
public override void Run()
{
if (Success)
{
// Update the ongoing notification
App.Kp2a.UpdateOngoingNotification();
if (PreferenceManager.GetDefaultSharedPreferences(_activity).GetBoolean(_activity.GetString(Resource.String.RememberRecentFiles_key), _activity.Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default)))
{
// Add to recent files
FileDbHelper dbHelper = App.Kp2a.FileDbHelper;
dbHelper.CreateFile(_ioc, Filename, true);
}
Intent data = new Intent();
data.PutExtra("ioc", IOConnectionInfo.SerializeToString(_ioc));
_activity.SetResult(Result.Ok, data);
_activity.Finish();
}
else
{
DisplayMessage(_activity);
try
{
App.Kp2a.GetFileStorage(_ioc).Delete(_ioc);
}
catch (Exception e)
{
//not nice, but not a catastrophic failure if we can't delete the file:
Kp2aLog.Log("couldn't delete file after failure! " + e);
}
}
}
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Android.Resource.Id.Home:
OnBackPressed();
return true;
}
return false;
}
}
}

View File

@@ -1,36 +0,0 @@
using System;
using Android.App;
using KeePassLib.Serialization;
namespace keepass2android
{
class CreateNewFilename : RunnableOnFinish
{
private readonly string _filename;
public CreateNewFilename(Activity activity, OnFinish finish, string filename)
: base(activity,finish)
{
_filename = filename;
}
public override void Run()
{
try
{
int lastIndexOfSlash = _filename.LastIndexOf("/", StringComparison.Ordinal);
string parent = _filename.Substring(0, lastIndexOfSlash);
string newFilename = _filename.Substring(lastIndexOfSlash + 1);
string resultingFilename = App.Kp2a.GetFileStorage(new IOConnectionInfo { Path = parent }).CreateFilePath(parent, newFilename);
Finish(true, resultingFilename);
}
catch (Exception e)
{
Finish(false, e.Message);
}
}
}
}

View File

@@ -1,107 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.App.Assist;
using Android.Content;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Service.Autofill;
using Android.Util;
using Android.Views;
using Android.Views.Autofill;
using Android.Widget;
using keepass2android.services;
using keepass2android.services.AutofillBase;
namespace keepass2android
{
[Activity(Label = "DisableAutofillForQueryActivity")]
public class DisableAutofillForQueryActivity : Activity
{
public IAutofillIntentBuilder IntentBuilder = new Kp2aAutofillIntentBuilder();
public const string ExtraIsDisable = "EXTRA_IS_DISABLE";
protected void RestartApp()
{
Intent intent = IntentBuilder.GetRestartAppIntent(this);
StartActivity(intent);
Finish();
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
string requestedUrl = Intent.GetStringExtra(ChooseForAutofillActivityBase.ExtraQueryString);
if (requestedUrl == null)
{
Toast.MakeText(this, "Cannot execute query for null.", ToastLength.Long).Show();
RestartApp();
return;
}
var prefs = PreferenceManager.GetDefaultSharedPreferences(this);
bool isDisable = Intent.GetBooleanExtra(ExtraIsDisable, true);
var disabledValues = prefs.GetStringSet("AutoFillDisabledQueries", new HashSet<string>() { }).ToHashSet();
if (isDisable)
{
disabledValues.Add(requestedUrl);
}
else
{
disabledValues.Remove(requestedUrl);
}
prefs.Edit().PutStringSet("AutoFillDisabledQueries", disabledValues).Commit();
bool isManual = Intent.GetBooleanExtra(ChooseForAutofillActivityBase.ExtraIsManualRequest, false);
Intent reply = new Intent();
FillResponse.Builder builder = new FillResponse.Builder();
AssistStructure structure = (AssistStructure)Intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure);
if (structure == null)
{
SetResult(Result.Canceled);
Finish();
return;
}
StructureParser parser = new StructureParser(this, structure);
try
{
parser.ParseForFill(isManual);
}
catch (Java.Lang.SecurityException e)
{
Log.Warn(CommonUtil.Tag, "Security exception handling request");
SetResult(Result.Canceled);
return;
}
try
{
reply.PutExtra(AutofillManager.ExtraAuthenticationResult, (FillResponse)null);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
throw;
}
SetResult(Result.Ok, reply);
Finish();
}
}
}

View File

@@ -1,245 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using KeePassLib.Utility;
namespace keepass2android
{
[Activity(Label = AppNames.AppName, Theme = "@style/MyTheme_ActionBar")]
public class DonateReminder : Activity
{
class Reminder
{
public DateTime From, To;
public int ResourceToShow;
public string Key;
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
foreach (Reminder r in GetReminders())
{
if ((DateTime.Now >= r.From)
&& (DateTime.Now < r.To))
{
SetContentView(r.ResourceToShow);
}
}
FindViewById(Resource.Id.ok_donate).Click += (sender, args) => { Util.GotoDonateUrl(this);Finish(); };
FindViewById(Resource.Id.no_donate).Click += (sender, args) =>
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
ISharedPreferencesEditor edit = prefs.Edit();
edit.PutBoolean("DismissedDonateReminder", true);
EditorCompat.Apply(edit);
Finish();
};
}
static IEnumerable<Reminder> GetReminders()
{
yield return new Reminder
{
From = new DateTime(2017, 09, 16),
To = new DateTime(2017, 09, 25),
Key = "DonationOktoberfest2017b"//b because year was incorrectly set to 2015 in 0.9.8b
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2017, 09, 25),
To = new DateTime(2017, 10, 04),
Key = "DonationOktoberfest2017b-2"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2018, 09, 22),
To = new DateTime(2018, 09, 30),
Key = "DonationOktoberfest2018b"//b because year was incorrectly set to 2015 in 0.9.8b
,ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2018, 09, 30),
To = new DateTime(2018, 10, 08),
Key = "DonationOktoberfest2018b-2"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2019, 09, 21),
To = new DateTime(2019, 09, 30),
Key = "DonationOktoberfest2019"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2019, 09, 30),
To = new DateTime(2019, 10, 07),
Key = "DonationOktoberfest2019-2"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2020, 09, 19),
To = new DateTime(2020, 09, 26),
Key = "DonationOktoberfest2020"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2020, 09, 26),
To = new DateTime(2020, 10, 05),
Key = "DonationOktoberfest2020-2"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2021, 09, 18),
To = new DateTime(2021, 09, 26),
Key = "DonationOktoberfest2021"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2021, 09, 26),
To = new DateTime(2021, 10, 04),
Key = "DonationOktoberfest2021-2"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2022, 09, 17),
To = new DateTime(2022, 09, 25),
Key = "DonationOktoberfest2022"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2022, 09, 25),
To = new DateTime(2022, 10, 04),
Key = "DonationOktoberfest2022-2"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2023, 09, 16),
To = new DateTime(2023, 09, 25),
Key = "DonationOktoberfest2023"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2023, 09, 25),
To = new DateTime(2023, 10, 04),
Key = "DonationOktoberfest2023-2"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2024, 09, 21),
To = new DateTime(2024, 09, 28),
Key = "DonationOktoberfest2024"
,
ResourceToShow = Resource.Layout.donate
};
yield return new Reminder
{
From = new DateTime(2024, 09, 28),
To = new DateTime(2024, 10, 08),
Key = "DonationOktoberfest2024-2"
,
ResourceToShow = Resource.Layout.donate
};
int thisYear = DateTime.Now.Year;
yield return new Reminder
{
From = new DateTime(thisYear, 05, 10),
To = new DateTime(thisYear, 05, 11),
Key = "DonationBirthday" + thisYear,
ResourceToShow = Resource.Layout.donate_bday
};
yield return new Reminder
{
From = new DateTime(thisYear, 05, 11),
To = new DateTime(thisYear, 05, 18),
Key = "DonationBirthday" + thisYear,
ResourceToShow = Resource.Layout.donate_bdaymissed
};
}
public static void ShowDonateReminderIfAppropriate(Activity context)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(context);
if (prefs.GetBoolean(context.GetString(Resource.String.NoDonationReminder_key), false))
return;
long usageCount = prefs.GetLong(context.GetString(Resource.String.UsageCount_key), 0);
if (usageCount <= 5)
return;
foreach (Reminder r in GetReminders())
{
if ((DateTime.Now >= r.From )
&& (DateTime.Now < r.To))
{
if (prefs.GetBoolean(r.Key, false) == false)
{
ISharedPreferencesEditor edit = prefs.Edit();
edit.PutBoolean(r.Key, true);
EditorCompat.Apply(edit);
context.StartActivity(new Intent(context, typeof(DonateReminder)));
break;
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +0,0 @@
using Android.Content;
using Android.Graphics.Drawables;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to copy a string to clipboard
/// </summary>
class CopyToClipboardPopupMenuIcon : IPopupMenuItem
{
private readonly Context _context;
private readonly IStringView _stringView;
private readonly bool _isProtected;
public CopyToClipboardPopupMenuIcon(Context context, IStringView stringView, bool isProtected)
{
_context = context;
_stringView = stringView;
_isProtected = isProtected;
}
public Drawable Icon
{
get
{
return _context.Resources.GetDrawable(Resource.Drawable.ic_menu_copy_holo_dark);
}
}
public string Text
{
get { return _context.Resources.GetString(Resource.String.copy_to_clipboard); }
}
public void HandleClick()
{
CopyToClipboardService.CopyValueToClipboardWithTimeout(_context, _stringView.Text, _isProtected);
}
}
}

View File

@@ -1,47 +0,0 @@
using System;
using Android.Views;
using Android.Widget;
namespace keepass2android
{
internal class ExtraStringView : IStringView
{
private readonly View _container;
private readonly TextView _valueView;
private readonly TextView _visibleValueView;
private readonly TextView _keyView;
public ExtraStringView(LinearLayout container, TextView valueView, TextView visibleValueView, TextView keyView)
{
_container = container;
_valueView = valueView;
_visibleValueView = visibleValueView;
_keyView = keyView;
}
public View View
{
get { return _container; }
}
public string Text
{
get { return _valueView.Text; }
set
{
if (String.IsNullOrEmpty(value))
{
_container.Visibility = ViewStates.Gone;
}
else
{
_container.Visibility = ViewStates.Visible;
_valueView.Text = value;
if (_visibleValueView != null)
_visibleValueView.Text = value;
}
}
}
}
}

View File

@@ -1,35 +0,0 @@
using Android.Graphics.Drawables;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to go to the URL in the field
/// </summary>
class GotoUrlMenuItem : IPopupMenuItem
{
public string UrlFieldKey { get; }
private readonly EntryActivity _ctx;
public GotoUrlMenuItem(EntryActivity ctx, string urlFieldKey)
{
UrlFieldKey = urlFieldKey;
_ctx = ctx;
}
public Drawable Icon
{
get { return _ctx.Resources.GetDrawable(Resource.Drawable.ic_menu_upload_grey); }
}
public string Text
{
get { return _ctx.Resources.GetString(Resource.String.menu_url); }
}
public void HandleClick()
{
_ctx.GotoUrl(UrlFieldKey);
}
}
}

View File

@@ -1,17 +0,0 @@
using System;
using Android.Graphics.Drawables;
using KeePassLib;
namespace keepass2android
{
/// <summary>
/// Interface for popup menu items in EntryActivity
/// </summary>
internal interface IPopupMenuItem
{
Drawable Icon { get; }
String Text { get; }
void HandleClick();
}
}

View File

@@ -1,7 +0,0 @@
namespace keepass2android
{
internal interface IStringView
{
string Text { set; get; }
}
}

View File

@@ -1,39 +0,0 @@
using Android.Graphics.Drawables;
namespace keepass2android
{
/// <summary>
/// Represents the popup menu item in EntryActivity to open the associated attachment
/// </summary>
internal class OpenBinaryPopupItem : IPopupMenuItem
{
private readonly string _key;
private readonly EntryActivity _entryActivity;
public OpenBinaryPopupItem(string key, EntryActivity entryActivity)
{
_key = key;
_entryActivity = entryActivity;
}
public Drawable Icon
{
get { return _entryActivity.Resources.GetDrawable(Resource.Drawable.ic_menu_share_grey); }
}
public string Text
{
get { return _entryActivity.Resources.GetString(Resource.String.SaveAttachmentDialog_open); }
}
public void HandleClick()
{
Android.Net.Uri newUri = _entryActivity.WriteBinaryToFile(_key, true);
if (newUri != null)
{
_entryActivity.OpenBinaryFile(newUri);
}
}
}
}

View File

@@ -1,14 +0,0 @@
using Android.Content;
using Android.Graphics.Drawables;
namespace keepass2android
{
class PluginMenuOption
{
public string DisplayText { get; set; }
public Drawable Icon { get; set; }
public Intent Intent { get; set; }
}
}

View File

@@ -1,59 +0,0 @@
using Android.Content;
using Android.Graphics.Drawables;
using Android.OS;
using Keepass2android.Pluginsdk;
namespace keepass2android
{
/// <summary>
/// Represents a popup menu item in EntryActivity which was added by a plugin. The click will therefore broadcast to the plugin.
/// </summary>
class PluginPopupMenuItem : IPopupMenuItem
{
private readonly EntryActivity _activity;
private readonly string _pluginPackage;
private readonly string _fieldId;
private readonly string _popupItemId;
private readonly string _displayText;
private readonly int _iconId;
private readonly Bundle _bundleExtra;
public PluginPopupMenuItem(EntryActivity activity, string pluginPackage, string fieldId, string popupItemId, string displayText, int iconId, Bundle bundleExtra)
{
_activity = activity;
_pluginPackage = pluginPackage;
_fieldId = fieldId;
_popupItemId = popupItemId;
_displayText = displayText;
_iconId = iconId;
_bundleExtra = bundleExtra;
}
public Drawable Icon
{
get { return _activity.PackageManager.GetResourcesForApplication(_pluginPackage).GetDrawable(_iconId); }
}
public string Text
{
get { return _displayText; }
}
public string PopupItemId
{
get { return _popupItemId; }
}
public void HandleClick()
{
Intent i = new Intent(Strings.ActionEntryActionSelected);
i.SetPackage(_pluginPackage);
i.PutExtra(Strings.ExtraActionData, _bundleExtra);
i.PutExtra(Strings.ExtraFieldId, _fieldId);
i.PutExtra(Strings.ExtraSender, _activity.PackageName);
_activity.AddEntryToIntent(i);
_activity.SendBroadcast(i);
}
}
}

View File

@@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Android.App;
using Android.Views;
using Android.Widget;
namespace keepass2android
{
internal class StandardStringView : IStringView
{
private readonly List<int> _viewIds;
private readonly int _containerViewId;
private readonly Activity _activity;
public StandardStringView(List<int> viewIds, int containerViewId, Activity activity)
{
_viewIds = viewIds;
_containerViewId = containerViewId;
_activity = activity;
}
public string Text
{
set
{
View container = _activity.FindViewById(_containerViewId);
foreach (int viewId in _viewIds)
{
TextView tv = (TextView) _activity.FindViewById(viewId);
if (String.IsNullOrEmpty(value))
{
container.Visibility = tv.Visibility = ViewStates.Gone;
}
else
{
container.Visibility = tv.Visibility = ViewStates.Visible;
tv.Text = value;
}
}
}
get
{
TextView tv = (TextView) _activity.FindViewById(_viewIds.First());
return tv.Text;
}
}
}
}

View File

@@ -1,47 +0,0 @@
using Android.Graphics.Drawables;
using Android.Widget;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to toggle visibility of all protected strings (e.g. Password)
/// </summary>
class ToggleVisibilityPopupMenuItem : IPopupMenuItem
{
private readonly EntryActivity _activity;
private readonly TextView _valueView;
public ToggleVisibilityPopupMenuItem(EntryActivity activity, TextView valueView)
{
_activity = activity;
_valueView = valueView;
}
public Drawable Icon
{
get
{
//return new TextDrawable("\uF06E", _activity);
return _activity.Resources.GetDrawable(Resource.Drawable.ic_menu_view_grey);
}
}
public string Text
{
get
{
return _activity.Resources.GetString(
_activity.GetVisibilityForProtectedView(_valueView) ?
Resource.String.menu_hide_password
: Resource.String.show_password);
}
}
public void HandleClick()
{
_activity.ToggleVisibility(_valueView);
}
}
}

View File

@@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
namespace keepass2android.EntryActivityClasses
{
internal class ViewImagePopupItem:IPopupMenuItem
{
private readonly string _key;
private readonly EntryActivity _entryActivity;
public ViewImagePopupItem(string key, EntryActivity entryActivity)
{
_key = key;
_entryActivity = entryActivity;
}
public Drawable Icon
{
get
{
return _entryActivity.Resources.GetDrawable(Resource.Drawable.ic_picture);
}
}
public string Text
{
get
{
return _entryActivity.Resources.GetString(Resource.String.ShowAttachedImage);
}
}
public void HandleClick()
{
_entryActivity.ShowAttachedImage(_key);
}
}
}

View File

@@ -1,34 +0,0 @@
using Android.Graphics.Drawables;
namespace keepass2android
{
/// <summary>
/// Represents the popup menu item in EntryActivity to store the binary attachment on SD card
/// </summary>
internal class WriteBinaryToFilePopupItem : IPopupMenuItem
{
private readonly string _key;
private readonly EntryActivity _activity;
public WriteBinaryToFilePopupItem(string key, EntryActivity activity)
{
_key = key;
_activity = activity;
}
public Drawable Icon
{
get { return _activity.Resources.GetDrawable(Resource.Drawable.ic_menu_save_grey); }
}
public string Text
{
get { return _activity.Resources.GetString(Resource.String.SaveAttachmentDialog_save); }
}
public void HandleClick()
{
_activity.WriteBinaryToFile(_key, false);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,88 +0,0 @@
using System.Collections.Generic;
using KeePassLib;
using KeePassLib.Security;
namespace keepass2android
{
public abstract class EditModeBase
{
public virtual bool IsVisible(string fieldKey)
{
return true;
}
public virtual IEnumerable<string> SortExtraFieldKeys(IEnumerable<string> keys)
{
return keys;
}
protected bool? manualShowAddAttachments = null;
public virtual bool ShowAddAttachments
{
get
{
if (manualShowAddAttachments != null) return (bool)manualShowAddAttachments;
return true; }
set { manualShowAddAttachments = value; }
}
protected bool? manualShowAddExtras = null;
public virtual bool ShowAddExtras
{
get
{
if (manualShowAddExtras != null) return (bool) manualShowAddExtras;
return true;
}
set { manualShowAddExtras = value; }
}
public virtual string GetTitle(string key)
{
return key;
}
public virtual string GetFieldType(string key)
{
return "";
}
public virtual void InitializeEntry(PwEntry entry)
{
}
public virtual void PrepareForSaving(PwEntry entry)
{
}
}
/// <summary>
/// Holds the state of the EntrryEditActivity. This is required to be able to keep a partially modified entry in memory
/// through the App variable. Serializing this state (especially the Entry/EntryInDatabase) can be a performance problem
/// when there are big attachements.
/// </summary>
internal class EntryEditActivityState
{
internal PwEntry Entry, EntryInDatabase;
internal bool ShowPassword = false;
internal bool IsNew;
internal PwIcon SelectedIconId;
internal PwUuid SelectedCustomIconId = PwUuid.Zero;
internal bool SelectedIcon = false;
internal PwGroup ParentGroup;
internal bool EntryModified;
public EditModeBase EditMode { get; set; }
//the key of the extra field to which the last triggered file selection process belongs
public string LastTriggeredFileSelectionProcessKey;
}
}

View File

@@ -1,149 +0,0 @@
using System;
using System.IO;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Util;
using Android.Widget;
using KeePass.DataExchange;
using KeePass.DataExchange.Formats;
using KeePassLib.Interfaces;
using KeePassLib.Serialization;
using keepass2android.Io;
namespace keepass2android
{
public class ExportDbProcessManager: FileSaveProcessManager
{
private readonly FileFormatProvider _ffp;
public ExportDbProcessManager(int requestCode, Activity activity, FileFormatProvider ffp) : base(requestCode, activity)
{
_ffp = ffp;
}
protected override void SaveFile(IOConnectionInfo ioc)
{
var exportDb = new ExportDatabaseActivity.ExportDb(_activity, App.Kp2a, new ActionOnFinish(_activity, (success, message, activity) =>
{
if (!success)
Toast.MakeText(activity, message, ToastLength.Long).Show();
else
Toast.MakeText(activity, _activity.GetString(Resource.String.export_database_successful), ToastLength.Long).Show();
activity.Finish();
}
), _ffp, ioc);
ProgressTask pt = new ProgressTask(App.Kp2a, _activity, exportDb);
pt.Run();
}
}
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_ActionBar", Exported = true)]
[IntentFilter(new[] {"kp2a.action.ExportDatabaseActivity"}, Categories = new[] {Intent.CategoryDefault})]
public class ExportDatabaseActivity : LockCloseActivity
{
FileFormatProvider[] _ffp = new FileFormatProvider[]
{
new KeePassKdb2x(),
new KeePassXml2x(),
new KeePassCsv1x()
};
private int _fileFormatIndex;
private ExportDbProcessManager _exportDbProcessManager;
protected override void OnCreate(Android.OS.Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetTitle(Resource.String.export_fileformats_title);
builder.SetSingleChoiceItems(Resource.Array.export_fileformat_options, _fileFormatIndex,
delegate(object sender, DialogClickEventArgs args) { _fileFormatIndex = args.Which; });
builder.SetPositiveButton(Android.Resource.String.Ok, delegate
{
_exportDbProcessManager = new ExportDbProcessManager(0, this, _ffp[_fileFormatIndex]);
_exportDbProcessManager.StartProcess();
});
builder.SetNegativeButton(Resource.String.cancel, delegate {
Finish();
});
builder.Show();
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (_exportDbProcessManager?.OnActivityResult(requestCode, resultCode, data) == true)
return;
Finish();
}
protected int RequestCodeDbFilename
{
get { return 0; }
}
public class ExportDb : RunnableOnFinish
{
private readonly IKp2aApp _app;
private readonly FileFormatProvider _fileFormat;
private IOConnectionInfo _targetIoc;
public ExportDb(Activity activity, IKp2aApp app, OnFinish onFinish, FileFormatProvider fileFormat, IOConnectionInfo targetIoc) : base(activity, onFinish)
{
_app = app;
this._fileFormat = fileFormat;
_targetIoc = targetIoc;
}
public override void Run()
{
StatusLogger.UpdateMessage(UiStringKey.exporting_database);
var pd = _app.CurrentDb.KpDatabase;
PwExportInfo pwInfo = new PwExportInfo(pd.RootGroup, pd, true);
try
{
var fileStorage = _app.GetFileStorage(_targetIoc);
if (fileStorage is IOfflineSwitchable)
{
((IOfflineSwitchable) fileStorage).IsOffline = false;
}
using (var writeTransaction = fileStorage.OpenWriteTransaction(_targetIoc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
{
Stream sOut = writeTransaction.OpenFile();
_fileFormat.Export(pwInfo, sOut, new NullStatusLogger());
if (sOut != null) sOut.Close();
writeTransaction.CommitWrite();
}
if (fileStorage is IOfflineSwitchable)
{
((IOfflineSwitchable)fileStorage).IsOffline = App.Kp2a.OfflineMode;
}
Finish(true);
}
catch (Exception ex)
{
Finish(false, ex.Message);
}
}
}
}
}

View File

@@ -1,155 +0,0 @@
using System;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Widget;
using keepass2android.Io;
using KeePassLib.Serialization;
namespace keepass2android
{
public abstract class FileSaveProcessManager
{
protected readonly int _requestCode;
protected readonly Activity _activity;
public FileSaveProcessManager(int requestCode, Activity activity)
{
_requestCode = requestCode;
_activity = activity;
}
public bool OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == _requestCode)
{
if (resultCode == KeePass.ExitFileStorageSelectionOk)
{
string protocolId = data.GetStringExtra("protocolId");
if (protocolId == "content")
{
Util.ShowBrowseDialog(_activity, _requestCode, true, true);
}
else
{
FileSelectHelper fileSelectHelper = new FileSelectHelper(_activity, true, true, _requestCode);
fileSelectHelper.OnOpen += (sender, ioc) =>
{
SaveFile(ioc);
};
App.Kp2a.GetFileStorage(protocolId).StartSelectFile(
new FileStorageSetupInitiatorActivity(_activity, (i, result, arg3) => OnActivityResult(i, result, arg3), s => fileSelectHelper.PerformManualFileSelect(s)),
true,
_requestCode,
protocolId);
}
return true;
}
if (resultCode == (Result)FileStorageResults.FileUsagePrepared)
{
var ioc = new IOConnectionInfo();
Util.SetIoConnectionFromIntent(ioc, data);
SaveFile(ioc);
return true;
}
if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
{
IOConnectionInfo ioc = new IOConnectionInfo();
Util.SetIoConnectionFromIntent(ioc, data);
new FileSelectHelper(_activity, true, true, _requestCode).StartFileChooser(ioc.Path);
return true;
}
if (resultCode == Result.Ok)
{
if (requestCode == _requestCode)
{
if (data.Data.Scheme == "content")
{
if ((int)Android.OS.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)
{
SaveFile(new IOConnectionInfo { Path = FileSelectHelper.ConvertFilenameToIocPath(filename) });
}
else
{
var task = new CreateNewFilename(_activity, new ActionOnFinish(_activity, (success, messageOrFilename, activity) =>
{
if (!success)
{
Toast.MakeText(activity, messageOrFilename, ToastLength.Long).Show();
return;
}
SaveFile(new IOConnectionInfo { Path = FileSelectHelper.ConvertFilenameToIocPath(messageOrFilename) });
}), filename);
new ProgressTask(App.Kp2a, _activity, task).Run();
}
return true;
}
}
Clear();
return true;
}
return false;
}
protected virtual void Clear()
{
}
protected abstract void SaveFile(IOConnectionInfo ioc);
public void StartProcess()
{
Intent intent = new Intent(_activity, typeof(FileStorageSelectionActivity));
//intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, true);
_activity.StartActivityForResult(intent, _requestCode);
}
public virtual void OnSaveInstanceState(Bundle outState)
{
}
}
}

View File

@@ -1,815 +0,0 @@
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 Java.IO;
using keepass2android.Io;
#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
AlertDialog.Builder builder = new AlertDialog.Builder(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
AlertDialog.Builder builder = new AlertDialog.Builder(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
AlertDialog.Builder builder = new AlertDialog.Builder(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);
AlertDialog.Builder builder = new AlertDialog.Builder(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
AlertDialog.Builder builder = new AlertDialog.Builder(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 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(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
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).Dispose();
}
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, 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;
}
}
}

View File

@@ -1,276 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Android;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Content.Res;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Support.V4.Content;
using Android.Support.V7.App;
using Android.Text;
using Android.Util;
using Android.Views;
using Android.Widget;
using keepass2android.Io;
using keepass2android.view;
using AlertDialog = Android.App.AlertDialog;
using Object = Java.Lang.Object;
namespace keepass2android
{
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_Blue")]
public class FileStorageSelectionActivity : AndroidX.AppCompat.App.AppCompatActivity
{
private readonly ActivityDesign _design;
private FileStorageAdapter _fileStorageAdapter;
private const int RequestExternalStoragePermission = 1;
public FileStorageSelectionActivity()
{
_design = new ActivityDesign(this);
}
public const string AllowThirdPartyAppGet = "AllowThirdPartyAppGet";
public const string AllowThirdPartyAppSend = "AllowThirdPartyAppSend";
class FileStorageAdapter: BaseAdapter
{
private readonly FileStorageSelectionActivity _context;
private readonly List<string> _displayedProtocolIds = new List<string>();
public FileStorageAdapter(FileStorageSelectionActivity context)
{
_context = context;
//show all supported protocols:
foreach (IFileStorage fs in App.Kp2a.FileStorages)
_displayedProtocolIds.AddRange(fs.SupportedProtocols);
//this is there for legacy reasons, new protocol is onedrive
_displayedProtocolIds.Remove("skydrive");
//onedrive was replaced by onedrive2 in a later implementation, but we still have the previous implementation to open existing connections (without the need to re-authenticate etc.)
_displayedProtocolIds.Remove("onedrive");
//special handling for local files:
if (!Util.IsKitKatOrLater)
{
//put file:// to the top
_displayedProtocolIds.Remove("file");
_displayedProtocolIds.Insert(0, "file");
//remove "content" (covered by androidget)
//On KitKat, content is handled by AndroidContentStorage taking advantage
//of persistable permissions and ACTION_OPEN/CREATE_DOCUMENT
_displayedProtocolIds.Remove("content");
}
else
{
_displayedProtocolIds.Remove("file");
}
//starting with Android 11, we don't show the Third party app option. Due to restricted permissions,
//this no longer works.
if ((int)Build.VERSION.SdkInt < 30)
if (context.Intent.GetBooleanExtra(AllowThirdPartyAppGet, false))
_displayedProtocolIds.Add("androidget");
if (context.Intent.GetBooleanExtra(AllowThirdPartyAppSend, false))
_displayedProtocolIds.Add("androidsend");
#if NoNet
//don't display "get regular version", is classified as deceptive ad by Google. Haha.
//_displayedProtocolIds.Add("kp2a");
#endif
_displayedProtocolIds = _displayedProtocolIds.GroupBy(p => App.Kp2a.GetStorageMainTypeDisplayName(p))
.Select(g => string.Join(",", g)).ToList();
}
public override Object GetItem(int position)
{
return _displayedProtocolIds[position];
}
public override long GetItemId(int position)
{
return position;
}
public static float convertDpToPixel(float dp, Context context)
{
return Util.convertDpToPixel(dp, context);
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
Button btn;
if (convertView == null)
{ // if it's not recycled, initialize some attributes
btn = new Button(_context);
btn.LayoutParameters = new GridView.LayoutParams((int)convertDpToPixel(90, _context), (int)convertDpToPixel(110, _context));
btn.SetBackgroundResource(Resource.Drawable.storagetype_button_bg);
btn.SetPadding((int)convertDpToPixel(4, _context),
(int)convertDpToPixel(20, _context),
(int)convertDpToPixel(4, _context),
(int)convertDpToPixel(4, _context));
btn.SetTextSize(ComplexUnitType.Sp, 11);
btn.SetTextColor(new Color(115, 115, 115));
btn.SetSingleLine(false);
btn.Gravity = GravityFlags.Center;
btn.Click += (sender, args) => _context.OnItemSelected( (string) ((Button)sender).Tag);
}
else
{
btn = (Button)convertView;
}
var protocolId = _displayedProtocolIds[position];
btn.Tag = protocolId;
string firstProtocolInList = protocolId.Split(",").First();
Drawable drawable = App.Kp2a.GetStorageIcon(firstProtocolInList);
String title =
protocolId == "kp2a" ? App.Kp2a.GetResourceString("get_regular_version")
:
App.Kp2a.GetStorageMainTypeDisplayName(firstProtocolInList);
var str = new SpannableString(title);
btn.TextFormatted = str;
//var drawable = ContextCompat.GetDrawable(context, Resource.Drawable.Icon);
btn.SetCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
return btn;
}
public override int Count
{
get { return _displayedProtocolIds.Count; }
}
}
private void OnItemSelected(string protocolId)
{
if (protocolId == "kp2a")
{
//send user to market page of regular edition to get more protocols
Util.GotoUrl(this, GetString(Resource.String.MarketURL) + "keepass2android.keepass2android");
return;
}
if (protocolId.Contains(","))
{
//bring up a selection dialog to select the variant of the file storage
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetItems(protocolId.Split(",").Select(singleProtocolId => App.Kp2a.GetStorageDisplayName(singleProtocolId)).ToArray(),
delegate(object sender, DialogClickEventArgs args)
{
string[] singleProtocolIds = protocolId.Split(",");
OnItemSelected(singleProtocolIds[args.Which]);
});
builder.Show();
return;
}
var field = typeof(Resource.String).GetField("filestoragehelp_" + protocolId);
if (field == null)
{
//no help available
ReturnProtocol(protocolId);
}
else
{
//set help:
string help = GetString((int)field.GetValue(null));
new AlertDialog.Builder(this)
.SetTitle(GetString(Resource.String.app_name))
.SetMessage(help)
.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => ReturnProtocol(protocolId))
.Create()
.Show();
}
}
private void ReturnProtocol(string protocolId)
{
if ((protocolId == "androidget") && ((int) Build.VERSION.SdkInt >= 23) &&
( CheckSelfPermission(Manifest.Permission.WriteExternalStorage) != Permission.Granted))
{
RequestPermissions(new string[]{Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage},RequestExternalStoragePermission);
return;
}
Intent intent = new Intent();
intent.PutExtra("protocolId", protocolId);
SetResult(KeePass.ExitFileStorageSelectionOk, intent);
Finish();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
if ((requestCode == RequestExternalStoragePermission) && (grantResults[0] == Permission.Granted))
{
ReturnProtocol("androidget");
}
}
protected override void OnCreate(Bundle bundle)
{
_design.ApplyTheme();
base.OnCreate(bundle);
SetContentView(Resource.Layout.filestorage_selection);
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar);
SetSupportActionBar(toolbar);
SupportActionBar.Title = RemoveTrailingColon(GetString(Resource.String.select_storage_type));
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetDisplayShowHomeEnabled(true);
toolbar.NavigationClick += (sender, args) => OnBackPressed();
_fileStorageAdapter = new FileStorageAdapter(this);
var gridView = FindViewById<GridView>(Resource.Id.gridview);
gridView.ItemClick +=
(sender, args) => OnItemSelected((string)_fileStorageAdapter.GetItem(args.Position));
gridView.Adapter = _fileStorageAdapter;
}
private string RemoveTrailingColon(string str)
{
if (str.EndsWith(":"))
return str.Substring(0, str.Length - 1);
return str;
}
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
}
}
}

View File

@@ -1,342 +0,0 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Android;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Hardware.Fingerprints;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Java.Lang;
using KeePassLib.Keys;
using KeePassLib.Utility;
using Enum = System.Enum;
using Exception = System.Exception;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_ActionBar", MainLauncher = false, Exported = true)]
[IntentFilter(new[] { "kp2a.action.FingerprintSetupActivity" }, Categories = new[] { Intent.CategoryDefault })]
public class BiometricSetupActivity : LockCloseActivity, IBiometricAuthCallback
{
private readonly ActivityDesign _activityDesign;
public BiometricSetupActivity(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
_activityDesign = new ActivityDesign(this);
}
public BiometricSetupActivity()
{
_activityDesign = new ActivityDesign(this);
}
private FingerprintUnlockMode _unlockMode = FingerprintUnlockMode.Disabled;
private FingerprintUnlockMode _desiredUnlockMode;
private BiometricEncryption _enc;
private RadioButton[] _radioButtons;
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Android.Resource.Id.Home:
Finish();
return true;
}
return base.OnOptionsItemSelected(item);
}
protected override void OnCreate(Bundle savedInstanceState)
{
_activityDesign.ApplyTheme();
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.fingerprint_setup);
Enum.TryParse(
PreferenceManager.GetDefaultSharedPreferences(this).GetString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, ""),
out _unlockMode);
_fpIcon = FindViewById<ImageView>(Resource.Id.fingerprint_icon);
_fpTextView = FindViewById<TextView>(Resource.Id.fingerprint_status);
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetHomeButtonEnabled(true);
int[] radioButtonIds =
{
Resource.Id.radio_fingerprint_quickunlock, Resource.Id.radio_fingerprint_unlock,
Resource.Id.radio_fingerprint_disabled
};
_radioButtons = radioButtonIds.Select(FindViewById<RadioButton>).ToArray();
_radioButtons[0].Tag = FingerprintUnlockMode.QuickUnlock.ToString();
_radioButtons[1].Tag = FingerprintUnlockMode.FullUnlock.ToString();
_radioButtons[2].Tag = FingerprintUnlockMode.Disabled.ToString();
foreach (RadioButton r in _radioButtons)
{
r.CheckedChange += (sender, args) =>
{
var rbSender = ((RadioButton) sender);
if (!rbSender.Checked) return;
foreach (RadioButton rOther in _radioButtons)
{
if (rOther == sender) continue;
rOther.Checked = false;
}
FingerprintUnlockMode newMode;
Enum.TryParse(rbSender.Tag.ToString(), out newMode);
ChangeUnlockMode(_unlockMode, newMode);
};
}
CheckCurrentRadioButton();
int errorId = Resource.String.fingerprint_os_error;
SetError(errorId);
FindViewById(Resource.Id.cancel_button).Click += (sender, args) =>
{
_enc.StopListening();
_unlockMode = FingerprintUnlockMode.Disabled; //cancelling a FingerprintEncryption means a new key has been created but not been authenticated to encrypt something. We can't keep the previous state.
StoreUnlockMode();
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
_enc = null;
CheckCurrentRadioButton();
};
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
FindViewById<CheckBox>(Resource.Id.close_database_after_failed).Checked =
Util.GetCloseDatabaseAfterFailedBiometricQuickUnlock(this);
FindViewById<CheckBox>(Resource.Id.close_database_after_failed).CheckedChange += (sender, args) =>
{
PreferenceManager.GetDefaultSharedPreferences(this)
.Edit()
.PutBoolean(GetString(Resource.String.CloseDatabaseAfterFailedBiometricQuickUnlock_key), args.IsChecked)
.Commit();
};
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
}
private void UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility()
{
FindViewById(Resource.Id.close_database_after_failed).Visibility = _unlockMode == FingerprintUnlockMode.QuickUnlock ? ViewStates.Visible : ViewStates.Gone;
}
string CurrentPreferenceKey
{
get { return App.Kp2a.CurrentDb.CurrentFingerprintPrefKey; }
}
private void StoreUnlockMode()
{
ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
if (_unlockMode == FingerprintUnlockMode.Disabled)
{
edit.PutString(CurrentPreferenceKey, "");
}
else
{
try
{
if (_unlockMode == FingerprintUnlockMode.FullUnlock)
{
var userKey = App.Kp2a.CurrentDb.KpDatabase.MasterKey.GetUserKey<KcpPassword>();
_enc.StoreEncrypted(userKey != null ? userKey.Password.ReadString() : "", CurrentPreferenceKey, edit);
}
else
_enc.StoreEncrypted("QuickUnlock" /*some dummy data*/, CurrentPreferenceKey, edit);
}
catch (Exception e)
{
new AlertDialog.Builder(this)
.SetTitle(GetString(Resource.String.ErrorOcurred))
.SetMessage(GetString(Resource.String.FingerprintSetupFailed))
.SetCancelable(false)
.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => { })
.Show();
}
}
edit.PutString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, _unlockMode.ToString());
edit.Commit();
}
private void CheckCurrentRadioButton()
{
foreach (RadioButton r in _radioButtons)
{
FingerprintUnlockMode um;
Enum.TryParse(r.Tag.ToString(), out um);
if (um == _unlockMode)
r.Checked = true;
}
}
private void SetError(int errorId)
{
var tv = FindViewById<TextView>(Resource.Id.tvFatalError);
tv.Text = GetString(Resource.String.fingerprint_fatal) + " " + GetString(errorId);
tv.Visibility = ViewStates.Visible;
}
private void ShowRadioButtons()
{
FindViewById<TextView>(Resource.Id.tvFatalError).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
}
private void HideRadioButtons()
{
FindViewById<TextView>(Resource.Id.tvFatalError).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
}
private void ChangeUnlockMode(FingerprintUnlockMode oldMode, FingerprintUnlockMode newMode)
{
if (oldMode == newMode)
return;
if (newMode == FingerprintUnlockMode.Disabled)
{
_unlockMode = newMode;
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
StoreUnlockMode();
return;
}
_desiredUnlockMode = newMode;
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Visible;
try
{
_enc = new BiometricEncryption(new BiometricModule(this), CurrentPreferenceKey);
if (!_enc.Init())
throw new Exception("Failed to initialize cipher");
ResetErrorTextRunnable();
_enc.StartListening(new BiometricAuthCallbackAdapter(this, this));
}
catch (Exception e)
{
CheckCurrentRadioButton();
Toast.MakeText(this, e.ToString(), ToastLength.Long).Show();
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
}
}
static readonly long ERROR_TIMEOUT_MILLIS = 1600;
static readonly long SUCCESS_DELAY_MILLIS = 1300;
private ImageView _fpIcon;
private TextView _fpTextView;
public void OnBiometricAuthSucceeded()
{
_unlockMode = _desiredUnlockMode;
_fpTextView.RemoveCallbacks(ResetErrorTextRunnable);
_fpIcon.SetImageResource(Resource.Drawable.ic_fingerprint_success);
_fpTextView.SetTextColor(_fpTextView.Resources.GetColor(Resource.Color.success_color, null));
_fpTextView.Text = _fpTextView.Resources.GetString(Resource.String.fingerprint_success);
_fpIcon.PostDelayed(() =>
{
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
StoreUnlockMode();
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
}, SUCCESS_DELAY_MILLIS);
}
public void OnBiometricError(string error)
{
_fpIcon.SetImageResource(Resource.Drawable.ic_fingerprint_error);
_fpTextView.Text = error;
_fpTextView.SetTextColor(
_fpTextView.Resources.GetColor(Resource.Color.warning_color, null));
_fpTextView.RemoveCallbacks(ResetErrorTextRunnable);
_fpTextView.PostDelayed(ResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
}
public void OnBiometricAttemptFailed(string message)
{
//ignore
}
void ResetErrorTextRunnable()
{
_fpTextView.SetTextColor(
_fpTextView.Resources.GetColor(Resource.Color.hint_color, null));
_fpTextView.Text = "";
_fpIcon.SetImageResource(Resource.Drawable.ic_fp_40px);
}
protected override void OnResume()
{
base.OnResume();
BiometricModule fpModule = new BiometricModule(this);
HideRadioButtons();
if (!fpModule.IsHardwareAvailable)
{
SetError(Resource.String.fingerprint_hardware_error);
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
return;
}
if (!fpModule.IsAvailable)
{
SetError(Resource.String.fingerprint_no_enrolled);
return;
}
ShowRadioButtons();
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
}
protected override void OnPause()
{
base.OnPause();
if (_enc != null)
_enc.StopListening();
}
}
}

View File

@@ -1,9 +0,0 @@
namespace keepass2android
{
public enum FingerprintUnlockMode
{
Disabled = 0,
QuickUnlock = 1,
FullUnlock = 2,
}
}

View File

@@ -1,88 +0,0 @@
using System;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Util;
namespace keepass2android
{
public class FixedDrawerLayout : Android.Support.V4.Widget.DrawerLayout
{
private bool _fitsSystemWindows;
protected FixedDrawerLayout(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public FixedDrawerLayout(Context context, IAttributeSet attrs, int defStyle)
: base(context, attrs, defStyle)
{
}
public FixedDrawerLayout(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
public FixedDrawerLayout(Context context)
: base(context)
{
}
private int[] mInsets = new int[4];
protected override bool FitSystemWindows(Rect insets)
{
if (Util.IsKitKatOrLater)
{
// Intentionally do not modify the bottom inset. For some reason,
// if the bottom inset is modified, window resizing stops working.
// TODO: Figure out why.
mInsets[0] = insets.Left;
mInsets[1] = insets.Top;
mInsets[2] = insets.Right;
insets.Left = 0;
insets.Top = 0;
insets.Right = 0;
}
return base.FitSystemWindows(insets);
}
public int[] GetInsets()
{
return mInsets;
}
public struct MeasureArgs
{
public int ActualHeight;
public int ProposedHeight;
}
public event EventHandler<MeasureArgs> MeasureEvent;
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
MeasureArgs args;
args.ProposedHeight = MeasureSpec.GetSize(heightMeasureSpec);
args.ActualHeight = Height;
OnMeasureEvent(args);
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected virtual void OnMeasureEvent(MeasureArgs args)
{
var handler = MeasureEvent;
if (handler != null) handler(this, args);
}
}
}

View File

@@ -1,605 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Android.App;
using Android.App.Admin;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Preferences;
using Android.Views;
using Android.Widget;
using Java.Util;
using KeePassLib.Cryptography;
using Newtonsoft.Json;
using OtpKeyProv;
namespace keepass2android
{
[Activity(Label = "@string/app_name", Theme = "@style/MyTheme_ActionBar", WindowSoftInputMode = SoftInput.StateHidden, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]
public class GeneratePasswordActivity :
#if DEBUG
LifecycleAwareActivity
#else
LockCloseActivity
#endif
{
private readonly int[] _buttonLengthButtonIds = new[] {Resource.Id.btn_length6,
Resource.Id.btn_length8,
Resource.Id.btn_length12,
Resource.Id.btn_length16,
Resource.Id.btn_length24,
Resource.Id.btn_length32};
private readonly int[] _checkboxIds = new[] {Resource.Id.cb_uppercase,
Resource.Id.cb_lowercase,
Resource.Id.cb_digits,
Resource.Id.cb_minus,
Resource.Id.cb_underline,
Resource.Id.cb_space,
Resource.Id.cb_specials,
Resource.Id.cb_specials_extended,
Resource.Id.cb_brackets,
Resource.Id.cb_at_least_one_from_each_group,
Resource.Id.cb_exclude_lookalike
};
PasswordFont _passwordFont = new PasswordFont();
private ActivityDesign _design;
public GeneratePasswordActivity()
{
_design = new ActivityDesign(this);
}
public static void Launch(Activity act) {
Intent i = new Intent(act, typeof(GeneratePasswordActivity));
act.StartActivityForResult(i, 0);
}
public static void LaunchWithoutLockCheck(Activity act)
{
Intent i = new Intent(act, typeof(GeneratePasswordActivity));
#if DEBUG
#else
i.PutExtra(NoLockCheck, true);
#endif
act.StartActivityForResult(i, 0);
}
private PasswordProfiles _profiles;
private bool _updateDisabled = false;
class PasswordProfiles
{
public List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>> Profiles { get; set; }
public PasswordGenerator.CombinedKeyOptions LastUsedSettings { get; set; }
public int? TryFindLastUsedProfileIndex()
{
for (int i=0;i<Profiles.Count;i++)
{
var kvp = Profiles[i];
if (kvp.Value.Equals(LastUsedSettings))
return i;
}
return null;
}
public void Add(string key, PasswordGenerator.CombinedKeyOptions options)
{
for (var index = 0; index < Profiles.Count; index++)
{
var kvp = Profiles[index];
if (kvp.Key == key)
{
Profiles[index] = new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(key, options);
return;
}
}
Profiles.Add(new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(key, options));
}
public void Remove(in int profileIndex)
{
Profiles.RemoveAt(profileIndex);
}
}
protected override void OnCreate(Bundle savedInstanceState) {
_design.ApplyTheme();
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.generate_password);
SetResult(KeePass.ExitNormal);
var prefs = GetPreferences(FileCreationMode.Private);
string jsonOptions = prefs.GetString("password_generator_profiles", null);
if (jsonOptions != null)
{
try
{
_profiles = JsonConvert.DeserializeObject<PasswordProfiles>(jsonOptions);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
}
else
{
PasswordGenerator.CombinedKeyOptions options = new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
{
Length = prefs.GetInt("length", 12),
UpperCase = prefs.GetBoolean("cb_uppercase", true),
LowerCase = prefs.GetBoolean("cb_lowercase", true),
Digits = prefs.GetBoolean("cb_digits", true),
Minus = prefs.GetBoolean("cb_minus", false),
Underline = prefs.GetBoolean("cb_underline", false),
Space = prefs.GetBoolean("cb_space", false),
Specials = prefs.GetBoolean("cb_specials", false),
SpecialsExtended = false,
Brackets = prefs.GetBoolean("cb_brackets", false)
}
};
_profiles = new PasswordProfiles()
{
LastUsedSettings = options,
Profiles = GetDefaultProfiles()
};
}
_profiles ??= new PasswordProfiles();
_profiles.LastUsedSettings ??= new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
{Length = 7, UpperCase = true, LowerCase = true, Digits = true}
};
_profiles.Profiles ??= new List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>>();
_updateDisabled = true;
PopulateFieldsFromOptions(_profiles.LastUsedSettings);
_updateDisabled = false;
var profileSpinner = UpdateProfileSpinner();
profileSpinner.ItemSelected += (sender, args) =>
{
if (profileSpinner.SelectedItemPosition > 0)
{
_profiles.LastUsedSettings = _profiles.Profiles[profileSpinner.SelectedItemPosition - 1].Value;
_updateDisabled = true;
PopulateFieldsFromOptions(_profiles.LastUsedSettings);
_updateDisabled = false;
UpdatePassword();
}
};
foreach (int id in _buttonLengthButtonIds) {
Button button = (Button) FindViewById(id);
button.Click += (sender, e) =>
{
Button b = (Button) sender;
EditText editText = (EditText) FindViewById(Resource.Id.length);
editText.Text = b.Text;
};
}
FindViewById<EditText>(Resource.Id.length).TextChanged += (sender, args) => UpdatePassword();
FindViewById<EditText>(Resource.Id.wordcount).TextChanged += (sender, args) => UpdatePassword();
FindViewById<EditText>(Resource.Id.wordseparator).TextChanged += (sender, args) => UpdatePassword();
foreach (int id in _checkboxIds)
{
FindViewById<CheckBox>(id).CheckedChange += (sender, args) => UpdatePassword();
}
var mode_spinner = FindViewById<Spinner>(Resource.Id.spinner_password_generator_mode);
mode_spinner.ItemSelected += (sender, args) =>
{
FindViewById(Resource.Id.passphraseOptions).Visibility =
mode_spinner.SelectedItemPosition == 0 ? ViewStates.Gone : ViewStates.Visible;
FindViewById(Resource.Id.passwordOptions).Visibility =
mode_spinner.SelectedItemPosition == 1 ? ViewStates.Gone : ViewStates.Visible;
UpdatePassword();
};
FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode).ItemSelected += (sender, args) =>
{
UpdatePassword();
};
Button genPassButton = (Button) FindViewById(Resource.Id.generate_password_button);
genPassButton.Click += (sender, e) => { UpdatePassword(); };
View acceptButton = FindViewById(Resource.Id.accept_button);
acceptButton.Click += (sender, e) => {
EditText password = (EditText) FindViewById(Resource.Id.password_edit);
Intent intent = new Intent();
intent.PutExtra("keepass2android.password.generated_password", password.Text);
SetResult(KeePass.ResultOkPasswordGenerator, intent);
Finish();
};
View cancelButton = FindViewById(Resource.Id.cancel_button);
cancelButton.Click += (sender, e) =>
{
SetResult(Result.Canceled);
Finish();
};
FindViewById(Resource.Id.btn_password_generator_profile_save)
.Click += (sender, args) =>
{
var editText = new EditText(this);
new AlertDialog.Builder(this)
.SetMessage(Resource.String.save_password_generation_profile_text)
.SetView(editText)
.SetPositiveButton(Android.Resource.String.Ok, (o, eventArgs) =>
{
_profiles.Add(editText.Text, GetOptions());
UpdateProfileSpinner();
})
.Show();
};
FindViewById(Resource.Id.btn_password_generator_profile_delete)
.Click += (sender, args) =>
{
if (profileSpinner.SelectedItemPosition > 0)
{
_profiles.Remove(profileSpinner.SelectedItemPosition-1);
UpdateProfileSpinner();
}
};
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit);
_passwordFont.ApplyTo(txtPasswordToSet);
UpdatePassword();
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetHomeButtonEnabled(true);
}
private Spinner UpdateProfileSpinner()
{
string[] profileNames = new List<string> {GetString(Resource.String.custom_settings)}
.Concat(_profiles.Profiles.Select(p => p.Key))
.ToArray();
ArrayAdapter<String> profileArrayAdapter = new ArrayAdapter<String>(this,
Android.Resource.Layout.SimpleSpinnerDropDownItem,
profileNames);
var profileSpinner = FindViewById<Spinner>(Resource.Id.spinner_password_generator_profile);
profileSpinner.Adapter = profileArrayAdapter;
UpdateProfileSpinnerSelection();
return profileSpinner;
}
private static List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>> GetDefaultProfiles()
{
return new List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>>()
{
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Simple12", new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions
= new PasswordGenerator.PasswordGenerationOptions()
{
Length = 12, AtLeastOneFromEachGroup = true, ExcludeLookAlike = true,
Digits = true, LowerCase = true, UpperCase = true
}
}
),
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Special12", new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions
= new PasswordGenerator.PasswordGenerationOptions()
{
Length = 12, AtLeastOneFromEachGroup = true, ExcludeLookAlike = true,
Digits = true, LowerCase = true, UpperCase = true,Specials = true,Brackets = true
}
}
),
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Password64", new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions
= new PasswordGenerator.PasswordGenerationOptions()
{
Length = 64, AtLeastOneFromEachGroup = true,
Digits = true, LowerCase = true, UpperCase = true, ExcludeLookAlike = false,Specials = true,Brackets = true, Minus = true, Space = true, SpecialsExtended = true,Underline = true
}
}
),
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Passphrase7", new PasswordGenerator.CombinedKeyOptions()
{
PassphraseGenerationOptions
= new PasswordGenerator.PassphraseGenerationOptions()
{
WordCount = 7,
CaseMode = PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode.Lowercase,
Separator = " "
}
}
),
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Passphrase5Plus", new PasswordGenerator.CombinedKeyOptions()
{
PassphraseGenerationOptions
= new PasswordGenerator.PassphraseGenerationOptions()
{
WordCount = 5,
CaseMode = PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode.PascalCase,
Separator = " "
},
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
{
Length = 2,
AtLeastOneFromEachGroup = true,
ExcludeLookAlike = true,
Digits = true,
Specials = true
}
}
)
};
}
private void PopulateFieldsFromOptions(PasswordGenerator.CombinedKeyOptions combinedOptions)
{
PasswordGenerator.PasswordGenerationOptions passwordOptions = combinedOptions.PasswordGenerationOptions;
if (passwordOptions != null)
{
((CheckBox)FindViewById(Resource.Id.cb_uppercase)).Checked = passwordOptions.UpperCase;
((CheckBox)FindViewById(Resource.Id.cb_lowercase)).Checked = passwordOptions.LowerCase;
((CheckBox)FindViewById(Resource.Id.cb_digits)).Checked = passwordOptions.Digits;
((CheckBox)FindViewById(Resource.Id.cb_minus)).Checked = passwordOptions.Minus;
((CheckBox)FindViewById(Resource.Id.cb_underline)).Checked = passwordOptions.Underline;
((CheckBox)FindViewById(Resource.Id.cb_space)).Checked = passwordOptions.Space;
((CheckBox)FindViewById(Resource.Id.cb_specials)).Checked = passwordOptions.Specials;
((CheckBox)FindViewById(Resource.Id.cb_specials_extended)).Checked = passwordOptions.SpecialsExtended;
((CheckBox)FindViewById(Resource.Id.cb_brackets)).Checked = passwordOptions.Brackets;
((CheckBox)FindViewById(Resource.Id.cb_exclude_lookalike)).Checked = passwordOptions.ExcludeLookAlike;
((CheckBox)FindViewById(Resource.Id.cb_at_least_one_from_each_group)).Checked = passwordOptions.AtLeastOneFromEachGroup;
((EditText)FindViewById(Resource.Id.length)).Text = passwordOptions.Length.ToString(CultureInfo.InvariantCulture);
FindViewById(Resource.Id.passwordOptions).Visibility = ViewStates.Visible;
}
else
{
FindViewById(Resource.Id.passwordOptions).Visibility = ViewStates.Gone;
}
var passphraseOptions = combinedOptions.PassphraseGenerationOptions;
if (passphraseOptions != null)
{
FindViewById<EditText>(Resource.Id.wordcount).Text = passphraseOptions.WordCount.ToString(CultureInfo.InvariantCulture);
FindViewById<EditText>(Resource.Id.wordseparator).Text = passphraseOptions.Separator;
FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode)
.SetSelection((int)passphraseOptions.CaseMode);
FindViewById(Resource.Id.passphraseOptions).Visibility = ViewStates.Visible;
}
else
{
FindViewById(Resource.Id.passphraseOptions).Visibility = ViewStates.Gone;
}
int mode;
if (combinedOptions.PasswordGenerationOptions != null &&
combinedOptions.PassphraseGenerationOptions != null)
mode = 2;
else if (combinedOptions.PasswordGenerationOptions == null &&
combinedOptions.PassphraseGenerationOptions != null)
mode = 1;
else mode = 0;
FindViewById<Spinner>(Resource.Id.spinner_password_generator_mode)
.SetSelection(mode);
}
private void UpdatePassword()
{
if (_updateDisabled)
return;
String password = GeneratePassword();
EditText txtPassword = (EditText) FindViewById(Resource.Id.password_edit);
txtPassword.Text = password;
var progressBar = FindViewById<ProgressBar>(Resource.Id.pb_password_strength);
var passwordBits = QualityEstimation.EstimatePasswordBits(password.ToCharArray());
progressBar.Progress = (int)passwordBits;
progressBar.Max = 128;
Color color = new Color(196, 63, 49);
if (passwordBits > 40)
{
color = new Color(219, 152, 55);
}
if (passwordBits > 64)
{
color = new Color(96, 138, 38);
}
if (passwordBits > 100)
{
color = new Color(31, 128, 31);
}
progressBar.ProgressDrawable.SetColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SrcIn));
FindViewById<TextView>(Resource.Id.tv_password_strength).Text = " " + passwordBits + " bits";
UpdateProfileSpinnerSelection();
}
private void UpdateProfileSpinnerSelection()
{
int? lastUsedIndex = _profiles.TryFindLastUsedProfileIndex();
FindViewById<Spinner>(Resource.Id.spinner_password_generator_profile)
.SetSelection(lastUsedIndex != null ? lastUsedIndex.Value + 1 : 0);
}
public String GeneratePassword() {
String password = "";
try
{
PasswordGenerator generator = new PasswordGenerator(this);
var options = GetOptions();
try
{
password = generator.GeneratePassword(options);
}
catch (Exception e)
{
throw new ArgumentException(GetString(Resource.String.error_pass_gen_type));
}
_profiles.LastUsedSettings = options;
SaveProfiles();
}
catch (Exception e)
{
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
}
return password;
}
private void SaveProfiles()
{
var prefs = GetPreferences(FileCreationMode.Private);
prefs.Edit()
.PutString("password_generator_profiles", JsonConvert.SerializeObject(_profiles))
.Commit();
}
private PasswordGenerator.CombinedKeyOptions GetOptions()
{
PasswordGenerator.CombinedKeyOptions options = new PasswordGenerator.CombinedKeyOptions();
if (FindViewById(Resource.Id.passphraseOptions).Visibility == ViewStates.Visible)
{
int wordCount;
if (!int.TryParse(((EditText)FindViewById(Resource.Id.wordcount)).Text, out wordCount))
{
throw new Exception(GetString(Resource.String.error_wrong_length));
}
options.PassphraseGenerationOptions =
new PasswordGenerator.PassphraseGenerationOptions()
{
WordCount = wordCount,
Separator = FindViewById<EditText>(Resource.Id.wordseparator).Text,
CaseMode = (PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode)FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode).SelectedItemPosition
};
}
if (FindViewById(Resource.Id.passwordOptions).Visibility == ViewStates.Visible)
{
int length;
if (!int.TryParse(((EditText) FindViewById(Resource.Id.length)).Text, out length))
{
throw new Exception(GetString(Resource.String.error_wrong_length));
}
options.PasswordGenerationOptions =
new PasswordGenerator.PasswordGenerationOptions()
{
Length = length,
UpperCase = ((CheckBox) FindViewById(Resource.Id.cb_uppercase)).Checked,
LowerCase = ((CheckBox) FindViewById(Resource.Id.cb_lowercase)).Checked,
Digits = ((CheckBox) FindViewById(Resource.Id.cb_digits)).Checked,
Minus = ((CheckBox) FindViewById(Resource.Id.cb_minus)).Checked,
Underline = ((CheckBox) FindViewById(Resource.Id.cb_underline)).Checked,
Space = ((CheckBox) FindViewById(Resource.Id.cb_space)).Checked,
Specials = ((CheckBox) FindViewById(Resource.Id.cb_specials)).Checked,
SpecialsExtended = ((CheckBox) FindViewById(Resource.Id.cb_specials_extended)).Checked,
Brackets = ((CheckBox) FindViewById(Resource.Id.cb_brackets)).Checked,
ExcludeLookAlike = ((CheckBox) FindViewById(Resource.Id.cb_exclude_lookalike)).Checked,
AtLeastOneFromEachGroup = ((CheckBox) FindViewById(Resource.Id.cb_at_least_one_from_each_group))
.Checked
};
}
return options;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Android.Resource.Id.Home:
OnBackPressed();
return true;
}
return false;
}
}
}

View File

@@ -1,317 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Views;
using Android.Widget;
using KeePassLib;
using Android.Util;
using KeePassLib.Utility;
using keepass2android.view;
using Android.Content.PM;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Preferences;
using Android.Runtime;
using Android.Support.V4.View;
using Android.Support.V7.App;
using keepass2android.Io;
using KeePassLib.Security;
using AlertDialog = Android.App.AlertDialog;
using Object = Java.Lang.Object;
namespace keepass2android
{
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_ActionBar", Exported = true)]
[MetaData("android.app.default_searchable", Value = "keepass2android.search.SearchResults")]
#if NoNet
[MetaData("android.app.searchable", Resource = "@xml/searchable_offline")]
#else
#if DEBUG
[MetaData("android.app.searchable", Resource = "@xml/searchable_debug")]
#else
[MetaData("android.app.searchable", Resource = "@xml/searchable")]
#endif
#endif
[IntentFilter(new string[]{"android.intent.action.SEARCH"})]
[MetaData("android.app.searchable",Resource=AppNames.Searchable)]
public class GroupActivity : GroupBaseActivity {
public const int Uninit = -1;
private const String Tag = "Group Activity:";
private const string Askaddtemplates = "AskAddTemplates";
public static void Launch(Activity act, AppTask appTask, ActivityLaunchMode launchMode) {
Launch(act, null, appTask, launchMode);
}
public static void Launch (Activity act, PwGroup g, AppTask appTask, ActivityLaunchMode launchMode)
{
Intent i = new Intent(act, typeof(GroupActivity));
if ( g != null ) {
i.PutExtra(KeyEntry, g.Uuid.ToHexString());
}
appTask.ToIntent(i);
launchMode.Launch(act, i);
}
protected PwUuid RetrieveGroupId(Intent i)
{
String uuid = i.GetStringExtra(KeyEntry);
if ( String.IsNullOrEmpty(uuid) ) {
return null;
}
return new PwUuid(MemUtil.HexStringToByteArray(uuid));
}
public override void SetupNormalButtons()
{
SetNormalButtonVisibility(AddGroupEnabled, AddEntryEnabled);
}
protected override bool AddEntryEnabled
{
get { return App.Kp2a.CurrentDb.CanWrite && ((this.Group.ParentGroup != null) || App.Kp2a.CurrentDb.DatabaseFormat.CanHaveEntriesInRootGroup); }
}
protected override bool AddGroupEnabled
{
get { return App.Kp2a.CurrentDb.CanWrite; }
}
private class TemplateListAdapter : ArrayAdapter<PwEntry>
{
public TemplateListAdapter(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
{
}
public TemplateListAdapter(Context context, int textViewResourceId) : base(context, textViewResourceId)
{
}
public TemplateListAdapter(Context context, int resource, int textViewResourceId) : base(context, resource, textViewResourceId)
{
}
public TemplateListAdapter(Context context, int textViewResourceId, PwEntry[] objects) : base(context, textViewResourceId, objects)
{
}
public TemplateListAdapter(Context context, int resource, int textViewResourceId, PwEntry[] objects) : base(context, resource, textViewResourceId, objects)
{
}
public TemplateListAdapter(Context context, int textViewResourceId, IList<PwEntry> objects) : base(context, textViewResourceId, objects)
{
}
public TemplateListAdapter(Context context, int resource, int textViewResourceId, IList<PwEntry> objects) : base(context, resource, textViewResourceId, objects)
{
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View v = base.GetView(position, convertView, parent);
TextView tv = (TextView)v.FindViewById(Android.Resource.Id.Text1);
tv.SetPadding(tv.PaddingLeft,0,tv.PaddingRight,0);
PwEntry templateEntry = this.GetItem(position);
int size = (int)(Util.convertDpToPixel(Util.convertDpToPixel(20, Context), Context));
var bmp =
Bitmap.CreateScaledBitmap(
Util.DrawableToBitmap(App.Kp2a.CurrentDb .DrawableFactory.GetIconDrawable(Context, App.Kp2a.CurrentDb.KpDatabase, templateEntry.IconId, PwUuid.Zero, false)),
size, size,
true);
Drawable icon = new BitmapDrawable(bmp);
if (
PreferenceManager.GetDefaultSharedPreferences(Context)
.GetString("IconSetKey", Context.PackageName) == Context.PackageName)
{
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
Color color = new Color(189, 189, 189);
icon.SetColorFilter(color, mMode);
}
//Put the image on the TextView
tv.SetCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
tv.Text = templateEntry.Strings.ReadSafe(PwDefs.TitleField);
tv.SetTextSize(ComplexUnitType.Dip, 20);
tv.CompoundDrawablePadding = (int)Util.convertDpToPixel(8, Context);
return v;
}
};
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
if (IsFinishing) {
return;
}
SetResult (KeePass.ExitNormal);
Log.Warn (Tag, "Creating group view");
Intent intent = Intent;
PwUuid id = RetrieveGroupId (intent);
Database db = App.Kp2a.CurrentDb;
if (db != null)
{
Group = id == null ? db.Root : db.GroupsById[id];
}
Log.Warn (Tag, "Retrieved group");
if (Group == null) {
Log.Warn (Tag, "Group was null");
return;
}
if (AddGroupEnabled) {
// Add Group button
View addGroup = FindViewById (Resource.Id.fabAddNewGroup);
addGroup.Click += (sender, e) => {
GroupEditActivity.Launch (this, Group);
};
}
if (AddEntryEnabled)
{
View addEntry = FindViewById (Resource.Id.fabAddNewEntry);
addEntry.Click += (sender, e) =>
{
if (App.Kp2a.CurrentDb.DatabaseFormat.SupportsTemplates &&
!AddTemplateEntries.ContainsAllTemplates(App.Kp2a.CurrentDb) &&
PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(Askaddtemplates, true))
{
App.Kp2a.AskYesNoCancel(UiStringKey.AskAddTemplatesTitle, UiStringKey.AskAddTemplatesMessage,UiStringKey.yes, UiStringKey.no,
(o, args) =>
{
//yes
ProgressTask pt = new ProgressTask(App.Kp2a, this,
new AddTemplateEntries(this, App.Kp2a, new ActionOnFinish(this,
(success, message, activity) => ((GroupActivity)activity)?.StartAddEntry())));
pt.Run();
},
(o, args) =>
{
var edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
edit.PutBoolean(Askaddtemplates, false);
edit.Commit();
//no
StartAddEntry();
},null, this);
}
else
StartAddEntry();
};
}
SetGroupTitle();
SetGroupIcon();
FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group);
Log.Warn(Tag, "Finished creating group");
}
private void StartAddEntry()
{
PwEntry defaultTemplate = new PwEntry(false, false);
defaultTemplate.IconId = PwIcon.Key;
defaultTemplate.Strings.Set(PwDefs.TitleField, new ProtectedString(false, GetString(Resource.String.DefaultTemplate)));
List<PwEntry> templates = new List<PwEntry>() {defaultTemplate};
if ((!PwUuid.Zero.Equals(App.Kp2a.CurrentDb.KpDatabase.EntryTemplatesGroup))
&& (App.Kp2a.CurrentDb.KpDatabase.RootGroup.FindGroup(App.Kp2a.CurrentDb.KpDatabase.EntryTemplatesGroup, true) != null))
{
templates.AddRange(
App.Kp2a.CurrentDb.GroupsById[App.Kp2a.CurrentDb.KpDatabase.EntryTemplatesGroup].Entries.OrderBy(
entr => entr.Strings.ReadSafe(PwDefs.TitleField)));
}
if (templates.Count > 1)
{
new AlertDialog.Builder(this)
.SetAdapter(new TemplateListAdapter(this, Android.Resource.Layout.SelectDialogItem,
Android.Resource.Id.Text1, templates),
(o, args) => { EntryEditActivity.Launch(this, Group, templates[args.Which].Uuid, AppTask); })
.Show();
}
else
{
EntryEditActivity.Launch(this, Group, PwUuid.Zero, AppTask);
}
}
public override void OnCreateContextMenu(IContextMenu menu, View v,
IContextMenuContextMenuInfo menuInfo) {
AdapterView.AdapterContextMenuInfo acmi = (AdapterView.AdapterContextMenuInfo) menuInfo;
ClickView cv = (ClickView) acmi.TargetView;
cv.OnCreateMenu(menu, menuInfo);
}
public override bool EntriesBelongToCurrentDatabaseOnly
{
get { return true; }
}
public override ElementAndDatabaseId FullGroupId
{
get { return new ElementAndDatabaseId(App.Kp2a.FindDatabaseForElement(Group), Group); }
}
public override void OnBackPressed()
{
base.OnBackPressed();
//if ((Group != null) && (Group.ParentGroup != null))
//OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
}
public override bool OnContextItemSelected(IMenuItem item) {
AdapterView.AdapterContextMenuInfo acmi = (AdapterView.AdapterContextMenuInfo)item.MenuInfo;
ClickView cv = (ClickView) acmi.TargetView;
return cv.OnContextItemSelected(item);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,172 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Widget;
using KeePassLib;
using KeePassLib.Utility;
namespace keepass2android
{
[Activity(Label = "@string/app_name", Theme = "@style/Dialog")]
public class GroupEditActivity : LifecycleAwareActivity
{
public const String KeyParent = "parent";
public const String KeyName = "name";
public const String KeyIconId = "icon_id";
public const String KeyCustomIconId = "custom_icon_id";
public const string KeyGroupUuid = "group_uuid";
private ActivityDesign _design;
private int _selectedIconId;
private PwUuid _selectedCustomIconId = PwUuid.Zero;
private PwGroup _groupToEdit;
public GroupEditActivity()
{
_design = new ActivityDesign(this);
}
protected GroupEditActivity(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public const int RequestCodeGroupEdit = 9713;
public static void Launch(Activity act, PwGroup parentGroup)
{
Intent i = new Intent(act, typeof(GroupEditActivity));
PwGroup parent = parentGroup;
i.PutExtra(KeyParent, parent.Uuid.ToHexString());
act.StartActivityForResult(i, RequestCodeGroupEdit);
}
public static void Launch(Activity act, PwGroup parentGroup, PwGroup groupToEdit)
{
Intent i = new Intent(act, typeof(GroupEditActivity));
PwGroup parent = parentGroup;
i.PutExtra(KeyParent, parent.Uuid.ToHexString());
i.PutExtra(KeyGroupUuid, groupToEdit.Uuid.ToHexString());
act.StartActivityForResult(i, GroupEditActivity.RequestCodeGroupEdit);
}
protected override void OnCreate (Bundle savedInstanceState)
{
_design.ApplyDialogTheme();
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.group_edit);
ImageButton iconButton = (ImageButton)FindViewById (Resource.Id.icon_button);
iconButton.Click += (sender, e) =>
{
IconPickerActivity.Launch (this);
};
_selectedIconId = (int) PwIcon.FolderOpen;
iconButton.SetImageDrawable(App.Kp2a.CurrentDb.DrawableFactory.GetIconDrawable(this, App.Kp2a.CurrentDb.KpDatabase, (PwIcon)_selectedIconId, null, true));
Button okButton = (Button)FindViewById (Resource.Id.ok);
okButton.Click += (sender, e) => {
TextView nameField = (TextView)FindViewById (Resource.Id.group_name);
String name = nameField.Text;
if (name.Length > 0) {
Intent intent = new Intent ();
intent.PutExtra (KeyName, name);
intent.PutExtra (KeyIconId, _selectedIconId);
if (_selectedCustomIconId != null)
intent.PutExtra(KeyCustomIconId, MemUtil.ByteArrayToHexString(_selectedCustomIconId.UuidBytes));
if (_groupToEdit != null)
intent.PutExtra(KeyGroupUuid, MemUtil.ByteArrayToHexString(_groupToEdit.Uuid.UuidBytes));
SetResult (Result.Ok, intent);
Finish ();
} else {
Toast.MakeText (this, Resource.String.error_no_name, ToastLength.Long).Show ();
}
};
if (Intent.HasExtra(KeyGroupUuid))
{
string groupUuid = Intent.Extras.GetString(KeyGroupUuid);
_groupToEdit = App.Kp2a.CurrentDb.GroupsById[new PwUuid(MemUtil.HexStringToByteArray(groupUuid))];
_selectedIconId = (int) _groupToEdit.IconId;
_selectedCustomIconId = _groupToEdit.CustomIconUuid;
TextView nameField = (TextView)FindViewById(Resource.Id.group_name);
nameField.Text = _groupToEdit.Name;
App.Kp2a.CurrentDb.DrawableFactory.AssignDrawableTo(iconButton, this, App.Kp2a.CurrentDb.KpDatabase, _groupToEdit.IconId, _groupToEdit.CustomIconUuid, false);
SetTitle(Resource.String.edit_group_title);
}
else
{
SetTitle(Resource.String.add_group_title);
}
Button cancel = (Button)FindViewById (Resource.Id.cancel);
cancel.Click += (sender, e) => {
Intent intent = new Intent ();
SetResult (Result.Canceled, intent);
Finish ();
};
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
switch ((int)resultCode)
{
case EntryEditActivity.ResultOkIconPicker:
_selectedIconId = data.Extras.GetInt(IconPickerActivity.KeyIconId, (int) PwIcon.Key);
String customIconIdString = data.Extras.GetString(IconPickerActivity.KeyCustomIconId);
if (!String.IsNullOrEmpty(customIconIdString))
_selectedCustomIconId = new PwUuid(MemUtil.HexStringToByteArray(customIconIdString));
else
_selectedCustomIconId = PwUuid.Zero;
ImageButton currIconButton = (ImageButton) FindViewById(Resource.Id.icon_button);
App.Kp2a.CurrentDb.DrawableFactory.AssignDrawableTo(currIconButton, this, App.Kp2a.CurrentDb.KpDatabase, (PwIcon) _selectedIconId, _selectedCustomIconId, false);
break;
}
}
protected override void OnResume()
{
base.OnResume();
//DON'T: _design.ReapplyTheme();
// (This causes endless loop creating/recreating. More correct: ReapplyDialogTheme (which doesn't exist) Not required anyways...)
}
}
}

View File

@@ -1,7 +0,0 @@
namespace keepass2android
{
public interface ILockCloseActivity
{
void OnLockDatabase(bool lockedByTimeout);
}
}

View File

@@ -1,246 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Preferences;
using Android.Views;
using Android.Widget;
using Java.Lang;
using KeePassLib;
using KeePassLib.Utility;
using FileNotFoundException = Java.IO.FileNotFoundException;
using IOException = Java.IO.IOException;
namespace keepass2android
{
[Activity(Label = "@string/app_name", Theme = "@style/MyTheme_ActionBar", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]
public class IconPickerActivity : LockCloseActivity
{
public const string KeyIconId = "icon_id";
public const string KeyCustomIconId = "custom_icon_id";
public static void Launch(Activity act)
{
Intent i = new Intent(act, typeof(IconPickerActivity));
act.StartActivityForResult(i, 0);
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.icon_picker);
GridView currIconGridView = (GridView)FindViewById(Resource.Id.IconGridView);
currIconGridView.Adapter = new ImageAdapter(this, App.Kp2a.CurrentDb.KpDatabase);
currIconGridView.ItemClick += (sender, e) =>
{
Intent intent = new Intent();
if (((ImageAdapter) currIconGridView.Adapter).IsCustomIcon(e.Position))
{
intent.PutExtra(KeyCustomIconId,
MemUtil.ByteArrayToHexString(((ImageAdapter) currIconGridView.Adapter).GetCustomIcon(e.Position).Uuid.UuidBytes));
}
else
{
intent.PutExtra(KeyIconId, e.Position);
}
SetResult((Result)EntryEditActivity.ResultOkIconPicker, intent);
Finish();
};
}
private const int AddCustomIconId = 1;
private const int RequestCodePickImage = 2;
public override bool OnCreateOptionsMenu(IMenu menu)
{
base.OnCreateOptionsMenu(menu);
menu.Add(0, AddCustomIconId, 0, GetString(Resource.String.AddCustomIcon));
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
if (item.ItemId == AddCustomIconId)
{
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
intent.AddCategory(Intent.CategoryOpenable);
StartActivityForResult(intent, RequestCodePickImage);
}
return base.OnOptionsItemSelected(item);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == RequestCodePickImage && resultCode == Result.Ok)
try
{
var stream = ContentResolver.OpenInputStream(data.Data);
var bitmap = BitmapFactory.DecodeStream(stream);
stream.Close();
float maxSize = 128;
using (MemoryStream ms = new MemoryStream())
{
if ((bitmap.Width > maxSize) || (bitmap.Height > maxSize))
{
float scale = System.Math.Min(maxSize / bitmap.Width, maxSize / bitmap.Height);
var scaleWidth = (int)(bitmap.Width * scale);
var scaleHeight = (int)(bitmap.Height * scale);
var scaledBitmap = Bitmap.CreateScaledBitmap(bitmap, scaleWidth, scaleHeight, true);
Bitmap newRectBitmap = Bitmap.CreateBitmap((int)maxSize, (int)maxSize, Bitmap.Config.Argb8888);
Canvas c = new Canvas(newRectBitmap);
c.DrawBitmap(scaledBitmap, (maxSize - scaledBitmap.Width)/2.0f, (maxSize - scaledBitmap.Height)/2.0f, null);
bitmap = newRectBitmap;
}
;
bitmap.Compress(Bitmap.CompressFormat.Png, 90, ms);
PwCustomIcon pwci = new PwCustomIcon(new PwUuid(true), ms.ToArray());
App.Kp2a.CurrentDb.KpDatabase.CustomIcons.Add(pwci);
}
var gridView = ((GridView)FindViewById(Resource.Id.IconGridView));
((BaseAdapter)gridView.Adapter).NotifyDataSetInvalidated();
gridView.SmoothScrollToPosition(((BaseAdapter)gridView.Adapter).Count-1);
}
catch (FileNotFoundException e)
{
e.PrintStackTrace();
}
catch (IOException e)
{
e.PrintStackTrace();
}
}
public class ImageAdapter : BaseAdapter
{
readonly IconPickerActivity _act;
private readonly PwDatabase _db;
public ImageAdapter(IconPickerActivity act, PwDatabase db)
{
_act = act;
_db = db;
}
public override int Count
{
get
{
return (int)PwIcon.Count + _db.CustomIcons.Count;
}
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return 0;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View currView;
if(convertView == null)
{
LayoutInflater li = (LayoutInflater) _act.GetSystemService(LayoutInflaterService);
currView = li.Inflate(Resource.Layout.icon, null);
}
else
{
currView = convertView;
}
TextView tv = (TextView) currView.FindViewById(Resource.Id.icon_text);
ImageView iv = (ImageView) currView.FindViewById(Resource.Id.icon_image);
if (position < (int)PwIcon.Count)
{
tv.Text = "" + position;
var drawable = App.Kp2a.CurrentDb .DrawableFactory.GetIconDrawable(_act, App.Kp2a.CurrentDb.KpDatabase, (KeePassLib.PwIcon) position, null, false);
drawable = new BitmapDrawable(Util.DrawableToBitmap(drawable));
iv.SetImageDrawable(drawable);
//App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iv, _act, App.Kp2a.GetDb().KpDatabase, (KeePassLib.PwIcon) position, null, false);
if (
PreferenceManager.GetDefaultSharedPreferences(currView.Context)
.GetString("IconSetKey", currView.Context.PackageName) == currView.Context.PackageName)
{
Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
Color color = new Color(189, 189, 189);
iv.SetColorFilter(color, mMode);
}
}
else
{
int pos = position - (int)PwIcon.Count;
var icon = _db.CustomIcons[pos];
tv.Text = pos.ToString();
iv.SetColorFilter(null);
iv.SetImageBitmap(icon.Image);
}
return currView;
}
public bool IsCustomIcon(int position)
{
return position >= (int)PwIcon.Count;
}
public PwCustomIcon GetCustomIcon(int position)
{
if (!IsCustomIcon(position))
return null;
return _db.CustomIcons[position - (int)PwIcon.Count];
}
}
}
}

View File

@@ -1,333 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using KeePassLib;
using KeePassLib.Utility;
using Object = Java.Lang.Object;
namespace keepass2android
{
public class ZoomableImageView : ImageView
{
private class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener
{
private ZoomableImageView parent;
public ScaleListener(ZoomableImageView parent)
{
this.parent = parent;
}
public override bool OnScaleBegin(ScaleGestureDetector detector)
{
parent.mode = ZOOM;
return true;
}
public override bool OnScale(ScaleGestureDetector detector)
{
float scaleFactor = detector.ScaleFactor;
float newScale = parent.saveScale * scaleFactor;
if (newScale < parent.maxScale && newScale > parent.minScale)
{
parent.saveScale = newScale;
float width = parent.Width;
float height = parent.Height;
parent.right = (parent.originalBitmapWidth * parent.saveScale) - width;
parent.bottom = (parent.originalBitmapHeight * parent.saveScale) - height;
float scaledBitmapWidth = parent.originalBitmapWidth * parent.saveScale;
float scaledBitmapHeight = parent.originalBitmapHeight * parent.saveScale;
if (scaledBitmapWidth <= width || scaledBitmapHeight <= height)
{
parent.matrix.PostScale(scaleFactor, scaleFactor, width / 2, height / 2);
}
else
{
parent.matrix.PostScale(scaleFactor, scaleFactor, detector.FocusX, detector.FocusY);
}
}
return true;
}
}
const int NONE = 0;
const int DRAG = 1;
const int ZOOM = 2;
const int CLICK = 3;
private int mode = NONE;
private Matrix matrix = new Matrix();
private PointF last = new PointF();
private PointF start = new PointF();
private float minScale = 1.0f;
private float maxScale = 100.0f;
private float[] m;
private float redundantXSpace, redundantYSpace;
private float saveScale = 1f;
private float right, bottom, originalBitmapWidth, originalBitmapHeight;
private ScaleGestureDetector mScaleDetector;
protected ZoomableImageView(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public ZoomableImageView(Context context)
: base(context)
{
init(context);
}
public ZoomableImageView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
init(context);
}
public ZoomableImageView(Context context, IAttributeSet attrs, int defStyleAttr)
: base(context, attrs, defStyleAttr)
{
init(context);
}
private void init(Context context)
{
base.Clickable = true;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this));
m = new float[9];
ImageMatrix = matrix;
SetScaleType(ScaleType.Matrix);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
int bmHeight = getBmHeight();
int bmWidth = getBmWidth();
float width = MeasuredWidth;
float height = MeasuredHeight;
//Fit to screen.
float scale = width > height ? height / bmHeight : width / bmWidth;
matrix.SetScale(scale, scale);
saveScale = 1f;
originalBitmapWidth = scale * bmWidth;
originalBitmapHeight = scale * bmHeight;
// Center the image
redundantYSpace = (height - originalBitmapHeight);
redundantXSpace = (width - originalBitmapWidth);
matrix.PostTranslate(redundantXSpace / 2, redundantYSpace / 2);
ImageMatrix = matrix;
}
public override bool OnTouchEvent(MotionEvent event_)
{
mScaleDetector.OnTouchEvent(event_);
matrix.GetValues(m);
float x = m[Matrix.MtransX];
float y = m[Matrix.MtransY];
PointF curr = new PointF(event_.GetX(), event_.GetY());
Log.Debug("TOUCH", event_.Action.ToString(), " mode=" + mode);
switch (event_.Action)
{
//when one finger is touching
//set the mode to DRAG
case MotionEventActions.Down:
last.Set(event_.GetX(), event_.GetY());
start.Set(last);
mode = DRAG;
break;
//when two fingers are touching
//set the mode to ZOOM
case MotionEventActions.Pointer2Down:
case MotionEventActions.PointerDown:
last.Set(event_.GetX(), event_.GetY());
start.Set(last);
mode = ZOOM;
break;
//when a finger moves
//If mode is applicable move image
case MotionEventActions.Move:
//if the mode is ZOOM or
//if the mode is DRAG and already zoomed
if (mode == ZOOM || (mode == DRAG && saveScale > minScale))
{
float deltaX = curr.X - last.X;// x difference
float deltaY = curr.Y - last.Y;// y difference
float scaleWidth = (float)System.Math.Round(originalBitmapWidth * saveScale);// width after applying current scale
float scaleHeight = (float)System.Math.Round(originalBitmapHeight * saveScale);// height after applying current scale
bool limitX = false;
bool limitY = false;
//if scaleWidth is smaller than the views width
//in other words if the image width fits in the view
//limit left and right movement
if (scaleWidth < Width && scaleHeight < Height)
{
// don't do anything
}
else if (scaleWidth < Width)
{
deltaX = 0;
limitY = true;
}
//if scaleHeight is smaller than the views height
//in other words if the image height fits in the view
//limit up and down movement
else if (scaleHeight < Height)
{
deltaY = 0;
limitX = true;
}
//if the image doesnt fit in the width or height
//limit both up and down and left and right
else
{
limitX = true;
limitY = true;
}
if (limitY)
{
if (y + deltaY > 0)
{
deltaY = -y;
}
else if (y + deltaY < -bottom)
{
deltaY = -(y + bottom);
}
}
if (limitX)
{
if (x + deltaX > 0)
{
deltaX = -x;
}
else if (x + deltaX < -right)
{
deltaX = -(x + right);
}
}
//move the image with the matrix
matrix.PostTranslate(deltaX, deltaY);
//set the last touch location to the current
last.Set(curr.X, curr.Y);
}
break;
//first finger is lifted
case MotionEventActions.Up:
mode = NONE;
int xDiff = (int)System.Math.Abs(curr.X - start.X);
int yDiff = (int)System.Math.Abs(curr.Y - start.Y);
if (xDiff < CLICK && yDiff < CLICK)
PerformClick();
break;
// second finger is lifted
case MotionEventActions.Pointer2Up:
case MotionEventActions.PointerUp:
mode = NONE;
break;
}
ImageMatrix = matrix;
Invalidate();
return true;
}
public void setMaxZoom(float x)
{
maxScale = x;
}
private int getBmWidth()
{
Drawable drawable = Drawable;
if (drawable != null)
{
return drawable.IntrinsicWidth;
}
return 0;
}
private int getBmHeight()
{
Drawable drawable = Drawable;
if (drawable != null)
{
return drawable.IntrinsicHeight;
}
return 0;
}
}
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_ActionBar")]
public class ImageViewActivity : LockCloseActivity
{
private ActivityDesign _activityDesign;
public ImageViewActivity()
{
_activityDesign = new ActivityDesign(this);
}
protected override void OnResume()
{
base.OnResume();
_activityDesign.ReapplyTheme();
}
protected override void OnCreate(Bundle savedInstanceState)
{
_activityDesign.ApplyTheme();
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ImageViewActivity);
ElementAndDatabaseId fullId = new ElementAndDatabaseId(Intent.GetStringExtra("EntryId"));
var uuid = new PwUuid(MemUtil.HexStringToByteArray(fullId.ElementIdString));
string key = Intent.GetStringExtra("EntryKey");
var binary = App.Kp2a.GetDatabase(fullId.DatabaseId).EntriesById[uuid].Binaries.Get(key);
SupportActionBar.Title = key;
byte[] pbdata = binary.ReadData();
var bmp = BitmapFactory.DecodeByteArray(pbdata,0,pbdata.Length);
FindViewById<ImageView>(Resource.Id.imageView).SetImageBitmap(bmp);
}
}
}

View File

@@ -1,465 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Android.App;
using Android.OS;
using Android.Provider;
using Android.Webkit;
using Android.Widget;
using Java.Nio.FileNio;
using KeePass.DataExchange;
using KeePass.Util.Spr;
using KeePassLib;
using KeePassLib.Keys;
using KeePassLib.Security;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using Debug = System.Diagnostics.Debug;
namespace keepass2android
{
public sealed class AutoExecItem
{
private PwEntry m_pe;
public PwEntry Entry
{
get { return m_pe; }
}
private PwDatabase m_pdContext;
public PwDatabase Database
{
get { return m_pdContext; }
}
public bool Enabled = true;
public bool Visible = true;
public long Priority = 0;
public string IfDevice = null;
public AutoExecItem(PwEntry pe, PwDatabase pdContext)
{
if (pe == null) throw new ArgumentNullException("pe");
m_pe = pe;
m_pdContext = pdContext;
}
}
public sealed class KeeAutoExecExt
{
public const string _ifDevice = "IfDevice";
private static string _thisDevice = null;
public static string ThisDeviceId
{
get
{
if (_thisDevice != null)
return _thisDevice;
String android_id = Settings.Secure.GetString(LocaleManager.LocalizedAppContext.ContentResolver, Settings.Secure.AndroidId);
string deviceName = Build.Manufacturer+" "+Build.Model;
_thisDevice = deviceName + " (" + android_id + ")";
_thisDevice = _thisDevice.Replace("!", "_");
_thisDevice = _thisDevice.Replace(",", "_");
_thisDevice = _thisDevice.Replace(";", "_");
return _thisDevice;
}
}
private static int PrioritySort(AutoExecItem x, AutoExecItem y)
{
if (x == null) { Debug.Assert(false); return ((y == null) ? 0 : -1); }
if (y == null) { Debug.Assert(false); return 1; }
return x.Priority.CompareTo(y.Priority);
}
private static void AddAutoExecEntries(List<PwEntry> l, PwGroup pg)
{
if (pg.Name.Equals("AutoOpen", StrUtil.CaseIgnoreCmp))
l.AddRange(pg.GetEntries(true));
else
{
foreach (PwGroup pgSub in pg.Groups)
AddAutoExecEntries(l, pgSub);
}
}
public static Dictionary<string, bool> GetIfDevice(AutoExecItem item)
{
Dictionary<string, bool> result = new Dictionary<string, bool>();
string strList = item.IfDevice;
if (string.IsNullOrEmpty(strList))
return result;
CsvOptions opt = new CsvOptions
{
BackslashIsEscape = false,
TrimFields = true
};
CsvStreamReaderEx csv = new CsvStreamReaderEx(strList, opt);
string[] vFlt = csv.ReadLine();
if (vFlt == null) { Debug.Assert(false); return result; }
foreach (string strFlt in vFlt)
{
if (string.IsNullOrEmpty(strFlt)) continue;
if (strFlt[0] == '!') // Exclusion
{
result[strFlt.Substring(1).TrimStart()] = false;
}
else // Inclusion
{
result[strFlt] = true;
}
}
return result;
}
public static string BuildIfDevice(Dictionary<string, bool> devices)
{
CsvOptions opt = new CsvOptions
{
BackslashIsEscape = false,
TrimFields = true
};
bool hasEnabledDevices = devices.Any(kvp => kvp.Value);
string result = "";
foreach (var deviceWithEnabled in devices)
{
if (result != "")
{
result += opt.FieldSeparator;
}
//if the list of devices has enabled devices, we do not need to include a negated expression
if (hasEnabledDevices && !deviceWithEnabled.Value)
continue;
string deviceValue = (deviceWithEnabled.Value ? "" : "!") + deviceWithEnabled.Key;
if (deviceValue.Contains(opt.FieldSeparator) || deviceValue.Contains("\\") ||
deviceValue.Contains("\""))
{
//add escaping:
deviceValue = deviceValue.Replace("\"", "\\\"");
deviceValue = "\"" + deviceValue + "\"";
}
result += deviceValue;
}
return result;
}
public static bool IsDeviceEnabled(AutoExecItem a, string strDevice, out bool isExplicit)
{
isExplicit = false;
var ifDevices = GetIfDevice(a);
if (!ifDevices.Any() || string.IsNullOrEmpty(strDevice))
return true;
bool bHasIncl = false, bHasExcl = false;
foreach (var kvp in ifDevices)
{
if (string.IsNullOrEmpty(kvp.Key)) continue;
if (strDevice.Equals(kvp.Key, StrUtil.CaseIgnoreCmp))
{
isExplicit = true;
return kvp.Value;
}
if (kvp.Value == false)
{
bHasExcl = true;
}
else
{
bHasIncl = true;
}
}
return (bHasExcl || !bHasIncl);
}
public static void SetDeviceEnabled(AutoExecItem a, string strDevice, bool enabled=true)
{
if (string.IsNullOrEmpty(strDevice))
{
return;
}
var devices = GetIfDevice(a);
devices[strDevice] = enabled;
string result = BuildIfDevice(devices);
a.Entry.Strings.Set(_ifDevice, new ProtectedString(false,result));
}
public static List<AutoExecItem> GetAutoExecItems(PwDatabase pd)
{
List<AutoExecItem> l = new List<AutoExecItem>();
if (pd == null) { Debug.Assert(false); return l; }
if (!pd.IsOpen) return l;
PwGroup pgRoot = pd.RootGroup;
if (pgRoot == null) { Debug.Assert(false); return l; }
List<PwEntry> lAutoEntries = new List<PwEntry>();
AddAutoExecEntries(lAutoEntries, pgRoot);
long lPriStd = 0;
foreach (PwEntry pe in lAutoEntries)
{
if (pe.Strings.ReadSafe(PwDefs.UrlField).Length == 0) continue;
var a = MakeAutoExecItem(pd, pe, lPriStd);
l.Add(a);
++lPriStd;
}
l.Sort(KeeAutoExecExt.PrioritySort);
return l;
}
public static AutoExecItem MakeAutoExecItem(PwDatabase pd, PwEntry pe, long lPriStd)
{
string str = pe.Strings.ReadSafe(PwDefs.UrlField);
AutoExecItem a = new AutoExecItem(pe, pd);
SprContext ctx = new SprContext(pe, pd, SprCompileFlags.All);
if (pe.Expires && (pe.ExpiryTime <= DateTime.UtcNow))
a.Enabled = false;
bool? ob = GetBoolEx(pe, "Enabled", ctx);
if (ob.HasValue) a.Enabled = ob.Value;
ob = GetBoolEx(pe, "Visible", ctx);
if (ob.HasValue) a.Visible = ob.Value;
long lItemPri = lPriStd;
if (GetString(pe, "Priority", ctx, true, out str))
long.TryParse(str, out lItemPri);
a.Priority = lItemPri;
if (GetString(pe, _ifDevice, ctx, true, out str))
a.IfDevice = str;
return a;
}
public static bool AutoOpenEntry(Activity activity, AutoExecItem item, bool bManual,
ActivityLaunchMode launchMode)
{
string str;
PwEntry pe = item.Entry;
SprContext ctxNoEsc = new SprContext(pe, item.Database, SprCompileFlags.All);
IOConnectionInfo ioc;
if (!TryGetDatabaseIoc(item, out ioc)) return false;
var ob = GetBoolEx(pe, "SkipIfNotExists", ctxNoEsc);
if (!ob.HasValue) // Backw. compat.
ob = GetBoolEx(pe, "Skip if not exists", ctxNoEsc);
if (ob.HasValue && ob.Value)
{
if (!CheckFileExsts(ioc)) return false;
}
CompositeKey ck = new CompositeKey();
if (GetString(pe, PwDefs.PasswordField, ctxNoEsc, false, out str))
ck.AddUserKey(new KcpPassword(str));
if (GetString(pe, PwDefs.UserNameField, ctxNoEsc, false, out str))
{
string strAbs = str;
IOConnectionInfo iocKey = IOConnectionInfo.FromPath(strAbs);
if (iocKey.IsLocalFile() && !UrlUtil.IsAbsolutePath(strAbs))
{
//local relative paths not supported on Android
return false;
}
ob = GetBoolEx(pe, "SkipIfKeyFileNotExists", ctxNoEsc);
if (ob.HasValue && ob.Value)
{
IOConnectionInfo iocKeyAbs = IOConnectionInfo.FromPath(strAbs);
if (!CheckFileExsts(iocKeyAbs)) return false;
}
try { ck.AddUserKey(new KcpKeyFile(strAbs)); }
catch (InvalidOperationException)
{
Toast.MakeText(LocaleManager.LocalizedAppContext,Resource.String.error_adding_keyfile,ToastLength.Long).Show();
return false;
}
catch (Exception) { throw; }
}
else // Try getting key file from attachments
{
ProtectedBinary pBin = pe.Binaries.Get("KeyFile.bin");
if (pBin != null)
ck.AddUserKey(new KcpKeyFile(IOConnectionInfo.FromPath(
StrUtil.DataToDataUri(pBin.ReadData(), null))));
}
GetString(pe, "Focus", ctxNoEsc, true, out str);
bool bRestoreFocus = str.Equals("Restore", StrUtil.CaseIgnoreCmp);
PasswordActivity.Launch(activity,ioc,ck,launchMode, !
bRestoreFocus);
App.Kp2a.RegisterChildDatabase(ioc);
return true;
}
private static bool CheckFileExsts(IOConnectionInfo ioc)
{
try
{
var fileStorage = App.Kp2a.GetFileStorage(ioc);
//we're assuming that remote files always exist (if we have a file storage, i.e. we check after receiving a file storage)
//The SkipIfNotExists switch only makes sense for local files, because remote files either exist for all devices or none
//(Ok, there are exceptions like files available in a (W)LAN. But then we still have the device switch and caches.)
//We cannot use OpenFileForRead on remote storages because this method is called from the main thread.
if (!ioc.IsLocalFile())
return true;
using (var stream = fileStorage.OpenFileForRead(ioc))
{
}
}
catch (NoFileStorageFoundException e)
{
return false;
}
catch (FileNotFoundException)
{
return false;
}
return true;
}
public static bool TryGetDatabaseIoc(AutoExecItem a, out IOConnectionInfo ioc)
{
PwEntry pe = a.Entry;
PwDatabase pdContext = a.Database;
SprContext ctxNoEsc = new SprContext(pe, pdContext, SprCompileFlags.All);
SprContext ctxEsc = new SprContext(pe, pdContext, SprCompileFlags.All,
false, true);
ioc = null;
string strDb;
if (!GetString(pe, PwDefs.UrlField, ctxEsc, true, out strDb)) return false;
ioc = IOConnectionInfo.FromPath(strDb);
//TODO
/*if (ioc.IsLocalFile() && !UrlUtil.IsAbsolutePath(strDb))
ioc = IOConnectionInfo.FromPath(UrlUtil.MakeAbsolutePath(
WinUtil.GetExecutable(), strDb));*/
if (ioc.Path.Length == 0) return false;
string strIocUserName;
if (GetString(pe, "IocUserName", ctxNoEsc, true, out strIocUserName))
ioc.UserName = strIocUserName;
string strIocPassword;
if (GetString(pe, "IocPassword", ctxNoEsc, true, out strIocPassword))
ioc.Password = strIocPassword;
if ((strIocUserName.Length != 0) && (strIocPassword.Length != 0))
ioc.IsComplete = true;
string str;
if (GetString(pe, "IocTimeout", ctxNoEsc, true, out str))
{
long l;
if (long.TryParse(str, out l))
ioc.Properties.SetLong(IocKnownProperties.Timeout, l);
}
bool? ob = GetBoolEx(pe, "IocPreAuth", ctxNoEsc);
if (ob.HasValue)
ioc.Properties.SetBool(IocKnownProperties.PreAuth, ob.Value);
if (GetString(pe, "IocUserAgent", ctxNoEsc, true, out str))
ioc.Properties.Set(IocKnownProperties.UserAgent, str);
ob = GetBoolEx(pe, "IocExpect100Continue", ctxNoEsc);
if (ob.HasValue)
ioc.Properties.SetBool(IocKnownProperties.Expect100Continue, ob.Value);
ob = GetBoolEx(pe, "IocPassive", ctxNoEsc);
if (ob.HasValue)
ioc.Properties.SetBool(IocKnownProperties.Passive, ob.Value);
return true;
}
private static bool GetString(PwEntry pe, string strName, SprContext ctx,
bool bTrim, out string strValue)
{
if ((pe == null) || (strName == null))
{
Debug.Assert(false);
strValue = string.Empty;
return false;
}
string str = pe.Strings.ReadSafe(strName);
if (ctx != null) str = SprEngine.Compile(str, ctx);
if (bTrim) str = str.Trim();
strValue = str;
return (str.Length != 0);
}
private static bool? GetBoolEx(PwEntry pe, string strName, SprContext ctx)
{
string str;
if (GetString(pe, strName, ctx, true, out str))
{
if (str.Equals("True", StrUtil.CaseIgnoreCmp))
return true;
if (str.Equals("False", StrUtil.CaseIgnoreCmp))
return false;
}
return null;
}
}
public class KeeAutoExec
{
}
}

View File

@@ -1,205 +0,0 @@
/* KeeChallenge--Provides Yubikey challenge-response capability to Keepass
* Copyright (C) 2014 Ben Rush
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Xml;
using KeePassLib.Keys;
using KeePassLib.Utility;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Serialization;
using keepass2android;
using keepass2android.Io;
namespace KeeChallenge
{
public sealed class KeeChallengeProv
{
private const string m_name = "Yubikey challenge-response";
public static string Name { get { return m_name; } }
public const int keyLenBytes = 20;
public const int challengeLenBytes = 64;
public const int responseLenBytes = 20;
public const int secretLenBytes = 20;
//If variable length challenges are enabled, a 63 byte challenge is sent instead.
//See GenerateChallenge() and http://forum.yubico.com/viewtopic.php?f=16&t=1078
//This field is automatically set by calling GetSecret(). However, when creating
//a new database it will need to be set manually based on the user's yubikey settings
public bool LT64
{
get;
set;
}
public KeeChallengeProv()
{
LT64 = false;
}
private byte[] GenerateChallenge()
{
byte[] chal = CryptoRandom.Instance.GetRandomBytes(challengeLenBytes);
if (LT64)
{
chal[challengeLenBytes - 2] = (byte)~chal[challengeLenBytes - 1];
}
return chal;
}
private byte[] GenerateResponse(byte[] challenge, byte[] key)
{
HMACSHA1 hmac = new HMACSHA1(key);
if (LT64)
challenge = challenge.Take(challengeLenBytes - 1).ToArray();
byte[] resp = hmac.ComputeHash(challenge);
hmac.Clear();
return resp;
}
/// <summary>
/// A method for generating encrypted ChallengeInfo to be saved. For security, this method should
/// be called every time you get a successful challenge-response pair from the Yubikey. Failure to
/// do so will permit password re-use attacks.
/// </summary>
/// <param name="secret">The un-encrypted secret</param>
/// <returns>A fully populated ChallengeInfo object ready to be saved</returns>
public ChallengeInfo Encrypt(byte[] secret)
{
//generate a random challenge for use next time
byte[] challenge = GenerateChallenge();
//generate the expected HMAC-SHA1 response for the challenge based on the secret
byte[] resp = GenerateResponse(challenge, secret);
//use the response to encrypt the secret
SHA256 sha = SHA256Managed.Create();
byte[] key = sha.ComputeHash(resp); // get a 256 bit key from the 160 bit hmac response
byte[] secretHash = sha.ComputeHash(secret);
StandardAesEngine aes = new StandardAesEngine();
const uint aesIVLenBytes = 16 ;
byte[] IV = CryptoRandom.Instance.GetRandomBytes(aesIVLenBytes);
byte[] encrypted;
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = (CryptoStream)aes.EncryptStream(msEncrypt, key, IV))
{
csEncrypt.Write(secret, 0, secret.Length);
csEncrypt.Close();
}
encrypted = msEncrypt.ToArray();
msEncrypt.Close();
}
ChallengeInfo inf = new ChallengeInfo (encrypted, IV, challenge, secretHash, LT64);
sha.Clear();
return inf;
}
private bool DecryptSecret(byte[] yubiResp, ChallengeInfo inf, out byte[] secret)
{
secret = new byte[keyLenBytes];
if (inf.IV == null) return false;
if (inf.Verification == null) return false;
//use the response to decrypt the secret
SHA256 sha = SHA256Managed.Create();
byte[] key = sha.ComputeHash(yubiResp); // get a 256 bit key from the 160 bit hmac response
StandardAesEngine aes = new StandardAesEngine();
using (MemoryStream msDecrypt = new MemoryStream(inf.EncryptedSecret))
{
using (CryptoStream csDecrypt = (CryptoStream)aes.DecryptStream(msDecrypt, key, inf.IV))
{
csDecrypt.Read(secret, 0, secret.Length);
csDecrypt.Close();
}
msDecrypt.Close();
}
byte[] secretHash = sha.ComputeHash(secret);
for (int i = 0; i < secretHash.Length; i++)
{
if (secretHash[i] != inf.Verification[i])
{
//wrong response
Array.Clear(secret, 0, secret.Length);
return false;
}
}
//return the secret
sha.Clear();
return true;
}
/// <summary>
/// The primary access point for challenge-response utility functions. Accepts a pre-populated ChallengeInfo object
/// containing at least the IV, EncryptedSecret, and Verification fields. These fields are combined with the Yubikey response
/// to decrypt and verify the secret.
/// </summary>
/// <param name="inf">A pre-populated object containing minimally the IV, EncryptedSecret and Verification fields.
/// This should be populated from the database.xml auxilliary file</param>
/// <param name="resp" >The Yubikey's response to the issued challenge</param>
/// <returns>The common secret, used as a composite key to encrypt a Keepass database</returns>
public byte[] GetSecret(ChallengeInfo inf, byte[] resp)
{
if (resp.Length != responseLenBytes)
return null;
if (inf == null)
return null;
if (inf.Challenge == null ||
inf.Verification == null)
return null;
LT64 = inf.LT64;
byte[] secret;
if (DecryptSecret(resp, inf, out secret))
{
return secret;
}
else
{
return null;
}
}
}
}

View File

@@ -1,275 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.App;
using Android.Content;
using Android.Widget;
using Android.OS;
using Android.Preferences;
using Android.Content.PM;
using Android.Text;
using Android.Text.Method;
using Java.Lang;
using Java.Lang.Reflect;
using KeePassLib.Serialization;
using Exception = System.Exception;
using String = System.String;
/**
* General documentation
*
* Activity stack and activity results
* ===================================
*
* Keepass2Android comprises quite a number of different activities and entry points: The app can be started
* using the launcher icon (-> Activity "Keepass"), or by sending a URL (-> SelectCurrentDb) or opening a .kdb(x)-file (->SelectCurrentDb)
* There is either only the KeePass activity on stack (no db loaded then) or the first activity on Stack is SelectCurrentDb
*
* Some possible stacks:
* SelectCurrentDb -> Group ( -> Group (subgroups) ... ) -> EntryView -> EntryEdit
* (AdvancedSearch Menu) -> Search -> SearchResults -> EntryView -> EntryEdit
* (SearchWidget) -> SearchResults -> EntryView -> EntryEdit
* SelectCurrentDb -> ShareUrlResults -> EntryView
* SelectCurrentDb -> Password / CreateDb
*
* If the current database changes (e.g. by selecting a search result from another database), the Group/Entry activities of the previously selected database close automatically.
* SelectCurrentDb is only noticable by the user if there are actually several databases, otherwises it either closes or starts another activity when it resumes.
*
* In each of the activities SelectCurrentDb/Group/Entry (but not Password/CreateDb/FileSelect), an AppTask may be present and must be passed to started activities and ActivityResults
* must be returned. Therefore, if any Activity calls { StartActivity(newActivity);Finish(); }, it must specify FLAG_ACTIVITY_FORWARD_RESULT.
*
* Further sub-activities may be opened (e.g. Settings -> ExportDb, ...), but these are not necesarrily
* part of the AppTask. Then, neither the task has to be passed nor must the sub-activity return an ActivityResult.
*
* Activities with AppTasks should check if they get a new AppTask in OnActivityResult.
*
* Note: Chrome fires the ActionSend (Share URL) intent with NEW_TASK (i.e. KP2A appears in a separate task, either a new one,
* or, if it was running before, in the KP2A task), whereas Firefox doesn't specify that flag and KP2A appears "inside" Firefox.
* This means that the AppTask must be cleared for use in Chrome after finding an entry or pressing back button in ShareUrlResults.
* This would not be necessary for Firefox where the (Android) Task of standalone KP2A is not affected by the search.
*/
namespace keepass2android
{
/// <summary>
/// Launcher activity of Keepass2Android. This activity usually forwards to SelectCurrentDb but may show the revision dialog after installation or updates.
/// </summary>
[Activity(Label = AppNames.AppName, MainLauncher = false, Theme = "@style/MyTheme_Blue", Exported = true)]
[IntentFilter(new[] { Intent.ActionMain }, Categories = new[] { "android.intent.category.LAUNCHER", "android.intent.category.MULTIWINDOW_LAUNCHER" })]
public class KeePass : LifecycleAwareActivity, IDialogInterfaceOnDismissListener
{
public const Result ExitNormal = Result.FirstUser;
public const Result ExitLock = Result.FirstUser+1;
public const Result ExitRefresh = Result.FirstUser+2;
public const Result ExitRefreshTitle = Result.FirstUser+3;
public const Result ExitCloseAfterTaskComplete = Result.FirstUser+4;
public const Result TaskComplete = Result.FirstUser + 5;
public const Result ExitReloadDb = Result.FirstUser+6;
public const Result ExitClose = Result.FirstUser + 7;
public const Result ExitFileStorageSelectionOk = Result.FirstUser + 8;
public const Result ResultOkPasswordGenerator = Result.FirstUser + 9;
public const Result ExitLoadAnotherDb = Result.FirstUser + 10;
public const Result ExitLockByTimeout = Result.FirstUser + 11;
public const string AndroidAppScheme = "androidapp://";
public const string TagsKey = "@tags";
public const string OverrideUrlKey = "@override";
public const string ExpDateKey = "@exp_date";
private AppTask _appTask;
private AppTask AppTask
{
get { return _appTask; }
set
{
_appTask = value;
Kp2aLog.LogTask(value, MyDebugName);
}
}
private ActivityDesign _design;
protected override void OnCreate(Bundle savedInstanceState)
{
_design.ApplyTheme();
base.OnCreate(savedInstanceState);
//see comment to this in PasswordActivity.
//Note that this activity is affected even though it's finished when the app is closed because it
//seems that the "app launch intent" is re-delivered, so this might end up here.
if ((AppTask == null) && (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)))
{
AppTask = new NullTask();
}
else
{
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
}
Kp2aLog.Log("KeePass.OnCreate");
}
protected override void OnResume()
{
base.OnResume();
Kp2aLog.Log("KeePass.OnResume");
_design.ReapplyTheme();
}
protected override void OnStart()
{
base.OnStart();
Kp2aLog.Log("KeePass.OnStart");
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
bool showChangeLog = false;
try
{
PackageInfo packageInfo = PackageManager.GetPackageInfo(PackageName, 0);
int lastInfoVersionCode = prefs.GetInt(GetString(Resource.String.LastInfoVersionCode_key), 0);
if (packageInfo.VersionCode > lastInfoVersionCode)
{
showChangeLog = true;
ISharedPreferencesEditor edit = prefs.Edit();
edit.PutInt(GetString(Resource.String.LastInfoVersionCode_key), packageInfo.VersionCode);
EditorCompat.Apply(edit);
}
}
catch (PackageManager.NameNotFoundException)
{
}
#if DEBUG
showChangeLog = false;
#endif
if (showChangeLog)
{
ChangeLog.ShowChangeLog(this, LaunchNextActivity);
}
else
{
LaunchNextActivity();
}
}
private static String SELECT_RUNTIME_PROPERTY = "persist.sys.dalvik.vm.lib";
private static String LIB_DALVIK = "libdvm.so";
private static String LIB_ART = "libart.so";
private static String LIB_ART_D = "libartd.so";
public static string StartWithTask = "keepass2android.ACTION_START_WITH_TASK";
public KeePass()
{
_design = new ActivityDesign(this);
}
private String GetCurrentRuntimeValue()
{
try
{
Class systemProperties = Class.ForName("android.os.SystemProperties");
try
{
Method get = systemProperties.GetMethod("get",
Class.FromType(typeof (Java.Lang.String)),
Class.FromType(typeof (Java.Lang.String)));
if (get == null)
{
return "WTF?!";
}
try
{
String value = (String) get.Invoke(
systemProperties, SELECT_RUNTIME_PROPERTY,
/* Assuming default is */"Dalvik");
if (LIB_DALVIK.Equals(value))
{
return "Dalvik";
}
else if (LIB_ART.Equals(value))
{
return "ART";
}
else if (LIB_ART_D.Equals(value))
{
return "ART debug build";
}
return value;
}
catch (IllegalAccessException e)
{
return "IllegalAccessException";
}
catch (IllegalArgumentException e)
{
return "IllegalArgumentException";
}
catch (InvocationTargetException e)
{
return "InvocationTargetException";
}
}
catch (NoSuchMethodException e)
{
return "SystemProperties.get(String key, String def) method is not found";
}
}
catch (ClassNotFoundException e)
{
return "SystemProperties class is not found";
}
}
private void LaunchNextActivity() {
Intent intent = new Intent(this, typeof(SelectCurrentDbActivity));
AppTask.ToIntent(intent);
intent.AddFlags(ActivityFlags.ForwardResult);
StartActivity(intent);
Finish();
}
protected override void OnDestroy() {
Kp2aLog.Log("KeePass.OnDestroy"+IsFinishing.ToString());
base.OnDestroy();
}
public void OnDismiss(IDialogInterface dialog)
{
LaunchNextActivity();
}
}
}

View File

@@ -1,177 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using KeePassLib;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace keepass2android
{
class KpEntryTemplatedEdit : EditModeBase
{
internal class KeyOrderComparer : IComparer<string>
{
private readonly KpEntryTemplatedEdit _kpEntryTemplatedEdit;
public KeyOrderComparer(KpEntryTemplatedEdit kpEntryTemplatedEdit)
{
_kpEntryTemplatedEdit = kpEntryTemplatedEdit;
}
public int Compare(string x, string y)
{
int orderX = _kpEntryTemplatedEdit.GetPosition(x);
int orderY = _kpEntryTemplatedEdit.GetPosition(y);
if (orderX == orderY)
return String.Compare(x, y, StringComparison.CurrentCulture);
else
return orderX.CompareTo(orderY);
}
}
private int GetPosition(string key)
{
int res;
if (!Int32.TryParse(_templateEntry.Strings.ReadSafe("_etm_position_" + key), out res))
return Int32.MaxValue;
return res;
}
public const string EtmTemplateUuid = "_etm_template_uuid";
private const string EtmTitle = "_etm_title_";
private readonly PwEntry _entry;
private readonly PwEntry _templateEntry;
public static bool IsTemplated(Database db, PwEntry entry)
{
if (entry.Strings.Exists(EtmTemplateUuid))
{
byte[] uuidBytes = MemUtil.HexStringToByteArray(entry.Strings.ReadSafe(EtmTemplateUuid));
if (uuidBytes != null)
{
PwUuid templateUuid = new PwUuid(uuidBytes);
return db.EntriesById.ContainsKey(templateUuid);
}
}
return false;
}
public KpEntryTemplatedEdit(Database db, PwEntry entry)
{
_entry = entry;
PwUuid templateUuid = new PwUuid(MemUtil.HexStringToByteArray(entry.Strings.ReadSafe(EtmTemplateUuid)));
_templateEntry = db.EntriesById[templateUuid];
}
public static void InitializeEntry(PwEntry entry, PwEntry templateEntry)
{
entry.Strings.Set("_etm_template_uuid", new ProtectedString(false, templateEntry.Uuid.ToHexString()));
entry.IconId = templateEntry.IconId;
entry.CustomIconUuid = templateEntry.CustomIconUuid;
entry.AutoType = templateEntry.AutoType.CloneDeep();
entry.Binaries = templateEntry.Binaries.CloneDeep();
entry.BackgroundColor = templateEntry.BackgroundColor;
entry.ForegroundColor = templateEntry.ForegroundColor;
foreach (string name in templateEntry.Strings.GetKeys())
{
if (name.StartsWith(EtmTitle))
{
String fieldName = name.Substring(EtmTitle.Length);
if (fieldName.StartsWith("@"))
{
if (fieldName == KeePass.TagsKey) entry.Tags = templateEntry.Tags;
if (fieldName == KeePass.OverrideUrlKey) entry.OverrideUrl = templateEntry.OverrideUrl;
if (fieldName == KeePass.ExpDateKey)
{
entry.Expires = templateEntry.Expires;
if (entry.Expires)
entry.ExpiryTime = templateEntry.ExpiryTime;
}
continue;
}
String type = templateEntry.Strings.ReadSafe("_etm_type_" + fieldName);
if ((type == "Divider") || (type == "@confirm"))
continue;
bool protectedField = type.StartsWith("Protected");
entry.Strings.Set(fieldName, new ProtectedString(protectedField, templateEntry.Strings.ReadSafe(fieldName)));
}
}
}
public override bool IsVisible(string fieldKey)
{
if (fieldKey == EtmTemplateUuid)
return false;
if (fieldKey == PwDefs.TitleField)
return true;
if ((fieldKey.StartsWith("@") || (PwDefs.IsStandardField(fieldKey))))
{
return !String.IsNullOrEmpty(GetFieldValue(fieldKey))
|| _templateEntry.Strings.Exists(EtmTitle+fieldKey);
}
return true;
}
private string GetFieldValue(string fieldKey)
{
if (fieldKey == KeePass.ExpDateKey)
return _entry.Expires ? _entry.ExpiryTime.ToString(CultureInfo.CurrentUICulture) : "";
if (fieldKey == KeePass.OverrideUrlKey)
return _entry.OverrideUrl;
if (fieldKey == KeePass.TagsKey)
return StrUtil.TagsToString(_entry.Tags, true);
return _entry.Strings.ReadSafe(fieldKey);
}
public override IEnumerable<string> SortExtraFieldKeys(IEnumerable<string> keys)
{
var c = new KeyOrderComparer(this);
return keys.OrderBy(s => s, c);
}
public override bool ShowAddAttachments
{
get
{
if (manualShowAddAttachments != null) return (bool)manualShowAddAttachments;
return false;
}
}
public override bool ShowAddExtras
{
get {
if (manualShowAddExtras != null) return (bool)manualShowAddExtras;
return false;
}
}
public override string GetTitle(string key)
{
return key;
}
public override string GetFieldType(string key)
{
//TODO return "bool" for boolean fields
return "";
}
public static bool IsTemplate(PwEntry entry)
{
if (entry == null) return false;
return entry.Strings.Exists("_etm_template");
}
}
}

View File

@@ -1,120 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.InteropServices;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
namespace keepass2android
{
public abstract class LifecycleAwareActivity : AndroidX.AppCompat.App.AppCompatActivity
{
protected override void AttachBaseContext(Context baseContext)
{
base.AttachBaseContext(LocaleManager.setLocale(baseContext));
}
protected LifecycleAwareActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected LifecycleAwareActivity()
{
}
string _className;
string ClassName
{
get {
if (_className == null)
_className = GetType().Name;
return _className;
}
}
public string MyDebugName
{
get { return ClassName + " " + ID; }
}
private static int staticCount = 0;
private int ID = staticCount++;
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
Kp2aLog.Log(ClassName + ".OnNewIntent " + ID);
}
protected override void OnResume()
{
base.OnResume();
Kp2aLog.Log(ClassName+".OnResume " + ID);
if (App.Kp2a.CurrentDb== null)
{
Kp2aLog.Log(" DB null" + " " + ID);
}
else
{
Kp2aLog.Log(" DatabaseIsUnlocked=" + App.Kp2a.DatabaseIsUnlocked + " " + ID);
}
}
protected override void OnStart()
{
ProgressTask.SetNewActiveActivity(this);
base.OnStart();
Kp2aLog.Log(ClassName+".OnStart" + " " + ID);
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Kp2aLog.Log(ClassName+".OnCreate" + " " + ID);
Kp2aLog.Log(ClassName+":apptask="+Intent.GetStringExtra("KP2A_APP_TASK_TYPE") + " " + ID);
}
protected override void OnDestroy()
{
base.OnDestroy();
Kp2aLog.Log(ClassName+".OnDestroy"+IsFinishing.ToString() + " " + ID);
}
protected override void OnPause()
{
base.OnPause();
Kp2aLog.Log(ClassName+".OnPause" + " " + ID);
}
protected override void OnStop()
{
base.OnStop();
Kp2aLog.Log(ClassName+".OnStop" + " " + ID);
ProgressTask.RemoveActiveActivity(this);
}
}
}

View File

@@ -1,129 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.Content;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using KeePassLib.Serialization;
namespace keepass2android
{
/// <summary>
/// Base class for activities displaying sensitive information.
/// </summary>
/// Checks in OnResume whether the timeout occured and the database must be locked/closed.
public class LockCloseActivity : LockingActivity, ILockCloseActivity
{
//the check if the database was locked/closed can be disabled by the caller for activities
//which may be used "outside" the database (e.g. GeneratePassword for creating a master password)
protected const string NoLockCheck = "NO_LOCK_CHECK";
protected IOConnectionInfo _ioc;
private BroadcastReceiver _intentReceiver;
private ActivityDesign _design;
public LockCloseActivity()
{
_design = new ActivityDesign(this);
}
protected LockCloseActivity(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
_design = new ActivityDesign(this);
}
protected override void OnCreate(Bundle savedInstanceState)
{
_design.ApplyTheme();
base.OnCreate(savedInstanceState);
Util.MakeSecureDisplay(this);
_ioc = App.Kp2a.CurrentDb?.Ioc;
if (Intent.GetBooleanExtra(NoLockCheck, false))
return;
_intentReceiver = new LockCloseActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
filter.AddAction(Intent.ActionScreenOff);
RegisterReceiver(_intentReceiver, filter);
}
protected override void OnDestroy()
{
if (Intent.GetBooleanExtra(NoLockCheck, false) == false)
{
try
{
UnregisterReceiver(_intentReceiver);
}
catch (Exception ex)
{
Kp2aLog.LogUnexpectedError(ex);
}
}
base.OnDestroy();
}
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
if (Intent.GetBooleanExtra(NoLockCheck, false))
return;
if (TimeoutHelper.CheckDbChanged(this, _ioc))
{
Finish();
return;
}
//todo: it seems like OnResume can be called after dismissing a dialog, e.g. the Delete-permanently-Dialog.
//in this case the following check might run in parallel with the check performed during the SaveDb check (triggered after the
//aforementioned dialog is closed) which can cause odd behavior. However, this is a rare case and hard to resolve so this is currently
//accepted. (If the user clicks cancel on the reload-dialog, everything will work.)
App.Kp2a.CheckForOpenFileChanged(this);
}
public void OnLockDatabase(bool lockedByTimeout)
{
TimeoutHelper.Lock(this, lockedByTimeout);
}
}
}

View File

@@ -1,26 +0,0 @@
using Android.Content;
namespace keepass2android
{
public class LockCloseActivityBroadcastReceiver : BroadcastReceiver
{
readonly ILockCloseActivity _activity;
public LockCloseActivityBroadcastReceiver(ILockCloseActivity activity)
{
_activity = activity;
}
public override void OnReceive(Context context, Intent intent)
{
switch (intent.Action)
{
case Intents.DatabaseLocked:
_activity.OnLockDatabase(intent.GetBooleanExtra("ByTimeout", false));
break;
case Intent.ActionScreenOff:
App.Kp2a.OnScreenOff();
break;
}
}
}
}

View File

@@ -1,104 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.Content;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using KeePassLib.Serialization;
namespace keepass2android
{
/// <summary>
/// Base class for list activities displaying sensitive information.
/// </summary>
/// Checks in OnResume whether the timeout occured and the database must be locked/closed.
public class LockCloseListActivity : LockingListActivity, ILockCloseActivity
{
public LockCloseListActivity()
{
_design = new ActivityDesign(this);
}
IOConnectionInfo _ioc;
private BroadcastReceiver _intentReceiver;
private ActivityDesign _design;
protected override void OnCreate(Bundle savedInstanceState)
{
_design.ApplyTheme();
base.OnCreate(savedInstanceState);
Util.MakeSecureDisplay(this);
_ioc = App.Kp2a.CurrentDb.Ioc;
_intentReceiver = new LockCloseActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
filter.AddAction(Intent.ActionScreenOff);
RegisterReceiver(_intentReceiver, filter);
}
public LockCloseListActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
_design = new ActivityDesign(this);
}
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
if (TimeoutHelper.CheckDbChanged(this, _ioc))
{
Finish();
return;
}
//todo: see LockCloseActivity.OnResume
App.Kp2a.CheckForOpenFileChanged(this);
}
protected override void OnDestroy()
{
try
{
UnregisterReceiver(_intentReceiver);
}
catch (Exception ex)
{
Kp2aLog.LogUnexpectedError(ex);
}
base.OnDestroy();
}
public void OnLockDatabase(bool lockedByTimeout)
{
TimeoutHelper.Lock(this, lockedByTimeout);
}
}
}

View File

@@ -1,160 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Runtime;
namespace keepass2android
{
/// <summary>
/// Base class for activities. Notifies the TimeoutHelper whether the app is active or not.
/// </summary>
public class LockingActivity : LifecycleAwareActivity {
public LockingActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public LockingActivity()
{
}
protected override void OnStart()
{
base.OnStart();
var xcKey = App.Kp2a.CurrentDb?.KpDatabase.MasterKey.GetUserKey<ChallengeXCKey>();
if (CurrentlyWaitingKey == null && xcKey != null)
{
CurrentlyWaitingKey = xcKey;
}
if (CurrentlyWaitingKey != null)
{
CurrentlyWaitingKey.Activity = this;
}
}
protected override void OnStop()
{
base.OnStop();
var xcKey = App.Kp2a.CurrentDb?.KpDatabase.MasterKey.GetUserKey<ChallengeXCKey>();
if (xcKey != null)
{
//don't store a pointer to this activity in the static database object to avoid memory leak
if (xcKey.Activity == this) //don't reset if another activity has come to foreground already
xcKey.Activity = null;
}
}
protected override void OnPause() {
base.OnPause();
TimeoutHelper.Pause(this);
}
protected override void OnDestroy()
{
base.OnDestroy();
GC.Collect();
}
protected override void OnResume() {
base.OnResume();
TimeoutHelper.Resume(this);
}
public const int RequestCodeChallengeYubikey = 793;
//need to store this in the App object to make sure it survives activity recreation
protected ChallengeXCKey CurrentlyWaitingKey
{
get { return App.Kp2a._currentlyWaitingXcKey; }
set { App.Kp2a._currentlyWaitingXcKey = value; }
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
Kp2aLog.Log("LockingActivity: OnActivityResult " + (requestCode == RequestCodeChallengeYubikey ? "yubichall" : ""));
base.OnActivityResult(requestCode, resultCode, data);
if ((requestCode == RequestCodeChallengeYubikey) && (CurrentlyWaitingKey != null))
{
if (resultCode == Result.Ok)
{
byte[] challengeResponse = data.GetByteArrayExtra("response");
if ((challengeResponse != null) && (challengeResponse.Length > 0))
{
CurrentlyWaitingKey.Response = challengeResponse;
}
else
CurrentlyWaitingKey.Error = "Did not receive a valid response.";
}
else
{
CurrentlyWaitingKey.Error = "Cancelled Yubichallenge.";
}
}
}
public Intent TryGetYubichallengeIntentOrPrompt(byte[] challenge, bool promptToInstall)
{
Intent chalIntent = new Intent("net.pp3345.ykdroid.intent.action.CHALLENGE_RESPONSE");
chalIntent.PutExtra("challenge", challenge);
IList<ResolveInfo> activities = PackageManager.QueryIntentActivities(chalIntent, 0);
bool isIntentSafe = activities.Count > 0;
if (isIntentSafe)
{
return chalIntent;
}
if (promptToInstall)
{
AlertDialog.Builder b = new AlertDialog.Builder(this);
string message = GetString(Resource.String.NoChallengeApp) + " " + GetString(Resource.String.PleaseInstallApp, new Java.Lang.Object[]{"ykDroid"});
Intent yubichalIntent = new Intent("com.yubichallenge.NFCActivity.CHALLENGE");
IList<ResolveInfo> yubichallengeactivities = PackageManager.QueryIntentActivities(yubichalIntent, 0);
bool hasYubichallenge = yubichallengeactivities.Count > 0;
if (hasYubichallenge)
message += " " + GetString(Resource.String.AppOutdated, new Java.Lang.Object[] {"YubiChallenge"});
b.SetMessage(message);
b.SetPositiveButton(Android.Resource.String.Ok,
delegate { Util.GotoUrl(this, GetString(Resource.String.MarketURL) + "net.pp3345.ykdroid"); });
b.SetNegativeButton(Resource.String.cancel, delegate { });
b.Create().Show();
}
return null;
}
}
}

View File

@@ -1,79 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.Content;
using Android.OS;
using KeePassLib.Serialization;
namespace keepass2android
{
public class LockingClosePreferenceActivity : LockingPreferenceActivity, ILockCloseActivity
{
IOConnectionInfo _ioc;
private BroadcastReceiver _intentReceiver;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
_ioc = App.Kp2a.CurrentDb.Ioc;
_intentReceiver = new LockCloseActivityBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
RegisterReceiver(_intentReceiver, filter);
}
protected override void OnResume() {
base.OnResume();
if (TimeoutHelper.CheckDbChanged(this, _ioc))
{
Finish();
return;
}
}
protected override void OnDestroy()
{
try
{
UnregisterReceiver(_intentReceiver);
}
catch (Exception ex)
{
Kp2aLog.LogUnexpectedError(ex);
}
base.OnDestroy();
}
public void OnLockDatabase(bool lockedByTimeout)
{
TimeoutHelper.Lock(this, lockedByTimeout);
}
}
}

View File

@@ -1,91 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.App;
using Android.OS;
using Android.Runtime;
namespace keepass2android
{
/// <summary>
/// Base class for list activities. Notifies the TimeoutHelper whether the app is active or not.
/// </summary>
public class LockingListActivity : ListActivity {
public LockingListActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public LockingListActivity ()
{
}
string _className;
string ClassName
{
get {
if (_className == null)
_className = GetType().Name;
return _className;
}
}
protected override void OnResume()
{
base.OnResume();
TimeoutHelper.Resume(this);
Kp2aLog.Log(ClassName+".OnResume");
}
protected override void OnStart()
{
base.OnStart();
Kp2aLog.Log(ClassName+".OnStart");
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Kp2aLog.Log(ClassName+".OnCreate");
}
protected override void OnDestroy()
{
base.OnDestroy();
GC.Collect();
Kp2aLog.Log(ClassName+".OnDestroy"+IsFinishing.ToString());
}
protected override void OnPause()
{
base.OnPause();
TimeoutHelper.Pause(this);
Kp2aLog.Log(ClassName+".OnPause");
}
protected override void OnStop()
{
base.OnStop();
Kp2aLog.Log(ClassName+".OnStop");
}
}
}

View File

@@ -1,194 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using Android.Content.Res;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Preferences;
using Android.Support.V7.App;
using Android.Views;
using Java.Lang;
using keepass2android;
namespace keepass2android
{
public class AppCompatPreferenceActivity: PreferenceActivity
{
public AppCompatPreferenceActivity(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public AppCompatPreferenceActivity()
{
}
private AppCompatDelegate _appCompatDelegate;
AppCompatDelegate Delegate
{
get
{
if (_appCompatDelegate == null)
_appCompatDelegate = AppCompatDelegate.Create(this, null);
return _appCompatDelegate;
}
}
protected override void OnCreate(Bundle savedInstanceState)
{
Delegate.InstallViewFactory();
Delegate.OnCreate(savedInstanceState);
base.OnCreate(savedInstanceState);
}
public override MenuInflater MenuInflater
{
get { return Delegate.MenuInflater; }
}
public override void SetContentView(int layoutResId)
{
Delegate.SetContentView(layoutResId);
}
public override void SetContentView(View view) {
Delegate.SetContentView(view);
}
public override void SetContentView(View view, ViewGroup.LayoutParams @params) {
Delegate.SetContentView(view, @params);
}
public override void AddContentView(View view, ViewGroup.LayoutParams @params) {
Delegate.AddContentView(view, @params);
}
protected override void OnPostResume()
{
base.OnPostResume();
Delegate.OnPostResume();
}
protected override void OnTitleChanged(ICharSequence title, Color color)
{
base.OnTitleChanged(title, color);
Delegate.SetTitle(title);
}
public override void OnConfigurationChanged(Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
Delegate.OnConfigurationChanged(newConfig);
}
protected override void OnStop()
{
base.OnStop();
Delegate.OnStop();
}
protected override void OnDestroy()
{
base.OnDestroy();
Delegate.OnDestroy();
}
public override void InvalidateOptionsMenu()
{
Delegate.InvalidateOptionsMenu();
}
}
public class LockingPreferenceActivity : AppCompatPreferenceActivity {
public LockingPreferenceActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public LockingPreferenceActivity ()
{
}
string _className = null;
string ClassName
{
get {
if (_className == null)
_className = this.GetType().Name;
return _className;
}
}
protected override void OnResume()
{
base.OnResume();
TimeoutHelper.Resume(this);
Kp2aLog.Log(ClassName+".OnResume");
}
protected override void OnStart()
{
base.OnStart();
Kp2aLog.Log(ClassName+".OnStart");
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Kp2aLog.Log(ClassName+".OnCreate");
}
protected override void OnDestroy()
{
base.OnDestroy();
GC.Collect();
Kp2aLog.Log(ClassName+".OnDestroy"+IsFinishing.ToString());
}
protected override void OnPause()
{
base.OnPause();
TimeoutHelper.Pause(this);
Kp2aLog.Log(ClassName+".OnPause");
}
protected override void OnStop()
{
base.OnStop();
Kp2aLog.Log(ClassName+".OnStop");
}
}
}

View File

@@ -1,64 +0,0 @@
using System;
using Android.Content;
using Android.Runtime;
using Android.Util;
using Android.Widget;
namespace keepass2android
{
public class MeasuringRelativeLayout : RelativeLayout
{
protected MeasuringRelativeLayout(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public MeasuringRelativeLayout(Context context)
: base(context)
{
}
public MeasuringRelativeLayout(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
public MeasuringRelativeLayout(Context context, IAttributeSet attrs, int defStyleAttr)
: base(context, attrs, defStyleAttr)
{
}
public MeasuringRelativeLayout(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes)
: base(context, attrs, defStyleAttr, defStyleRes)
{
}
public class MeasureArgs
{
public int ActualHeight;
public int ProposedHeight;
}
public event EventHandler<MeasureArgs> MeasureEvent;
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
MeasureArgs args = new MeasureArgs();
args.ProposedHeight = MeasureSpec.GetSize(heightMeasureSpec);
args.ActualHeight = Height;
OnMeasureEvent(args);
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected virtual void OnMeasureEvent(MeasureArgs args)
{
var handler = MeasureEvent;
if (handler != null) handler(this, args);
}
}
}

View File

@@ -1,165 +0,0 @@
using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Nfc;
using Android.OS;
using Android.Widget;
using Java.Util;
using Java.Util.Regex;
using Keepass2android.Yubiclip.Scancode;
using Pattern = Java.Util.Regex.Pattern;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation |
ConfigChanges.KeyboardHidden,
NoHistory = true,
Exported = true,
ExcludeFromRecents = true,
Theme = "@android:style/Theme.Dialog")]
[IntentFilter(new[] { "android.nfc.action.NDEF_DISCOVERED" },
Label = "@string/app_name",
Categories = new[] { Intent.CategoryDefault },
DataHost = "my.yubico.com",
DataPathPrefix = "/neo",
DataScheme = "https")]
[IntentFilter(new[] { "android.nfc.action.NDEF_DISCOVERED", Android.Content.Intent.ActionView },
Label = "@string/app_name",
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataHost = "keepass2android.crocoll.net",
DataPathPrefix = "/neo",
DataScheme = "https",
AutoVerify=true)]
public class NfcOtpActivity : Activity
{
private String GetOtpFromIntent(Intent intent)
{
var patterns = new List<Pattern>{OTP_PATTERN, OTP_PATTERN2};
foreach (var pattern in patterns)
{
Matcher matcher = pattern.Matcher(intent.DataString);
if (matcher.Matches())
{
String otp = matcher.Group(1);
return otp;
}
else
{
IParcelable[] raw = intent.GetParcelableArrayExtra(NfcAdapter.ExtraNdefMessages);
byte[] bytes = ((NdefMessage)raw[0]).ToByteArray();
if (bytes[0] == URL_NDEF_RECORD && Arrays.Equals(URL_PREFIX_BYTES, Arrays.CopyOfRange(bytes, 3, 3 + URL_PREFIX_BYTES.Length)))
{
if (Arrays.Equals(new Java.Lang.String("/neo/").GetBytes(), Arrays.CopyOfRange(bytes, 18, 18 + 5)))
{
bytes[22] = (byte)'#';
}
for (int i = 0; i < bytes.Length; i++)
{
if (bytes[i] == '#')
{
bytes = Arrays.CopyOfRange(bytes, i + 1, bytes.Length);
String layout = "US";
KeyboardLayout kbd = KeyboardLayout.ForName(layout);
String otp = kbd.FromScanCodes(bytes);
return otp;
}
}
}
}
}
/*
Matcher matcher = OtpPattern.Matcher(data);
if (matcher.Matches())
{
String otp = matcher.Group(1);
return otp;
}
else
{
IParcelable[] raw = Intent.GetParcelableArrayExtra(NfcAdapter.ExtraNdefMessages);
byte[] bytes = ((NdefMessage) raw[0]).ToByteArray();
bytes = Arrays.CopyOfRange(bytes, DATA_OFFSET, bytes.Length);
String layout = "US";
KeyboardLayout kbd = KeyboardLayout.ForName(layout);
String otp = kbd.FromScanCodes(bytes);
return otp;
}*/
return null;
}
private static String URL_PREFIX = "https://my.yubico.com/";
private static byte URL_NDEF_RECORD = (byte)0xd1;
private static byte[] URL_PREFIX_BYTES = new byte[URL_PREFIX.Length + 2 - 8];
private static Pattern OTP_PATTERN = Pattern.Compile("^https://my\\.yubico\\.com/[a-z]+/#?([a-zA-Z0-9!]+)$");
private static Pattern OTP_PATTERN2 = Pattern.Compile("^https://keepass2android\\.crocoll\\.net/[a-z]+/#?([a-zA-Z0-9!]+)$");
static NfcOtpActivity()
{
URL_PREFIX_BYTES[0] = 85;
URL_PREFIX_BYTES[1] = 4;
Java.Lang.JavaSystem.Arraycopy(new Java.Lang.String(URL_PREFIX.Substring(8)).GetBytes(), 0, URL_PREFIX_BYTES, 2, URL_PREFIX_BYTES.Length - 2);
}
private ActivityDesign _design;
public NfcOtpActivity()
{
_design = new ActivityDesign(this);
}
protected override void OnCreate(Bundle bundle)
{
_design.ApplyTheme();
base.OnCreate(bundle);
Intent i = new Intent(this, typeof (PasswordActivity));
i.SetAction(Intents.StartWithOtp);
//things to consider:
// PasswordActivity should be resumed if currently active -> this is why single top is used and why PasswordActivity is started
// If PasswordActivity is not open already, it may be the wrong place to send our OTP to because maybe the user first needs to select
// a file (which might require UI action like entering credentials, all of which is handled in FileSelectActivity)
// FileSelectActivity is not on the back stack, it finishes itself.
// -> PasswordActivity needs to handle this and return to FSA.
i.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
try
{
string otp = GetOtpFromIntent(Intent);
if (otp == null)
throw new Exception("Otp must not be null!");
i.PutExtra(Intents.OtpExtraKey, otp);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
Toast.MakeText(this, "No Yubikey OTP found!", ToastLength.Long).Show();
Finish();
return;
}
StartActivity(i);
Finish();
}
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
}
}
}

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
namespace keepass2android
{
[Activity(Label = AppNames.AppName, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_Blue",
LaunchMode = LaunchMode.SingleInstance)]
public class NoSecureDisplayActivity : AndroidX.AppCompat.App.AppCompatActivity
{
private readonly ActivityDesign _design;
public NoSecureDisplayActivity()
{
_design = new ActivityDesign(this);
}
protected override void OnCreate(Bundle savedInstanceState)
{
_design.ApplyTheme();
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.no_secure_display_layout);
FindViewById<Button>(Resource.Id.btn_goto_settings).Click += (sender, args) =>
{
AppSettingsActivity.Launch(this);
};
FindViewById<Button>(Resource.Id.disable_secure_screen_check).Click += (sender, args) =>
{
var prefs = PreferenceManager.GetDefaultSharedPreferences(LocaleManager.LocalizedAppContext);
prefs.Edit()
.PutBoolean("no_secure_display_check", true)
.Commit();
Finish();
};
FindViewById<Button>(Resource.Id.btn_close).Click += (sender, args) =>
{
Finish();
};
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar);
SetSupportActionBar(toolbar);
SupportActionBar.Title = AppNames.AppName;
}
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
//close if displays changed
if (!Util.SecureDisplayConfigured(this) || !Util.HasUnsecureDisplay(this))
Finish();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +0,0 @@
using Android.Graphics;
using Android.Widget;
namespace keepass2android
{
public class PasswordFont
{
private static Typeface _passwordFont;
public void ApplyTo(TextView view)
{
if (_passwordFont == null)
_passwordFont = Typeface.CreateFromAsset(view.Context.Assets, "SourceCodePro-Regular.ttf");
view.Typeface = _passwordFont;
}
}
}

View File

@@ -1,349 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using Android.Content;
using Android.Views;
using Android.Widget;
using Android.Preferences;
using KeePassLib;
using keepass2android.view;
using KeePassLib.Interfaces;
namespace keepass2android
{
public interface IGroupViewSortOrder
{
int ResourceId { get; }
bool RequiresSort { get; }
int CompareEntries(PwEntry a, PwEntry b);
int CompareGroups(PwGroup a, PwGroup b);
}
class ModDateSortOrder : IGroupViewSortOrder
{
public int ResourceId
{
get { return Resource.String.sort_moddate; }
}
public bool RequiresSort
{
get { return true; }
}
public int CompareEntries(PwEntry a, PwEntry b)
{
return -a.LastModificationTime.CompareTo(b.LastModificationTime);
}
public int CompareGroups(PwGroup a, PwGroup b)
{
return -a.LastModificationTime.CompareTo(b.LastModificationTime);
}
}
class CreationDateSortOrder : IGroupViewSortOrder
{
public int ResourceId
{
get { return Resource.String.sort_db; }
}
public bool RequiresSort
{
get { return true; }
}
public int CompareEntries(PwEntry a, PwEntry b)
{
return a.CreationTime.CompareTo(b.CreationTime);
}
public int CompareGroups(PwGroup a, PwGroup b)
{
return a.CreationTime.CompareTo(b.CreationTime);
}
}
public class DefaultSortOrder: IGroupViewSortOrder
{
public int ResourceId
{
get { return Resource.String.sort_default; }
}
public bool RequiresSort
{
get { return false; }
}
public int CompareEntries(PwEntry a, PwEntry b)
{
return 0;
}
public int CompareGroups(PwGroup a, PwGroup b)
{
return 0;
}
}
public class NameSortOrder: IGroupViewSortOrder
{
public int ResourceId
{
get { return Resource.String.sort_name; }
}
public bool RequiresSort
{
get { return true; }
}
public int CompareEntries(PwEntry x, PwEntry y)
{
String nameX = x.Strings.ReadSafe(PwDefs.TitleField);
String nameY = y.Strings.ReadSafe(PwDefs.TitleField);
if (nameX.ToLower() != nameY.ToLower())
return String.Compare(nameX, nameY, StringComparison.CurrentCultureIgnoreCase);
else
{
if (PwDefs.IsTanEntry(x) && PwDefs.IsTanEntry(y))
{
//compare the user name fields (=TAN index)
String userX = x.Strings.ReadSafe(PwDefs.UserNameField);
String userY = y.Strings.ReadSafe(PwDefs.UserNameField);
if (userX != userY)
{
try
{
return int.Parse(userX).CompareTo(int.Parse(userY));
}
catch (Exception)
{
//ignore
}
return String.Compare(userX, userY, StringComparison.CurrentCultureIgnoreCase);
}
}
//use creation time for non-tan entries:
return x.CreationTime.CompareTo(y.CreationTime);
}
}
public int CompareGroups(PwGroup a, PwGroup b)
{
return String.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase);
}
}
public class GroupViewSortOrderManager
{
private readonly Context _context;
private readonly IGroupViewSortOrder[] _orders = new IGroupViewSortOrder[] { new DefaultSortOrder(), new NameSortOrder(), new ModDateSortOrder(), new CreationDateSortOrder()};
private readonly ISharedPreferences _prefs;
public GroupViewSortOrderManager(Context context)
{
_context = context;
_prefs = PreferenceManager.GetDefaultSharedPreferences(_context);
}
public IGroupViewSortOrder[] SortOrders
{
get { return _orders; }
}
public bool SortGroups
{
get { return true; }
//_prefs.GetBoolean(_context.GetString(Resource.String.sortgroups_key), false); }
}
public IGroupViewSortOrder GetCurrentSortOrder()
{
return SortOrders[GetCurrentSortOrderIndex()];
}
public int GetCurrentSortOrderIndex()
{
String sortKeyOld = _context.GetString(Resource.String.sort_key_old);
String sortKey = _context.GetString(Resource.String.sort_key);
int sortId = _prefs.GetInt(sortKey, -1);
if (sortId == -1)
{
sortId = _prefs.GetBoolean(sortKeyOld, true) ? 1 : 0;
}
return sortId;
}
public void SetNewSortOrder(int selectedAfter)
{
String sortKey = _context.GetString(Resource.String.sort_key);
ISharedPreferencesEditor editor = _prefs.Edit();
editor.PutInt(sortKey, selectedAfter);
//editor.PutBoolean(_context.GetString(Resource.String.sortgroups_key), false);
EditorCompat.Apply(editor);
}
}
public class PwGroupListAdapter : BaseAdapter
{
private readonly GroupBaseActivity _act;
private readonly PwGroup _group;
private List<PwGroup> _groupsForViewing;
private List<PwEntry> _entriesForViewing;
public bool InActionMode { get; set; }
public PwGroupListAdapter(GroupBaseActivity act, PwGroup group) {
_act = act;
_group = group;
FilterAndSort();
}
public override void NotifyDataSetChanged() {
base.NotifyDataSetChanged();
FilterAndSort();
}
public override void NotifyDataSetInvalidated() {
base.NotifyDataSetInvalidated();
FilterAndSort();
}
private void FilterAndSort() {
_entriesForViewing = new List<PwEntry>();
foreach (PwEntry entry in _group.Entries)
{
_entriesForViewing.Add(entry);
}
GroupViewSortOrderManager sortOrderManager = new GroupViewSortOrderManager(_act);
var sortOrder = sortOrderManager.GetCurrentSortOrder();
if ( sortOrder.RequiresSort )
{
var sortGroups = sortOrderManager.SortGroups;
_groupsForViewing = new List<PwGroup>(_group.Groups);
_groupsForViewing.Sort( (x, y) =>
{
if (sortGroups)
return sortOrder.CompareGroups(x, y);
else
return String.Compare (x.Name, y.Name, true);
});
_entriesForViewing.Sort(sortOrder.CompareEntries);
} else {
_groupsForViewing = new List<PwGroup>(_group.Groups);
}
}
public override int Count
{
get{
return _groupsForViewing.Count + _entriesForViewing.Count;
}
}
public override Java.Lang.Object GetItem(int position) {
return position;
}
public bool IsGroupAtPosition(int position)
{
return position < _groupsForViewing.Count;
}
public override long GetItemId(int position) {
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent) {
int size = _groupsForViewing.Count;
GroupListItemView view;
if ( position < size ) {
view = CreateGroupView(position, convertView);
} else {
view = CreateEntryView(position - size, convertView);
}
view.SetRightArrowVisibility(!InActionMode);
return view;
}
private PwGroupView CreateGroupView(int position, View convertView) {
PwGroup g = _groupsForViewing[position];
PwGroupView gv;
if (convertView == null || !(convertView is PwGroupView)) {
gv = PwGroupView.GetInstance(_act, g);
}
else {
gv = (PwGroupView) convertView;
gv.ConvertView(g);
}
return gv;
}
private PwEntryView CreateEntryView(int position, View convertView) {
PwEntry entry = _entriesForViewing[position];
PwEntryView ev;
if (convertView == null || !(convertView is PwEntryView)) {
ev = PwEntryView.GetInstance(_act, entry, position);
}
else {
ev = (PwEntryView) convertView;
ev.ConvertView(entry, position);
}
return ev;
}
public IStructureItem GetItemAtPosition(int keyAt)
{
if (keyAt < _groupsForViewing.Count)
{
return _groupsForViewing[keyAt];
}
else
{
return _entriesForViewing[keyAt - _groupsForViewing.Count];
}
}
}
}

View File

@@ -1,209 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Keepass2android.Pluginsdk;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Exported = true,
Theme = "@style/MyTheme_ActionBar")]
[IntentFilter(new[] { Strings.ActionQueryCredentials},
Categories = new[] { Intent.CategoryDefault })]
[IntentFilter(new[] { Strings.ActionQueryCredentialsForOwnPackage },
Categories = new[] { Intent.CategoryDefault })]
public class QueryCredentialsActivity : Activity
{
private const int RequestCodePluginAccess = 1;
private const int RequestCodeQuery = 2;
private const string IsRecreate = "isRecreate";
private const string StartedQuery = "startedQuery";
private bool _startedQuery;
private string _requiredScope;
private string _requestedUrl;
private string _pluginPackage;
public QueryCredentialsActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public QueryCredentialsActivity()
{
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
//if launched from history, don't re-use the task. Proceed to FileSelect instead.
if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory))
{
Kp2aLog.Log("Forwarding to SelectCurrentDbActivity. QueryCredentialsActivity started from history.");
Intent intent = new Intent(this, typeof(SelectCurrentDbActivity));
intent.AddFlags(ActivityFlags.ForwardResult);
StartActivity(intent);
Finish();
return;
}
_pluginPackage = null;
if (CallingActivity != null)
_pluginPackage = CallingActivity.PackageName;
if (_pluginPackage == null)
{
Kp2aLog.Log("Couldn't retrieve calling package. Probably activity was started without startActivityForResult()");
Finish();
return;
}
if (Intent.Action == Strings.ActionQueryCredentialsForOwnPackage)
{
_requiredScope = Strings.ScopeQueryCredentialsForOwnPackage;
_requestedUrl = KeePass.AndroidAppScheme + _pluginPackage;
}
else if (Intent.Action == Strings.ActionQueryCredentials)
{
_requiredScope = Strings.ScopeQueryCredentials;
_requestedUrl = Intent.GetStringExtra(Strings.ExtraQueryString);
}
else
{
Kp2aLog.Log("Invalid action for QueryCredentialsActivity: " + Intent.Action);
SetResult(Result.FirstUser);
Finish();
return;
}
//only start the query or request plugin access when creating the first time.
//if we're restarting (after config change or low memory), we will get onActivityResult() later
//which will either start the next activity or finish this one.
if ((savedInstanceState == null) || (savedInstanceState.GetBoolean(IsRecreate, false) == false))
{
ShowToast();
if (new PluginDatabase(this).HasAcceptedScope(_pluginPackage,_requiredScope))
{
StartQuery();
}
else
{
RequestPluginAccess();
}
}
}
protected override void OnStart()
{
base.OnStart();
Kp2aLog.Log("Starting QueryCredentialsActivity");
}
protected override void OnResume()
{
base.OnResume();
Kp2aLog.Log("Resuming QueryCredentialsActivity");
}
private void ShowToast()
{
string pluginDisplayName = _pluginPackage;
try
{
pluginDisplayName = PackageManager.GetApplicationLabel(PackageManager.GetApplicationInfo(_pluginPackage, 0));
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
if (String.IsNullOrEmpty(_requestedUrl))
Toast.MakeText(this, GetString(Resource.String.query_credentials, new Java.Lang.Object[] {pluginDisplayName}), ToastLength.Long).Show();
else
Toast.MakeText(this,
GetString(Resource.String.query_credentials_for_url,
new Java.Lang.Object[] { pluginDisplayName, _requestedUrl }), ToastLength.Long).Show(); ;
}
private void StartQuery()
{
//launch SelectCurrentDbActivity (which is root of the stack (exception: we're even below!)) with the appropriate task.
//will return the results later
Intent i = new Intent(this, typeof (SelectCurrentDbActivity));
//don't show user notifications when an entry is opened.
var task = new SearchUrlTask() {UrlToSearchFor = _requestedUrl, ShowUserNotifications = ActivationCondition.WhenTotp, ActivateKeyboard = ActivationCondition.Never };
task.ToIntent(i);
StartActivityForResult(i, RequestCodeQuery);
_startedQuery = true;
}
private void RequestPluginAccess()
{
Intent i = new Intent(this, typeof(PluginDetailsActivity));
i.SetAction(Strings.ActionEditPluginSettings);
i.PutExtra(Strings.ExtraPluginPackage, _pluginPackage);
StartActivityForResult(i, RequestCodePluginAccess);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == RequestCodePluginAccess)
{
if (new PluginDatabase(this).HasAcceptedScope(_pluginPackage, _requiredScope))
{
//user granted access. Search for the requested credentials:
StartQuery();
}
else
{
//user didn't grant access
SetResult(Result.Canceled);
Finish();
}
}
if (requestCode == RequestCodeQuery)
{
if (resultCode == KeePass.ExitCloseAfterTaskComplete)
{
//double check we really have the permission
if (!new PluginDatabase(this).HasAcceptedScope(_pluginPackage, _requiredScope))
{
Kp2aLog.LogUnexpectedError(new Exception("Ohoh! Scope not available, shouldn't get here. Malicious app somewhere?"));
SetResult(Result.Canceled);
Finish();
return;
}
//return credentials to caller:
Intent credentialData = new Intent();
PluginHost.AddEntryToIntent(credentialData, App.Kp2a.LastOpenedEntry);
credentialData.PutExtra(Strings.ExtraQueryString,_requestedUrl);
SetResult(Result.Ok, credentialData);
Finish();
}
else
{
SetResult(Result.Canceled);
Finish();
}
}
}
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutBoolean(StartedQuery, _startedQuery);
outState.PutBoolean(IsRecreate, true);
}
}
}

View File

@@ -1,509 +0,0 @@
/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Linq;
using Android;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Views;
using Android.Widget;
using Android.Content.PM;
using KeePassLib.Keys;
using Android.Preferences;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Views.InputMethods;
using KeePassLib;
using KeePassLib.Serialization;
namespace keepass2android
{
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation,
WindowSoftInputMode = SoftInput.AdjustResize,
MainLauncher = false,
Theme = "@style/MyTheme_Blue")]
public class QuickUnlock : LifecycleAwareActivity, IBiometricAuthCallback
{
private IOConnectionInfo _ioc;
private QuickUnlockBroadcastReceiver _intentReceiver;
private ActivityDesign _design;
private IBiometricIdentifier _biometryIdentifier;
private int _quickUnlockLength;
private int numFailedAttempts = 0;
private int maxNumFailedAttempts = int.MaxValue;
public QuickUnlock()
{
_design = new ActivityDesign(this);
}
protected override void OnCreate(Bundle bundle)
{
_design.ApplyTheme();
base.OnCreate(bundle);
//use FlagSecure to make sure the last (revealed) character of the password is not visible in recent apps
Util.MakeSecureDisplay(this);
_ioc = App.Kp2a.GetDbForQuickUnlock()?.Ioc;
if (_ioc == null)
{
Finish();
return;
}
SetContentView(Resource.Layout.QuickUnlock);
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar);
SetSupportActionBar(toolbar);
var collapsingToolbar = FindViewById<Android.Support.Design.Widget.CollapsingToolbarLayout>(Resource.Id.collapsing_toolbar);
collapsingToolbar.SetTitle(GetString(Resource.String.QuickUnlock_prefs));
if (App.Kp2a.GetDbForQuickUnlock().KpDatabase.Name != "")
{
FindViewById(Resource.Id.filename_label).Visibility = ViewStates.Visible;
((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetDbForQuickUnlock().KpDatabase.Name;
}
else
{
if (
PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.RememberRecentFiles_key),
Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default)))
{
((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetFileStorage(_ioc).GetDisplayName(_ioc);
}
else
{
((TextView) FindViewById(Resource.Id.filename_label)).Text = "*****";
}
}
TextView txtLabel = (TextView) FindViewById(Resource.Id.QuickUnlock_label);
_quickUnlockLength = App.Kp2a.QuickUnlockKeyLength;
bool useUnlockKeyFromDatabase =
QuickUnlockFromDatabaseEnabled
&& FindQuickUnlockEntry() != null;
if (useUnlockKeyFromDatabase || PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.QuickUnlockHideLength_key), false))
{
txtLabel.Text = GetString(Resource.String.QuickUnlock_label_secure);
}
else
{
txtLabel.Text = GetString(Resource.String.QuickUnlock_label, new Java.Lang.Object[] { _quickUnlockLength });
}
EditText pwd = (EditText) FindViewById(Resource.Id.QuickUnlock_password);
pwd.SetEms(_quickUnlockLength);
Util.MoveBottomBarButtons(Resource.Id.QuickUnlock_buttonLock, Resource.Id.QuickUnlock_button, Resource.Id.bottom_bar, this);
Button btnUnlock = (Button) FindViewById(Resource.Id.QuickUnlock_button);
btnUnlock.Click += (object sender, EventArgs e) =>
{
OnUnlock(pwd);
};
Button btnLock = (Button) FindViewById(Resource.Id.QuickUnlock_buttonLock);
btnLock.Text = btnLock.Text.Replace("<22>", "ss");
btnLock.Click += (object sender, EventArgs e) =>
{
App.Kp2a.Lock(false);
Finish();
};
pwd.EditorAction += (sender, args) =>
{
if ((args.ActionId == ImeAction.Done) || ((args.ActionId == ImeAction.ImeNull) && (args.Event.Action == KeyEventActions.Down)))
OnUnlock(pwd);
};
_intentReceiver = new QuickUnlockBroadcastReceiver(this);
IntentFilter filter = new IntentFilter();
filter.AddAction(Intents.DatabaseLocked);
RegisterReceiver(_intentReceiver, filter);
Util.SetNoPersonalizedLearning(FindViewById<EditText>(Resource.Id.QuickUnlock_password));
if (bundle != null)
numFailedAttempts = bundle.GetInt(NumFailedAttemptsKey, 0);
}
private bool QuickUnlockFromDatabaseEnabled =>
PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.QuickUnlockKeyFromDatabase_key), false);
private static PwEntry FindQuickUnlockEntry()
{
return App.Kp2a.GetDbForQuickUnlock()?.KpDatabase?.RootGroup?.Entries.SingleOrDefault(e => e.Strings.GetSafe(PwDefs.TitleField).ReadString() == "QuickUnlock");
}
private const string NumFailedAttemptsKey = "FailedAttempts";
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutInt(NumFailedAttemptsKey, numFailedAttempts);
}
protected override void OnStart()
{
base.OnStart();
DonateReminder.ShowDonateReminderIfAppropriate(this);
}
public void OnBiometricError(string message)
{
Kp2aLog.Log("fingerprint error: " + message);
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.PostDelayed(() =>
{
btn.SetImageResource(Resource.Drawable.ic_fp_40px);
}, 1300);
Toast.MakeText(this, message, ToastLength.Long).Show();
}
public void OnBiometricAttemptFailed(string message)
{
numFailedAttempts++;
if (numFailedAttempts >= maxNumFailedAttempts)
{
FindViewById<ImageButton>(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone;
_biometryIdentifier.StopListening();
}
}
public void OnBiometricAuthSucceeded()
{
Kp2aLog.Log("OnFingerprintAuthSucceeded");
_biometryIdentifier.StopListening();
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.SetImageResource(Resource.Drawable.ic_fingerprint_success);
EditText pwd = (EditText)FindViewById(Resource.Id.QuickUnlock_password);
pwd.Text = ExpectedPasswordPart;
btn.PostDelayed(() =>
{
UnlockAndSyncAndClose();
}, 500);
}
private bool InitFingerprintUnlock()
{
Kp2aLog.Log("InitFingerprintUnlock");
if (_biometryIdentifier != null)
{
Kp2aLog.Log("Already listening for fingerprint!");
return true;
}
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
try
{
FingerprintUnlockMode um;
Enum.TryParse(PreferenceManager.GetDefaultSharedPreferences(this).GetString(App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintModePrefKey, ""), out um);
btn.Visibility = (um != FingerprintUnlockMode.Disabled) ? ViewStates.Visible : ViewStates.Gone;
if (um == FingerprintUnlockMode.Disabled)
{
_biometryIdentifier = null;
return false;
}
if (um == FingerprintUnlockMode.QuickUnlock && Util.GetCloseDatabaseAfterFailedBiometricQuickUnlock(this))
{
maxNumFailedAttempts = 3;
}
BiometricModule fpModule = new BiometricModule(this);
Kp2aLog.Log("fpModule.IsHardwareAvailable=" + fpModule.IsHardwareAvailable);
if (fpModule.IsHardwareAvailable) //see FingerprintSetupActivity
_biometryIdentifier = new BiometricDecryption(fpModule, App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey, this,
App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey);
if (_biometryIdentifier == null)
{
FindViewById<ImageButton>(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone;
return false;
}
if (_biometryIdentifier.Init())
{
Kp2aLog.Log("successfully initialized fingerprint.");
btn.SetImageResource(Resource.Drawable.ic_fp_40px);
_biometryIdentifier.StartListening(this);
return true;
}
else
{
Kp2aLog.Log("failed to initialize fingerprint.");
HandleFingerprintKeyInvalidated();
}
}
catch (Exception e)
{
Kp2aLog.Log("Error initializing Fingerprint Unlock: " + e);
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.Tag = "Error initializing Fingerprint Unlock: " + e;
_biometryIdentifier = null;
}
return false;
}
private void HandleFingerprintKeyInvalidated()
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
//key invalidated permanently
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.Tag = GetString(Resource.String.fingerprint_unlock_failed) + " " + GetString(Resource.String.fingerprint_reenable2);
_biometryIdentifier = null;
}
private void OnUnlock(EditText pwd)
{
var expectedPasswordPart = ExpectedPasswordPart;
if (pwd.Text == expectedPasswordPart)
{
UnlockAndSyncAndClose();
}
else
{
Kp2aLog.Log("QuickUnlock not successful!");
App.Kp2a.Lock(false);
Toast.MakeText(this, GetString(Resource.String.QuickUnlock_fail), ToastLength.Long).Show();
Finish();
}
}
private void UnlockAndSyncAndClose()
{
App.Kp2a.UnlockDatabase();
if (PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.SyncAfterQuickUnlock_key), false))
{
new SyncUtil(this).SynchronizeDatabase(Finish);
}
else
Finish();
}
private string ExpectedPasswordPart
{
get
{
if (QuickUnlockFromDatabaseEnabled)
{
var quickUnlockEntry = FindQuickUnlockEntry();
if (quickUnlockEntry != null)
{
return quickUnlockEntry.Strings.ReadSafe(PwDefs.PasswordField).ToString();
}
}
KcpPassword kcpPassword = (KcpPassword) App.Kp2a.GetDbForQuickUnlock().KpDatabase.MasterKey.GetUserKey(typeof (KcpPassword));
String password = kcpPassword.Password.ReadString();
var passwordStringInfo = new System.Globalization.StringInfo(password);
int passwordLength = passwordStringInfo.LengthInTextElements;
String expectedPasswordPart = passwordStringInfo.SubstringByTextElements(Math.Max(0, passwordLength - _quickUnlockLength),
Math.Min(passwordLength, _quickUnlockLength));
return expectedPasswordPart;
}
}
private void OnLockDatabase()
{
CheckIfUnloaded();
}
protected override void OnResume()
{
base.OnResume();
_design.ReapplyTheme();
CheckIfUnloaded();
InitFingerprintUnlock();
bool showKeyboard = true;
EditText pwd = (EditText)FindViewById(Resource.Id.QuickUnlock_password);
pwd.PostDelayed(() =>
{
pwd.RequestFocus();
InputMethodManager keyboard = (InputMethodManager)GetSystemService(Context.InputMethodService);
if (showKeyboard)
keyboard.ShowSoftInput(pwd, ShowFlags.Implicit);
else
keyboard.HideSoftInputFromWindow(pwd.WindowToken, HideSoftInputFlags.ImplicitOnly);
}, 50);
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.Click += (sender, args) =>
{
if ((_biometryIdentifier != null) && ((_biometryIdentifier.HasUserInterface)|| string.IsNullOrEmpty((string)btn.Tag) ))
{
_biometryIdentifier.StartListening(this);
}
else
{
AlertDialog.Builder b = new AlertDialog.Builder(this);
b.SetTitle(Resource.String.fingerprint_prefs);
b.SetMessage(btn.Tag.ToString());
b.SetPositiveButton(Android.Resource.String.Ok, (o, eventArgs) => ((Dialog)o).Dismiss());
if (_biometryIdentifier != null)
{
b.SetNegativeButton(Resource.String.disable_sensor, (senderAlert, alertArgs) =>
{
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
_biometryIdentifier?.StopListening();
_biometryIdentifier = null;
});
}
else
{
b.SetNegativeButton(Resource.String.enable_sensor, (senderAlert, alertArgs) =>
{
InitFingerprintUnlock();
});
}
b.Show();
}
};
}
protected override void OnPause()
{
if (_biometryIdentifier != null)
{
Kp2aLog.Log("FP: Stop listening");
_biometryIdentifier.StopListening();
}
base.OnPause();
}
protected override void OnDestroy()
{
base.OnDestroy();
try
{
UnregisterReceiver(_intentReceiver);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
}
private void CheckIfUnloaded()
{
if (App.Kp2a.OpenDatabases.Any() == false)
{
Finish();
}
}
public override void OnBackPressed()
{
SetResult(KeePass.ExitClose);
base.OnBackPressed();
}
private class QuickUnlockBroadcastReceiver : BroadcastReceiver
{
readonly QuickUnlock _activity;
public QuickUnlockBroadcastReceiver(QuickUnlock activity)
{
_activity = activity;
}
public override void OnReceive(Context context, Intent intent)
{
switch (intent.Action)
{
case Intents.DatabaseLocked:
_activity.OnLockDatabase();
break;
}
}
}
}
}

View File

@@ -1,44 +0,0 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.axml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable/
icon.png
layout/
main.axml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called "R"
(this is an Android convention) that contains the tokens for each one of the resources
included. For example, for the above Resources layout, this is what the R class would expose:
public class R {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
to reference the layout/main.axml file, or R.Strings.first_string to reference the first
string in the dictionary file values/strings.xml.

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400" />
</set>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="250"/>
</set>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400"/>
</set>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="250" />
</set>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More