update targetSdkVersion to 29 (Android 10/Q). Change canvas clipping in Soft keyboard to adhere to behavior changes in Android 10. Migrate to AndroidX. Migrate to using BiometricPrompt instead of FingerprintManager, keeping compatibility to SamsungPass API for devices like SGS5. Closes #795, should close #626, closes #910

This commit is contained in:
Philipp Crocoll
2019-11-05 20:55:02 +01:00
parent 8a9c781de2
commit 683cde5a8b
39 changed files with 1007 additions and 722 deletions

View File

@@ -0,0 +1,48 @@
Additions allow you to add arbitrary C# to the generated classes
before they are compiled. This can be helpful for providing convenience
methods or adding pure C# classes.
== Adding Methods to Generated Classes ==
Let's say the library being bound has a Rectangle class with a constructor
that takes an x and y position, and a width and length size. It will look like
this:
public partial class Rectangle
{
public Rectangle (int x, int y, int width, int height)
{
// JNI bindings
}
}
Imagine we want to add a constructor to this class that takes a Point and
Size structure instead of 4 ints. We can add a new file called Rectangle.cs
with a partial class containing our new method:
public partial class Rectangle
{
public Rectangle (Point location, Size size) :
this (location.X, location.Y, size.Width, size.Height)
{
}
}
At compile time, the additions class will be added to the generated class
and the final assembly will a Rectangle class with both constructors.
== Adding C# Classes ==
Another thing that can be done is adding fully C# managed classes to the
generated library. In the above example, let's assume that there isn't a
Point class available in Java or our library. The one we create doesn't need
to interact with Java, so we'll create it like a normal class in C#.
By adding a Point.cs file with this class, it will end up in the binding library:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}

View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}</ProjectGuid>
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{77efb91c-a7e9-4b0e-a7c5-31eeec3c6d46}</TemplateGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BiometricBinding</RootNamespace>
<AssemblyName>BiometricBinding</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
<AndroidClassParser>class-parse</AndroidClassParser>
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Jars\AboutJars.txt" />
<None Include="Additions\AboutAdditions.txt" />
<LibraryProjectZip Include="Jars\biometric-1.0.0-rc02.aar" />
</ItemGroup>
<ItemGroup>
<TransformFile Include="Transforms\Metadata.xml" />
<TransformFile Include="Transforms\EnumFields.xml" />
<TransformFile Include="Transforms\EnumMethods.xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.AndroidX.Fragment">
<Version>1.0.0-preview02</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,24 @@
This directory is for Android .jars.
There are 2 types of jars that are supported:
== Input Jar ==
This is the jar that bindings should be generated for.
For example, if you were binding the Google Maps library, this would
be Google's "maps.jar".
Set the build action for these jars in the properties page to "InputJar".
== Reference Jars ==
These are jars that are referenced by the input jar. C# bindings will
not be created for these jars. These jars will be used to resolve
types used by the input jar.
NOTE: Do not add "android.jar" as a reference jar. It will be added automatically
based on the Target Framework selected.
Set the build action for these jars in the properties page to "ReferenceJar".

Binary file not shown.

View File

@@ -0,0 +1,30 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("BiometricBinding")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BiometricBinding")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,14 @@
<enum-field-mappings>
<!--
This example converts the constants Fragment_id, Fragment_name,
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
to an enum called Android.Support.V4.App.FragmentTagType with values
Id, Name, and Tag.
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
<field jni-name="Fragment_name" clr-name="Name" value="0" />
<field jni-name="Fragment_id" clr-name="Id" value="1" />
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
</mapping>
-->
</enum-field-mappings>

View File

@@ -0,0 +1,13 @@
<enum-method-mappings>
<!--
This example changes the Java method:
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
to be:
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
when bound in C#.
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
</mapping>
-->
</enum-method-mappings>

View File

@@ -0,0 +1,9 @@
<metadata>
<!--
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />
This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
-->
</metadata>

View File

@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2009
# Visual Studio Version 16
VisualStudioVersion = 16.0.29418.71
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePassLib2Android", "KeePassLib2Android\KeePassLib2Android.csproj", "{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}"
EndProject
@@ -29,6 +29,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBindings\PCloudBindings.csproj", "{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BiometricBinding", "BiometricBinding\BiometricBinding.csproj", "{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -315,6 +317,30 @@ Global
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Win32.Build.0 = ReleaseNoNet|Any CPU
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.Build.0 = ReleaseNoNet|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Debug|Win32.ActiveCfg = Debug|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Debug|Win32.Build.0 = Debug|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Debug|x64.ActiveCfg = Debug|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Debug|x64.Build.0 = Debug|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Release|Any CPU.Build.0 = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Release|Win32.ActiveCfg = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Release|Win32.Build.0 = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Release|x64.ActiveCfg = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.Release|x64.Build.0 = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{2B1DE455-BF8E-4F8A-87BE-AE7EA354F3E4}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -26,6 +26,7 @@ import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region.Op;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
@@ -774,7 +775,9 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx
mKeyboardChanged = false;
}
final Canvas canvas = mCanvas;
canvas.clipRect(mDirtyRect, Op.REPLACE);
canvas.save();
canvas.clipRect(new RectF(mDirtyRect));
if (mKeyboard == null) return;
@@ -907,6 +910,8 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx
}
}
canvas.restore();
mDrawPending = false;
mDirtyRect.setEmpty();
}

View File

