towards sdk-style projects. As of now, all code/resources etc. is duplicated for new projects in new folders. this will be moved back to the original locations (and then squashed). App currently does not build.

This commit is contained in:
Philipp Crocoll
2024-11-05 10:58:57 +01:00
parent dad9b0e53f
commit 43f6064faa
1994 changed files with 261699 additions and 1467 deletions

View File

@@ -0,0 +1,342 @@
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();
}
}
}