@@ -0,0 +1,549 @@
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 static class Kp2aLog
{
private static bool? _logToFile;
private static object _fileLocker = new object();
public static void Log(string message)
{
if (message != null)
Android.Util.Log.Debug("KP2A", message);
if (LogToFile)
{
lock (_fileLocker)
{
try
{
using (var streamWriter = System.IO.File.AppendText(LogFilename))
{
string stringToLog = DateTime.Now + ":" + DateTime.Now.Millisecond + " -- " + message;
streamWriter.WriteLine(stringToLog);
}
}
catch (Exception e)
{
Android.Util.Log.Debug("KP2A", "Couldn't write to log file. " + e);
}
}
}
}
private static string LogFilename
{
get { return Application.Context.FilesDir.CanonicalPath + "/keepass2android.log"; }
}
private static bool LogToFile
{
get
{
if (_logToFile == null)
_logToFile = System.IO.File.Exists(LogFilename);
return (bool)_logToFile;
}
}
public static event EventHandler<Exception> OnUnexpectedError;
public static void LogUnexpectedError(Exception exception)
{
Log(exception.ToString());
if (OnUnexpectedError != null)
OnUnexpectedError(null, exception);
}
public static void CreateLogFile()
{
if (!System.IO.File.Exists(LogFilename))
{
System.IO.File.Create(LogFilename).Dispose();
_logToFile = true;
}
}
public static void FinishLogFile()
{
if (System.IO.File.Exists(LogFilename))
{
_logToFile = false;
int count = 0;
while (System.IO.File.Exists(LogFilename + "." + count))
count++;
System.IO.File.Move(LogFilename, LogFilename + "." + count);
}
}
public static void SendLog(Context ctx)
{
if (!System.IO.File.Exists(LogFilename))
return;
Intent sendIntent = new Intent();
sendIntent.SetAction(Intent.ActionSend);
sendIntent.PutExtra(Intent.ExtraText, File.ReadAllText(LogFilename));
sendIntent.PutExtra(Intent.ExtraEmail, "crocoapps@gmail.com");
sendIntent.PutExtra(Intent.ExtraSubject, "Keepass2Android log");
sendIntent.SetType("text/plain");
ctx.StartActivity(Intent.CreateChooser(sendIntent, "Send log to..."));
}
}
public interface IBiometricAuthCallback
{
void OnBiometricAuthSucceeded();
void OnBiometricError(string toString);
}
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();
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;
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)
{
StartListening(new BiometricAuthCallbackAdapter(callback, _activity));
}
public void StopListening()
{
}
public bool HasUserInterface
{
get { return true; }
}
public void StartListening(BiometricPrompt.AuthenticationCallback callback)
{
Kp2aLog.Log("Fingerprint: StartListening ");
var executor = Executors.NewSingleThreadExecutor();
_biometricPrompt = new Androidx.Biometric.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))
.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 key = _keystore.GetKey(GetAlias(_keyId), 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, 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 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);
_keyGen.Init(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)
.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;
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)
{
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricError(errString.ToString()));
}
public override void OnAuthenticationFailed()
{
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricError(_context.Resources.GetString(Resource.String.fingerprint_not_recognized)));
}
}
}

View File

@@ -15,7 +15,7 @@ using Android.Widget;
namespace keepass2android
{
[Activity(Label = AppNames.AppName, Theme = "@style/MyTheme_ActionBar", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]
public class CloseImmediatelyActivity : AppCompatActivity
public class CloseImmediatelyActivity : AndroidX.AppCompat.App.AppCompatActivity
{
protected override void OnResume()
{

View File

@@ -284,7 +284,7 @@ namespace keepass2android
var listView = FindViewById<ListView>(Android.Resource.Id.List);
listView.Adapter = _adapter;
SetSupportActionBar(FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar));
SetSupportActionBar(FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar));
FindViewById<Button>(Resource.Id.add_child_db_button).Click += (sender, args) =>
{

View File

@@ -21,7 +21,7 @@ namespace keepass2android
[Activity(Label = "@string/app_name",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_ActionBar")]
public class CreateDatabaseActivity : AppCompatActivity
public class CreateDatabaseActivity : AndroidX.AppCompat.App.AppCompatActivity
{
private IOConnectionInfo _ioc;
private string _keyfileFilename;

View File

@@ -23,7 +23,7 @@ using Object = Java.Lang.Object;
namespace keepass2android
{
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_Blue")]
public class FileStorageSelectionActivity : AppCompatActivity
public class FileStorageSelectionActivity : AndroidX.AppCompat.App.AppCompatActivity
{
private readonly ActivityDesign _design;
@@ -238,7 +238,7 @@ namespace keepass2android
SetContentView(Resource.Layout.filestorage_selection);
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar);
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar);
SetSupportActionBar(toolbar);

View File

@@ -1,472 +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.Hardware.Fingerprints;
using Android.OS;
using Android.Security.Keystore;
using Android.Preferences;
using Android.Util;
using Android.Widget;
using Java.IO;
using Java.Security.Cert;
using Javax.Crypto.Spec;
namespace keepass2android
{
public interface IFingerprintAuthCallback
{
void OnFingerprintAuthSucceeded();
void OnFingerprintError(string toString);
}
public class FingerprintModule
{
public Context Context { get; set; }
public FingerprintModule (Context context)
{
Context = context;
}
public FingerprintManager FingerprintManager
{
get { return Context.GetSystemService(Context.FingerprintService) as FingerprintManager; }
}
public KeyguardManager KeyguardManager
{
get
{
return (KeyguardManager) Context.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 InputMethodManager InputMethodManager
{
get { return (InputMethodManager) Context.GetSystemService(Context.InputMethodService); }
}
public ISharedPreferences SharedPreferences
{
get { return PreferenceManager.GetDefaultSharedPreferences(Context); }
}
}
public abstract class FingerprintCrypt: FingerprintManager.AuthenticationCallback, IFingerprintIdentifier
{
protected const string FailedToInitCipher = "Failed to init Cipher";
public override void OnAuthenticationError(FingerprintState errorCode, ICharSequence errString)
{
Kp2aLog.Log("FP: OnAuthenticationError: " + errString + ", " + _selfCancelled);
if (!_selfCancelled)
_callback.OnAuthenticationError(errorCode, errString);
}
public override void OnAuthenticationFailed()
{
Kp2aLog.Log("FP: OnAuthenticationFailed " + _selfCancelled);
_callback.OnAuthenticationFailed();
}
public override void OnAuthenticationHelp(FingerprintState helpCode, ICharSequence helpString)
{
_callback.OnAuthenticationHelp(helpCode, helpString);
}
public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)
{
Kp2aLog.Log("FP: OnAuthenticationSucceeded ");
StopListening();
_callback.OnAuthenticationSucceeded(result);
}
protected readonly string _keyId;
protected Cipher _cipher;
private bool _selfCancelled;
private CancellationSignal _cancellationSignal;
protected FingerprintManager.CryptoObject _cryptoObject;
private FingerprintManager.AuthenticationCallback _callback;
protected KeyStore _keystore;
private FingerprintManager _fingerprintManager;
public FingerprintCrypt(FingerprintModule fingerprint, string keyId)
{
Kp2aLog.Log("FP: Create " + this.GetType().Name);
_keyId = keyId;
_cipher = fingerprint.Cipher;
_keystore = fingerprint.Keystore;
_fingerprintManager = fingerprint.FingerprintManager;
}
public abstract bool Init();
protected static string GetAlias(string keyId)
{
return "keepass2android." + keyId;
}
protected static string GetIvPrefKey(string prefKey)
{
return prefKey + "_iv";
}
public bool IsFingerprintAuthAvailable
{
get
{
return _fingerprintManager.IsHardwareDetected
&& _fingerprintManager.HasEnrolledFingerprints;
}
}
public void StartListening(Context ctx, IFingerprintAuthCallback callback)
{
StartListening(new FingerprintAuthCallbackAdapter(callback, ctx));
}
public void StartListening(FingerprintManager.AuthenticationCallback callback)
{
if (!IsFingerprintAuthAvailable)
return;
Kp2aLog.Log("FP: StartListening ");
var thisSignal = new CancellationSignal();
_cancellationSignal = thisSignal;
_cancellationSignal.CancelEvent += (sender, args) =>
{
if (_cancellationSignal == thisSignal) _cancellationSignal = null;
};
_selfCancelled = false;
_callback = callback;
_fingerprintManager.Authenticate(_cryptoObject, _cancellationSignal, 0 /* flags */, this, null);
}
public void StopListening()
{
if (_cancellationSignal != null)
{
Kp2aLog.Log("FP: StopListening ");
_selfCancelled = true;
try
{
_cancellationSignal.Cancel();
}
catch (System.Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
_cancellationSignal = null;
}
}
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 IFingerprintIdentifier
{
bool Init();
void StartListening(Context ctx, IFingerprintAuthCallback callback);
void StopListening();
}
public class FingerprintDecryption : FingerprintCrypt
{
private readonly Context _context;
private readonly byte[] _iv;
public FingerprintDecryption(FingerprintModule fingerprint, string keyId, byte[] iv) : base(fingerprint, keyId)
{
_iv = iv;
}
public FingerprintDecryption(FingerprintModule fingerprint, string keyId, Context context, string prefKey)
: base(fingerprint, 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 key = _keystore.GetKey(GetAlias(_keyId), null);
var ivParams = new IvParameterSpec(_iv);
_cipher.Init(CipherMode.DecryptMode, key, ivParams);
_cryptoObject = new FingerprintManager.CryptoObject(_cipher);
return true;
}
catch (KeyPermanentlyInvalidatedException e)
{
Kp2aLog.Log("FP: KeyPermanentlyInvalidatedException." + e.ToString());
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 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 FingerprintEncryption : FingerprintCrypt
{
private KeyGenerator _keyGen;
public FingerprintEncryption(FingerprintModule fingerprint, string keyId) :
base(fingerprint, keyId)
{
_keyGen = fingerprint.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 fingerprint.
/// </summary>
private void CreateKey()
{
try
{
_keystore.Load(null);
_keyGen.Init(new KeyGenParameterSpec.Builder(GetAlias(_keyId),
KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
.SetBlockModes(KeyProperties.BlockModeCbc)
// Require the user to authenticate with a fingerprint to authorize every use
// of the key
.SetUserAuthenticationRequired(true)
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingPkcs7)
.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);
}
}
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 FingerprintManager.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 FingerprintAuthCallbackAdapter : FingerprintManager.AuthenticationCallback
{
private readonly IFingerprintAuthCallback _callback;
private readonly Context _context;
public FingerprintAuthCallbackAdapter(IFingerprintAuthCallback callback, Context context)
{
_callback = callback;
_context = context;
}
public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)
{
_callback.OnFingerprintAuthSucceeded();
}
public override void OnAuthenticationError(FingerprintState errorCode, ICharSequence errString)
{
_callback.OnFingerprintError(errString.ToString());
}
public override void OnAuthenticationHelp(FingerprintState helpCode, ICharSequence helpString)
{
_callback.OnFingerprintError(helpString.ToString());
}
public override void OnAuthenticationFailed()
{
_callback.OnFingerprintError(
_context.Resources.GetString(Resource.String.fingerprint_not_recognized));
}
}
}

View File

@@ -5,6 +5,7 @@ using System.Text;
using Android.App;
using Android.Content;
using Android.Hardware.Biometrics;
using Android.OS;
using Android.Runtime;
using Android.Util;
@@ -16,13 +17,15 @@ using Java.Lang;
namespace keepass2android
{
class FingerprintSamsungIdentifier: IFingerprintIdentifier
class BiometrySamsungIdentifier: IBiometricIdentifier
{
SpassFingerprint _spassFingerprint;
private readonly Context _context;
SpassFingerprint _spassFingerprint;
Spass _spass;
public FingerprintSamsungIdentifier(Context context)
public BiometrySamsungIdentifier(Context context)
{
_spass = new Spass();
_context = context;
_spass = new Spass();
try
{
@@ -61,12 +64,12 @@ namespace keepass2android
}
class IdentifyListener : Java.Lang.Object, IIdentifyListener
{
private readonly IFingerprintAuthCallback _callback;
private readonly IBiometricAuthCallback _callback;
private readonly Context _context;
private readonly FingerprintSamsungIdentifier _id;
private readonly BiometrySamsungIdentifier _id;
public IdentifyListener(IFingerprintAuthCallback callback, Context context, FingerprintSamsungIdentifier id)
public IdentifyListener(IBiometricAuthCallback callback, Context context, BiometrySamsungIdentifier id)
{
_callback = callback;
_context = context;
@@ -80,11 +83,11 @@ namespace keepass2android
_id.Listening = false;
if (responseCode == SpassFingerprint.StatusAuthentificationSuccess)
{
_callback.OnFingerprintAuthSucceeded();
_callback.OnBiometricAuthSucceeded();
}
else if (responseCode == SpassFingerprint.StatusAuthentificationPasswordSuccess)
{
_callback.OnFingerprintAuthSucceeded();
_callback.OnBiometricAuthSucceeded();
}
}
@@ -106,22 +109,22 @@ namespace keepass2android
}
public void StartListening(Context ctx, IFingerprintAuthCallback callback)
public void StartListening(IBiometricAuthCallback callback)
{
if (Listening) return;
try
{
_spassFingerprint.StartIdentifyWithDialog(ctx, new IdentifyListener(callback, ctx, this), false);
_spassFingerprint.StartIdentifyWithDialog(_context, new IdentifyListener(callback, _context, this), false);
Listening = true;
}
catch (SpassInvalidStateException m)
{
callback.OnFingerprintError(m.Message);
callback.OnBiometricError(m.Message);
}
catch (IllegalStateException ex)
{
callback.OnFingerprintError(ex.Message);
callback.OnBiometricError(ex.Message);
}
}
@@ -141,5 +144,10 @@ namespace keepass2android
Kp2aLog.LogUnexpectedError(e);
}
}
}
public bool HasUserInterface
{
get { return false; }
}
}
}

View File

@@ -15,6 +15,7 @@ using Android.Widget;
using Java.Lang;
using KeePassLib.Keys;
using KeePassLib.Utility;
using Kotlin.Text;
using Enum = System.Enum;
using Exception = System.Exception;
@@ -24,16 +25,16 @@ namespace keepass2android
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_ActionBar", MainLauncher = false)]
[IntentFilter(new[] { "kp2a.action.FingerprintSetupActivity" }, Categories = new[] { Intent.CategoryDefault })]
public class FingerprintSetupActivity : LockCloseActivity, IFingerprintAuthCallback
public class BiometricSetupActivity : LockCloseActivity, IBiometricAuthCallback
{
private readonly ActivityDesign _activityDesign;
public FingerprintSetupActivity(IntPtr javaReference, JniHandleOwnership transfer)
public BiometricSetupActivity(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
_activityDesign = new ActivityDesign(this);
}
public FingerprintSetupActivity()
public BiometricSetupActivity()
{
_activityDesign = new ActivityDesign(this);
}
@@ -42,7 +43,7 @@ namespace keepass2android
private FingerprintUnlockMode _unlockMode = FingerprintUnlockMode.Disabled;
private FingerprintUnlockMode _desiredUnlockMode;
private FingerprintEncryption _enc;
private BiometricEncryption _enc;
private RadioButton[] _radioButtons;
public override bool OnOptionsItemSelected(IMenuItem item)
{
@@ -70,6 +71,7 @@ namespace keepass2android
_fpIcon = FindViewById<ImageView>(Resource.Id.fingerprint_icon);
_fpTextView = FindViewById<TextView>(Resource.Id.fingerprint_status);
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetHomeButtonEnabled(true);
@@ -128,14 +130,8 @@ namespace keepass2android
.PutBoolean(GetString(Resource.String.ShowKeyboardWhileFingerprint_key), args.IsChecked)
.Commit();
};
if ((int)Build.VERSION.SdkInt >= 23)
RequestPermissions(new[] {Manifest.Permission.UseFingerprint}, FingerprintPermissionRequestCode);
else
{
TrySetupSamsung();
}
UpdateKeyboardCheckboxVisibility();
@@ -143,10 +139,7 @@ namespace keepass2android
private void UpdateKeyboardCheckboxVisibility()
{
FindViewById(Resource.Id.show_keyboard_while_fingerprint).Visibility = (_unlockMode == FingerprintUnlockMode.Disabled) ||
(_samsungFingerprint != null)
? ViewStates.Gone
: ViewStates.Visible;
FindViewById(Resource.Id.show_keyboard_while_fingerprint).Visibility = ViewStates.Gone;
}
private bool TrySetupSamsung()
@@ -154,20 +147,20 @@ namespace keepass2android
try
{
//try to create a Samsung ID object
_samsungFingerprint = new FingerprintSamsungIdentifier(this);
if (!_samsungFingerprint.Init())
_samsungBiometry = new BiometrySamsungIdentifier(this);
if (!_samsungBiometry.Init())
{
SetError(Resource.String.fingerprint_no_enrolled);
}
ShowRadioButtons();
FindViewById(Resource.Id.container_fingerprint_unlock).Visibility = _samsungFingerprint == null
FindViewById(Resource.Id.container_fingerprint_unlock).Visibility = _samsungBiometry == null
? ViewStates.Visible
: ViewStates.Gone;
return true;
}
catch (Exception)
{
_samsungFingerprint = null;
_samsungBiometry = null;
return false;
}
}
@@ -231,30 +224,7 @@ namespace keepass2android
tv.Visibility = ViewStates.Visible;
}
const int FingerprintPermissionRequestCode = 0;
public override void OnRequestPermissionsResult (int requestCode, string[] permissions, Permission[] grantResults)
{
if (requestCode == FingerprintPermissionRequestCode && grantResults[0] == Permission.Granted)
{
FingerprintModule fpModule = new FingerprintModule(this);
if (fpModule.FingerprintManager == null || (!fpModule.FingerprintManager.IsHardwareDetected))
{
//seems like not all Samsung Devices (e.g. Note 4) don't support the Android 6 fingerprint API
if (!TrySetupSamsung())
SetError(Resource.String.fingerprint_hardware_error);
UpdateKeyboardCheckboxVisibility();
return;
}
if (!fpModule.FingerprintManager.HasEnrolledFingerprints)
{
SetError(Resource.String.fingerprint_no_enrolled);
return;
}
ShowRadioButtons();
UpdateKeyboardCheckboxVisibility();
}
}
private void ShowRadioButtons()
{
FindViewById<TextView>(Resource.Id.tvFatalError).Visibility = ViewStates.Gone;
@@ -262,14 +232,21 @@ namespace keepass2android
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)
private void ChangeUnlockMode(FingerprintUnlockMode oldMode, FingerprintUnlockMode newMode)
{
if (oldMode == newMode)
return;
if (_samsungFingerprint != null)
if (_samsungBiometry != null)
{
_unlockMode = newMode;
UpdateKeyboardCheckboxVisibility();
@@ -294,14 +271,15 @@ namespace keepass2android
FindViewById(Resource.Id.show_keyboard_while_fingerprint).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Visible;
_enc = new FingerprintEncryption(new FingerprintModule(this), CurrentPreferenceKey);
try
try
{
if (!_enc.Init())
_enc = new BiometricEncryption(new BiometricModule(this), CurrentPreferenceKey);
if (!_enc.Init())
throw new Exception("Failed to initialize cipher");
ResetErrorTextRunnable();
_enc.StartListening(new FingerprintAuthCallbackAdapter(this, this));
}
_enc.StartListening(new BiometricAuthCallbackAdapter(this, this));
}
catch (Exception e)
{
CheckCurrentRadioButton();
@@ -318,9 +296,9 @@ namespace keepass2android
private ImageView _fpIcon;
private TextView _fpTextView;
private FingerprintSamsungIdentifier _samsungFingerprint;
private BiometrySamsungIdentifier _samsungBiometry;
public void OnFingerprintAuthSucceeded()
public void OnBiometricAuthSucceeded()
{
_unlockMode = _desiredUnlockMode;
@@ -344,7 +322,7 @@ namespace keepass2android
public void OnFingerprintError(string error)
public void OnBiometricError(string error)
{
_fpIcon.SetImageResource(Resource.Drawable.ic_fingerprint_error);
_fpTextView.Text = error;
@@ -365,9 +343,26 @@ namespace keepass2android
protected override void OnResume()
{
base.OnResume();
if (_enc != null)
_enc.StartListening(new FingerprintAuthCallbackAdapter(this, this));
}
BiometricModule fpModule = new BiometricModule(this);
HideRadioButtons();
if (!fpModule.IsHardwareAvailable)
{
//seems like not all Samsung Devices (e.g. Note 4) don't support the Android 6 fingerprint API
if (!TrySetupSamsung())
SetError(Resource.String.fingerprint_hardware_error);
UpdateKeyboardCheckboxVisibility();
return;
}
if (!fpModule.IsAvailable)
{
SetError(Resource.String.fingerprint_no_enrolled);
return;
}
ShowRadioButtons();
UpdateKeyboardCheckboxVisibility();
}
protected override void OnPause()
{

View File

@@ -495,7 +495,7 @@ namespace keepass2android
{
FindViewById(Resource.Id.enable_fingerprint).Click += (sender, args) =>
{
StartActivity(typeof(FingerprintSetupActivity));
StartActivity(typeof(BiometricSetupActivity));
};
}
@@ -689,8 +689,8 @@ namespace keepass2android
if (!disabledForAll && !disabledForDatabase && !App.Kp2a.IsChildDatabase(App.Kp2a.CurrentDb))
{
FingerprintModule fpModule = new FingerprintModule(this);
if (fpModule.FingerprintManager != null && fpModule.FingerprintManager.IsHardwareDetected)
BiometricModule biometricModule = new BiometricModule(this);
if (biometricModule.IsAvailable)
{
FingerprintUnlockMode um;
Enum.TryParse(_prefs.GetString(Database.GetFingerprintModePrefKey(App.Kp2a.CurrentDb.Ioc), ""), out um);

View File

@@ -24,8 +24,8 @@ using Android.Support.V7.App;
namespace keepass2android
{
public abstract class LifecycleAwareActivity : AppCompatActivity
{
public abstract class LifecycleAwareActivity : AndroidX.AppCompat.App.AppCompatActivity
{
protected LifecycleAwareActivity (IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{

View File

@@ -16,7 +16,7 @@ namespace keepass2android
{
[Activity(Label = AppNames.AppName, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_Blue",
LaunchMode = LaunchMode.SingleInstance)]
public class NoSecureDisplayActivity : AppCompatActivity
public class NoSecureDisplayActivity : AndroidX.AppCompat.App.AppCompatActivity
{
private readonly ActivityDesign _design;
@@ -35,7 +35,7 @@ namespace keepass2android
AppSettingsActivity.Launch(this);
};
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar);
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar);
SetSupportActionBar(toolbar);

View File

@@ -69,7 +69,7 @@ namespace keepass2android
LaunchMode = LaunchMode.SingleInstance,
WindowSoftInputMode = SoftInput.AdjustResize,
Theme = "@style/MyTheme_Blue")]
public class PasswordActivity : LockingActivity, IFingerprintAuthCallback
public class PasswordActivity : LockingActivity, IBiometricAuthCallback
{
enum KeyProviders
@@ -579,7 +579,7 @@ namespace keepass2android
private string mDrawerTitle;
private MeasuringRelativeLayout.MeasureArgs _measureArgs;
private ActivityDesign _activityDesign;
private FingerprintDecryption _fingerprintDec;
private BiometricDecryption _biometricDec;
private bool _fingerprintPermissionGranted;
private PasswordActivityBroadcastReceiver _intentReceiver;
private int _appnameclickCount;
@@ -759,11 +759,15 @@ namespace keepass2android
mDrawerTitle = Title;
InitializeToolbarCollapsing();
if ((int)Build.VERSION.SdkInt >= 23)
RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FingerprintPermissionRequestCode);
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.Click += (sender, args) =>
{
_biometricDec?.StartListening(this);
if (App.Kp2a.TrySelectCurrentDb(_ioConnection))
};
if (App.Kp2a.TrySelectCurrentDb(_ioConnection))
{
//database already opened. return the ioc and we're good.
LaunchNextActivity();
@@ -887,41 +891,8 @@ namespace keepass2android
}
}
const int FingerprintPermissionRequestCode = 99;
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
if ((requestCode == FingerprintPermissionRequestCode) && (grantResults.Length > 0) && (grantResults[0] == Permission.Granted))
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.Click += (sender, args) =>
{
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 (_fingerprintDec != null)
{
b.SetNegativeButton(Resource.String.disable_sensor, (senderAlert, alertArgs) =>
{
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
_fingerprintDec?.StopListening();
_fingerprintDec = null;
});
}
else
{
b.SetNegativeButton(Resource.String.enable_sensor, (senderAlert, alertArgs) =>
{
InitFingerprintUnlock();
});
}
b.Show();
};
_fingerprintPermissionGranted = true;
}
}
private void ClearFingerprintUnlockData()
{
@@ -931,7 +902,7 @@ namespace keepass2android
edit.Commit();
}
public void OnFingerprintError(string message)
public void OnBiometricError(string message)
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
@@ -944,7 +915,7 @@ namespace keepass2android
Toast.MakeText(this, message, ToastLength.Long).Show();
}
public void OnFingerprintAuthSucceeded()
public void OnBiometricAuthSucceeded()
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
@@ -952,7 +923,7 @@ namespace keepass2android
try
{
var masterPassword = _fingerprintDec.DecryptStored(Database.GetFingerprintPrefKey(_ioConnection));
var masterPassword = _biometricDec.DecryptStored(Database.GetFingerprintPrefKey(_ioConnection));
_password = FindViewById<EditText>(Resource.Id.password_edit).Text = masterPassword;
FindViewById<EditText>(Resource.Id.password_edit).Enabled = false; //prevent accidental modification of password
@@ -1020,7 +991,7 @@ namespace keepass2android
private void InitializeToolbar()
{
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar);
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar);
SetSupportActionBar(toolbar);
@@ -1524,7 +1495,7 @@ namespace keepass2android
protected override void OnPause()
{
_fingerprintDec?.StopListening();
_biometricDec?.StopListening();
_lastOnPauseTime = DateTime.Now;
base.OnPause();
@@ -1774,16 +1745,10 @@ namespace keepass2android
bool showKeyboard = (Util.GetShowKeyboardDuringFingerprintUnlock(this));
if (_fingerprintPermissionGranted)
{
if (!InitFingerprintUnlock())
showKeyboard = true;
}
else
{
FindViewById<ImageButton>(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone;
if (!InitFingerprintUnlock())
showKeyboard = true;
}
EditText pwd = (EditText) FindViewById(Resource.Id.password_edit);
@@ -1861,16 +1826,16 @@ namespace keepass2android
return false;
}
FingerprintModule fpModule = new FingerprintModule(this);
_fingerprintDec = new FingerprintDecryption(fpModule, Database.GetFingerprintPrefKey(_ioConnection), this,
BiometricModule fpModule = new BiometricModule(this);
_biometricDec = new BiometricDecryption(fpModule, Database.GetFingerprintPrefKey(_ioConnection), this,
Database.GetFingerprintPrefKey(_ioConnection));
btn.Tag = GetString(Resource.String.fingerprint_unlock_hint);
if (_fingerprintDec.Init())
if (_biometricDec.Init())
{
btn.SetImageResource(Resource.Drawable.ic_fp_40px);
_fingerprintDec.StartListening(new FingerprintAuthCallbackAdapter(this, this));
_biometricDec.StartListening(new BiometricAuthCallbackAdapter(this, this));
return true;
}
else
@@ -1891,7 +1856,7 @@ namespace keepass2android
Toast.MakeText(this, Resource.String.fingerprint_reenable2, ToastLength.Long).Show();
_fingerprintDec = null;
_biometricDec = null;
return false;
}
@@ -1904,7 +1869,7 @@ namespace keepass2android
//key invalidated permanently
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.Tag = GetString(Resource.String.fingerprint_unlock_failed) + " " + GetString(Resource.String.fingerprint_reenable2);
_fingerprintDec = null;
_biometricDec = null;
ClearFingerprintUnlockData();
}
@@ -2191,8 +2156,8 @@ namespace keepass2android
private void OnScreenLocked()
{
if (_fingerprintDec != null)
_fingerprintDec.StopListening();
if (_biometricDec != null)
_biometricDec.StopListening();
}

View File

@@ -135,6 +135,7 @@
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<!-- Samsung Pass permission -->
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />

View File

@@ -137,6 +137,8 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalSearch" />

View File

@@ -38,13 +38,13 @@ namespace keepass2android
WindowSoftInputMode = SoftInput.AdjustResize,
MainLauncher = false,
Theme = "@style/MyTheme_Blue")]
public class QuickUnlock : LifecycleAwareActivity, IFingerprintAuthCallback
public class QuickUnlock : LifecycleAwareActivity, IBiometricAuthCallback
{
private IOConnectionInfo _ioc;
private QuickUnlockBroadcastReceiver _intentReceiver;
private ActivityDesign _design;
private bool _fingerprintPermissionGranted;
private IFingerprintIdentifier _fingerprintIdentifier;
private IBiometricIdentifier _biometryIdentifier;
private int _quickUnlockLength;
private const int FingerprintPermissionRequestCode = 0;
@@ -71,7 +71,7 @@ namespace keepass2android
SetContentView(Resource.Layout.QuickUnlock);
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar);
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar);
SetSupportActionBar(toolbar);
@@ -145,15 +145,7 @@ namespace keepass2android
filter.AddAction(Intents.DatabaseLocked);
RegisterReceiver(_intentReceiver, filter);
if ((int) Build.VERSION.SdkInt >= 23)
{
Kp2aLog.Log("requesting fingerprint permission");
RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FingerprintPermissionRequestCode);
}
else
{
}
}
@@ -164,51 +156,9 @@ namespace keepass2android
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
Kp2aLog.Log("OnRequestPermissionsResult " + (requestCode == FingerprintPermissionRequestCode) +
((grantResults.Length > 0) && (grantResults[0] == Permission.Granted)));
if ((requestCode == FingerprintPermissionRequestCode) && (grantResults.Length > 0) && (grantResults[0] == Permission.Granted))
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.Click += (sender, args) =>
{
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 (_fingerprintIdentifier != null)
{
b.SetNegativeButton(Resource.String.disable_sensor, (senderAlert, alertArgs) =>
{
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
_fingerprintIdentifier?.StopListening();
_fingerprintIdentifier = null;
});
}
else
{
b.SetNegativeButton(Resource.String.enable_sensor, (senderAlert, alertArgs) =>
{
InitFingerprintUnlock();
});
}
b.Show();
};
_fingerprintPermissionGranted = true;
Kp2aLog.Log("_fingerprintPermissionGranted");
if (_onResumeDone)
{
//it seems the permission result is called after onResume sometimes. Repeat fingerprint unlock then.
InitFingerprintUnlock();
}
}
}
bool _onResumeDone = false;
public void OnFingerprintError(string message)
public void OnBiometricError(string message)
{
Kp2aLog.Log("fingerprint error: " + message);
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
@@ -222,10 +172,10 @@ namespace keepass2android
Toast.MakeText(this, message, ToastLength.Long).Show();
}
public void OnFingerprintAuthSucceeded()
public void OnBiometricAuthSucceeded()
{
Kp2aLog.Log("OnFingerprintAuthSucceeded");
_fingerprintIdentifier.StopListening();
_biometryIdentifier.StopListening();
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.SetImageResource(Resource.Drawable.ic_fingerprint_success);
@@ -246,7 +196,7 @@ namespace keepass2android
{
Kp2aLog.Log("InitFingerprintUnlock");
if (_fingerprintIdentifier != null)
if (_biometryIdentifier != null)
{
Kp2aLog.Log("Already listening for fingerprint!");
return true;
@@ -262,49 +212,47 @@ namespace keepass2android
if (um == FingerprintUnlockMode.Disabled)
{
_fingerprintIdentifier = null;
_biometryIdentifier = null;
return false;
}
if (_fingerprintPermissionGranted)
{
FingerprintModule fpModule = new FingerprintModule(this);
Kp2aLog.Log("fpModule.FingerprintManager.IsHardwareDetected=" + fpModule.FingerprintManager.IsHardwareDetected);
if (fpModule.FingerprintManager.IsHardwareDetected) //see FingerprintSetupActivity
_fingerprintIdentifier = new FingerprintDecryption(fpModule, App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey, this,
App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey);
}
if ((_fingerprintIdentifier == null) && (!FingerprintDecryption.IsSetUp(this, App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey)))
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) && (!BiometricDecryption.IsSetUp(this, App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey)))
{
try
{
Kp2aLog.Log("trying Samsung Fingerprint API...");
_fingerprintIdentifier = new FingerprintSamsungIdentifier(this);
_biometryIdentifier = new BiometrySamsungIdentifier(this);
btn.Click += (sender, args) =>
{
if (_fingerprintIdentifier.Init())
_fingerprintIdentifier.StartListening(this, this);
if (_biometryIdentifier.Init())
_biometryIdentifier.StartListening(this);
};
Kp2aLog.Log("trying Samsung Fingerprint API...Seems to work!");
}
catch (Exception)
{
Kp2aLog.Log("trying Samsung Fingerprint API...failed.");
_fingerprintIdentifier = null;
_biometryIdentifier = null;
}
}
if (_fingerprintIdentifier == null)
if (_biometryIdentifier == null)
{
FindViewById<ImageButton>(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone;
return false;
}
btn.Tag = GetString(Resource.String.fingerprint_unlock_hint);
if (_fingerprintIdentifier.Init())
if (_biometryIdentifier.Init())
{
Kp2aLog.Log("successfully initialized fingerprint.");
btn.SetImageResource(Resource.Drawable.ic_fp_40px);
_fingerprintIdentifier.StartListening(this, this);
_biometryIdentifier.StartListening(this);
return true;
}
else
@@ -319,7 +267,7 @@ namespace keepass2android
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.Tag = "Error initializing Fingerprint Unlock: " + e;
_fingerprintIdentifier = null;
_biometryIdentifier = null;
}
return false;
@@ -331,7 +279,7 @@ namespace keepass2android
//key invalidated permanently
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.Tag = GetString(Resource.String.fingerprint_unlock_failed) + " " + GetString(Resource.String.fingerprint_reenable2);
_fingerprintIdentifier = null;
_biometryIdentifier = null;
}
private void OnUnlock(int quickUnlockLength, EditText pwd)
@@ -393,25 +341,59 @@ namespace keepass2android
keyboard.HideSoftInputFromWindow(pwd.WindowToken, HideSoftInputFlags.ImplicitOnly);
}, 50);
_onResumeDone = true;
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.Click += (sender, args) =>
{
if (_biometryIdentifier.HasUserInterface)
{
_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 (_fingerprintIdentifier != null)
if (_biometryIdentifier != null && !_biometryIdentifier.HasUserInterface)
{
Kp2aLog.Log("FP: Stop listening");
_fingerprintIdentifier.StopListening();
_fingerprintIdentifier = null;
}
_biometryIdentifier.StopListening();
}
base.OnPause();
}

View File

@@ -92,7 +92,7 @@ android:fitsSystemWindows="true">
android:textSize="10sp"
android:text="/storage/emulated/0/keepass/keepass/database.kdbx" />
</RelativeLayout>
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/mytoolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"

View File

@@ -6,7 +6,7 @@
android:fitsSystemWindows="true"
android:layout_height="fill_parent"
android:gravity="center_horizontal">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/mytoolbar"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"

View File

@@ -7,7 +7,7 @@
android:fitsSystemWindows="true"
android:layout_height="fill_parent"
android:gravity="center_horizontal">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/mytoolbar"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"

View File

@@ -8,7 +8,7 @@
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:paddingTop="16dp">
<android.support.v7.widget.Toolbar android:id="@+id/mytoolbar"
<androidx.appcompat.widget.Toolbar android:id="@+id/mytoolbar"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:titleTextStyle="@style/MyTitleTextStyle"

View File

@@ -7,7 +7,7 @@
android:fitsSystemWindows="true"
android:layout_height="fill_parent"
android:gravity="center_horizontal">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/mytoolbar"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"

View File

@@ -89,7 +89,7 @@
android:textSize="10sp"
android:text="/storage/emulated/0/keepass/keepass/database.kdbx" />
</RelativeLayout>
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/mytoolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="@+id/mytoolbar"

View File

@@ -29,7 +29,7 @@ using Object = Java.Lang.Object;
namespace keepass2android
{
[Activity(Label = AppNames.AppName, MainLauncher = false, Theme = "@style/MyTheme_Blue", LaunchMode = LaunchMode.SingleInstance)] //caution, see manifest file
public class SelectCurrentDbActivity : AppCompatActivity
public class SelectCurrentDbActivity : AndroidX.AppCompat.App.AppCompatActivity
{
private int ReqCodeOpenNewDb = 1;
@@ -188,7 +188,7 @@ namespace keepass2android
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.open_db_selection);
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar);
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar);
SetSupportActionBar(toolbar);

View File

@@ -114,7 +114,7 @@ namespace keepass2android
edit.Commit();
Toast.MakeText(_dlg.Context, Resource.String.fingerprint_reenable, ToastLength.Long).Show();
_dlg.Context.StartActivity(typeof(FingerprintSetupActivity));
_dlg.Context.StartActivity(typeof(BiometricSetupActivity));
}
_dlg.Dismiss();

View File

@@ -44,7 +44,7 @@ namespace keepass2android
ConfigurationChanges=ConfigChanges.Orientation|
ConfigChanges.KeyboardHidden,
Theme = "@style/MyTheme_Blue")]
public class FileSelectActivity : AppCompatActivity
public class FileSelectActivity : AndroidX.AppCompat.App.AppCompatActivity
{
private readonly ActivityDesign _design;
public FileSelectActivity (IntPtr javaReference, JniHandleOwnership transfer)

View File

@@ -162,7 +162,7 @@
<Compile Include="EntryActivityClasses\ToggleVisibilityPopupMenuItem.cs" />
<Compile Include="EntryActivityClasses\WriteBinaryToFilePopupItem.cs" />
<Compile Include="FileSelectHelper.cs" />
<Compile Include="FingerprintModule.cs" />
<Compile Include="BiometricModule.cs" />
<Compile Include="FingerprintSamsungIdentifier.cs" />
<Compile Include="FingerprintSetupActivity.cs" />
<Compile Include="ExportDatabaseActivity.cs" />
@@ -808,6 +808,10 @@
<Folder Include="SupportLib\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BiometricBinding\BiometricBinding.csproj">
<Project>{2b1de455-bf8e-4f8a-87be-ae7ea354f3e4}</Project>
<Name>BiometricBinding</Name>
</ProjectReference>
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
<Project>{48574278-4779-4B3A-A9E4-9CF1BC285D0B}</Project>
<Name>JavaFileStorageBindings</Name>
@@ -2018,9 +2022,21 @@
<PackageReference Include="Xamarin.Android.Support.ViewPager">
<Version>28.0.0.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.Browser">
<Version>1.0.0-preview02</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V13">
<Version>1.0.0-preview02</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.Migration">
<Version>1.0.0-preview05</Version>
</PackageReference>
<PackageReference Include="Xamarin.Build.Download">
<Version>0.4.2</Version>
</PackageReference>
<PackageReference Include="Xamarin.Google.Android.Material">
<Version>1.0.0-preview02</Version>
</PackageReference>
<PackageReference Include="Xamarin.GooglePlayServices.Drive">
<Version>27.0.0</Version>
</PackageReference>

View File

@@ -16,7 +16,7 @@ using System.Linq;
namespace keepass2android.services.AutofillBase
{
public abstract class ChooseForAutofillActivityBase : AppCompatActivity
public abstract class ChooseForAutofillActivityBase : AndroidX.AppCompat.App.AppCompatActivity
{
protected Intent ReplyIntent;

View File

@@ -110,7 +110,7 @@ namespace keepass2android
SetContentView(Resource.Layout.preference);
SetSupportActionBar(FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar));
SetSupportActionBar(FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar));
FragmentManager.FindFragmentById<SettingsFragment>(Resource.Id.settings_fragment).FindPreference(GetString(Resource.String.db_key)).Enabled = false;

View File

@@ -843,7 +843,7 @@ namespace keepass2android
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.preference);
SetSupportActionBar(FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar));
SetSupportActionBar(FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.mytoolbar));
}