(R.id.fingerprint_setting_way_text)
+ fingerprintSettingWayTextView.setOnClickListener { startActivity(Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS)) }
+
+ fingerPrintAnimatedVector = FingerPrintAnimatedVector(activity,
+ rootView.findViewById(R.id.fingerprint_image))
+
+ builder.setView(rootView)
+ .setPositiveButton(android.R.string.ok) { _, _ -> }
+ return builder.create()
+ }
+ return super.onCreateDialog(savedInstanceState)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ fingerPrintAnimatedVector?.startScan()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ fingerPrintAnimatedVector?.stopScan()
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/fingerprint/FingerPrintHelper.java b/app/src/main/java/com/kunzisoft/keepass/fingerprint/FingerPrintHelper.java
deleted file mode 100644
index 763adbcdd..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/fingerprint/FingerPrintHelper.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright 2017 Hans Cappelle
- *
- * This file is part of KeePassDroid.
- *
- * KeePassDroid 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.
- *
- * KeePassDroid 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 KeePassDroid. If not, see .
- *
- */
-package com.kunzisoft.keepass.fingerprint;
-
-import android.annotation.SuppressLint;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.hardware.fingerprint.FingerprintManager;
-import android.os.Build;
-import android.os.CancellationSignal;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyPermanentlyInvalidatedException;
-import android.security.keystore.KeyProperties;
-import android.support.annotation.RequiresApi;
-import android.util.Base64;
-import android.util.Log;
-
-import java.io.IOException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-
-@RequiresApi(api = Build.VERSION_CODES.M)
-public class FingerPrintHelper {
-
- private static final String TAG = FingerPrintHelper.class.getName();
-
- private static final String FINGERPRINT_KEYSTORE_KEY = "com.kunzisoft.keepass.fingerprint.key";
-
- private FingerprintManager fingerprintManager;
- private KeyStore keyStore = null;
- private KeyGenerator keyGenerator = null;
- private Cipher cipher = null;
- private KeyguardManager keyguardManager = null;
- private FingerprintManager.CryptoObject cryptoObject = null;
-
- private boolean initOk = false;
- private FingerPrintCallback fingerPrintCallback;
- private CancellationSignal cancellationSignal;
- private FingerprintManager.AuthenticationCallback authenticationCallback;
-
- public void setAuthenticationCallback(final FingerprintManager.AuthenticationCallback authenticationCallback) {
- this.authenticationCallback = authenticationCallback;
- }
-
- public synchronized void startListening() {
- // starts listening for fingerprints with the initialised crypto object
- cancellationSignal = new CancellationSignal();
- fingerprintManager.authenticate(
- cryptoObject,
- cancellationSignal,
- 0 /* flags */,
- authenticationCallback,
- null);
- }
-
- public synchronized void stopListening() {
- if (!isFingerprintInitialized(false)) {
- return;
- }
- if (cancellationSignal != null) {
- cancellationSignal.cancel();
- cancellationSignal = null;
- }
- }
-
- public FingerPrintHelper(
- final Context context,
- final FingerPrintCallback fingerPrintCallback) {
-
- this.fingerprintManager = context.getSystemService(FingerprintManager.class);
- if (!isFingerprintSupported(fingerprintManager)) {
- // really not much to do when no fingerprint support found
- setInitOk(false);
- return;
- }
- this.keyguardManager = (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);
- this.fingerPrintCallback = fingerPrintCallback;
-
- if (hasEnrolledFingerprints()) {
- try {
- this.keyStore = KeyStore.getInstance("AndroidKeyStore");
- this.keyGenerator = KeyGenerator.getInstance(
- KeyProperties.KEY_ALGORITHM_AES,
- "AndroidKeyStore");
- this.cipher = Cipher.getInstance(
- KeyProperties.KEY_ALGORITHM_AES + "/"
- + KeyProperties.BLOCK_MODE_CBC + "/"
- + KeyProperties.ENCRYPTION_PADDING_PKCS7);
- this.cryptoObject = new FingerprintManager.CryptoObject(cipher);
- setInitOk(true);
- } catch (final Exception e) {
- Log.e(TAG, "Unable to initialize the keystore", e);
- setInitOk(false);
- fingerPrintCallback.onFingerPrintException(e);
- }
- }
- }
-
- public static boolean isFingerprintSupported(FingerprintManager fingerprintManager) {
- return fingerprintManager != null
- && fingerprintManager.isHardwareDetected();
- }
-
- public boolean isFingerprintInitialized() {
- return isFingerprintInitialized(true);
- }
-
- private boolean isFingerprintInitialized(boolean throwException) {
- boolean isFingerprintInit = hasEnrolledFingerprints() && initOk;
- if (!isFingerprintInit && fingerPrintCallback != null) {
- if(throwException)
- fingerPrintCallback.onFingerPrintException(new Exception("FingerPrint not initialized"));
- }
- return isFingerprintInit;
- }
-
- public void initEncryptData() {
- if (!isFingerprintInitialized()) {
- return;
- }
- try {
- stopListening();
-
- createNewKeyIfNeeded(false); // no need to keep deleting existing keys
- keyStore.load(null);
- final SecretKey key = (SecretKey) keyStore.getKey(FINGERPRINT_KEYSTORE_KEY, null);
- cipher.init(Cipher.ENCRYPT_MODE, key);
-
- startListening();
- } catch (final UnrecoverableKeyException unrecoverableKeyException) {
- Log.e(TAG, "Unable to initialize encrypt data", unrecoverableKeyException);
- deleteEntryKey();
- } catch (final KeyPermanentlyInvalidatedException invalidKeyException) {
- Log.e(TAG, "Unable to initialize encrypt data", invalidKeyException);
- fingerPrintCallback.onInvalidKeyException(invalidKeyException);
- } catch (final Exception e) {
- Log.e(TAG, "Unable to initialize encrypt data", e);
- fingerPrintCallback.onFingerPrintException(e);
- }
- }
-
- public void encryptData(final String value) {
- if (!isFingerprintInitialized()) {
- return;
- }
- try {
- // actual do encryption here
- byte[] encrypted = cipher.doFinal(value.getBytes());
- final String encryptedValue = Base64.encodeToString(encrypted, Base64.NO_WRAP);
-
- // passes updated iv spec on to callback so this can be stored for decryption
- final IvParameterSpec spec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
- final String ivSpecValue = Base64.encodeToString(spec.getIV(), Base64.NO_WRAP);
- fingerPrintCallback.handleEncryptedResult(encryptedValue, ivSpecValue);
-
- } catch (final Exception e) {
- Log.e(TAG, "Unable to encrypt data", e);
- fingerPrintCallback.onFingerPrintException(e);
- }
- }
-
- public void initDecryptData(final String ivSpecValue) {
- if (!isFingerprintInitialized()) {
- return;
- }
- try {
- stopListening();
-
- createNewKeyIfNeeded(false);
- keyStore.load(null);
- final SecretKey key = (SecretKey) keyStore.getKey(FINGERPRINT_KEYSTORE_KEY, null);
-
- // important to restore spec here that was used for decryption
- final byte[] iv = Base64.decode(ivSpecValue, Base64.NO_WRAP);
- final IvParameterSpec spec = new IvParameterSpec(iv);
- cipher.init(Cipher.DECRYPT_MODE, key, spec);
-
- startListening();
- } catch (final UnrecoverableKeyException unrecoverableKeyException) {
- Log.e(TAG, "Unable to initialize decrypt data", unrecoverableKeyException);
- deleteEntryKey();
- } catch (final KeyPermanentlyInvalidatedException invalidKeyException) {
- Log.e(TAG, "Unable to initialize decrypt data", invalidKeyException);
- fingerPrintCallback.onInvalidKeyException(invalidKeyException);
- } catch (final Exception e) {
- Log.e(TAG, "Unable to initialize decrypt data", e);
- fingerPrintCallback.onFingerPrintException(e);
- }
- }
-
- public void decryptData(final String encryptedValue) {
- if (!isFingerprintInitialized()) {
- return;
- }
- try {
- // actual decryption here
- final byte[] encrypted = Base64.decode(encryptedValue, Base64.NO_WRAP);
- byte[] decrypted = cipher.doFinal(encrypted);
- final String decryptedString = new String(decrypted);
-
- //final String encryptedString = Base64.encodeToString(encrypted, 0 /* flags */);
- fingerPrintCallback.handleDecryptedResult(decryptedString);
- } catch (final BadPaddingException badPaddingException) {
- Log.e(TAG, "Unable to decrypt data", badPaddingException);
- fingerPrintCallback.onInvalidKeyException(badPaddingException);
- } catch (final Exception e) {
- Log.e(TAG, "Unable to decrypt data", e);
- fingerPrintCallback.onFingerPrintException(e);
- }
- }
-
- @SuppressLint("NewApi")
- private void createNewKeyIfNeeded(final boolean allowDeleteExisting) {
- if (!isFingerprintInitialized()) {
- return;
- }
- try {
- keyStore.load(null);
- if (allowDeleteExisting
- && keyStore.containsAlias(FINGERPRINT_KEYSTORE_KEY)) {
- keyStore.deleteEntry(FINGERPRINT_KEYSTORE_KEY);
- }
-
- // Create new key if needed
- if (!keyStore.containsAlias(FINGERPRINT_KEYSTORE_KEY)) {
- // Set the alias of the entry in Android KeyStore where the key will appear
- // and the constrains (purposes) in the constructor of the Builder
- keyGenerator.init(
- new KeyGenParameterSpec.Builder(
- FINGERPRINT_KEYSTORE_KEY,
- KeyProperties.PURPOSE_ENCRYPT |
- KeyProperties.PURPOSE_DECRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
- // Require the user to authenticate with a fingerprint to authorize every use
- // of the key
- .setUserAuthenticationRequired(true)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
- .build());
- keyGenerator.generateKey();
- }
- } catch (final Exception e) {
- Log.e(TAG, "Unable to create a key in keystore", e);
- fingerPrintCallback.onFingerPrintException(e);
- }
- }
-
- public void deleteEntryKey() {
- try {
- keyStore.load(null);
- keyStore.deleteEntry(FINGERPRINT_KEYSTORE_KEY);
- } catch (KeyStoreException
- | CertificateException
- | NoSuchAlgorithmException
- | IOException
- | NullPointerException e) {
- Log.e(TAG, "Unable to delete entry key in keystore", e);
- if (fingerPrintCallback != null)
- fingerPrintCallback.onFingerPrintException(e);
- }
- }
-
- @SuppressLint("NewApi")
- public boolean hasEnrolledFingerprints() {
- // fingerprint hardware supported and api level OK
- return isFingerprintSupported(fingerprintManager)
- // fingerprints enrolled
- && fingerprintManager.hasEnrolledFingerprints()
- // and lockscreen configured
- && keyguardManager.isKeyguardSecure();
- }
-
- private void setInitOk(final boolean initOk) {
- this.initOk = initOk;
- }
-
- /**
- * Remove entry key in keystore
- */
- public static void deleteEntryKeyInKeystoreForFingerprints(final Context context,
- final FingerPrintErrorCallback fingerPrintCallback) {
- FingerPrintHelper fingerPrintHelper = new FingerPrintHelper(
- context, new FingerPrintCallback() {
- @Override
- public void handleEncryptedResult(String value, String ivSpec) {}
-
- @Override
- public void handleDecryptedResult(String value) {}
-
- @Override
- public void onInvalidKeyException(Exception e) {
- fingerPrintCallback.onInvalidKeyException(e);
- }
-
- @Override
- public void onFingerPrintException(Exception e) {
- fingerPrintCallback.onFingerPrintException(e);
- }
- });
- fingerPrintHelper.deleteEntryKey();
- }
-
- public interface FingerPrintErrorCallback {
- void onInvalidKeyException(Exception e);
- void onFingerPrintException(Exception e);
- }
-
- public interface FingerPrintCallback extends FingerPrintErrorCallback {
- void handleEncryptedResult(String value, String ivSpec);
- void handleDecryptedResult(String value);
- }
-
- public enum Mode {
- NOT_CONFIGURED_MODE, WAITING_PASSWORD_MODE, STORE_MODE, OPEN_MODE
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/fingerprint/FingerPrintHelper.kt b/app/src/main/java/com/kunzisoft/keepass/fingerprint/FingerPrintHelper.kt
new file mode 100644
index 000000000..ed6035ad9
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/fingerprint/FingerPrintHelper.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.fingerprint
+
+import android.annotation.SuppressLint
+import android.app.KeyguardManager
+import android.content.Context
+import android.hardware.fingerprint.FingerprintManager
+import android.os.Build
+import android.os.CancellationSignal
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyPermanentlyInvalidatedException
+import android.security.keystore.KeyProperties
+import android.support.annotation.RequiresApi
+import android.util.Base64
+import android.util.Log
+
+import java.io.IOException
+import java.security.KeyStore
+import java.security.KeyStoreException
+import java.security.NoSuchAlgorithmException
+import java.security.UnrecoverableKeyException
+import java.security.cert.CertificateException
+
+import javax.crypto.BadPaddingException
+import javax.crypto.Cipher
+import javax.crypto.KeyGenerator
+import javax.crypto.SecretKey
+import javax.crypto.spec.IvParameterSpec
+
+@RequiresApi(api = Build.VERSION_CODES.M)
+class FingerPrintHelper(context: Context, private val fingerPrintCallback: FingerPrintCallback?) {
+
+ private val fingerprintManager: FingerprintManager? =
+ context.getSystemService(FingerprintManager::class.java)
+ private var keyStore: KeyStore? = null
+ private var keyGenerator: KeyGenerator? = null
+ private var cipher: Cipher? = null
+ private var keyguardManager: KeyguardManager? = null
+ private var cryptoObject: FingerprintManager.CryptoObject? = null
+
+ private var initOk = false
+ private var cancellationSignal: CancellationSignal? = null
+ private var authenticationCallback: FingerprintManager.AuthenticationCallback? = null
+
+ val isFingerprintInitialized: Boolean
+ get() = isFingerprintInitialized(true)
+
+ fun setAuthenticationCallback(authenticationCallback: FingerprintManager.AuthenticationCallback) {
+ this.authenticationCallback = authenticationCallback
+ }
+
+ @Synchronized
+ fun startListening() {
+ // starts listening for fingerprints with the initialised crypto object
+ cancellationSignal = CancellationSignal()
+ fingerprintManager?.authenticate(
+ cryptoObject,
+ cancellationSignal,
+ 0 /* flags */,
+ authenticationCallback!!, null)
+ }
+
+ @Synchronized
+ fun stopListening() {
+ if (!isFingerprintInitialized(false)) {
+ return
+ }
+ if (cancellationSignal != null) {
+ cancellationSignal?.cancel()
+ cancellationSignal = null
+ }
+ }
+
+ init {
+
+ if (!isFingerprintSupported(fingerprintManager)) {
+ // really not much to do when no fingerprint support found
+ setInitOk(false)
+ } else {
+ this.keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
+
+ if (hasEnrolledFingerprints()) {
+ try {
+ this.keyStore = KeyStore.getInstance("AndroidKeyStore")
+ this.keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES,
+ "AndroidKeyStore")
+ this.cipher = Cipher.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_CBC + "/"
+ + KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ this.cryptoObject = FingerprintManager.CryptoObject(cipher!!)
+ setInitOk(true)
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to initialize the keystore", e)
+ setInitOk(false)
+ fingerPrintCallback?.onFingerPrintException(e)
+ }
+
+ }
+ }
+ }
+
+ private fun isFingerprintInitialized(throwException: Boolean): Boolean {
+ val isFingerprintInit = hasEnrolledFingerprints() && initOk
+ if (!isFingerprintInit && fingerPrintCallback != null) {
+ if (throwException)
+ fingerPrintCallback.onFingerPrintException(Exception("FingerPrint not initialized"))
+ }
+ return isFingerprintInit
+ }
+
+ fun initEncryptData() {
+ if (!isFingerprintInitialized) {
+ return
+ }
+ try {
+ stopListening()
+
+ createNewKeyIfNeeded(false) // no need to keep deleting existing keys
+ keyStore?.load(null)
+ val key = keyStore?.getKey(FINGERPRINT_KEYSTORE_KEY, null) as SecretKey
+ cipher?.init(Cipher.ENCRYPT_MODE, key)
+
+ startListening()
+ } catch (unrecoverableKeyException: UnrecoverableKeyException) {
+ Log.e(TAG, "Unable to initialize encrypt data", unrecoverableKeyException)
+ deleteEntryKey()
+ } catch (invalidKeyException: KeyPermanentlyInvalidatedException) {
+ Log.e(TAG, "Unable to initialize encrypt data", invalidKeyException)
+ fingerPrintCallback?.onInvalidKeyException(invalidKeyException)
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to initialize encrypt data", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ }
+
+ }
+
+ fun encryptData(value: String) {
+ if (!isFingerprintInitialized) {
+ return
+ }
+ try {
+ // actual do encryption here
+ val encrypted = cipher?.doFinal(value.toByteArray())
+ val encryptedValue = Base64.encodeToString(encrypted, Base64.NO_WRAP)
+
+ // passes updated iv spec on to callback so this can be stored for decryption
+ cipher?.parameters?.getParameterSpec(IvParameterSpec::class.java)?.let{ spec ->
+ val ivSpecValue = Base64.encodeToString(spec.iv, Base64.NO_WRAP)
+ fingerPrintCallback?.handleEncryptedResult(encryptedValue, ivSpecValue)
+ }
+
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to encrypt data", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ }
+
+ }
+
+ fun initDecryptData(ivSpecValue: String) {
+ if (!isFingerprintInitialized) {
+ return
+ }
+ try {
+ stopListening()
+
+ createNewKeyIfNeeded(false)
+ keyStore?.load(null)
+ val key = keyStore?.getKey(FINGERPRINT_KEYSTORE_KEY, null) as SecretKey
+
+ // important to restore spec here that was used for decryption
+ val iv = Base64.decode(ivSpecValue, Base64.NO_WRAP)
+ val spec = IvParameterSpec(iv)
+ cipher?.init(Cipher.DECRYPT_MODE, key, spec)
+
+ startListening()
+ } catch (unrecoverableKeyException: UnrecoverableKeyException) {
+ Log.e(TAG, "Unable to initialize decrypt data", unrecoverableKeyException)
+ deleteEntryKey()
+ } catch (invalidKeyException: KeyPermanentlyInvalidatedException) {
+ Log.e(TAG, "Unable to initialize decrypt data", invalidKeyException)
+ fingerPrintCallback?.onInvalidKeyException(invalidKeyException)
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to initialize decrypt data", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ }
+
+ }
+
+ fun decryptData(encryptedValue: String) {
+ if (!isFingerprintInitialized) {
+ return
+ }
+ try {
+ // actual decryption here
+ val encrypted = Base64.decode(encryptedValue, Base64.NO_WRAP)
+ cipher?.doFinal(encrypted)?.let { decrypted ->
+ //final String encryptedString = Base64.encodeToString(encrypted, 0 /* flags */);
+ fingerPrintCallback?.handleDecryptedResult(String(decrypted))
+ }
+ } catch (badPaddingException: BadPaddingException) {
+ Log.e(TAG, "Unable to decrypt data", badPaddingException)
+ fingerPrintCallback?.onInvalidKeyException(badPaddingException)
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to decrypt data", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ }
+
+ }
+
+ @SuppressLint("NewApi")
+ private fun createNewKeyIfNeeded(allowDeleteExisting: Boolean) {
+ if (!isFingerprintInitialized) {
+ return
+ }
+ try {
+ keyStore?.load(null)
+ if (allowDeleteExisting && keyStore != null && keyStore!!.containsAlias(FINGERPRINT_KEYSTORE_KEY)) {
+ keyStore?.deleteEntry(FINGERPRINT_KEYSTORE_KEY)
+ }
+
+ // Create new key if needed
+ if (keyStore != null && !keyStore!!.containsAlias(FINGERPRINT_KEYSTORE_KEY)) {
+ // Set the alias of the entry in Android KeyStore where the key will appear
+ // and the constrains (purposes) in the constructor of the Builder
+ keyGenerator?.init(
+ KeyGenParameterSpec.Builder(
+ FINGERPRINT_KEYSTORE_KEY,
+ KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ // Require the user to authenticate with a fingerprint to authorize every use
+ // of the key
+ .setUserAuthenticationRequired(true)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .build())
+ keyGenerator?.generateKey()
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to create a key in keystore", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ }
+
+ }
+
+ fun deleteEntryKey() {
+ try {
+ keyStore?.load(null)
+ keyStore?.deleteEntry(FINGERPRINT_KEYSTORE_KEY)
+ } catch (e: KeyStoreException) {
+ Log.e(TAG, "Unable to delete entry key in keystore", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ } catch (e: CertificateException) {
+ Log.e(TAG, "Unable to delete entry key in keystore", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ } catch (e: NoSuchAlgorithmException) {
+ Log.e(TAG, "Unable to delete entry key in keystore", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ } catch (e: IOException) {
+ Log.e(TAG, "Unable to delete entry key in keystore", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ } catch (e: NullPointerException) {
+ Log.e(TAG, "Unable to delete entry key in keystore", e)
+ fingerPrintCallback?.onFingerPrintException(e)
+ }
+
+ }
+
+ @SuppressLint("NewApi")
+ fun hasEnrolledFingerprints(): Boolean {
+ // fingerprint hardware supported and api level OK
+ return (isFingerprintSupported(fingerprintManager)
+ // fingerprints enrolled
+ && fingerprintManager != null && fingerprintManager!!.hasEnrolledFingerprints()
+ // and lockscreen configured
+ && keyguardManager != null && keyguardManager!!.isKeyguardSecure)
+ }
+
+ private fun setInitOk(initOk: Boolean) {
+ this.initOk = initOk
+ }
+
+ interface FingerPrintErrorCallback {
+ fun onInvalidKeyException(e: Exception)
+ fun onFingerPrintException(e: Exception)
+ }
+
+ interface FingerPrintCallback : FingerPrintErrorCallback {
+ fun handleEncryptedResult(value: String, ivSpec: String)
+ fun handleDecryptedResult(value: String)
+ }
+
+ enum class Mode {
+ NOT_CONFIGURED_MODE, WAITING_PASSWORD_MODE, STORE_MODE, OPEN_MODE
+ }
+
+ companion object {
+
+ private val TAG = FingerPrintHelper::class.java.name
+
+ private const val FINGERPRINT_KEYSTORE_KEY = "com.kunzisoft.keepass.fingerprint.key"
+
+ fun isFingerprintSupported(fingerprintManager: FingerprintManager?): Boolean {
+ return fingerprintManager != null && fingerprintManager.isHardwareDetected
+ }
+
+ /**
+ * Remove entry key in keystore
+ */
+ fun deleteEntryKeyInKeystoreForFingerprints(context: Context,
+ fingerPrintCallback: FingerPrintErrorCallback) {
+ val fingerPrintHelper = FingerPrintHelper( context, object : FingerPrintCallback {
+
+ override fun handleEncryptedResult(value: String, ivSpec: String) {}
+
+ override fun handleDecryptedResult(value: String) {}
+
+ override fun onInvalidKeyException(e: Exception) {
+ fingerPrintCallback.onInvalidKeyException(e)
+ }
+
+ override fun onFingerPrintException(e: Exception) {
+ fingerPrintCallback.onFingerPrintException(e)
+ }
+ })
+ fingerPrintHelper.deleteEntryKey()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java
deleted file mode 100644
index f878e6494..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.icons;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.widget.ImageViewCompat;
-import android.util.Log;
-import android.widget.ImageView;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.database.element.PwIcon;
-import com.kunzisoft.keepass.database.element.PwIconCustom;
-import com.kunzisoft.keepass.database.element.PwIconStandard;
-
-import org.apache.commons.collections.map.AbstractReferenceMap;
-import org.apache.commons.collections.map.ReferenceMap;
-
-/**
- * Factory class who build database icons dynamically, can assign an icon of IconPack, or a custom icon to an ImageView with a tint
- */
-public class IconDrawableFactory {
-
- private static final String TAG = IconDrawableFactory.class.getName();
-
- private static Drawable blank = null;
- private static int blankWidth = -1;
- private static int blankHeight = -1;
-
- /** customIconMap
- * Cache for icon drawable.
- * Keys: UUID, Values: Drawables
- */
- private ReferenceMap customIconMap = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK);
-
- /** standardIconMap
- * Cache for icon drawable.
- * Keys: Integer, Values: Drawables
- */
- private ReferenceMap standardIconMap = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK);
-
- /**
- * Assign a default database icon to an ImageView and tint it if needed
- *
- * @param context Context to build the drawable
- * @param iconView ImageView that will host the drawable
- * @param tintColor Use this color to tint tintable icon
- */
- public void assignDefaultDatabaseIconTo(Context context, ImageView iconView, int tintColor) {
- if (IconPackChooser.getSelectedIconPack(context).tintable()) {
- assignDrawableTo(context,
- iconView,
- IconPackChooser.getSelectedIconPack(context).getDefaultIconId(),
- true,
- tintColor);
- } else {
- assignDrawableTo(context,
- iconView,
- IconPackChooser.getSelectedIconPack(context).getDefaultIconId(),
- false,
- Color.WHITE);
- }
- }
-
- /**
- * Assign a database icon to an ImageView and tint it if needed
- *
- * @param context Context to build the drawable
- * @param iconView ImageView that will host the drawable
- * @param icon The icon from the database
- * @param tintColor Use this color to tint tintable icon
- */
- public void assignDatabaseIconTo(Context context, ImageView iconView, PwIcon icon, int tintColor) {
- if (IconPackChooser.getSelectedIconPack(context).tintable()) {
- assignDrawableToImageView(getIconDrawable(context, icon, true, tintColor),
- iconView,
- true,
- tintColor);
- } else {
- assignDrawableToImageView(getIconDrawable(context, icon, true, tintColor),
- iconView,
- false,
- Color.WHITE);
- }
- }
-
- /**
- * Assign an image by its resourceId to an ImageView and tint it
- *
- * @param context Context to build the drawable
- * @param imageView ImageView that will host the drawable
- * @param iconId iconId from the resources
- * @param tint true will tint the drawable with tintColor
- * @param tintColor Use this color if tint is true
- */
- public void assignDrawableTo(Context context, ImageView imageView, int iconId, boolean tint, int tintColor) {
- assignDrawableToImageView(new SuperDrawable(getIconDrawable(context, iconId, tint, tintColor)),
- imageView,
- tint,
- tintColor);
- }
-
- /**
- * Utility method to assign a drawable to an ImageView and tint it
- */
- private void assignDrawableToImageView(SuperDrawable superDrawable, ImageView imageView, boolean tint, int tintColor) {
- if (imageView != null && superDrawable.drawable != null) {
- imageView.setImageDrawable(superDrawable.drawable);
- if (!superDrawable.custom && tint) {
- ImageViewCompat.setImageTintList(imageView, ColorStateList.valueOf(tintColor));
- } else {
- ImageViewCompat.setImageTintList(imageView, null);
- }
- }
- }
-
- /**
- * Get the drawable icon from cache or build it and add it to the cache if not exists yet
- *
- * @param context Context to build the drawable
- * @param icon The icon from database
- * @return The build drawable
- */
- public Drawable getIconDrawable(Context context, PwIcon icon) {
- return getIconDrawable(context, icon, false, Color.WHITE).drawable;
- }
-
- /**
- * Get the drawable icon from cache or build it and add it to the cache if not exists yet then tint it if needed
- *
- * @param context Context to build the drawable
- * @param icon The icon from database
- * @param tint true will tint the drawable with tintColor
- * @param tintColor Use this color if tint is true
- * @return The build drawable
- */
- public SuperDrawable getIconDrawable(Context context, PwIcon icon, boolean tint, int tintColor) {
- if (icon instanceof PwIconStandard) {
- return new SuperDrawable(getIconDrawable(context.getApplicationContext(), (PwIconStandard) icon, tint, tintColor));
- } else {
- return new SuperDrawable(getIconDrawable(context, (PwIconCustom) icon), true);
- }
- }
-
- /**
- * Build a blank drawable
- * @param res Resource to build the drawable
- */
- private static void initBlank(Resources res) {
- if (blank==null) {
- blankWidth = (int) res.getDimension(R.dimen.icon_size);
- blankHeight = (int) res.getDimension(R.dimen.icon_size);
- blank = new ColorDrawable(Color.TRANSPARENT);
- blank.setBounds(0, 0, blankWidth, blankHeight);
- }
- }
-
- /**
- * Key class to retrieve a Drawable in the cache if it's tinted or not
- */
- private class CacheKey {
- int resId;
- boolean isTint;
- int color;
-
- CacheKey(int resId, boolean isTint, int color) {
- this.resId = resId;
- this.isTint = isTint;
- this.color = color;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- CacheKey cacheKey = (CacheKey) o;
- if (isTint)
- return resId == cacheKey.resId &&
- cacheKey.isTint &&
- color == cacheKey.color;
- else
- return resId == cacheKey.resId &&
- !cacheKey.isTint;
- }
- }
-
- /**
- * Get the drawable icon from cache or build it and add it to the cache if not exists yet
- *
- * @param context Context to make drawable
- * @param icon Icon from database
- * @param isTint Tint the drawable if true
- * @param tintColor Use this color if tint is true
- * @return The drawable
- */
- private Drawable getIconDrawable(Context context, PwIconStandard icon, boolean isTint, int tintColor) {
- int resId = IconPackChooser.getSelectedIconPack(context).iconToResId(icon.getIconId());
-
- return getIconDrawable(context, resId, isTint, tintColor);
- }
-
- /**
- * Get the drawable icon from cache or build it and add it to the cache if not exists yet
- *
- * @param context Context to make drawable
- * @param iconId iconId from resources
- * @param isTint Tint the drawable if true
- * @param tintColor Use this color if tint is true
- * @return The drawable
- */
- private Drawable getIconDrawable(Context context, int iconId, boolean isTint, int tintColor) {
- CacheKey newCacheKey = new CacheKey(iconId, isTint, tintColor);
-
- Drawable draw = (Drawable) standardIconMap.get(newCacheKey);
- if (draw == null) {
- try {
- draw = ContextCompat.getDrawable(context, iconId);
- } catch (Exception e) {
- Log.e(TAG, "Can't get icon", e);
- }
- if (draw != null) {
- standardIconMap.put(newCacheKey, draw);
- }
- }
-
- if (draw == null) {
- if (blank == null)
- initBlank(context.getResources());
- draw = blank;
- }
-
- return draw;
- }
-
- /**
- * Utility class to prevent a custom icon to be tint
- */
- private class SuperDrawable {
- Drawable drawable;
- boolean custom;
-
- SuperDrawable(Drawable drawable) {
- this.drawable = drawable;
- this.custom = false;
- }
-
- SuperDrawable(Drawable drawable, boolean custom) {
- this.drawable = drawable;
- this.custom = custom;
- }
- }
-
- /**
- * Build a custom icon from database
- * @param context Context to build the drawable
- * @param icon Icon from database
- * @return The drawable
- */
- private Drawable getIconDrawable(Context context, PwIconCustom icon) {
- initBlank(context.getResources());
- if (icon == null) {
- return blank;
- }
-
- Drawable draw = (Drawable) customIconMap.get(icon.getUuid());
-
- if (draw == null) {
-
- Bitmap bitmap = BitmapFactory.decodeByteArray(icon.getImageData(), 0, icon.getImageData().length);
-
- // Could not understand custom icon
- if (bitmap == null) {
- return blank;
- }
-
- bitmap = resize(bitmap);
-
- draw = new BitmapDrawable(context.getResources(), bitmap);
- customIconMap.put(icon.getUuid(), draw);
- }
-
- return draw;
- }
-
- /**
- * Resize the custom icon to match the built in icons
- *
- * @param bitmap Bitmap to resize
- * @return Bitmap resized
- */
- private Bitmap resize(Bitmap bitmap) {
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
-
- if (width == blankWidth && height == blankHeight) {
- return bitmap;
- }
-
- return Bitmap.createScaledBitmap(bitmap, blankWidth, blankHeight, true);
- }
-
- /**
- * Clear the cache of icons
- */
- public void clearCache() {
- standardIconMap.clear();
- customIconMap.clear();
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt
new file mode 100644
index 000000000..c6904138f
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.icons
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.support.v4.content.res.ResourcesCompat
+import android.support.v4.widget.ImageViewCompat
+import android.util.Log
+import android.widget.ImageView
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.database.element.PwIcon
+import com.kunzisoft.keepass.database.element.PwIconCustom
+import com.kunzisoft.keepass.database.element.PwIconStandard
+import org.apache.commons.collections.map.AbstractReferenceMap
+import org.apache.commons.collections.map.ReferenceMap
+
+/**
+ * Factory class who build database icons dynamically, can assign an icon of IconPack, or a custom icon to an ImageView with a tint
+ */
+class IconDrawableFactory {
+
+ /** customIconMap
+ * Cache for icon drawable.
+ * Keys: UUID, Values: Drawables
+ */
+ private val customIconMap = ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK)
+
+ /** standardIconMap
+ * Cache for icon drawable.
+ * Keys: Integer, Values: Drawables
+ */
+ private val standardIconMap = ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK)
+
+ /**
+ * Utility method to assign a drawable to an ImageView and tint it
+ */
+ fun assignDrawableToImageView(superDrawable: SuperDrawable, imageView: ImageView?, tint: Boolean, tintColor: Int) {
+ if (imageView != null) {
+ imageView.setImageDrawable(superDrawable.drawable)
+ if (!superDrawable.custom && tint) {
+ ImageViewCompat.setImageTintList(imageView, ColorStateList.valueOf(tintColor))
+ } else {
+ ImageViewCompat.setImageTintList(imageView, null)
+ }
+ }
+ }
+
+ /**
+ * Get the [SuperDrawable] [icon] (from cache, or build it and add it to the cache if not exists yet), then [tint] it with [tintColor] if needed
+ */
+ fun getIconSuperDrawable(context: Context, icon: PwIcon, tint: Boolean = false, tintColor: Int = Color.WHITE): SuperDrawable {
+ return when (icon) {
+ is PwIconStandard -> {
+ val resId = IconPackChooser.getSelectedIconPack(context)?.iconToResId(icon.iconId) ?: R.drawable.ic_blank_32dp
+ getIconSuperDrawable(context, resId, tint, tintColor)
+ }
+ is PwIconCustom -> {
+ SuperDrawable(getIconDrawable(context.resources, icon), true)
+ }
+ else -> {
+ SuperDrawable(PatternIcon(context.resources).blankDrawable)
+ }
+ }
+ }
+
+ /**
+ * Get the [SuperDrawable] PwIconStandard from [iconId] (cache, or build it and add it to the cache if not exists yet)
+ * , then [tint] it with [tintColor] if needed
+ */
+ fun getIconSuperDrawable(context: Context, iconId: Int, tint: Boolean, tintColor: Int): SuperDrawable {
+ return SuperDrawable(getIconDrawable(context.resources, iconId, tint, tintColor))
+ }
+
+ /**
+ * Key class to retrieve a Drawable in the cache if it's tinted or not
+ */
+ private inner class CacheKey internal constructor(internal var resId: Int, internal var isTint: Boolean, internal var color: Int) {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || javaClass != other.javaClass) return false
+ val cacheKey = other as CacheKey
+ return if (isTint)
+ resId == cacheKey.resId &&
+ cacheKey.isTint &&
+ color == cacheKey.color
+ else
+ resId == cacheKey.resId && !cacheKey.isTint
+ }
+
+ override fun hashCode(): Int {
+ var result = resId
+ result = 31 * result + isTint.hashCode()
+ result = 31 * result + color
+ return result
+ }
+ }
+
+ /**
+ * Build a custom [Drawable] from custom [icon]
+ */
+ private fun getIconDrawable(resources: Resources, icon: PwIconCustom): Drawable {
+ val patternIcon = PatternIcon(resources)
+
+ var draw: Drawable? = customIconMap[icon.uuid] as Drawable?
+ if (draw == null) {
+ var bitmap: Bitmap? = BitmapFactory.decodeByteArray(icon.imageData, 0, icon.imageData.size)
+ // Could not understand custom icon
+ bitmap?.let { bitmapIcon ->
+ bitmap = resize(bitmapIcon, patternIcon)
+ draw = BitmapDrawable(resources, bitmap)
+ customIconMap[icon.uuid] = draw
+ return draw!!
+ }
+ } else {
+ return draw!!
+ }
+ return patternIcon.blankDrawable
+ }
+
+ /**
+ * Get the standard [Drawable] icon from [iconId] (cache or build it and add it to the cache if not exists yet)
+ * , then [tint] it with [tintColor] if needed
+ */
+ private fun getIconDrawable(resources: Resources, iconId: Int, tint: Boolean, tintColor: Int): Drawable {
+ val newCacheKey = CacheKey(iconId, tint, tintColor)
+
+ var draw: Drawable? = standardIconMap[newCacheKey] as Drawable?
+ if (draw == null) {
+ try {
+ draw = ResourcesCompat.getDrawable(resources, iconId, null)
+ } catch (e: Exception) {
+ Log.e(TAG, "Can't get icon", e)
+ }
+
+ if (draw != null) {
+ standardIconMap[newCacheKey] = draw
+ }
+ }
+
+ if (draw == null) {
+ draw = PatternIcon(resources).blankDrawable
+ }
+
+ return draw
+ }
+
+ /**
+ * Resize the custom icon to match the built in icons
+ *
+ * @param bitmap Bitmap to resize
+ * @return Bitmap resized
+ */
+ private fun resize(bitmap: Bitmap, dimensionPattern: PatternIcon): Bitmap {
+ val width = bitmap.width
+ val height = bitmap.height
+
+ return if (width == dimensionPattern.width && height == dimensionPattern.height) {
+ bitmap
+ } else Bitmap.createScaledBitmap(bitmap, dimensionPattern.width, dimensionPattern.height, true)
+
+ }
+
+ /**
+ * Clear the cache of icons
+ */
+ fun clearCache() {
+ standardIconMap.clear()
+ customIconMap.clear()
+ }
+
+ private class PatternIcon
+ /**
+ * Build a blankDrawable drawable
+ * @param res Resource to build the drawable
+ */(res: Resources) {
+
+ var blankDrawable: Drawable = ColorDrawable(Color.TRANSPARENT)
+ var width = -1
+ var height = -1
+
+ init {
+ width = res.getDimension(R.dimen.icon_size).toInt()
+ height = res.getDimension(R.dimen.icon_size).toInt()
+ blankDrawable.setBounds(0, 0, width, height)
+ }
+ }
+
+ /**
+ * Utility class to prevent a custom icon to be tint
+ */
+ class SuperDrawable(var drawable: Drawable, var custom: Boolean = false)
+
+ companion object {
+
+ private val TAG = IconDrawableFactory::class.java.name
+ }
+
+}
+
+/**
+ * Assign a default database icon to an ImageView and tint it with [tintColor] if needed
+ */
+fun ImageView.assignDefaultDatabaseIcon(iconFactory: IconDrawableFactory, tintColor: Int = Color.WHITE) {
+ IconPackChooser.getSelectedIconPack(context)?.let { selectedIconPack ->
+
+ iconFactory.assignDrawableToImageView(
+ iconFactory.getIconSuperDrawable(context,
+ selectedIconPack.defaultIconId,
+ selectedIconPack.tintable(),
+ tintColor),
+ this,
+ selectedIconPack.tintable(),
+ tintColor)
+ }
+}
+
+/**
+ * Assign a database [icon] to an ImageView and tint it with [tintColor] if needed
+ */
+fun ImageView.assignDatabaseIcon(iconFactory: IconDrawableFactory, icon: PwIcon, tintColor: Int = Color.WHITE) {
+ IconPackChooser.getSelectedIconPack(context)?.let { selectedIconPack ->
+
+ iconFactory.assignDrawableToImageView(
+ iconFactory.getIconSuperDrawable(context,
+ icon,
+ true,
+ tintColor),
+ this,
+ selectedIconPack.tintable(),
+ tintColor)
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconPack.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconPack.java
deleted file mode 100644
index 96cecae7c..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/icons/IconPack.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.icons;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.SparseIntArray;
-
-import com.kunzisoft.keepass.R;
-
-import java.text.DecimalFormat;
-
-/**
- * Class who construct dynamically database icons contains in a separate library
- *
- * It only supports icons with specific nomenclature [stringId]_[%2d]_32dp
- * where [stringId] contains in a string xml attribute with id resource_id and
- * [%2d] 2 numerical numbers between 00 and 68 included,
- *
- * See icon-pack-classic module as sample
- *
- *
- */
-public class IconPack {
-
- private static final int NB_ICONS = 68;
-
- private SparseIntArray icons;
- private String resourceStringId;
- private String name;
- private boolean tintable;
-
- private Resources resources;
-
- /**
- * Construct dynamically the icon pack provide by the string resource id
- *
- * @param context Context of the app to retrieve the resources
- * @param resourceId String Id of the pack (ex : com.kunzisoft.keepass.icon.classic.R.string.resource_id)
- */
- IconPack(Context context, int resourceId) {
-
- resources = context.getResources();
- icons = new SparseIntArray();
- resourceStringId = context.getString(resourceId);
- // If finish with a _ remove it
- if (resourceStringId.lastIndexOf('_') == resourceStringId.length() - 1)
- resourceStringId = resourceStringId.substring(0, resourceStringId.length() -1);
-
- // Build the list of icons
- int num = 0;
- while(num <= NB_ICONS) {
- // To construct the id with name_ic_XX_32dp (ex : classic_ic_08_32dp )
- int resId = resources.getIdentifier(
- resourceStringId + "_" + new DecimalFormat("00").format(num) + "_32dp",
- "drawable",
- context.getPackageName());
- icons.put(num, resId);
- num++;
- }
- // Get visual name
- name = resources.getString(
- resources.getIdentifier(
- resourceStringId + "_" + "name",
- "string",
- context.getPackageName()
- )
- );
- // If icons are tintable
- tintable = resources.getBoolean(
- resources.getIdentifier(
- resourceStringId + "_" + "tintable",
- "bool",
- context.getPackageName()
- )
- );
- }
-
- /**
- * Get the name of the IconPack
- *
- * @return String visual name of the pack
- */
- public String getName() {
- return name;
- }
-
- /**
- * Get the id of the IconPack
- *
- * @return String id of the pack
- */
- public String getId() {
- return resourceStringId;
- }
-
- /**
- * Determine if each icon in the pack can be tint
- *
- * @return true if icons are tintable
- */
- public boolean tintable() {
- return tintable;
- }
-
- /**
- * Get the number of icons in this pack
- *
- * @return int Number of database icons
- */
- public int numberOfIcons() {
- return icons.size();
- }
-
- /**
- * Icon as a resourceId
- *
- * @param iconId Icon database Id of the icon to retrieve
- * @return int resourceId
- */
- public int iconToResId(int iconId) {
- return icons.get(iconId, R.drawable.ic_blank_32dp);
- }
-
- /**
- * @return int Get the default icon resource id
- */
- public int getDefaultIconId() {
- return iconToResId(0);
- }
-
- /**
- * Icon as a drawable
- *
- * @param iconId Icon database Id of the icon to retrieve
- * @return int resourceId
- */
- public Drawable getDrawable(int iconId) {
- return resources.getDrawable(iconToResId(iconId));
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconPack.kt b/app/src/main/java/com/kunzisoft/keepass/icons/IconPack.kt
new file mode 100644
index 000000000..3018c7d92
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconPack.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.icons
+
+import android.content.res.Resources
+import android.util.SparseIntArray
+import com.kunzisoft.keepass.R
+import java.text.DecimalFormat
+
+/**
+ * Class who construct dynamically database icons contains in a separate library
+ *
+ *
+ * It only supports icons with specific nomenclature **[stringId]_[%2d]_32dp**
+ * where [stringId] contains in a string xml attribute with id **resource_id** and
+ * [%2d] 2 numerical numbers between 00 and 68 included,
+ *
+ *
+ * See *icon-pack-classic* module as sample
+ *
+ *
+ */
+class IconPack
+/**
+ * Construct dynamically the icon pack provide by the string resource id
+ *
+ * @param packageName Context of the app to retrieve the resources
+ * @param packageName Context of the app to retrieve the resources
+ * @param resourceId String Id of the pack (ex : com.kunzisoft.keepass.icon.classic.R.string.resource_id)
+ */
+internal constructor(packageName: String, resources: Resources, resourceId: Int) {
+
+ private val icons: SparseIntArray = SparseIntArray()
+ /**
+ * Get the id of the IconPack
+ *
+ * @return String id of the pack
+ */
+ var id: String? = null
+ private set
+ /**
+ * Get the name of the IconPack
+ *
+ * @return String visual name of the pack
+ */
+ val name: String
+
+ private val tintable: Boolean
+
+ /**
+ * @return int Get the default icon resource id
+ */
+ val defaultIconId: Int
+ get() = iconToResId(0)
+
+ init {
+
+ id = resources.getString(resourceId)
+ // If finish with a _ remove it
+ id?.let { idRes ->
+ if (idRes.lastIndexOf('_') == idRes.length - 1)
+ id = idRes.substring(0, idRes.length - 1)
+ }
+
+ // Build the list of icons
+ var num = 0
+ while (num <= NB_ICONS) {
+ // To construct the id with name_ic_XX_32dp (ex : classic_ic_08_32dp )
+ val resId = resources.getIdentifier(
+ id + "_" + DecimalFormat("00").format(num.toLong()) + "_32dp",
+ "drawable",
+ packageName)
+ icons.put(num, resId)
+ num++
+ }
+ // Get visual name
+ name = resources.getString(
+ resources.getIdentifier(
+ id + "_" + "name",
+ "string",
+ packageName
+ )
+ )
+ // If icons are tintable
+ tintable = resources.getBoolean(
+ resources.getIdentifier(
+ id + "_" + "tintable",
+ "bool",
+ packageName
+ )
+ )
+ }
+
+ /**
+ * Determine if each icon in the pack can be tint
+ *
+ * @return true if icons are tintable
+ */
+ fun tintable(): Boolean {
+ return tintable
+ }
+
+ /**
+ * Get the number of icons in this pack
+ *
+ * @return int Number of database icons
+ */
+ fun numberOfIcons(): Int {
+ return icons.size()
+ }
+
+ /**
+ * Icon as a resourceId
+ *
+ * @param iconId Icon database Id of the icon to retrieve
+ * @return int resourceId
+ */
+ fun iconToResId(iconId: Int): Int {
+ return icons.get(iconId, R.drawable.ic_blank_32dp)
+ }
+
+ companion object {
+
+ private const val NB_ICONS = 68
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.java
deleted file mode 100644
index 72ee9435f..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.icons;
-
-import android.content.Context;
-import android.util.Log;
-
-import com.kunzisoft.keepass.BuildConfig;
-import com.kunzisoft.keepass.app.App;
-import com.kunzisoft.keepass.settings.PreferencesUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Utility class to built and select an IconPack dynamically by libraries importation
- *
- * @author J-Jamet
- */
-public class IconPackChooser {
-
- private static final String TAG = IconPackChooser.class.getName();
-
- private static List iconPackList = new ArrayList<>();
- private static IconPack iconPackSelected = null;
-
- private static IconPackChooser sIconPackBuilder;
-
- /**
- * IconPackChooser as singleton
- */
- private IconPackChooser(){
- if (sIconPackBuilder != null){
- throw new RuntimeException("Use build() method to get the single instance of this class.");
- }
- }
-
- /**
- * Built the icon pack chooser based on imports made in build.gradle
- *
- * Dynamic import can be done for each flavor by prefixing the 'implementation' command with the name of the flavor.< br/>
- * (ex : {@code libreImplementation project(path: ':icon-pack-classic')}
- * Each name of icon pack must be in {@code ICON_PACKS} in the build.gradle file
- *
- * @param context Context to construct each pack with the resources
- * @return An unique instance of {@link IconPackChooser}, recall {@link #build(Context)} provide the same instance
- */
- @SuppressWarnings("JavaDoc")
- public static IconPackChooser build(Context context) {
- if (sIconPackBuilder == null) { //if there is no instance available... create new one
- synchronized (IconPackChooser.class) {
- if (sIconPackBuilder == null) {
- sIconPackBuilder = new IconPackChooser();
-
- for (String iconPackString : BuildConfig.ICON_PACKS) {
- addOrCatchNewIconPack(context, iconPackString);
- }
- if (iconPackList.isEmpty()) {
- Log.e(TAG, "Icon packs can't be load, retry with one by default");
- addDefaultIconPack(context);
- }
- }
- }
- }
-
- return sIconPackBuilder;
- }
-
- /**
- * Construct dynamically the icon pack provide by the default string resource "resource_id"
- */
- private static void addDefaultIconPack(Context context) {
- int resourceId = context.getResources().getIdentifier("resource_id", "string", context.getPackageName());
- iconPackList.add(new IconPack(context, resourceId));
- }
-
- /**
- * Utility method to add new icon pack or catch exception if not retrieve
- */
- private static void addOrCatchNewIconPack(Context context, String iconPackString) {
- try {
- iconPackList.add(new IconPack(context, context.getResources().getIdentifier(
- iconPackString + "_resource_id",
- "string",
- context.getPackageName())));
- } catch (Exception e) {
- Log.w(TAG, "Icon pack "+ iconPackString +" can't be load");
- }
- }
-
- public static void setSelectedIconPack(String iconPackIdString) {
- for(IconPack iconPack : iconPackList) {
- if (iconPack.getId().equals(iconPackIdString)) {
- App.Companion.getCurrentDatabase().getDrawFactory().clearCache();
- iconPackSelected = iconPack;
- break;
- }
- }
- }
-
- /**
- * Get the current IconPack used
- *
- * @param context Context to build the icon pack if not already build
- * @return IconPack currently in usage
- */
- public static IconPack getSelectedIconPack(Context context) {
- build(context);
- if (iconPackSelected == null)
- setSelectedIconPack(PreferencesUtil.getIconPackSelectedId(context));
- return iconPackSelected;
- }
-
- /**
- * Get the list of IconPack available
- *
- * @param context Context to build the icon pack if not already build
- * @return IconPack available
- */
- public static List getIconPackList(Context context) {
- build(context);
- return iconPackList;
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.kt b/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.kt
new file mode 100644
index 000000000..bd30ed8ca
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2018 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.icons
+
+import android.content.Context
+import android.util.Log
+
+import com.kunzisoft.keepass.BuildConfig
+import com.kunzisoft.keepass.app.App
+import com.kunzisoft.keepass.settings.PreferencesUtil
+
+import java.util.ArrayList
+
+/**
+ * Utility class to built and select an IconPack dynamically by libraries importation
+ *
+ * @author J-Jamet
+ */
+object IconPackChooser {
+
+ private val TAG = IconPackChooser::class.java.name
+
+ private val iconPackList = ArrayList()
+ private var iconPackSelected: IconPack? = null
+
+ private var isIconPackChooserBuilt: Boolean = false
+
+ /**
+ * Built the icon pack chooser based on imports made in *build.gradle*
+ *
+ *
+ * Dynamic import can be done for each flavor by prefixing the 'implementation' command with the name of the flavor.< br/>
+ * (ex : `libreImplementation project(path: ':icon-pack-classic')`
+ * Each name of icon pack must be in `ICON_PACKS` in the build.gradle file
+ *
+ * @param context Context to construct each pack with the resources
+ * @return An unique instance of [IconPackChooser], recall [.build] provide the same instance
+ */
+ fun build(context: Context) {
+ synchronized(IconPackChooser::class.java) {
+ if (!isIconPackChooserBuilt) {
+ isIconPackChooserBuilt = true
+
+ for (iconPackString in BuildConfig.ICON_PACKS) {
+ addOrCatchNewIconPack(context, iconPackString)
+ }
+ if (iconPackList.isEmpty()) {
+ Log.e(TAG, "Icon packs can't be load, retry with one by default")
+ addDefaultIconPack(context)
+ }
+ }
+ }
+ }
+
+ /**
+ * Construct dynamically the icon pack provide by the default string resource "resource_id"
+ */
+ private fun addDefaultIconPack(context: Context) {
+ val resourceId = context.resources.getIdentifier("resource_id", "string", context.packageName)
+ iconPackList.add(IconPack(context.packageName, context.resources, resourceId))
+ }
+
+ /**
+ * Utility method to add new icon pack or catch exception if not retrieve
+ */
+ private fun addOrCatchNewIconPack(context: Context, iconPackString: String) {
+ try {
+ iconPackList.add(IconPack(context.packageName, context.resources, context.resources.getIdentifier(
+ iconPackString + "_resource_id",
+ "string",
+ context.packageName)))
+ } catch (e: Exception) {
+ Log.w(TAG, "Icon pack $iconPackString can't be load")
+ }
+
+ }
+
+ fun setSelectedIconPack(iconPackIdString: String?) {
+ for (iconPack in iconPackList) {
+ if (iconPack.id == iconPackIdString) {
+ App.currentDatabase.drawFactory.clearCache()
+ iconPackSelected = iconPack
+ break
+ }
+ }
+ }
+
+ /**
+ * Get the current IconPack used
+ *
+ * @param context Context to build the icon pack if not already build
+ * @return IconPack currently in usage
+ */
+ fun getSelectedIconPack(context: Context): IconPack? {
+ build(context)
+ if (iconPackSelected == null)
+ setSelectedIconPack(PreferencesUtil.getIconPackSelectedId(context))
+ return iconPackSelected
+ }
+
+ /**
+ * Get the list of IconPack available
+ *
+ * @param context Context to build the icon pack if not already build
+ * @return IconPack available
+ */
+ fun getIconPackList(context: Context): List {
+ build(context)
+ return iconPackList
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconPackUnknownException.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconPackUnknownException.java
deleted file mode 100644
index 15bd56c6b..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/icons/IconPackUnknownException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.kunzisoft.keepass.icons;
-
-public class IconPackUnknownException extends Exception{
-
- IconPackUnknownException() {
- super("Icon pack isn't defined");
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.java
deleted file mode 100644
index 275ac645c..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.java
+++ /dev/null
@@ -1,170 +0,0 @@
-package com.kunzisoft.keepass.magikeyboard;
-
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.os.Build;
-import android.os.IBinder;
-import android.support.v4.app.NotificationCompat;
-import android.support.v7.preference.PreferenceManager;
-import android.util.Log;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver;
-import com.kunzisoft.keepass.magikeyboard.receiver.NotificationDeleteBroadcastReceiver;
-import com.kunzisoft.keepass.timeout.TimeoutHelper;
-
-import static com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver.LOCK_ACTION;
-
-public class KeyboardEntryNotificationService extends Service {
-
- private static final String TAG = KeyboardEntryNotificationService.class.getName();
-
- private static final String CHANNEL_ID_KEYBOARD = "com.kunzisoft.keyboard.notification.entry.channel";
- private static final String CHANNEL_NAME_KEYBOARD = "Magikeyboard notification";
-
- private NotificationManager notificationManager;
- private Thread cleanNotificationTimer;
- private int notificationId = 582;
- private long notificationTimeoutMilliSecs;
-
- private LockBroadcastReceiver lockBroadcastReceiver;
- private PendingIntent pendingDeleteIntent;
-
- public KeyboardEntryNotificationService() {
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-
- // Create notification channel for Oreo+
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- NotificationChannel channel = new NotificationChannel(CHANNEL_ID_KEYBOARD,
- CHANNEL_NAME_KEYBOARD,
- NotificationManager.IMPORTANCE_LOW);
- notificationManager.createNotificationChannel(channel);
- }
-
- // Register a lock receiver to stop notification service when lock on keyboard is performed
- lockBroadcastReceiver = new LockBroadcastReceiver() {
- @Override
- public void onReceiveLock(Context context, Intent intent) {
- context.stopService(new Intent(context, KeyboardEntryNotificationService.class));
- }
- };
- registerReceiver(lockBroadcastReceiver, new IntentFilter(LOCK_ACTION));
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent == null) {
- Log.w(TAG, "null intent");
- } else {
- newNotification();
-
- }
- return START_NOT_STICKY;
- }
-
- private void newNotification() {
-
- Intent deleteIntent = new Intent(this, NotificationDeleteBroadcastReceiver.class);
- pendingDeleteIntent =
- PendingIntent.getBroadcast(getApplicationContext(), 0, deleteIntent, 0);
-
- if (MagikIME.getEntryKey() != null) {
- String entryTitle = getString(R.string.keyboard_notification_entry_content_title_text);
- String entryUsername = "";
- if (!MagikIME.getEntryKey().getTitle().isEmpty())
- entryTitle = MagikIME.getEntryKey().getTitle();
- if (!MagikIME.getEntryKey().getUsername().isEmpty())
- entryUsername = MagikIME.getEntryKey().getUsername();
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_KEYBOARD)
- .setSmallIcon(R.drawable.ic_vpn_key_white_24dp)
- .setContentTitle(getString(R.string.keyboard_notification_entry_content_title, entryTitle))
- .setPriority(NotificationCompat.PRIORITY_DEFAULT)
- .setVisibility(NotificationCompat.VISIBILITY_SECRET)
- .setContentText(getString(R.string.keyboard_notification_entry_content_text, entryUsername))
- .setAutoCancel(false)
- .setContentIntent(null)
- .setDeleteIntent(pendingDeleteIntent);
-
- notificationManager.cancel(notificationId);
- notificationManager.notify(notificationId, builder.build());
-
-
-
- // Timeout only if notification clear is available
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- if (sharedPreferences.getBoolean(getString(R.string.keyboard_notification_entry_clear_close_key),
- getResources().getBoolean(R.bool.keyboard_notification_entry_clear_close_default))) {
- String keyboardTimeout = sharedPreferences.getString(getString(R.string.keyboard_entry_timeout_key),
- getString(R.string.timeout_default));
- try {
- notificationTimeoutMilliSecs = Long.parseLong(keyboardTimeout);
- } catch (NumberFormatException e) {
- notificationTimeoutMilliSecs = TimeoutHelper.DEFAULT_TIMEOUT;
- }
-
- if (notificationTimeoutMilliSecs != TimeoutHelper.TIMEOUT_NEVER) {
- stopTask(cleanNotificationTimer);
- cleanNotificationTimer = new Thread(() -> {
- int maxPos = 100;
- long posDurationMills = notificationTimeoutMilliSecs / maxPos;
- for (int pos = maxPos; pos > 0; --pos) {
- builder.setProgress(maxPos, pos, false);
- notificationManager.notify(notificationId, builder.build());
- try {
- Thread.sleep(posDurationMills);
- } catch (InterruptedException e) {
- break;
- }
- }
- try {
- pendingDeleteIntent.send();
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Unable to delete keyboard notification");
- }
- });
- cleanNotificationTimer.start();
- }
- }
- }
- }
-
- private void stopTask(Thread task) {
- if (task != null && task.isAlive())
- task.interrupt();
- }
-
- private void destroyKeyboardNotification() {
- stopTask(cleanNotificationTimer);
- cleanNotificationTimer = null;
- unregisterReceiver(lockBroadcastReceiver);
- pendingDeleteIntent.cancel();
-
- notificationManager.cancel(notificationId);
- }
-
- @Override
- public void onDestroy() {
-
- destroyKeyboardNotification();
-
- super.onDestroy();
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.kt
new file mode 100644
index 000000000..2bece3aca
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.kt
@@ -0,0 +1,160 @@
+package com.kunzisoft.keepass.magikeyboard
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Build
+import android.os.IBinder
+import android.support.v4.app.NotificationCompat
+import android.support.v7.preference.PreferenceManager
+import android.util.Log
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver
+import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver.Companion.LOCK_ACTION
+import com.kunzisoft.keepass.magikeyboard.receiver.NotificationDeleteBroadcastReceiver
+import com.kunzisoft.keepass.timeout.TimeoutHelper
+
+class KeyboardEntryNotificationService : Service() {
+
+ private var notificationManager: NotificationManager? = null
+ private var cleanNotificationTimer: Thread? = null
+ private val notificationId = 582
+ private var notificationTimeoutMilliSecs: Long = 0
+
+ private var lockBroadcastReceiver: LockBroadcastReceiver? = null
+ private var pendingDeleteIntent: PendingIntent? = null
+
+ override fun onBind(intent: Intent): IBinder? {
+ return null
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ // Create notification channel for Oreo+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel = NotificationChannel(CHANNEL_ID_KEYBOARD,
+ CHANNEL_NAME_KEYBOARD,
+ NotificationManager.IMPORTANCE_LOW)
+ notificationManager?.createNotificationChannel(channel)
+ }
+
+ // Register a lock receiver to stop notification service when lock on keyboard is performed
+ lockBroadcastReceiver = object : LockBroadcastReceiver() {
+ override fun onReceiveLock(context: Context, intent: Intent) {
+ context.stopService(Intent(context, KeyboardEntryNotificationService::class.java))
+ }
+ }
+ registerReceiver(lockBroadcastReceiver, IntentFilter(LOCK_ACTION))
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ if (intent == null) {
+ Log.w(TAG, "null intent")
+ } else {
+ newNotification()
+ }
+ return START_NOT_STICKY
+ }
+
+ private fun newNotification() {
+
+ val deleteIntent = Intent(this, NotificationDeleteBroadcastReceiver::class.java)
+ pendingDeleteIntent = PendingIntent.getBroadcast(applicationContext, 0, deleteIntent, 0)
+
+ if (MagikIME.entryKey != null) {
+ var entryTitle: String? = getString(R.string.keyboard_notification_entry_content_title_text)
+ var entryUsername: String? = ""
+ if (MagikIME.entryKey!!.title.isNotEmpty())
+ entryTitle = MagikIME.entryKey!!.title
+ if (MagikIME.entryKey!!.username.isNotEmpty())
+ entryUsername = MagikIME.entryKey!!.username
+
+ val builder = NotificationCompat.Builder(this, CHANNEL_ID_KEYBOARD)
+ .setSmallIcon(R.drawable.ic_vpn_key_white_24dp)
+ .setContentTitle(getString(R.string.keyboard_notification_entry_content_title, entryTitle))
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setVisibility(NotificationCompat.VISIBILITY_SECRET)
+ .setContentText(getString(R.string.keyboard_notification_entry_content_text, entryUsername))
+ .setAutoCancel(false)
+ .setContentIntent(null)
+ .setDeleteIntent(pendingDeleteIntent)
+
+ notificationManager?.cancel(notificationId)
+ notificationManager?.notify(notificationId, builder.build())
+
+
+ // Timeout only if notification clear is available
+ val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
+ if (sharedPreferences.getBoolean(getString(R.string.keyboard_notification_entry_clear_close_key),
+ resources.getBoolean(R.bool.keyboard_notification_entry_clear_close_default))) {
+ val keyboardTimeout = sharedPreferences.getString(getString(R.string.keyboard_entry_timeout_key),
+ getString(R.string.timeout_default))
+ notificationTimeoutMilliSecs = try {
+ java.lang.Long.parseLong(keyboardTimeout)
+ } catch (e: NumberFormatException) {
+ TimeoutHelper.DEFAULT_TIMEOUT
+ }
+
+ if (notificationTimeoutMilliSecs != TimeoutHelper.NEVER) {
+ stopTask(cleanNotificationTimer)
+ cleanNotificationTimer = Thread {
+ val maxPos = 100
+ val posDurationMills = notificationTimeoutMilliSecs / maxPos
+ for (pos in maxPos downTo 1) {
+ builder.setProgress(maxPos, pos, false)
+ notificationManager?.notify(notificationId, builder.build())
+ try {
+ Thread.sleep(posDurationMills)
+ } catch (e: InterruptedException) {
+ break
+ }
+
+ }
+ try {
+ pendingDeleteIntent?.send()
+ } catch (e: PendingIntent.CanceledException) {
+ Log.e(TAG, "Unable to delete keyboard notification")
+ }
+ }
+ cleanNotificationTimer?.start()
+ }
+ }
+ }
+ }
+
+ private fun stopTask(task: Thread?) {
+ if (task != null && task.isAlive)
+ task.interrupt()
+ }
+
+ private fun destroyKeyboardNotification() {
+ stopTask(cleanNotificationTimer)
+ cleanNotificationTimer = null
+ unregisterReceiver(lockBroadcastReceiver)
+ pendingDeleteIntent?.cancel()
+
+ notificationManager?.cancel(notificationId)
+ }
+
+ override fun onDestroy() {
+
+ destroyKeyboardNotification()
+
+ super.onDestroy()
+ }
+
+ companion object {
+
+ private val TAG = KeyboardEntryNotificationService::class.java.name
+
+ private const val CHANNEL_ID_KEYBOARD = "com.kunzisoft.keyboard.notification.entry.channel"
+ private const val CHANNEL_NAME_KEYBOARD = "Magikeyboard notification"
+ }
+
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java
deleted file mode 100644
index c5fe63a68..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.magikeyboard;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.inputmethodservice.InputMethodService;
-import android.inputmethodservice.Keyboard;
-import android.inputmethodservice.KeyboardView;
-import android.media.AudioManager;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.FrameLayout;
-import android.widget.PopupWindow;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.magikeyboard.adapter.FieldsAdapter;
-import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver;
-import com.kunzisoft.keepass.magikeyboard.view.MagikeyboardView;
-import com.kunzisoft.keepass.model.Entry;
-import com.kunzisoft.keepass.settings.PreferencesUtil;
-
-import static com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver.LOCK_ACTION;
-
-public class MagikIME extends InputMethodService
- implements KeyboardView.OnKeyboardActionListener {
- private static final String TAG = MagikIME.class.getName();
-
- public static final int KEY_BACK_KEYBOARD = 600;
- public static final int KEY_CHANGE_KEYBOARD = 601;
- private static final int KEY_UNLOCK = 610;
- private static final int KEY_LOCK = 611;
- private static final int KEY_ENTRY = 620;
- private static final int KEY_USERNAME = 500;
- private static final int KEY_PASSWORD = 510;
- private static final int KEY_URL = 520;
- private static final int KEY_FIELDS = 530;
-
- private static Entry entryKey = null;
-
- private KeyboardView keyboardView;
- private Keyboard keyboard;
- private Keyboard keyboard_entry;
- private PopupWindow popupCustomKeys;
- private FieldsAdapter fieldsAdapter;
- private boolean playSoundDuringCLick;
-
- private LockBroadcastReceiver lockBroadcastReceiver;
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- // Remove the entry and lock the keyboard when the lock signal is receive
- lockBroadcastReceiver = new LockBroadcastReceiver() {
- @Override
- public void onReceiveLock(Context context, Intent intent) {
- entryKey = null;
- assignKeyboardView();
- }
- };
- registerReceiver(lockBroadcastReceiver, new IntentFilter(LOCK_ACTION));
- }
-
- @Override
- public View onCreateInputView() {
- keyboardView = (MagikeyboardView) getLayoutInflater().inflate(R.layout.keyboard_view, null);
- keyboard = new Keyboard(this, R.xml.keyboard_password);
- keyboard_entry = new Keyboard(this, R.xml.keyboard_password_entry);
-
- assignKeyboardView();
- keyboardView.setOnKeyboardActionListener(this);
- keyboardView.setPreviewEnabled(false);
-
- Context context = getBaseContext();
- View popupFieldsView = LayoutInflater.from(context)
- .inflate(R.layout.keyboard_popup_fields, new FrameLayout(context));
-
- if (popupCustomKeys != null)
- popupCustomKeys.dismiss();
-
- popupCustomKeys = new PopupWindow(context);
- popupCustomKeys.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
- popupCustomKeys.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
- popupCustomKeys.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- popupCustomKeys.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
- popupCustomKeys.setContentView(popupFieldsView);
-
- RecyclerView recyclerView = popupFieldsView.findViewById(R.id.keyboard_popup_fields_list);
- fieldsAdapter = new FieldsAdapter(this);
- fieldsAdapter.setOnItemClickListener(item -> getCurrentInputConnection().commitText(item.getValue(), 1));
- recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true));
- recyclerView.setAdapter(fieldsAdapter);
-
- View closeView = popupFieldsView.findViewById(R.id.keyboard_popup_close);
- closeView.setOnClickListener(v -> popupCustomKeys.dismiss());
-
- return keyboardView;
- }
-
- private void assignKeyboardView() {
- if (keyboardView != null) {
- if (entryKey != null) {
- if (keyboard_entry != null)
- keyboardView.setKeyboard(keyboard_entry);
- } else {
- if (keyboard != null) {
- dismissCustomKeys();
- keyboardView.setKeyboard(keyboard);
- }
- }
-
- // Define preferences
- keyboardView.setHapticFeedbackEnabled(PreferencesUtil.enableKeyboardVibration(this));
- playSoundDuringCLick = PreferencesUtil.enableKeyboardSound(this);
- }
- }
-
- @Override
- public void onStartInputView(EditorInfo info, boolean restarting) {
- super.onStartInputView(info, restarting);
- assignKeyboardView();
- }
-
- public static Entry getEntryKey() {
- return entryKey;
- }
-
- public static void setEntryKey(Entry entry) {
- entryKey = entry;
- }
-
- public static void deleteEntryKey(Context context) {
- Intent lockIntent = new Intent(LOCK_ACTION);
- context.sendBroadcast(lockIntent);
- entryKey = null;
- }
-
- private void playVibration(int keyCode){
- switch(keyCode){
- case Keyboard.KEYCODE_DELETE:
- break;
- default: keyboardView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- }
- }
-
- private void playClick(int keyCode){
- AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
- if (am != null)
- switch(keyCode){
- case Keyboard.KEYCODE_DONE:
- case 10:
- am.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN);
- break;
- case Keyboard.KEYCODE_DELETE:
- am.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE);
- break;
- default: am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
- }
- }
-
- @Override
- public void onKey(int primaryCode, int[] keyCodes) {
- InputConnection inputConnection = getCurrentInputConnection();
-
- // Vibrate
- playVibration(primaryCode);
- // Play a sound
- if (playSoundDuringCLick)
- playClick(primaryCode);
-
- switch(primaryCode){
- case KEY_BACK_KEYBOARD:
- try {
- InputMethodManager imeManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
- assert imeManager != null;
- assert getWindow().getWindow() != null;
- imeManager.switchToLastInputMethod(getWindow().getWindow().getAttributes().token);
- } catch (Exception e) {
- Log.e(TAG, "Unable to switch to the previous IME", e);
- InputMethodManager imeManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
- if (imeManager != null)
- imeManager.showInputMethodPicker();
- }
- break;
- case KEY_CHANGE_KEYBOARD:
- InputMethodManager imeManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
- if (imeManager != null)
- imeManager.showInputMethodPicker();
- break;
- case KEY_UNLOCK:
- // TODO Unlock key
- break;
- case KEY_ENTRY:
- // Stop current service and reinit entry
- stopService(new Intent(this, KeyboardEntryNotificationService.class));
- entryKey = null;
- Intent intent = new Intent(this, KeyboardLauncherActivity.class);
- // New task needed because don't launch from an Activity context
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivity(intent);
- break;
- case KEY_LOCK:
- deleteEntryKey(this);
- dismissCustomKeys();
- break;
- case KEY_USERNAME:
- if (entryKey != null) {
- inputConnection.commitText(entryKey.getUsername(), 1);
- }
- break;
- case KEY_PASSWORD:
- if (entryKey != null) {
- inputConnection.commitText(entryKey.getPassword(), 1);
- }
- break;
- case KEY_URL:
- if (entryKey != null) {
- inputConnection.commitText(entryKey.getUrl(), 1);
- }
- break;
- case KEY_FIELDS:
- fieldsAdapter.setFields(entryKey.getCustomFields());
- popupCustomKeys.showAtLocation(keyboardView, Gravity.END | Gravity.TOP, 0, 0);
- break;
- case Keyboard.KEYCODE_DELETE :
- inputConnection.deleteSurroundingText(1, 0);
- break;
- case Keyboard.KEYCODE_DONE:
- inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
- break;
- default:
- }
- }
-
- @Override
- public void onPress(int primaryCode) {
- AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
- if (am != null)
- switch(primaryCode){
- case Keyboard.KEYCODE_DELETE:
- keyboardView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- break;
- default:
- }
- }
-
- @Override
- public void onRelease(int primaryCode) {
- switch(primaryCode){
- case Keyboard.KEYCODE_DELETE:
- break;
- default:
- }
- }
-
- @Override
- public void onText(CharSequence text) {}
-
- @Override
- public void swipeDown() {}
-
- @Override
- public void swipeLeft() {}
-
- @Override
- public void swipeRight() {}
-
- @Override
- public void swipeUp() {}
-
- private void dismissCustomKeys() {
- if (popupCustomKeys != null)
- popupCustomKeys.dismiss();
- if (fieldsAdapter != null)
- fieldsAdapter.clear();
- }
-
- @Override
- public void onDestroy() {
- dismissCustomKeys();
- unregisterReceiver(lockBroadcastReceiver);
- super.onDestroy();
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.kt
new file mode 100644
index 000000000..31cea54c5
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.kt
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.magikeyboard
+
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.inputmethodservice.InputMethodService
+import android.inputmethodservice.Keyboard
+import android.inputmethodservice.KeyboardView
+import android.media.AudioManager
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import android.util.Log
+import android.view.*
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import android.widget.FrameLayout
+import android.widget.PopupWindow
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.magikeyboard.adapter.FieldsAdapter
+import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver
+import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver.Companion.LOCK_ACTION
+import com.kunzisoft.keepass.magikeyboard.view.MagikeyboardView
+import com.kunzisoft.keepass.model.Entry
+import com.kunzisoft.keepass.model.Field
+import com.kunzisoft.keepass.settings.PreferencesUtil
+
+class MagikIME : InputMethodService(), KeyboardView.OnKeyboardActionListener {
+
+ private var keyboardView: KeyboardView? = null
+ private var keyboard: Keyboard? = null
+ private var keyboardEntry: Keyboard? = null
+ private var popupCustomKeys: PopupWindow? = null
+ private var fieldsAdapter: FieldsAdapter? = null
+ private var playSoundDuringCLick: Boolean = false
+
+ private var lockBroadcastReceiver: LockBroadcastReceiver? = null
+
+ override fun onCreate() {
+ super.onCreate()
+
+ // Remove the entry and lock the keyboard when the lock signal is receive
+ lockBroadcastReceiver = object : LockBroadcastReceiver() {
+ override fun onReceiveLock(context: Context, intent: Intent) {
+ entryKey = null
+ assignKeyboardView()
+ }
+ }
+ registerReceiver(lockBroadcastReceiver, IntentFilter(LOCK_ACTION))
+ }
+
+ override fun onCreateInputView(): View {
+ keyboardView = layoutInflater.inflate(R.layout.keyboard_view, null) as MagikeyboardView
+
+ if (keyboardView != null) {
+ keyboard = Keyboard(this, R.xml.keyboard_password)
+ keyboardEntry = Keyboard(this, R.xml.keyboard_password_entry)
+
+ assignKeyboardView()
+ keyboardView?.setOnKeyboardActionListener(this)
+ keyboardView?.isPreviewEnabled = false
+
+ val context = baseContext
+ val popupFieldsView = LayoutInflater.from(context)
+ .inflate(R.layout.keyboard_popup_fields, FrameLayout(context))
+
+ popupCustomKeys?.dismiss()
+
+ popupCustomKeys = PopupWindow(context)
+ popupCustomKeys?.width = WindowManager.LayoutParams.WRAP_CONTENT
+ popupCustomKeys?.height = WindowManager.LayoutParams.WRAP_CONTENT
+ popupCustomKeys?.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+ popupCustomKeys?.inputMethodMode = PopupWindow.INPUT_METHOD_NEEDED
+ popupCustomKeys?.contentView = popupFieldsView
+
+ val recyclerView = popupFieldsView.findViewById(R.id.keyboard_popup_fields_list)
+ fieldsAdapter = FieldsAdapter(this)
+ fieldsAdapter?.onItemClickListener = object : FieldsAdapter.OnItemClickListener {
+ override fun onItemClick(item: Field) {
+ currentInputConnection.commitText(item.value, 1)
+ }
+ }
+ recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true)
+ recyclerView.adapter = fieldsAdapter
+
+ val closeView = popupFieldsView.findViewById(R.id.keyboard_popup_close)
+ closeView.setOnClickListener { popupCustomKeys?.dismiss() }
+
+ return keyboardView!!
+ }
+
+ return super.onCreateInputView()
+ }
+
+ private fun assignKeyboardView() {
+ if (keyboardView != null) {
+ if (entryKey != null) {
+ if (keyboardEntry != null)
+ keyboardView?.keyboard = keyboardEntry
+ } else {
+ if (keyboard != null) {
+ dismissCustomKeys()
+ keyboardView?.keyboard = keyboard
+ }
+ }
+
+ // Define preferences
+ keyboardView?.isHapticFeedbackEnabled = PreferencesUtil.enableKeyboardVibration(this)
+ playSoundDuringCLick = PreferencesUtil.enableKeyboardSound(this)
+ }
+ }
+
+ override fun onStartInputView(info: EditorInfo, restarting: Boolean) {
+ super.onStartInputView(info, restarting)
+ assignKeyboardView()
+ }
+
+ private fun playVibration(keyCode: Int) {
+ when (keyCode) {
+ Keyboard.KEYCODE_DELETE -> {}
+ else -> keyboardView?.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
+ }
+ }
+
+ private fun playClick(keyCode: Int) {
+ val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager?
+ when (keyCode) {
+ Keyboard.KEYCODE_DONE, 10 -> audioManager?.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN)
+ Keyboard.KEYCODE_DELETE -> audioManager?.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE)
+ else -> audioManager?.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD)
+ }
+ }
+
+ override fun onKey(primaryCode: Int, keyCodes: IntArray) {
+ val inputConnection = currentInputConnection
+
+ // Vibrate
+ playVibration(primaryCode)
+ // Play a sound
+ if (playSoundDuringCLick)
+ playClick(primaryCode)
+
+ when (primaryCode) {
+ KEY_BACK_KEYBOARD -> try {
+ val imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ if (window.window != null)
+ imeManager.switchToLastInputMethod(window.window!!.attributes.token)
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to switch to the previous IME", e)
+ val imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ imeManager.showInputMethodPicker()
+ }
+
+ KEY_CHANGE_KEYBOARD -> {
+ val imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ imeManager.showInputMethodPicker()
+ }
+ KEY_UNLOCK -> {
+ }
+ KEY_ENTRY -> {
+ // Stop current service and reinit entry
+ stopService(Intent(this, KeyboardEntryNotificationService::class.java))
+ entryKey = null
+ val intent = Intent(this, KeyboardLauncherActivity::class.java)
+ // New task needed because don't launch from an Activity context
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ startActivity(intent)
+ }
+ KEY_LOCK -> {
+ deleteEntryKey(this)
+ dismissCustomKeys()
+ }
+ KEY_USERNAME -> {
+ if (entryKey != null) {
+ inputConnection.commitText(entryKey!!.username, 1)
+ }
+ }
+ KEY_PASSWORD -> {
+ if (entryKey != null) {
+ inputConnection.commitText(entryKey!!.password, 1)
+ }
+ }
+ KEY_URL -> {
+ if (entryKey != null) {
+ inputConnection.commitText(entryKey!!.url, 1)
+ }
+ }
+ KEY_FIELDS -> {
+ if (entryKey != null) {
+ fieldsAdapter?.fields = entryKey!!.customFields
+ }
+ popupCustomKeys?.showAtLocation(keyboardView, Gravity.END or Gravity.TOP, 0, 0)
+ }
+ Keyboard.KEYCODE_DELETE -> inputConnection.deleteSurroundingText(1, 0)
+ Keyboard.KEYCODE_DONE -> inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER))
+ }// TODO Unlock key
+ }
+
+ override fun onPress(primaryCode: Int) {
+ val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager?
+ if (audioManager != null)
+ when (primaryCode) {
+ Keyboard.KEYCODE_DELETE -> keyboardView?.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ }
+ }
+
+ override fun onRelease(primaryCode: Int) {
+ when (primaryCode) {
+ Keyboard.KEYCODE_DELETE -> { }
+ }
+ }
+
+ override fun onText(text: CharSequence) {}
+
+ override fun swipeDown() {}
+
+ override fun swipeLeft() {}
+
+ override fun swipeRight() {}
+
+ override fun swipeUp() {}
+
+ private fun dismissCustomKeys() {
+ popupCustomKeys?.dismiss()
+ fieldsAdapter?.clear()
+ }
+
+ override fun onDestroy() {
+ dismissCustomKeys()
+ unregisterReceiver(lockBroadcastReceiver)
+ super.onDestroy()
+ }
+
+ companion object {
+ private val TAG = MagikIME::class.java.name
+
+ const val KEY_BACK_KEYBOARD = 600
+ const val KEY_CHANGE_KEYBOARD = 601
+ private const val KEY_UNLOCK = 610
+ private const val KEY_LOCK = 611
+ private const val KEY_ENTRY = 620
+ private const val KEY_USERNAME = 500
+ private const val KEY_PASSWORD = 510
+ private const val KEY_URL = 520
+ private const val KEY_FIELDS = 530
+
+ var entryKey: Entry? = null
+
+ fun deleteEntryKey(context: Context) {
+ val lockIntent = Intent(LOCK_ACTION)
+ context.sendBroadcast(lockIntent)
+ entryKey = null
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/adapter/FieldsAdapter.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/adapter/FieldsAdapter.java
deleted file mode 100644
index e1d3cb1c2..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/adapter/FieldsAdapter.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.kunzisoft.keepass.magikeyboard.adapter;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.model.Field;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class FieldsAdapter extends RecyclerView.Adapter {
-
- private LayoutInflater inflater;
- private List fields;
- private OnItemClickListener listener;
-
- public FieldsAdapter(Context context) {
- this.inflater = LayoutInflater.from(context);
- this.fields = new ArrayList<>();
- }
-
- public void setFields(List fields) {
- this.fields = fields;
- }
-
- public void setOnItemClickListener(OnItemClickListener listener) {
- this.listener = listener;
- }
-
- @NonNull
- @Override
- public FieldViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View view = inflater.inflate(R.layout.keyboard_popup_fields_item, parent, false);
- return new FieldViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(@NonNull FieldViewHolder holder, int position) {
- Field field = fields.get(position);
- holder.name.setText(field.getName());
- holder.bind(field, listener);
- }
-
- @Override
- public int getItemCount() {
- return fields.size();
- }
-
- public void clear() {
- fields.clear();
- }
-
- public interface OnItemClickListener {
- void onItemClick(Field item);
- }
-
- class FieldViewHolder extends RecyclerView.ViewHolder {
-
- TextView name;
-
- FieldViewHolder(View itemView) {
- super(itemView);
- name = itemView.findViewById(R.id.keyboard_popup_field_item_name);
- }
-
- void bind(final Field item, final OnItemClickListener listener) {
- itemView.setOnClickListener(v -> listener.onItemClick(item));
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/adapter/FieldsAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/adapter/FieldsAdapter.kt
new file mode 100644
index 000000000..fa753cd6b
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/adapter/FieldsAdapter.kt
@@ -0,0 +1,53 @@
+package com.kunzisoft.keepass.magikeyboard.adapter
+
+import android.content.Context
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.model.Field
+
+import java.util.ArrayList
+
+class FieldsAdapter(context: Context) : RecyclerView.Adapter() {
+
+ private val inflater: LayoutInflater = LayoutInflater.from(context)
+ var fields: MutableList = ArrayList()
+ var onItemClickListener: OnItemClickListener? = null
+
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FieldViewHolder {
+ val view = inflater.inflate(R.layout.keyboard_popup_fields_item, parent, false)
+ return FieldViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: FieldViewHolder, position: Int) {
+ val field = fields[position]
+ holder.name.text = field.name
+ holder.bind(field, onItemClickListener)
+ }
+
+ override fun getItemCount(): Int {
+ return fields.size
+ }
+
+ fun clear() {
+ fields.clear()
+ }
+
+ interface OnItemClickListener {
+ fun onItemClick(item: Field)
+ }
+
+ inner class FieldViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+
+ var name: TextView = itemView.findViewById(R.id.keyboard_popup_field_item_name)
+
+ fun bind(item: Field, listener: OnItemClickListener?) {
+ itemView.setOnClickListener { listener?.onItemClick(item) }
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/LockBroadcastReceiver.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/LockBroadcastReceiver.java
deleted file mode 100644
index 721ff5220..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/LockBroadcastReceiver.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.kunzisoft.keepass.magikeyboard.receiver;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public abstract class LockBroadcastReceiver extends BroadcastReceiver {
-
- public static final String LOCK_ACTION = "com.kunzisoft.keepass.LOCK";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action != null
- && action.equals(LOCK_ACTION)) {
- onReceiveLock(context, intent);
- }
- }
-
- public abstract void onReceiveLock(Context context, Intent intent);
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/LockBroadcastReceiver.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/LockBroadcastReceiver.kt
new file mode 100644
index 000000000..15ec8f1bc
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/LockBroadcastReceiver.kt
@@ -0,0 +1,23 @@
+package com.kunzisoft.keepass.magikeyboard.receiver
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+
+abstract class LockBroadcastReceiver : BroadcastReceiver() {
+
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.action
+ if (action != null && action == LOCK_ACTION) {
+ onReceiveLock(context, intent)
+ }
+ }
+
+ abstract fun onReceiveLock(context: Context, intent: Intent)
+
+ companion object {
+
+ const val LOCK_ACTION = "com.kunzisoft.keepass.LOCK"
+ }
+
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/NotificationDeleteBroadcastReceiver.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/NotificationDeleteBroadcastReceiver.java
deleted file mode 100644
index f56352adf..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/NotificationDeleteBroadcastReceiver.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.kunzisoft.keepass.magikeyboard.receiver;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService;
-import com.kunzisoft.keepass.magikeyboard.MagikIME;
-
-public class NotificationDeleteBroadcastReceiver extends BroadcastReceiver {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- // Clear the entry if define in preferences
- SharedPreferences sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(context);
- if (sharedPreferences.getBoolean(context.getString(R.string.keyboard_notification_entry_clear_close_key),
- context.getResources().getBoolean(R.bool.keyboard_notification_entry_clear_close_default))) {
- MagikIME.deleteEntryKey(context);
- }
-
- // Stop the service in all cases
- context.stopService(new Intent(context, KeyboardEntryNotificationService.class));
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/NotificationDeleteBroadcastReceiver.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/NotificationDeleteBroadcastReceiver.kt
new file mode 100644
index 000000000..692bcbeb0
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/receiver/NotificationDeleteBroadcastReceiver.kt
@@ -0,0 +1,23 @@
+package com.kunzisoft.keepass.magikeyboard.receiver
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService
+import com.kunzisoft.keepass.magikeyboard.MagikIME
+
+class NotificationDeleteBroadcastReceiver : BroadcastReceiver() {
+
+ override fun onReceive(context: Context, intent: Intent) {
+ // Clear the entry if define in preferences
+ val sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(context)
+ if (sharedPreferences.getBoolean(context.getString(R.string.keyboard_notification_entry_clear_close_key),
+ context.resources.getBoolean(R.bool.keyboard_notification_entry_clear_close_default))) {
+ MagikIME.deleteEntryKey(context)
+ }
+
+ // Stop the service in all cases
+ context.stopService(Intent(context, KeyboardEntryNotificationService::class.java))
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.java
deleted file mode 100644
index 5b4ea4151..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.kunzisoft.keepass.magikeyboard.view;
-
-import android.content.Context;
-import android.inputmethodservice.Keyboard;
-import android.inputmethodservice.KeyboardView;
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.util.AttributeSet;
-
-import static com.kunzisoft.keepass.magikeyboard.MagikIME.KEY_BACK_KEYBOARD;
-import static com.kunzisoft.keepass.magikeyboard.MagikIME.KEY_CHANGE_KEYBOARD;
-
-public class MagikeyboardView extends KeyboardView {
-
- public MagikeyboardView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public MagikeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- public MagikeyboardView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected boolean onLongPress(Keyboard.Key key) {
- // TODO Action on long press
- if (key.codes[0] == KEY_BACK_KEYBOARD) {
- getOnKeyboardActionListener().onKey(KEY_CHANGE_KEYBOARD, null);
- return true;
- } else {
- //Log.d("LatinKeyboardView", "KEY: " + key.codes[0]);
- return super.onLongPress(key);
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.kt
new file mode 100644
index 000000000..bec10a337
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.kt
@@ -0,0 +1,32 @@
+package com.kunzisoft.keepass.magikeyboard.view
+
+import android.content.Context
+import android.inputmethodservice.Keyboard
+import android.inputmethodservice.KeyboardView
+import android.os.Build
+import android.support.annotation.RequiresApi
+import android.util.AttributeSet
+
+import com.kunzisoft.keepass.magikeyboard.MagikIME.Companion.KEY_BACK_KEYBOARD
+import com.kunzisoft.keepass.magikeyboard.MagikIME.Companion.KEY_CHANGE_KEYBOARD
+
+class MagikeyboardView : KeyboardView {
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
+
+ constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
+
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
+
+ override fun onLongPress(key: Keyboard.Key): Boolean {
+ // TODO Action on long press
+ if (key.codes[0] == KEY_BACK_KEYBOARD) {
+ onKeyboardActionListener.onKey(KEY_CHANGE_KEYBOARD, null)
+ return true
+ } else {
+ //Log.d("LatinKeyboardView", "KEY: " + key.codes[0]);
+ return super.onLongPress(key)
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/Entry.java b/app/src/main/java/com/kunzisoft/keepass/model/Entry.java
deleted file mode 100644
index 9cb01e9e3..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/model/Entry.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.kunzisoft.keepass.model;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class Entry implements Parcelable {
-
- private String title;
- private String username;
- private String password;
- private String url;
- private List customFields;
-
- public Entry() {
- this.title = "";
- this.username = "";
- this.password = "";
- this.url = "";
- this.customFields = new ArrayList<>();
- }
-
- protected Entry(Parcel in) {
- title = in.readString();
- username = in.readString();
- password = in.readString();
- url = in.readString();
- //noinspection unchecked
- customFields = in.readArrayList(Field.class.getClassLoader());
- }
-
- public static final Creator CREATOR = new Creator() {
- @Override
- public Entry createFromParcel(Parcel in) {
- return new Entry(in);
- }
-
- @Override
- public Entry[] newArray(int size) {
- return new Entry[size];
- }
- };
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
-
- public List getCustomFields() {
- return customFields;
- }
-
- public void addCustomField(Field field) {
- this.customFields.add(field);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int i) {
- parcel.writeString(title);
- parcel.writeString(username);
- parcel.writeString(password);
- parcel.writeString(url);
- parcel.writeArray(customFields.toArray());
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/Entry.kt b/app/src/main/java/com/kunzisoft/keepass/model/Entry.kt
new file mode 100644
index 000000000..19fae9899
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/model/Entry.kt
@@ -0,0 +1,51 @@
+package com.kunzisoft.keepass.model
+
+import android.os.Parcel
+import android.os.Parcelable
+
+import java.util.ArrayList
+
+class Entry : Parcelable {
+
+ var title: String = ""
+ var username: String = ""
+ var password: String = ""
+ var url: String = ""
+ var customFields: MutableList = ArrayList()
+
+ constructor()
+
+ private constructor(parcel: Parcel) {
+ title = parcel.readString()
+ username = parcel.readString()
+ password = parcel.readString()
+ url = parcel.readString()
+ parcel.readList(customFields, Field::class.java.classLoader)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ override fun writeToParcel(parcel: Parcel, i: Int) {
+ parcel.writeString(title)
+ parcel.writeString(username)
+ parcel.writeString(password)
+ parcel.writeString(url)
+ parcel.writeArray(customFields.toTypedArray())
+ }
+
+ companion object {
+
+ @JvmField
+ val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): Entry {
+ return Entry(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/Field.java b/app/src/main/java/com/kunzisoft/keepass/model/Field.java
deleted file mode 100644
index 69b5e0996..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/model/Field.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.kunzisoft.keepass.model;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class Field implements Parcelable {
-
- private String name;
- private String value;
-
- public Field(String name, String value) {
- this.name = name;
- this.value = value;
- }
-
- public Field(Parcel in) {
- this.name = in.readString();
- this.value = in.readString();
- }
-
- public static final Creator CREATOR = new Creator() {
- @Override
- public Field createFromParcel(Parcel in) {
- return new Field(in);
- }
-
- @Override
- public Field[] newArray(int size) {
- return new Field[size];
- }
- };
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(name);
- dest.writeString(value);
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/Field.kt b/app/src/main/java/com/kunzisoft/keepass/model/Field.kt
new file mode 100644
index 000000000..7a2001e85
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/model/Field.kt
@@ -0,0 +1,43 @@
+package com.kunzisoft.keepass.model
+
+import android.os.Parcel
+import android.os.Parcelable
+
+class Field : Parcelable {
+
+ var name: String? = null
+ var value: String? = null
+
+ constructor(name: String, value: String) {
+ this.name = name
+ this.value = value
+ }
+
+ constructor(parcel: Parcel) {
+ this.name = parcel.readString()
+ this.value = parcel.readString()
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ dest.writeString(name)
+ dest.writeString(value)
+ }
+
+ companion object {
+
+ @JvmField
+ val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): Field {
+ return Field(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java
deleted file mode 100644
index 3f1bb91e7..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.notifications;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.os.Build;
-import android.os.IBinder;
-import android.preference.PreferenceManager;
-import android.support.v4.app.NotificationCompat;
-import android.util.Log;
-import android.util.TypedValue;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.database.exception.SamsungClipboardException;
-import com.kunzisoft.keepass.activities.stylish.Stylish;
-import com.kunzisoft.keepass.timeout.ClipboardHelper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.Nullable;
-
-public class NotificationCopyingService extends Service {
-
- private static final String TAG = NotificationCopyingService.class.getName();
- private static final String CHANNEL_ID_COPYING = "CHANNEL_ID_COPYING";
- private static final String CHANNEL_NAME_COPYING = "Copy fields";
-
- public static final String ACTION_NEW_NOTIFICATION = "ACTION_NEW_NOTIFICATION";
- public static final String EXTRA_ENTRY_TITLE = "EXTRA_ENTRY_TITLE";
- public static final String EXTRA_FIELDS = "EXTRA_FIELDS";
- public static final String ACTION_CLEAN_CLIPBOARD = "ACTION_CLEAN_CLIPBOARD";
-
- private NotificationManager notificationManager;
- private ClipboardHelper clipboardHelper;
- private Thread cleanNotificationTimer;
- private Thread countingDownTask;
- private int notificationId = 1;
- private long notificationTimeoutMilliSecs;
-
- private int colorNotificationAccent;
-
- public NotificationCopyingService() {
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- clipboardHelper = new ClipboardHelper(this);
-
- // Create notification channel for Oreo+
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- NotificationChannel channel = new NotificationChannel(CHANNEL_ID_COPYING,
- CHANNEL_NAME_COPYING,
- NotificationManager.IMPORTANCE_LOW);
- notificationManager.createNotificationChannel(channel);
- }
-
- // Get the color
- setTheme(Stylish.getThemeId(this));
- TypedValue typedValue = new TypedValue();
- Resources.Theme theme = getTheme();
- theme.resolveAttribute(R.attr.colorPrimary, typedValue, true);
- colorNotificationAccent = typedValue.data;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- //Get settings
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- String sClipClear = prefs.getString(getString(R.string.clipboard_timeout_key),
- getString(R.string.clipboard_timeout_default));
- notificationTimeoutMilliSecs = Long.parseLong(sClipClear);
-
- if (intent == null) {
- Log.w(TAG, "null intent");
-
- } else if (ACTION_NEW_NOTIFICATION.equals(intent.getAction())) {
- String title = intent.getStringExtra(EXTRA_ENTRY_TITLE);
- newNotification(title, constructListOfField(intent));
-
- } else if (ACTION_CLEAN_CLIPBOARD.equals(intent.getAction())) {
- stopTask(countingDownTask);
- try {
- clipboardHelper.cleanClipboard();
- } catch (SamsungClipboardException e) {
- Log.e(TAG, "Clipboard can't be cleaned", e);
- }
-
- } else {
- for (String actionKey : NotificationField.getAllActionKeys()) {
- if (actionKey.equals(intent.getAction())) {
- NotificationField fieldToCopy = intent.getParcelableExtra(
- NotificationField.getExtraKeyLinkToActionKey(actionKey));
- ArrayList nextFields = constructListOfField(intent);
- // Remove the current field from the next fields
- if (nextFields.contains(fieldToCopy))
- nextFields.remove(fieldToCopy);
- copyField(fieldToCopy, nextFields);
- }
- }
- }
- return START_NOT_STICKY;
- }
-
- private ArrayList constructListOfField(Intent intent) {
- ArrayList fieldList = new ArrayList<>();
- if (intent != null && intent.getExtras() != null) {
- if (intent.getExtras().containsKey(EXTRA_FIELDS))
- fieldList = intent.getParcelableArrayListExtra(EXTRA_FIELDS);
- }
- return fieldList;
- }
-
- private PendingIntent getCopyPendingIntent(NotificationField fieldToCopy, ArrayList fieldsToAdd) {
- Intent copyIntent = new Intent(this, NotificationCopyingService.class);
- copyIntent.setAction(fieldToCopy.getActionKey());
- copyIntent.putExtra(fieldToCopy.getExtraKey(), fieldToCopy);
- copyIntent.putParcelableArrayListExtra(EXTRA_FIELDS, fieldsToAdd);
-
- return PendingIntent.getService(
- this, 0, copyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private void newNotification(@Nullable String title, ArrayList fieldsToAdd) {
- stopTask(countingDownTask);
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_COPYING)
- .setSmallIcon(R.drawable.ic_key_white_24dp)
- .setColor(colorNotificationAccent);
- if (title != null)
- builder.setContentTitle(title);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
- builder.setVisibility(Notification.VISIBILITY_SECRET);
-
- if (fieldsToAdd.size() > 0) {
- NotificationField field = fieldsToAdd.get(0);
- builder.setContentText(field.copyText);
- builder.setContentIntent(getCopyPendingIntent(field, fieldsToAdd));
-
- // Add extra actions without 1st field
- List fieldsWithoutFirstField = new ArrayList<>(fieldsToAdd);
- fieldsWithoutFirstField.remove(field);
- // Add extra actions
- for (NotificationField fieldToAdd : fieldsWithoutFirstField) {
- builder.addAction(R.drawable.ic_key_white_24dp, fieldToAdd.label,
- getCopyPendingIntent(fieldToAdd, fieldsToAdd));
- }
- }
-
- notificationManager.cancel(notificationId);
- notificationManager.notify(++notificationId, builder.build());
-
- int myNotificationId = notificationId;
- stopTask(cleanNotificationTimer);
- cleanNotificationTimer = new Thread(() -> {
- try {
- Thread.sleep(notificationTimeoutMilliSecs);
- } catch (InterruptedException e) {
- cleanNotificationTimer = null;
- return;
- }
- notificationManager.cancel(myNotificationId);
- });
- cleanNotificationTimer.start();
- }
-
- private void copyField(NotificationField fieldToCopy, ArrayList nextFields) {
- stopTask(countingDownTask);
- stopTask(cleanNotificationTimer);
-
- try {
- clipboardHelper.copyToClipboard(fieldToCopy.label, fieldToCopy.value);
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_COPYING)
- .setSmallIcon(R.drawable.ic_key_white_24dp)
- .setColor(colorNotificationAccent)
- .setContentTitle(fieldToCopy.label);
-
- // New action with next field if click
- if (nextFields.size() > 0) {
- NotificationField nextField = nextFields.get(0);
- builder.setContentText(nextField.copyText);
- builder.setContentIntent(getCopyPendingIntent(nextField, nextFields));
- // Else tell to swipe for a clean
- } else {
- builder.setContentText(getString(R.string.clipboard_swipe_clean));
- }
-
- Intent cleanIntent = new Intent(this, NotificationCopyingService.class);
- cleanIntent.setAction(ACTION_CLEAN_CLIPBOARD);
- PendingIntent cleanPendingIntent = PendingIntent.getService(
- this, 0, cleanIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- builder.setDeleteIntent(cleanPendingIntent);
-
- int myNotificationId = notificationId;
-
- countingDownTask = new Thread(() -> {
- int maxPos = 100;
- long posDurationMills = notificationTimeoutMilliSecs / maxPos;
- for (int pos = maxPos; pos > 0; --pos) {
- builder.setProgress(maxPos, pos, false);
- notificationManager.notify(myNotificationId, builder.build());
- try {
- Thread.sleep(posDurationMills);
- } catch (InterruptedException e) {
- break;
- }
- }
- countingDownTask = null;
- notificationManager.cancel(myNotificationId);
- // Clean password only if no next field
- if (nextFields.size() <= 0)
- try {
- clipboardHelper.cleanClipboard();
- } catch (SamsungClipboardException e) {
- Log.e(TAG, "Clipboard can't be cleaned", e);
- }
- });
- countingDownTask.start();
-
- } catch (Exception e) {
- Log.e(TAG, "Clipboard can't be populate", e);
- }
- }
-
- private void stopTask(Thread task) {
- if (task != null && task.isAlive())
- task.interrupt();
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.kt
new file mode 100644
index 000000000..8cd647478
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.notifications
+
+import android.app.*
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.IBinder
+import android.preference.PreferenceManager
+import android.support.v4.app.NotificationCompat
+import android.util.Log
+import android.util.TypedValue
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.activities.stylish.Stylish
+import com.kunzisoft.keepass.database.exception.SamsungClipboardException
+import com.kunzisoft.keepass.timeout.ClipboardHelper
+import java.util.*
+
+class NotificationCopyingService : Service() {
+
+ private var notificationManager: NotificationManager? = null
+ private var clipboardHelper: ClipboardHelper? = null
+ private var cleanNotificationTimer: Thread? = null
+ private var countingDownTask: Thread? = null
+ private var notificationId = 1
+ private var notificationTimeoutMilliSecs: Long = 0
+
+ private var colorNotificationAccent: Int = 0
+
+ override fun onBind(intent: Intent): IBinder? {
+ return null
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ clipboardHelper = ClipboardHelper(this)
+
+ // Create notification channel for Oreo+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel = NotificationChannel(CHANNEL_ID_COPYING,
+ CHANNEL_NAME_COPYING,
+ NotificationManager.IMPORTANCE_LOW)
+ notificationManager?.createNotificationChannel(channel)
+ }
+
+ // Get the color
+ setTheme(Stylish.getThemeId(this))
+ val typedValue = TypedValue()
+ val theme = theme
+ theme.resolveAttribute(R.attr.colorPrimary, typedValue, true)
+ colorNotificationAccent = typedValue.data
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ //Get settings
+ val prefs = PreferenceManager.getDefaultSharedPreferences(this)
+ val sClipClear = prefs.getString(getString(R.string.clipboard_timeout_key),
+ getString(R.string.clipboard_timeout_default))
+ notificationTimeoutMilliSecs = java.lang.Long.parseLong(sClipClear)
+
+ when {
+ intent == null -> Log.w(TAG, "null intent")
+ ACTION_NEW_NOTIFICATION == intent.action -> {
+ val title = intent.getStringExtra(EXTRA_ENTRY_TITLE)
+ newNotification(title, constructListOfField(intent))
+
+ }
+ ACTION_CLEAN_CLIPBOARD == intent.action -> {
+ stopTask(countingDownTask)
+ try {
+ clipboardHelper!!.cleanClipboard()
+ } catch (e: SamsungClipboardException) {
+ Log.e(TAG, "Clipboard can't be cleaned", e)
+ }
+
+ }
+ else -> for (actionKey in NotificationField.allActionKeys) {
+ if (actionKey == intent.action) {
+ val fieldToCopy = intent.getParcelableExtra(
+ NotificationField.getExtraKeyLinkToActionKey(actionKey))
+ val nextFields = constructListOfField(intent)
+ // Remove the current field from the next fields
+ nextFields.remove(fieldToCopy)
+ copyField(fieldToCopy, nextFields)
+ }
+ }
+ }
+ return START_NOT_STICKY
+ }
+
+ private fun constructListOfField(intent: Intent?): ArrayList {
+ var fieldList = ArrayList()
+ if (intent != null && intent.extras != null) {
+ if (intent.extras!!.containsKey(EXTRA_FIELDS))
+ fieldList = intent.getParcelableArrayListExtra(EXTRA_FIELDS)
+ }
+ return fieldList
+ }
+
+ private fun getCopyPendingIntent(fieldToCopy: NotificationField, fieldsToAdd: ArrayList): PendingIntent {
+ val copyIntent = Intent(this, NotificationCopyingService::class.java)
+ copyIntent.action = fieldToCopy.actionKey
+ copyIntent.putExtra(fieldToCopy.extraKey, fieldToCopy)
+ copyIntent.putParcelableArrayListExtra(EXTRA_FIELDS, fieldsToAdd)
+
+ return PendingIntent.getService(
+ this, 0, copyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
+ }
+
+ private fun newNotification(title: String?, fieldsToAdd: ArrayList) {
+ stopTask(countingDownTask)
+
+ val builder = NotificationCompat.Builder(this, CHANNEL_ID_COPYING)
+ .setSmallIcon(R.drawable.ic_key_white_24dp)
+ .setColor(colorNotificationAccent)
+ if (title != null)
+ builder.setContentTitle(title)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ builder.setVisibility(Notification.VISIBILITY_SECRET)
+
+ if (fieldsToAdd.size > 0) {
+ val field = fieldsToAdd[0]
+ builder.setContentText(field.copyText)
+ builder.setContentIntent(getCopyPendingIntent(field, fieldsToAdd))
+
+ // Add extra actions without 1st field
+ val fieldsWithoutFirstField = ArrayList(fieldsToAdd)
+ fieldsWithoutFirstField.remove(field)
+ // Add extra actions
+ for (fieldToAdd in fieldsWithoutFirstField) {
+ builder.addAction(R.drawable.ic_key_white_24dp, fieldToAdd.label,
+ getCopyPendingIntent(fieldToAdd, fieldsToAdd))
+ }
+ }
+
+ notificationManager?.cancel(notificationId)
+ notificationManager?.notify(++notificationId, builder.build())
+
+ val myNotificationId = notificationId
+ stopTask(cleanNotificationTimer)
+ cleanNotificationTimer = Thread {
+ try {
+ Thread.sleep(notificationTimeoutMilliSecs)
+ } catch (e: InterruptedException) {
+ cleanNotificationTimer = null
+ }
+ notificationManager?.cancel(myNotificationId)
+ }
+ cleanNotificationTimer?.start()
+ }
+
+ private fun copyField(fieldToCopy: NotificationField, nextFields: ArrayList) {
+ stopTask(countingDownTask)
+ stopTask(cleanNotificationTimer)
+
+ try {
+ clipboardHelper?.copyToClipboard(fieldToCopy.label, fieldToCopy.value)
+
+ val builder = NotificationCompat.Builder(this, CHANNEL_ID_COPYING)
+ .setSmallIcon(R.drawable.ic_key_white_24dp)
+ .setColor(colorNotificationAccent)
+ .setContentTitle(fieldToCopy.label)
+
+ // New action with next field if click
+ if (nextFields.size > 0) {
+ val nextField = nextFields[0]
+ builder.setContentText(nextField.copyText)
+ builder.setContentIntent(getCopyPendingIntent(nextField, nextFields))
+ // Else tell to swipe for a clean
+ } else {
+ builder.setContentText(getString(R.string.clipboard_swipe_clean))
+ }
+
+ val cleanIntent = Intent(this, NotificationCopyingService::class.java)
+ cleanIntent.action = ACTION_CLEAN_CLIPBOARD
+ val cleanPendingIntent = PendingIntent.getService(
+ this, 0, cleanIntent, PendingIntent.FLAG_UPDATE_CURRENT)
+ builder.setDeleteIntent(cleanPendingIntent)
+
+ val myNotificationId = notificationId
+
+ countingDownTask = Thread {
+ val maxPos = 100
+ val posDurationMills = notificationTimeoutMilliSecs / maxPos
+ for (pos in maxPos downTo 1) {
+ builder.setProgress(maxPos, pos, false)
+ notificationManager?.notify(myNotificationId, builder.build())
+ try {
+ Thread.sleep(posDurationMills)
+ } catch (e: InterruptedException) {
+ break
+ }
+
+ }
+ countingDownTask = null
+ notificationManager?.cancel(myNotificationId)
+ // Clean password only if no next field
+ if (nextFields.size <= 0)
+ try {
+ clipboardHelper?.cleanClipboard()
+ } catch (e: SamsungClipboardException) {
+ Log.e(TAG, "Clipboard can't be cleaned", e)
+ }
+ }
+ countingDownTask?.start()
+
+ } catch (e: Exception) {
+ Log.e(TAG, "Clipboard can't be populate", e)
+ }
+
+ }
+
+ private fun stopTask(task: Thread?) {
+ if (task != null && task.isAlive)
+ task.interrupt()
+ }
+
+ companion object {
+
+ private val TAG = NotificationCopyingService::class.java.name
+ private const val CHANNEL_ID_COPYING = "CHANNEL_ID_COPYING"
+ private const val CHANNEL_NAME_COPYING = "Copy fields"
+
+ const val ACTION_NEW_NOTIFICATION = "ACTION_NEW_NOTIFICATION"
+ const val EXTRA_ENTRY_TITLE = "EXTRA_ENTRY_TITLE"
+ const val EXTRA_FIELDS = "EXTRA_FIELDS"
+ const val ACTION_CLEAN_CLIPBOARD = "ACTION_CLEAN_CLIPBOARD"
+ }
+
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt
index 1573dea8d..96f517037 100644
--- a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt
@@ -66,7 +66,7 @@ object NotificationEntryCopyManager {
|| PreferencesUtil.allowCopyPasswordAndProtectedFields(context)) {
notificationFields.add(
NotificationField(
- NotificationField.NotificationFieldId.getAnonymousFieldId()[anonymousFieldNumber],
+ NotificationField.NotificationFieldId.anonymousFieldId[anonymousFieldNumber],
value.toString(),
key,
context.resources))
@@ -75,7 +75,7 @@ object NotificationEntryCopyManager {
}
})
} catch (e: ArrayIndexOutOfBoundsException) {
- Log.w("NotificationEntryCopyMg", "Only " + NotificationField.NotificationFieldId.getAnonymousFieldId().size +
+ Log.w("NotificationEntryCopyMg", "Only " + NotificationField.NotificationFieldId.anonymousFieldId.size +
" anonymous notifications are available")
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationField.java b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationField.java
deleted file mode 100644
index 6d35bbf3f..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationField.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- *
- * Copyright 2017 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.notifications;
-
-import android.content.res.Resources;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import com.kunzisoft.keepass.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.Nullable;
-
-/**
- * Utility class to manage fields in Notifications
- */
-public class NotificationField implements Parcelable {
-
- private static final String TAG = NotificationField.class.getName();
-
- private NotificationFieldId id;
- String value;
- String label;
- String copyText;
-
- public NotificationField(NotificationFieldId id, String value, Resources resources) {
- this.id = id;
- this.value = value;
- this.label = getLabel(resources);
- this.copyText = getCopyText(resources);
- }
-
- public NotificationField(NotificationFieldId id, String value, String label, Resources resources) {
- this.id = id;
- this.value = value;
- this.label = label;
- this.copyText = getCopyText(resources);
- }
-
- protected NotificationField(Parcel in) {
- id = NotificationFieldId.values()[in.readInt()];
- value = in.readString();
- label = in.readString();
- copyText = in.readString();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(id.ordinal());
- dest.writeString(value);
- dest.writeString(label);
- dest.writeString(copyText);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Creator CREATOR = new Creator() {
- @Override
- public NotificationField createFromParcel(Parcel in) {
- return new NotificationField(in);
- }
-
- @Override
- public NotificationField[] newArray(int size) {
- return new NotificationField[size];
- }
- };
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- NotificationField field = (NotificationField) o;
- return id.equals(field.id);
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- public enum NotificationFieldId {
- USERNAME, PASSWORD, FIELD_A, FIELD_B, FIELD_C;
-
- public static NotificationFieldId[] getAnonymousFieldId() {
- return new NotificationFieldId[] {FIELD_A, FIELD_B, FIELD_C};
- }
- }
-
- private static final String ACTION_COPY_PREFIX = "ACTION_COPY_";
- private static final String EXTRA_KEY_PREFIX = "EXTRA_";
-
- /**
- * Return EXTRA_KEY link to ACTION_KEY, or null if ACTION_KEY is unknown
- */
- public static @Nullable String getExtraKeyLinkToActionKey(String actionKey) {
- try {
- if (actionKey.startsWith(ACTION_COPY_PREFIX)) {
- String idName = actionKey.substring(ACTION_COPY_PREFIX.length(), actionKey.length());
- return getExtraKey(NotificationFieldId.valueOf(idName));
- }
- } catch (Exception e) {
- Log.e(TAG, "Can't get Extra Key from Action Key", e);
- }
- return null;
- }
-
- private static String getActionKey(NotificationFieldId id) {
- return ACTION_COPY_PREFIX + id.name();
- }
-
- public String getActionKey() {
- return getActionKey(id);
- }
-
- private static String getExtraKey(NotificationFieldId id) {
- return EXTRA_KEY_PREFIX + id.name();
- }
-
- public String getExtraKey() {
- return getExtraKey(id);
- }
-
- public static List getAllActionKeys() {
- List actionKeys = new ArrayList<>();
- for (NotificationFieldId id : NotificationFieldId.values()) {
- actionKeys.add(getActionKey(id));
- }
- return actionKeys;
- }
-
- private String getLabel(Resources resources) {
- switch (id) {
- case USERNAME:
- return resources.getString(R.string.entry_user_name);
- case PASSWORD:
- return resources.getString(R.string.entry_password);
- default:
- return id.name();
- }
- }
-
- private String getCopyText(Resources resources) {
- return resources.getString(R.string.select_to_copy, label);
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationField.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationField.kt
new file mode 100644
index 000000000..9d51a0d93
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationField.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.notifications
+
+import android.content.res.Resources
+import android.os.Parcel
+import android.os.Parcelable
+import android.util.Log
+
+import com.kunzisoft.keepass.R
+
+import java.util.ArrayList
+
+/**
+ * Utility class to manage fields in Notifications
+ */
+open class NotificationField : Parcelable {
+
+ private var id: NotificationFieldId = NotificationFieldId.UNKNOWN
+ var value: String
+ var label: String
+ var copyText: String
+
+ val actionKey: String
+ get() = getActionKey(id)
+
+ val extraKey: String
+ get() = getExtraKey(id)
+
+ constructor(id: NotificationFieldId, value: String, resources: Resources) {
+ this.id = id
+ this.value = value
+ this.label = getLabel(resources)
+ this.copyText = getCopyText(resources)
+ }
+
+ constructor(id: NotificationFieldId, value: String, label: String, resources: Resources) {
+ this.id = id
+ this.value = value
+ this.label = label
+ this.copyText = getCopyText(resources)
+ }
+
+ protected constructor(parcel: Parcel) {
+ id = NotificationFieldId.values()[parcel.readInt()]
+ value = parcel.readString()
+ label = parcel.readString()
+ copyText = parcel.readString()
+ }
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ dest.writeInt(id.ordinal)
+ dest.writeString(value)
+ dest.writeString(label)
+ dest.writeString(copyText)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || javaClass != other.javaClass) return false
+ val field = other as NotificationField
+ return id == field.id
+ }
+
+ override fun hashCode(): Int {
+ return id.hashCode()
+ }
+
+ enum class NotificationFieldId {
+ UNKNOWN, USERNAME, PASSWORD, FIELD_A, FIELD_B, FIELD_C;
+
+ companion object {
+
+ val anonymousFieldId: Array
+ get() = arrayOf(FIELD_A, FIELD_B, FIELD_C)
+ }
+ }
+
+ private fun getLabel(resources: Resources): String {
+ return when (id) {
+ NotificationFieldId.USERNAME -> resources.getString(R.string.entry_user_name)
+ NotificationFieldId.PASSWORD -> resources.getString(R.string.entry_password)
+ else -> id.name
+ }
+ }
+
+ private fun getCopyText(resources: Resources): String {
+ return resources.getString(R.string.select_to_copy, label)
+ }
+
+ companion object {
+
+ private val TAG = NotificationField::class.java.name
+
+ @JvmField
+ val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
+ override fun createFromParcel(`in`: Parcel): NotificationField {
+ return NotificationField(`in`)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+
+ private const val ACTION_COPY_PREFIX = "ACTION_COPY_"
+ private const val EXTRA_KEY_PREFIX = "EXTRA_"
+
+ /**
+ * Return EXTRA_KEY link to ACTION_KEY, or null if ACTION_KEY is unknown
+ */
+ fun getExtraKeyLinkToActionKey(actionKey: String): String? {
+ try {
+ if (actionKey.startsWith(ACTION_COPY_PREFIX)) {
+ val idName = actionKey.substring(ACTION_COPY_PREFIX.length, actionKey.length)
+ return getExtraKey(NotificationFieldId.valueOf(idName))
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Can't get Extra Key from Action Key", e)
+ }
+
+ return null
+ }
+
+ private fun getActionKey(id: NotificationFieldId): String {
+ return ACTION_COPY_PREFIX + id.name
+ }
+
+ private fun getExtraKey(id: NotificationFieldId): String {
+ return EXTRA_KEY_PREFIX + id.name
+ }
+
+ val allActionKeys: List
+ get() {
+ val actionKeys = ArrayList()
+ for (id in NotificationFieldId.values()) {
+ actionKeys.add(getActionKey(id))
+ }
+ return actionKeys
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.java
deleted file mode 100644
index 7a9f352ff..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.password;
-
-import android.content.Context;
-
-import com.kunzisoft.keepass.R;
-
-import java.security.SecureRandom;
-
-public class PasswordGenerator {
- private static final String UPPERCASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- private static final String LOWERCASE_CHARS = "abcdefghijklmnopqrstuvwxyz";
- private static final String DIGIT_CHARS = "0123456789";
- private static final String MINUS_CHAR = "-";
- private static final String UNDERLINE_CHAR = "_";
- private static final String SPACE_CHAR = " ";
- private static final String SPECIAL_CHARS = "!\"#$%&'*+,./:;=?@\\^`";
- private static final String BRACKET_CHARS = "[]{}()<>";
-
- // From KeePassXC code https://github.com/keepassxreboot/keepassxc/pull/538
- private String extendedChars() {
- StringBuilder charSet = new StringBuilder();
- // [U+0080, U+009F] are C1 control characters,
- // U+00A0 is non-breaking space
- for(char ch = '\u00A1'; ch <= '\u00AC'; ++ch)
- charSet.append(ch);
- // U+00AD is soft hyphen (format character)
- for(char ch = '\u00AE'; ch < '\u00FF'; ++ch)
- charSet.append(ch);
- charSet.append('\u00FF');
- return charSet.toString();
- }
-
- private Context cxt;
-
- public PasswordGenerator(Context cxt) {
- this.cxt = cxt;
- }
-
- public String generatePassword(int length,
- boolean upperCase,
- boolean lowerCase,
- boolean digits,
- boolean minus,
- boolean underline,
- boolean space,
- boolean specials,
- boolean brackets,
- boolean extended) throws IllegalArgumentException{
- // Desired password length is 0 or less
- if (length <= 0) {
- throw new IllegalArgumentException(cxt.getString(R.string.error_wrong_length));
- }
-
- // No option has been checked
- if ( !upperCase
- && !lowerCase
- && !digits
- && !minus
- && !underline
- && !space
- && !specials
- && !brackets
- && !extended) {
- throw new IllegalArgumentException(cxt.getString(R.string.error_pass_gen_type));
- }
-
- String characterSet = getCharacterSet(
- upperCase,
- lowerCase,
- digits,
- minus,
- underline,
- space,
- specials,
- brackets,
- extended);
-
- int size = characterSet.length();
-
- StringBuilder buffer = new StringBuilder();
-
- SecureRandom random = new SecureRandom(); // use more secure variant of Random!
- if (size > 0) {
- for (int i = 0; i < length; i++) {
- char c = characterSet.charAt((char) random.nextInt(size));
- buffer.append(c);
- }
- }
- return buffer.toString();
- }
-
- private String getCharacterSet(boolean upperCase,
- boolean lowerCase,
- boolean digits,
- boolean minus,
- boolean underline,
- boolean space,
- boolean specials,
- boolean brackets,
- boolean extended) {
- StringBuilder charSet = new StringBuilder();
-
- if (upperCase) {
- charSet.append(UPPERCASE_CHARS);
- }
-
- if (lowerCase) {
- charSet.append(LOWERCASE_CHARS);
- }
-
- if (digits) {
- charSet.append(DIGIT_CHARS);
- }
-
- if (minus) {
- charSet.append(MINUS_CHAR);
- }
-
- if (underline) {
- charSet.append(UNDERLINE_CHAR);
- }
-
- if (space) {
- charSet.append(SPACE_CHAR);
- }
-
- if (specials) {
- charSet.append(SPECIAL_CHARS);
- }
-
- if (brackets) {
- charSet.append(BRACKET_CHARS);
- }
-
- if (extended) {
- charSet.append(extendedChars());
- }
-
- return charSet.toString();
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.kt b/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.kt
new file mode 100644
index 000000000..fc722c115
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.password
+
+import android.content.res.Resources
+import com.kunzisoft.keepass.R
+import java.security.SecureRandom
+
+class PasswordGenerator(private val resources: Resources) {
+
+ // From KeePassXC code https://github.com/keepassxreboot/keepassxc/pull/538
+ private fun extendedChars(): String {
+ val charSet = StringBuilder()
+ // [U+0080, U+009F] are C1 control characters,
+ // U+00A0 is non-breaking space
+ run {
+ var ch = '\u00A1'
+ while (ch <= '\u00AC') {
+ charSet.append(ch)
+ ++ch
+ }
+ }
+ // U+00AD is soft hyphen (format character)
+ var ch = '\u00AE'
+ while (ch < '\u00FF') {
+ charSet.append(ch)
+ ++ch
+ }
+ charSet.append('\u00FF')
+ return charSet.toString()
+ }
+
+ @Throws(IllegalArgumentException::class)
+ fun generatePassword(length: Int,
+ upperCase: Boolean,
+ lowerCase: Boolean,
+ digits: Boolean,
+ minus: Boolean,
+ underline: Boolean,
+ space: Boolean,
+ specials: Boolean,
+ brackets: Boolean,
+ extended: Boolean): String {
+ // Desired password length is 0 or less
+ if (length <= 0) {
+ throw IllegalArgumentException(resources.getString(R.string.error_wrong_length))
+ }
+
+ // No option has been checked
+ if (!upperCase
+ && !lowerCase
+ && !digits
+ && !minus
+ && !underline
+ && !space
+ && !specials
+ && !brackets
+ && !extended) {
+ throw IllegalArgumentException(resources.getString(R.string.error_pass_gen_type))
+ }
+
+ val characterSet = getCharacterSet(
+ upperCase,
+ lowerCase,
+ digits,
+ minus,
+ underline,
+ space,
+ specials,
+ brackets,
+ extended)
+
+ val size = characterSet.length
+
+ val buffer = StringBuilder()
+
+ val random = SecureRandom() // use more secure variant of Random!
+ if (size > 0) {
+ for (i in 0 until length) {
+ buffer.append(characterSet[random.nextInt(size)])
+ }
+ }
+ return buffer.toString()
+ }
+
+ private fun getCharacterSet(upperCase: Boolean,
+ lowerCase: Boolean,
+ digits: Boolean,
+ minus: Boolean,
+ underline: Boolean,
+ space: Boolean,
+ specials: Boolean,
+ brackets: Boolean,
+ extended: Boolean): String {
+ val charSet = StringBuilder()
+
+ if (upperCase) {
+ charSet.append(UPPERCASE_CHARS)
+ }
+
+ if (lowerCase) {
+ charSet.append(LOWERCASE_CHARS)
+ }
+
+ if (digits) {
+ charSet.append(DIGIT_CHARS)
+ }
+
+ if (minus) {
+ charSet.append(MINUS_CHAR)
+ }
+
+ if (underline) {
+ charSet.append(UNDERLINE_CHAR)
+ }
+
+ if (space) {
+ charSet.append(SPACE_CHAR)
+ }
+
+ if (specials) {
+ charSet.append(SPECIAL_CHARS)
+ }
+
+ if (brackets) {
+ charSet.append(BRACKET_CHARS)
+ }
+
+ if (extended) {
+ charSet.append(extendedChars())
+ }
+
+ return charSet.toString()
+ }
+
+ companion object {
+ private const val UPPERCASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ private const val LOWERCASE_CHARS = "abcdefghijklmnopqrstuvwxyz"
+ private const val DIGIT_CHARS = "0123456789"
+ private const val MINUS_CHAR = "-"
+ private const val UNDERLINE_CHAR = "_"
+ private const val SPACE_CHAR = " "
+ private const val SPECIAL_CHARS = "!\"#$%&'*+,./:;=?@\\^`"
+ private const val BRACKET_CHARS = "[]{}()<>"
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java
deleted file mode 100644
index 306d10771..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings;
-
-import android.os.Bundle;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.activities.stylish.StylishActivity;
-
-public class MagikIMESettings extends StylishActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_toolbar);
- Toolbar toolbar = findViewById(R.id.toolbar);
- toolbar.setTitle(R.string.keyboard_setting_label);
- setSupportActionBar(toolbar);
- assert getSupportActionBar() != null;
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- if (savedInstanceState == null) {
- getSupportFragmentManager().beginTransaction()
- .replace(R.id.fragment_container, new MagikIMESettingsFragment())
- .commit();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch ( item.getItemId() ) {
- case android.R.id.home:
- onBackPressed();
- break;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.kt b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.kt
new file mode 100644
index 000000000..e84f678db
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings
+
+import android.os.Bundle
+import android.support.v7.widget.Toolbar
+import android.view.MenuItem
+
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.activities.stylish.StylishActivity
+
+class MagikIMESettings : StylishActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_toolbar)
+ val toolbar = findViewById(R.id.toolbar)
+ toolbar.setTitle(R.string.keyboard_setting_label)
+ setSupportActionBar(toolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+
+ if (savedInstanceState == null) {
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.fragment_container, MagikIMESettingsFragment())
+ .commit()
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ android.R.id.home -> onBackPressed()
+ }
+
+ return super.onOptionsItemSelected(item)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.java
deleted file mode 100644
index e99e6dd0c..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.kunzisoft.keepass.settings;
-
-import android.os.Bundle;
-import android.support.v7.preference.PreferenceFragmentCompat;
-
-import com.kunzisoft.keepass.R;
-
-public class MagikIMESettingsFragment extends PreferenceFragmentCompat {
-
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- // Load the preferences from an XML resource
- setPreferencesFromResource(R.xml.keyboard_preferences, rootKey);
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioViewHolder.java b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.kt
similarity index 58%
rename from app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioViewHolder.java
rename to app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.kt
index dd2549c34..bc35b54ac 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioViewHolder.java
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
@@ -17,21 +17,17 @@
* along with KeePass DX. If not, see .
*
*/
-package com.kunzisoft.keepass.settings.preferencedialogfragment.adapter;
+package com.kunzisoft.keepass.settings
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.widget.RadioButton;
+import android.os.Bundle
+import android.support.v7.preference.PreferenceFragmentCompat
-import com.kunzisoft.keepass.R;
+import com.kunzisoft.keepass.R
-public class ListRadioViewHolder extends RecyclerView.ViewHolder {
+class MagikIMESettingsFragment : PreferenceFragmentCompat() {
- public RadioButton radioButton;
-
- public ListRadioViewHolder(View itemView) {
- super(itemView);
-
- radioButton = itemView.findViewById(R.id.pref_dialog_list_radio);
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ // Load the preferences from an XML resource
+ setPreferencesFromResource(R.xml.keyboard_preferences, rootKey)
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java
deleted file mode 100644
index 82cc8743b..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceFragmentCompat;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.app.App;
-import com.kunzisoft.keepass.database.element.Database;
-
-public class MainPreferenceFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener {
-
- private Callback mCallback;
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
-
- if (context instanceof Callback) {
- mCallback = (Callback) context;
- } else {
- throw new IllegalStateException("Owner must implement " + Callback.class.getName());
- }
- }
-
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- setPreferencesFromResource(R.xml.preferences, rootKey);
-
- // add listeners for non-default actions
- Preference preference = findPreference(getString(R.string.app_key));
- preference.setOnPreferenceClickListener(this);
-
- preference = findPreference(getString(R.string.settings_form_filling_key));
- preference.setOnPreferenceClickListener(this);
-
- preference = findPreference(getString(R.string.settings_appearance_key));
- preference.setOnPreferenceClickListener(this);
-
- preference = findPreference(getString(R.string.db_key));
- preference.setOnPreferenceClickListener(this);
- Database db = App.Companion.getCurrentDatabase();
- if (!(db.getLoaded())) {
- preference.setEnabled(false);
- }
- }
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- // here you should use the same keys as you used in the xml-file
- if (preference.getKey().equals(getString(R.string.app_key))) {
- mCallback.onNestedPreferenceSelected(NestedSettingsFragment.Screen.APPLICATION);
- }
-
- if (preference.getKey().equals(getString(R.string.settings_form_filling_key))) {
- mCallback.onNestedPreferenceSelected(NestedSettingsFragment.Screen.FORM_FILLING);
- }
-
- if (preference.getKey().equals(getString(R.string.db_key))) {
- mCallback.onNestedPreferenceSelected(NestedSettingsFragment.Screen.DATABASE);
- }
-
- if (preference.getKey().equals(getString(R.string.settings_appearance_key))) {
- mCallback.onNestedPreferenceSelected(NestedSettingsFragment.Screen.APPEARANCE);
- }
-
- return false;
- }
-
- public interface Callback {
- void onNestedPreferenceSelected(NestedSettingsFragment.Screen key);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt
new file mode 100644
index 000000000..697961f5c
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings
+
+import android.content.Context
+import android.os.Bundle
+import android.support.v7.preference.Preference
+import android.support.v7.preference.PreferenceFragmentCompat
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.app.App
+
+class MainPreferenceFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
+
+ private var mCallback: Callback? = null
+
+ override fun onAttach(context: Context?) {
+ super.onAttach(context)
+
+ if (context is Callback) {
+ mCallback = context
+ } else {
+ throw IllegalStateException("Owner must implement " + Callback::class.java.name)
+ }
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ setPreferencesFromResource(R.xml.preferences, rootKey)
+
+ // add listeners for non-default actions
+ var preference = findPreference(getString(R.string.app_key))
+ preference.onPreferenceClickListener = this
+
+ preference = findPreference(getString(R.string.settings_form_filling_key))
+ preference.onPreferenceClickListener = this
+
+ preference = findPreference(getString(R.string.settings_appearance_key))
+ preference.onPreferenceClickListener = this
+
+ preference = findPreference(getString(R.string.db_key))
+ preference.onPreferenceClickListener = this
+
+ if (!App.currentDatabase.loaded) {
+ preference.isEnabled = false
+ }
+ }
+
+ override fun onPreferenceClick(preference: Preference): Boolean {
+ // here you should use the same keys as you used in the xml-file
+ if (preference.key == getString(R.string.app_key)) {
+ mCallback?.onNestedPreferenceSelected(NestedSettingsFragment.Screen.APPLICATION)
+ }
+
+ if (preference.key == getString(R.string.settings_form_filling_key)) {
+ mCallback?.onNestedPreferenceSelected(NestedSettingsFragment.Screen.FORM_FILLING)
+ }
+
+ if (preference.key == getString(R.string.db_key)) {
+ mCallback?.onNestedPreferenceSelected(NestedSettingsFragment.Screen.DATABASE)
+ }
+
+ if (preference.key == getString(R.string.settings_appearance_key)) {
+ mCallback?.onNestedPreferenceSelected(NestedSettingsFragment.Screen.APPEARANCE)
+ }
+
+ return false
+ }
+
+ interface Callback {
+ fun onNestedPreferenceSelected(key: NestedSettingsFragment.Screen)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java
deleted file mode 100644
index e5ea3ff7b..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- * Copyright 2017 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings;
-
-import android.content.ActivityNotFoundException;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.hardware.fingerprint.FingerprintManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.support.annotation.RequiresApi;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v4.app.DialogFragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceCategory;
-import android.support.v7.preference.PreferenceFragmentCompat;
-import android.util.Log;
-import android.view.autofill.AutofillManager;
-import android.widget.Toast;
-
-import com.kunzisoft.keepass.BuildConfig;
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper;
-import com.kunzisoft.keepass.app.App;
-import com.kunzisoft.keepass.database.element.Database;
-import com.kunzisoft.keepass.activities.dialogs.ProFeatureDialogFragment;
-import com.kunzisoft.keepass.activities.dialogs.UnavailableFeatureDialogFragment;
-import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFragment;
-import com.kunzisoft.keepass.education.Education;
-import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory;
-import com.kunzisoft.keepass.fingerprint.FingerPrintHelper;
-import com.kunzisoft.keepass.icons.IconPackChooser;
-import com.kunzisoft.keepass.activities.dialogs.KeyboardExplanationDialogFragment;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDescriptionPreferenceDialogFragmentCompat;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseKeyDerivationPreferenceDialogFragmentCompat;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseNamePreferenceDialogFragmentCompat;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.MemoryUsagePreferenceDialogFragmentCompat;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.ParallelismPreferenceDialogFragmentCompat;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.RoundsPreferenceDialogFragmentCompat;
-import com.kunzisoft.keepass.activities.stylish.Stylish;
-
-import java.lang.ref.WeakReference;
-
-public class NestedSettingsFragment extends PreferenceFragmentCompat
- implements Preference.OnPreferenceClickListener {
-
- public enum Screen {
- APPLICATION, FORM_FILLING, DATABASE, APPEARANCE
- }
-
- private static final String TAG_KEY = "NESTED_KEY";
-
- private static final int REQUEST_CODE_AUTOFILL = 5201;
-
- private Database database;
- private boolean databaseReadOnly;
-
- private int count = 0;
-
- private Preference roundPref;
- private Preference memoryPref;
- private Preference parallelismPref;
-
- public static NestedSettingsFragment newInstance(Screen key) {
- return newInstance(key, ReadOnlyHelper.READ_ONLY_DEFAULT);
- }
-
- public static NestedSettingsFragment newInstance(Screen key, boolean databaseReadOnly) {
- NestedSettingsFragment fragment = new NestedSettingsFragment();
- // supply arguments to bundle.
- Bundle args = new Bundle();
- args.putInt(TAG_KEY, key.ordinal());
- ReadOnlyHelper.INSTANCE.putReadOnlyInBundle(args, databaseReadOnly);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- SwitchPreference autoFillEnablePreference =
- (SwitchPreference) findPreference(getString(R.string.settings_autofill_enable_key));
- if (autoFillEnablePreference != null) {
- AutofillManager autofillManager = getActivity().getSystemService(AutofillManager.class);
- if (autofillManager != null && autofillManager.hasEnabledAutofillServices())
- autoFillEnablePreference.setChecked(true);
- else
- autoFillEnablePreference.setChecked(false);
- }
- }
- }
-
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- int key = 0;
- if (getArguments() != null)
- key = getArguments().getInt(TAG_KEY);
-
- database = App.Companion.getCurrentDatabase();
- databaseReadOnly = ReadOnlyHelper.INSTANCE.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, getArguments());
- databaseReadOnly = database.isReadOnly() || databaseReadOnly;
-
- // Load the preferences from an XML resource
- switch (Screen.values()[key]) {
- case APPLICATION:
- setPreferencesFromResource(R.xml.application_preferences, rootKey);
-
- allowCopyPassword();
-
- Preference keyFile = findPreference(getString(R.string.keyfile_key));
- keyFile.setOnPreferenceChangeListener((preference, newValue) -> {
- Boolean value = (Boolean) newValue;
- if (!value) {
- FileDatabaseHistory.Companion.getInstance(new WeakReference<>(getContext().getApplicationContext())).deleteAllKeys();
- }
- return true;
- });
-
- Preference recentHistory = findPreference(getString(R.string.recentfile_key));
- recentHistory.setOnPreferenceChangeListener((preference, newValue) -> {
- Boolean value = (Boolean) newValue;
- if (value == null) {
- value = true;
- }
- if (!value) {
- FileDatabaseHistory.Companion.getInstance(new WeakReference<>(getContext().getApplicationContext())).deleteAll();
- }
- return true;
- });
-
- SwitchPreference storageAccessFramework = (SwitchPreference) findPreference(getString(R.string.saf_key));
- storageAccessFramework.setOnPreferenceChangeListener((preference, newValue) -> {
- Boolean value = (Boolean) newValue;
- if (!value && getContext() != null) {
- AlertDialog alertDialog = new AlertDialog.Builder(getContext())
- .setMessage(getString(R.string.warning_disabling_storage_access_framework)).create();
- alertDialog.setButton(AlertDialog.BUTTON1, getText(android.R.string.ok),
- (dialog, which) -> {
- dialog.dismiss();
- });
- alertDialog.setButton(AlertDialog.BUTTON2, getText(android.R.string.cancel),
- (dialog, which) -> {
- storageAccessFramework.setChecked(true);
- dialog.dismiss();
- });
- alertDialog.show();
- }
- return true;
- });
-
- SwitchPreference fingerprintEnablePreference =
- (SwitchPreference) findPreference(getString(R.string.fingerprint_enable_key));
- // < M solve verifyError exception
- boolean fingerprintSupported = false;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
- && getActivity() != null)
- fingerprintSupported = FingerPrintHelper.isFingerprintSupported(
- getActivity().getSystemService(FingerprintManager.class));
- if (!fingerprintSupported) {
- // False if under Marshmallow
- fingerprintEnablePreference.setChecked(false);
- fingerprintEnablePreference.setOnPreferenceClickListener(preference -> {
- FragmentManager fragmentManager = getFragmentManager();
- assert fragmentManager != null;
- ((SwitchPreference) preference).setChecked(false);
- UnavailableFeatureDialogFragment.Companion.getInstance(Build.VERSION_CODES.M)
- .show(getFragmentManager(), "unavailableFeatureDialog");
- return false;
- });
- }
-
- Preference deleteKeysFingerprints = findPreference(getString(R.string.fingerprint_delete_all_key));
- if (!fingerprintSupported) {
- deleteKeysFingerprints.setEnabled(false);
- } else {
- deleteKeysFingerprints.setOnPreferenceClickListener(preference -> {
- new AlertDialog.Builder(getContext())
- .setMessage(getResources().getString(R.string.fingerprint_delete_all_warning))
- .setIcon(getResources().getDrawable(
- android.R.drawable.ic_dialog_alert))
- .setPositiveButton(
- getResources().getString(android.R.string.yes),
- new DialogInterface.OnClickListener() {
- @RequiresApi(api = Build.VERSION_CODES.M)
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- FingerPrintHelper.deleteEntryKeyInKeystoreForFingerprints(
- getContext(),
- new FingerPrintHelper.FingerPrintErrorCallback() {
- @Override
- public void onInvalidKeyException(Exception e) {}
-
- @Override
- public void onFingerPrintException(Exception e) {
- Toast.makeText(getContext(),
- getString(R.string.fingerprint_error, e.getLocalizedMessage()),
- Toast.LENGTH_SHORT).show();
- }
- });
- PreferencesUtil.deleteAllValuesFromNoBackupPreferences(getContext());
- }
- })
- .setNegativeButton(
- getResources().getString(android.R.string.no),
- (dialog, which) -> {
- }).show();
- return false;
- });
- }
-
- break;
-
- case FORM_FILLING:
- setPreferencesFromResource(R.xml.form_filling_preferences, rootKey);
-
- SwitchPreference autoFillEnablePreference =
- (SwitchPreference) findPreference(getString(R.string.settings_autofill_enable_key));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- assert getActivity() != null;
- AutofillManager autofillManager = getActivity().getSystemService(AutofillManager.class);
- if (autofillManager != null && autofillManager.hasEnabledAutofillServices())
- autoFillEnablePreference.setChecked(autofillManager.hasEnabledAutofillServices());
- autoFillEnablePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @RequiresApi(api = Build.VERSION_CODES.O)
- @Override
- public boolean onPreferenceClick(Preference preference) {
- if (((SwitchPreference) preference).isChecked()) {
- try {
- startEnableService();
- } catch (ActivityNotFoundException e) {
- String error = getString(R.string.error_autofill_enable_service);
- ((SwitchPreference) preference).setChecked(false);
- Log.d(getClass().getName(), error, e);
- Toast.makeText(getContext(), error, Toast.LENGTH_SHORT).show();
- }
- } else {
- disableService();
- }
- return false;
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- private void disableService() {
- if (autofillManager != null && autofillManager.hasEnabledAutofillServices()) {
- autofillManager.disableAutofillServices();
- } else {
- Log.d(getClass().getName(), "Sample service already disabled.");
- }
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- private void startEnableService() throws ActivityNotFoundException{
- if (autofillManager != null && !autofillManager.hasEnabledAutofillServices()) {
- Intent intent = new Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE);
- intent.setData(Uri.parse("package:com.example.android.autofill.service"));
- Log.d(getClass().getName(), "enableService(): intent=" + intent);
- startActivityForResult(intent, REQUEST_CODE_AUTOFILL);
- } else {
- Log.d(getClass().getName(), "Sample service already enabled.");
- }
- }
- });
- } else {
- autoFillEnablePreference.setOnPreferenceClickListener(preference -> {
- ((SwitchPreference) preference).setChecked(false);
- FragmentManager fragmentManager = getFragmentManager();
- assert fragmentManager != null;
- UnavailableFeatureDialogFragment.Companion.getInstance(Build.VERSION_CODES.O)
- .show(fragmentManager, "unavailableFeatureDialog");
- return false;
- });
- }
-
- Preference keyboardPreference = findPreference(getString(R.string.magic_keyboard_key));
- keyboardPreference.setOnPreferenceClickListener(preference -> {
- if (getFragmentManager() != null) {
- KeyboardExplanationDialogFragment keyboardDialog = new KeyboardExplanationDialogFragment();
- keyboardDialog.show(getFragmentManager(), "keyboardExplanationDialog");
- }
- return false;
- });
-
- Preference keyboardSubPreference = findPreference(getString(R.string.magic_keyboard_preference_key));
- keyboardSubPreference.setOnPreferenceClickListener(preference -> {
- startActivity(new Intent(getContext(), MagikIMESettings.class));
- return false;
- });
-
- // Present in two places
- allowCopyPassword();
-
- break;
-
- case DATABASE:
- setPreferencesFromResource(R.xml.database_preferences, rootKey);
-
- if (database.getLoaded()) {
-
- PreferenceCategory dbGeneralPrefCategory = (PreferenceCategory) findPreference(getString(R.string.database_general_key));
-
- // Db name
- Preference dbNamePref = findPreference(getString(R.string.database_name_key));
- if ( database.containsName() ) {
- dbNamePref.setSummary(database.getName());
- } else {
- dbGeneralPrefCategory.removePreference(dbNamePref);
- }
-
- // Db description
- Preference dbDescriptionPref = findPreference(getString(R.string.database_description_key));
- if ( database.containsDescription() ) {
- dbDescriptionPref.setSummary(database.getDescription());
- } else {
- dbGeneralPrefCategory.removePreference(dbDescriptionPref);
- }
-
- // Recycle bin
- SwitchPreference recycleBinPref = (SwitchPreference) findPreference(getString(R.string.recycle_bin_key));
- // TODO Recycle
- dbGeneralPrefCategory.removePreference(recycleBinPref); // To delete
- if (database.isRecycleBinAvailable()) {
- recycleBinPref.setChecked(database.isRecycleBinEnabled());
- recycleBinPref.setEnabled(false);
- } else {
- dbGeneralPrefCategory.removePreference(recycleBinPref);
- }
-
- // Version
- Preference dbVersionPref = findPreference(getString(R.string.database_version_key));
- dbVersionPref.setSummary(database.getVersion());
-
- // Encryption Algorithm
- Preference algorithmPref = findPreference(getString(R.string.encryption_algorithm_key));
- algorithmPref.setSummary(database.getEncryptionAlgorithmName(getResources()));
-
- // Key derivation function
- Preference kdfPref = findPreference(getString(R.string.key_derivation_function_key));
- kdfPref.setSummary(database.getKeyDerivationName(getResources()));
-
- // Round encryption
- roundPref = findPreference(getString(R.string.transform_rounds_key));
- roundPref.setSummary(database.getNumberKeyEncryptionRoundsAsString());
-
- // Memory Usage
- memoryPref = findPreference(getString(R.string.memory_usage_key));
- memoryPref.setSummary(database.getMemoryUsageAsString());
-
- // Parallelism
- parallelismPref = findPreference(getString(R.string.parallelism_key));
- parallelismPref.setSummary(database.getParallelismAsString());
-
- } else {
- Log.e(getClass().getName(), "Database isn't ready");
- }
-
- break;
-
- case APPEARANCE:
- setPreferencesFromResource(R.xml.appearance_preferences, rootKey);
-
- Preference stylePreference = findPreference(getString(R.string.setting_style_key));
- stylePreference.setOnPreferenceChangeListener((preference, newValue) -> {
- String styleIdString = (String) newValue;
- if (!(!BuildConfig.CLOSED_STORE && Education.Companion.isEducationScreenReclickedPerformed(getContext())))
- for (String themeIdDisabled : BuildConfig.STYLES_DISABLED) {
- if (themeIdDisabled.equals(styleIdString)) {
- ProFeatureDialogFragment dialogFragment = new ProFeatureDialogFragment();
- if (getFragmentManager() != null)
- dialogFragment.show(getFragmentManager(), "pro_feature_dialog");
- return false;
- }
- }
-
- Stylish.assignStyle(styleIdString);
- if (getActivity() != null)
- getActivity().recreate();
- return true;
- });
-
- Preference iconPackPreference = findPreference(getString(R.string.setting_icon_pack_choose_key));
- iconPackPreference.setOnPreferenceChangeListener((preference, newValue) -> {
- String iconPackId = (String) newValue;
- if (!(!BuildConfig.CLOSED_STORE && Education.Companion.isEducationScreenReclickedPerformed(getContext())))
- for (String iconPackIdDisabled : BuildConfig.ICON_PACKS_DISABLED) {
- if (iconPackIdDisabled.equals(iconPackId)) {
- ProFeatureDialogFragment dialogFragment = new ProFeatureDialogFragment();
- if (getFragmentManager() != null)
- dialogFragment.show(getFragmentManager(), "pro_feature_dialog");
- return false;
- }
- }
-
- IconPackChooser.setSelectedIconPack(iconPackId);
- return true;
- });
-
- Preference resetEducationScreens = findPreference(getString(R.string.reset_education_screens_key));
- resetEducationScreens.setOnPreferenceClickListener(preference -> {
- // To allow only one toast
- if (count == 0) {
- SharedPreferences sharedPreferences = Education.Companion.getEducationSharedPreferences(getContext());
- SharedPreferences.Editor editor = sharedPreferences.edit();
- for (int resourceId : Education.Companion.getEducationResourcesKeys()) {
- editor.putBoolean(getString(resourceId), false);
- }
- editor.apply();
- Toast.makeText(getContext(), R.string.reset_education_screens_text, Toast.LENGTH_SHORT).show();
- }
- count++;
- return false;
- });
-
- break;
-
- default:
- break;
- }
- }
-
- private void allowCopyPassword() {
- SwitchPreference copyPasswordPreference = (SwitchPreference) findPreference(getString(R.string.allow_copy_password_key));
- copyPasswordPreference.setOnPreferenceChangeListener((preference, newValue) -> {
- if ((Boolean) newValue && getContext() != null) {
- String message = getString(R.string.allow_copy_password_warning) +
- "\n\n" +
- getString(R.string.clipboard_warning);
- AlertDialog warningDialog = new AlertDialog.Builder(getContext())
- .setMessage(message).create();
- warningDialog.setButton(AlertDialog.BUTTON1, getText(android.R.string.ok),
- (dialog, which) -> dialog.dismiss());
- warningDialog.setButton(AlertDialog.BUTTON2, getText(android.R.string.cancel),
- (dialog, which) -> {
- copyPasswordPreference.setChecked(false);
- dialog.dismiss();
- });
- warningDialog.show();
- }
- return true;
- });
- }
-
- private void preferenceInDevelopment(Preference preferenceInDev) {
- preferenceInDev.setOnPreferenceClickListener(preference -> {
- FragmentManager fragmentManager = getFragmentManager();
- assert fragmentManager != null;
- try { // don't check if we can
- ((SwitchPreference) preference).setChecked(false);
- } catch (Exception ignored) {}
- new UnderDevelopmentFeatureDialogFragment().show(getFragmentManager(), "underDevFeatureDialog");
- return false;
- });
- }
-
- @Override
- public void onStop() {
- super.onStop();
- if(count==10) {
- if (getActivity()!=null)
- Education.Companion.getEducationSharedPreferences(getActivity()).edit()
- .putBoolean(getString(R.string.education_screen_reclicked_key), true).apply();
- }
- }
-
- @Override
- public void onDisplayPreferenceDialog(Preference preference) {
-
- assert getFragmentManager() != null;
-
- boolean otherDialogFragment = false;
-
- DialogFragment dialogFragment = null;
- if (preference.getKey().equals(getString(R.string.database_name_key))) {
- dialogFragment = DatabaseNamePreferenceDialogFragmentCompat.newInstance(preference.getKey());
- } else if (preference.getKey().equals(getString(R.string.database_description_key))) {
- dialogFragment = DatabaseDescriptionPreferenceDialogFragmentCompat.newInstance(preference.getKey());
- } else if (preference.getKey().equals(getString(R.string.encryption_algorithm_key))) {
- dialogFragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.newInstance(preference.getKey());
- } else if (preference.getKey().equals(getString(R.string.key_derivation_function_key))) {
- DatabaseKeyDerivationPreferenceDialogFragmentCompat keyDerivationDialogFragment = DatabaseKeyDerivationPreferenceDialogFragmentCompat.newInstance(preference.getKey());
- // Add other prefs to manage
- if (roundPref != null)
- keyDerivationDialogFragment.setRoundPreference(roundPref);
- if (memoryPref != null)
- keyDerivationDialogFragment.setMemoryPreference(memoryPref);
- if (parallelismPref != null)
- keyDerivationDialogFragment.setParallelismPreference(parallelismPref);
- dialogFragment = keyDerivationDialogFragment;
- } else if (preference.getKey().equals(getString(R.string.transform_rounds_key))) {
- dialogFragment = RoundsPreferenceDialogFragmentCompat.newInstance(preference.getKey());
- } else if (preference.getKey().equals(getString(R.string.memory_usage_key))) {
- dialogFragment = MemoryUsagePreferenceDialogFragmentCompat.newInstance(preference.getKey());
- } else if (preference.getKey().equals(getString(R.string.parallelism_key))) {
- dialogFragment = ParallelismPreferenceDialogFragmentCompat.newInstance(preference.getKey());
- } else {
- otherDialogFragment = true;
- }
-
- if (dialogFragment != null && !databaseReadOnly) {
- dialogFragment.setTargetFragment(this, 0);
- dialogFragment.show(getFragmentManager(), null);
- }
-
- // Could not be handled here. Try with the super method.
- else if (otherDialogFragment) {
- super.onDisplayPreferenceDialog(preference);
- }
- }
-
- public static String retrieveTitle(Resources resources, Screen key) {
- switch (key) {
- case APPLICATION:
- return resources.getString(R.string.menu_app_settings);
- case FORM_FILLING:
- return resources.getString(R.string.menu_form_filling_settings);
- case DATABASE:
- return resources.getString(R.string.menu_db_settings);
- case APPEARANCE:
- return resources.getString(R.string.appearance);
- default:
- return resources.getString(R.string.settings);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- ReadOnlyHelper.INSTANCE.onSaveInstanceState(outState, databaseReadOnly);
- super.onSaveInstanceState(outState);
- }
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- // TODO encapsulate
-
- return false;
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt
new file mode 100644
index 000000000..8c1f87482
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings
+
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.content.res.Resources
+import android.hardware.fingerprint.FingerprintManager
+import android.net.Uri
+import android.os.Build
+import android.os.Bundle
+import android.provider.Settings
+import android.support.annotation.RequiresApi
+import android.support.v14.preference.SwitchPreference
+import android.support.v4.app.DialogFragment
+import android.support.v7.app.AlertDialog
+import android.support.v7.preference.Preference
+import android.support.v7.preference.PreferenceCategory
+import android.support.v7.preference.PreferenceFragmentCompat
+import android.util.Log
+import android.view.autofill.AutofillManager
+import android.widget.Toast
+import com.kunzisoft.keepass.BuildConfig
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.activities.dialogs.KeyboardExplanationDialogFragment
+import com.kunzisoft.keepass.activities.dialogs.ProFeatureDialogFragment
+import com.kunzisoft.keepass.activities.dialogs.UnavailableFeatureDialogFragment
+import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFragment
+import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
+import com.kunzisoft.keepass.activities.stylish.Stylish
+import com.kunzisoft.keepass.app.App
+import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.education.Education
+import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
+import com.kunzisoft.keepass.fingerprint.FingerPrintHelper
+import com.kunzisoft.keepass.icons.IconPackChooser
+import com.kunzisoft.keepass.settings.preferencedialogfragment.*
+import java.lang.ref.WeakReference
+
+class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
+
+ private var database: Database = App.currentDatabase
+ private var databaseReadOnly: Boolean = false
+
+ private var count = 0
+
+ private var roundPref: Preference? = null
+ private var memoryPref: Preference? = null
+ private var parallelismPref: Preference? = null
+
+ enum class Screen {
+ APPLICATION, FORM_FILLING, DATABASE, APPEARANCE
+ }
+
+ override fun onResume() {
+ super.onResume()
+
+ activity?.let { activity ->
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val autoFillEnablePreference = findPreference(getString(R.string.settings_autofill_enable_key)) as SwitchPreference?
+ if (autoFillEnablePreference != null) {
+ val autofillManager = activity.getSystemService(AutofillManager::class.java)
+ autoFillEnablePreference.isChecked = autofillManager != null
+ && autofillManager.hasEnabledAutofillServices()
+ }
+ }
+ }
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+
+ var key = 0
+ if (arguments != null)
+ key = arguments!!.getInt(TAG_KEY)
+
+ databaseReadOnly = database.isReadOnly
+ || ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, arguments)
+
+ // Load the preferences from an XML resource
+ when (Screen.values()[key]) {
+ Screen.APPLICATION -> {
+ onCreateApplicationPreferences(rootKey)
+ }
+ Screen.FORM_FILLING -> {
+ onCreateFormFillingPreference(rootKey)
+ }
+ Screen.APPEARANCE -> {
+ onCreateAppearancePreferences(rootKey)
+ }
+ Screen.DATABASE -> {
+ onCreateDatabasePreference(rootKey)
+ }
+ }
+ }
+
+ private fun onCreateApplicationPreferences(rootKey: String?) {
+ setPreferencesFromResource(R.xml.application_preferences, rootKey)
+
+ activity?.let { activity ->
+ allowCopyPassword()
+
+ val keyFile = findPreference(getString(R.string.keyfile_key))
+ keyFile.setOnPreferenceChangeListener { _, newValue ->
+ if (!(newValue as Boolean)) {
+ FileDatabaseHistory.getInstance(WeakReference(activity.applicationContext)).deleteAllKeys()
+ }
+ true
+ }
+
+ val recentHistory = findPreference(getString(R.string.recentfile_key))
+ recentHistory.setOnPreferenceChangeListener { _, newValue ->
+ if (!(newValue as Boolean)) {
+ FileDatabaseHistory.getInstance(WeakReference(activity.applicationContext)).deleteAll()
+ }
+ true
+ }
+
+ val storageAccessFramework = findPreference(getString(R.string.saf_key)) as SwitchPreference
+ storageAccessFramework.setOnPreferenceChangeListener { _, newValue ->
+ if (!(newValue as Boolean) && context != null) {
+ val alertDialog = AlertDialog.Builder(context!!)
+ .setMessage(getString(R.string.warning_disabling_storage_access_framework)).create()
+ alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getText(android.R.string.ok)
+ ) { dialog, _ -> dialog.dismiss() }
+ alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getText(android.R.string.cancel)
+ ) { dialog, _ ->
+ storageAccessFramework.isChecked = true
+ dialog.dismiss()
+ }
+ alertDialog.show()
+ }
+ true
+ }
+
+ val fingerprintEnablePreference = findPreference(getString(R.string.fingerprint_enable_key)) as SwitchPreference
+ // < M solve verifyError exception
+ var fingerprintSupported = false
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ fingerprintSupported = FingerPrintHelper.isFingerprintSupported(
+ activity.getSystemService(FingerprintManager::class.java))
+ if (!fingerprintSupported) {
+ // False if under Marshmallow
+ fingerprintEnablePreference.isChecked = false
+ fingerprintEnablePreference.setOnPreferenceClickListener { preference ->
+ fragmentManager?.let { fragmentManager ->
+ (preference as SwitchPreference).isChecked = false
+ UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.M)
+ .show(fragmentManager, "unavailableFeatureDialog")
+ }
+ false
+ }
+ }
+
+ val deleteKeysFingerprints = findPreference(getString(R.string.fingerprint_delete_all_key))
+ if (!fingerprintSupported) {
+ deleteKeysFingerprints.isEnabled = false
+ } else {
+ deleteKeysFingerprints.setOnPreferenceClickListener {
+ context?.let { context ->
+ AlertDialog.Builder(context)
+ .setMessage(resources.getString(R.string.fingerprint_delete_all_warning))
+ .setIcon(resources.getDrawable(
+ android.R.drawable.ic_dialog_alert))
+ .setPositiveButton(resources.getString(android.R.string.yes)
+ ) { _, _ ->
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ FingerPrintHelper.deleteEntryKeyInKeystoreForFingerprints(
+ context,
+ object : FingerPrintHelper.FingerPrintErrorCallback {
+ override fun onInvalidKeyException(e: Exception) {}
+
+ override fun onFingerPrintException(e: Exception) {
+ Toast.makeText(context,
+ getString(R.string.fingerprint_error, e.localizedMessage),
+ Toast.LENGTH_SHORT).show()
+ }
+ })
+ }
+ PreferencesUtil.deleteAllValuesFromNoBackupPreferences(context)
+ }
+ .setNegativeButton(resources.getString(android.R.string.no))
+ { _, _ -> }.show()
+ }
+ false
+ }
+ }
+ }
+ }
+
+ private fun onCreateFormFillingPreference(rootKey: String?) {
+ setPreferencesFromResource(R.xml.form_filling_preferences, rootKey)
+
+ activity?.let { activity ->
+ val autoFillEnablePreference = findPreference(getString(R.string.settings_autofill_enable_key)) as SwitchPreference
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val autofillManager = activity.getSystemService(AutofillManager::class.java)
+ if (autofillManager != null && autofillManager.hasEnabledAutofillServices())
+ autoFillEnablePreference.isChecked = autofillManager.hasEnabledAutofillServices()
+ autoFillEnablePreference.onPreferenceClickListener = object : Preference.OnPreferenceClickListener {
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ override fun onPreferenceClick(preference: Preference): Boolean {
+ if ((preference as SwitchPreference).isChecked) {
+ try {
+ startEnableService()
+ } catch (e: ActivityNotFoundException) {
+ val error = getString(R.string.error_autofill_enable_service)
+ preference.isChecked = false
+ Log.d(javaClass.name, error, e)
+ Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
+ }
+
+ } else {
+ disableService()
+ }
+ return false
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private fun disableService() {
+ if (autofillManager != null && autofillManager.hasEnabledAutofillServices()) {
+ autofillManager.disableAutofillServices()
+ } else {
+ Log.d(javaClass.name, "Sample service already disabled.")
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ @Throws(ActivityNotFoundException::class)
+ private fun startEnableService() {
+ if (autofillManager != null && !autofillManager.hasEnabledAutofillServices()) {
+ val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
+ intent.data = Uri.parse("package:com.example.android.autofill.service")
+ Log.d(javaClass.name, "enableService(): intent=$intent")
+ startActivityForResult(intent, REQUEST_CODE_AUTOFILL)
+ } else {
+ Log.d(javaClass.name, "Sample service already enabled.")
+ }
+ }
+ }
+ } else {
+ autoFillEnablePreference.setOnPreferenceClickListener { preference ->
+ (preference as SwitchPreference).isChecked = false
+ val fragmentManager = fragmentManager!!
+ UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.O)
+ .show(fragmentManager, "unavailableFeatureDialog")
+ false
+ }
+ }
+ }
+
+ val keyboardPreference = findPreference(getString(R.string.magic_keyboard_key))
+ keyboardPreference.setOnPreferenceClickListener {
+ if (fragmentManager != null) {
+ KeyboardExplanationDialogFragment().show(fragmentManager!!, "keyboardExplanationDialog")
+ }
+ false
+ }
+
+ val keyboardSubPreference = findPreference(getString(R.string.magic_keyboard_preference_key))
+ keyboardSubPreference.setOnPreferenceClickListener {
+ startActivity(Intent(context, MagikIMESettings::class.java))
+ false
+ }
+
+ // Present in two places
+ allowCopyPassword()
+ }
+
+ private fun onCreateAppearancePreferences(rootKey: String?) {
+ setPreferencesFromResource(R.xml.appearance_preferences, rootKey)
+
+ activity?.let { activity ->
+ val stylePreference = findPreference(getString(R.string.setting_style_key))
+ stylePreference.setOnPreferenceChangeListener { _, newValue ->
+ var styleEnabled = true
+ val styleIdString = newValue as String
+ if (BuildConfig.CLOSED_STORE || !Education.isEducationScreenReclickedPerformed(context!!))
+ for (themeIdDisabled in BuildConfig.STYLES_DISABLED) {
+ if (themeIdDisabled == styleIdString) {
+ styleEnabled = false
+ fragmentManager?.let { fragmentManager ->
+ ProFeatureDialogFragment().show(fragmentManager, "pro_feature_dialog")
+ }
+ }
+ }
+ if (styleEnabled) {
+ Stylish.assignStyle(styleIdString)
+ activity.recreate()
+ }
+ styleEnabled
+ }
+
+ val iconPackPreference = findPreference(getString(R.string.setting_icon_pack_choose_key))
+ iconPackPreference.setOnPreferenceChangeListener { _, newValue ->
+ var iconPackEnabled = true
+ val iconPackId = newValue as String
+ if (BuildConfig.CLOSED_STORE || !Education.isEducationScreenReclickedPerformed(context!!))
+ for (iconPackIdDisabled in BuildConfig.ICON_PACKS_DISABLED) {
+ if (iconPackIdDisabled == iconPackId) {
+ iconPackEnabled = false
+ fragmentManager?.let { fragmentManager ->
+ ProFeatureDialogFragment().show(fragmentManager, "pro_feature_dialog")
+ }
+ }
+ }
+ if (iconPackEnabled) {
+ IconPackChooser.setSelectedIconPack(iconPackId)
+ }
+ iconPackEnabled
+ }
+
+ val resetEducationScreens = findPreference(getString(R.string.reset_education_screens_key))
+ resetEducationScreens.setOnPreferenceClickListener {
+ // To allow only one toast
+ if (count == 0) {
+ val sharedPreferences = Education.getEducationSharedPreferences(context!!)
+ val editor = sharedPreferences.edit()
+ for (resourceId in Education.educationResourcesKeys) {
+ editor.putBoolean(getString(resourceId), false)
+ }
+ editor.apply()
+ Toast.makeText(context, R.string.reset_education_screens_text, Toast.LENGTH_SHORT).show()
+ }
+ count++
+ false
+ }
+ }
+ }
+
+ private fun onCreateDatabasePreference(rootKey: String?) {
+ setPreferencesFromResource(R.xml.database_preferences, rootKey)
+
+ if (database.loaded) {
+
+ val dbGeneralPrefCategory = findPreference(getString(R.string.database_general_key)) as PreferenceCategory
+
+ // Db name
+ val dbNamePref = findPreference(getString(R.string.database_name_key))
+ if (database.containsName()) {
+ dbNamePref.summary = database.name
+ } else {
+ dbGeneralPrefCategory.removePreference(dbNamePref)
+ }
+
+ // Db description
+ val dbDescriptionPref = findPreference(getString(R.string.database_description_key))
+ if (database.containsDescription()) {
+ dbDescriptionPref.summary = database.description
+ } else {
+ dbGeneralPrefCategory.removePreference(dbDescriptionPref)
+ }
+
+ // Recycle bin
+ val recycleBinPref = findPreference(getString(R.string.recycle_bin_key)) as SwitchPreference
+ // TODO Recycle
+ dbGeneralPrefCategory.removePreference(recycleBinPref) // To delete
+ if (database.isRecycleBinAvailable) {
+ recycleBinPref.isChecked = database.isRecycleBinEnabled
+ recycleBinPref.isEnabled = false
+ } else {
+ dbGeneralPrefCategory.removePreference(recycleBinPref)
+ }
+
+ // Version
+ val dbVersionPref = findPreference(getString(R.string.database_version_key))
+ dbVersionPref.summary = database.getVersion()
+
+ // Encryption Algorithm
+ val algorithmPref = findPreference(getString(R.string.encryption_algorithm_key))
+ algorithmPref.summary = database.getEncryptionAlgorithmName(resources)
+
+ // Key derivation function
+ val kdfPref = findPreference(getString(R.string.key_derivation_function_key))
+ kdfPref.summary = database.getKeyDerivationName(resources)
+
+ // Round encryption
+ roundPref = findPreference(getString(R.string.transform_rounds_key))
+ roundPref?.summary = database.numberKeyEncryptionRoundsAsString
+
+ // Memory Usage
+ memoryPref = findPreference(getString(R.string.memory_usage_key))
+ memoryPref?.summary = database.memoryUsageAsString
+
+ // Parallelism
+ parallelismPref = findPreference(getString(R.string.parallelism_key))
+ parallelismPref?.summary = database.parallelismAsString
+
+ } else {
+ Log.e(javaClass.name, "Database isn't ready")
+ }
+ }
+
+ private fun allowCopyPassword() {
+ val copyPasswordPreference = findPreference(getString(R.string.allow_copy_password_key)) as SwitchPreference
+ copyPasswordPreference.setOnPreferenceChangeListener { _, newValue ->
+ if (newValue as Boolean && context != null) {
+ val message = getString(R.string.allow_copy_password_warning) +
+ "\n\n" +
+ getString(R.string.clipboard_warning)
+ AlertDialog
+ .Builder(context!!)
+ .setMessage(message)
+ .create()
+ .apply {
+ setButton(AlertDialog.BUTTON_POSITIVE, getText(android.R.string.ok))
+ { dialog, _ ->
+ dialog.dismiss()
+ }
+ setButton(AlertDialog.BUTTON_NEGATIVE, getText(android.R.string.cancel))
+ { dialog, _ ->
+ copyPasswordPreference.isChecked = false
+ dialog.dismiss()
+ }
+ show()
+ }
+ }
+ true
+ }
+ }
+
+ private fun preferenceInDevelopment(preferenceInDev: Preference) {
+ preferenceInDev.setOnPreferenceClickListener { preference ->
+ fragmentManager?.let { fragmentManager ->
+ try { // don't check if we can
+ (preference as SwitchPreference).isChecked = false
+ } catch (ignored: Exception) {
+ }
+ UnderDevelopmentFeatureDialogFragment().show(fragmentManager, "underDevFeatureDialog")
+ }
+ false
+ }
+ }
+
+ override fun onStop() {
+ super.onStop()
+ activity?.let { activity ->
+ if (count == 10) {
+ Education.getEducationSharedPreferences(activity).edit()
+ .putBoolean(getString(R.string.education_screen_reclicked_key), true).apply()
+ }
+ }
+ }
+
+ override fun onDisplayPreferenceDialog(preference: Preference?) {
+
+ var otherDialogFragment = false
+
+ fragmentManager?.let { fragmentManager ->
+ preference?.let { preference ->
+ var dialogFragment: DialogFragment? = null
+ when {
+ preference.key == getString(R.string.database_name_key) -> {
+ dialogFragment = DatabaseNamePreferenceDialogFragmentCompat.newInstance(preference.key)
+ }
+ preference.key == getString(R.string.database_description_key) -> {
+ dialogFragment = DatabaseDescriptionPreferenceDialogFragmentCompat.newInstance(preference.key)
+ }
+ preference.key == getString(R.string.encryption_algorithm_key) -> {
+ dialogFragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.newInstance(preference.key)
+ }
+ preference.key == getString(R.string.key_derivation_function_key) -> {
+ val keyDerivationDialogFragment = DatabaseKeyDerivationPreferenceDialogFragmentCompat.newInstance(preference.key)
+ // Add other prefs to manage
+ if (roundPref != null)
+ keyDerivationDialogFragment.setRoundPreference(roundPref!!)
+ if (memoryPref != null)
+ keyDerivationDialogFragment.setMemoryPreference(memoryPref!!)
+ if (parallelismPref != null)
+ keyDerivationDialogFragment.setParallelismPreference(parallelismPref!!)
+ dialogFragment = keyDerivationDialogFragment
+ }
+ preference.key == getString(R.string.transform_rounds_key) -> {
+ dialogFragment = RoundsPreferenceDialogFragmentCompat.newInstance(preference.key)
+ }
+ preference.key == getString(R.string.memory_usage_key) -> {
+ dialogFragment = MemoryUsagePreferenceDialogFragmentCompat.newInstance(preference.key)
+ }
+ preference.key == getString(R.string.parallelism_key) -> {
+ dialogFragment = ParallelismPreferenceDialogFragmentCompat.newInstance(preference.key)
+ }
+ else -> otherDialogFragment = true
+ }
+
+ if (dialogFragment != null && !databaseReadOnly) {
+ dialogFragment.setTargetFragment(this, 0)
+ dialogFragment.show(fragmentManager, null)
+ }
+ // Could not be handled here. Try with the super method.
+ else if (otherDialogFragment) {
+ super.onDisplayPreferenceDialog(preference)
+ }
+ }
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ ReadOnlyHelper.onSaveInstanceState(outState, databaseReadOnly)
+ super.onSaveInstanceState(outState)
+ }
+
+ override fun onPreferenceClick(preference: Preference?): Boolean {
+ // TODO encapsulate
+ return false
+ }
+
+ companion object {
+
+ private const val TAG_KEY = "NESTED_KEY"
+
+ private const val REQUEST_CODE_AUTOFILL = 5201
+
+ @JvmOverloads
+ fun newInstance(key: Screen, databaseReadOnly: Boolean = ReadOnlyHelper.READ_ONLY_DEFAULT)
+ : NestedSettingsFragment {
+ val fragment = NestedSettingsFragment()
+ // supply arguments to bundle.
+ val args = Bundle()
+ args.putInt(TAG_KEY, key.ordinal)
+ ReadOnlyHelper.putReadOnlyInBundle(args, databaseReadOnly)
+ fragment.arguments = args
+ return fragment
+ }
+
+ fun retrieveTitle(resources: Resources, key: Screen): String {
+ return when (key) {
+ Screen.APPLICATION -> resources.getString(R.string.menu_app_settings)
+ Screen.FORM_FILLING -> resources.getString(R.string.menu_form_filling_settings)
+ Screen.DATABASE -> resources.getString(R.string.menu_db_settings)
+ Screen.APPEARANCE -> resources.getString(R.string.appearance)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java
deleted file mode 100644
index 590ec481a..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.database.SortNodeEnum;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-public class PreferencesUtil {
-
- private static final String NO_BACKUP_PREFERENCE_FILE_NAME = "nobackup";
-
- public static SharedPreferences getNoBackupSharedPreferences(Context ctx) {
- return ctx.getSharedPreferences(
- PreferencesUtil.NO_BACKUP_PREFERENCE_FILE_NAME,
- Context.MODE_PRIVATE);
- }
-
- public static void deleteAllValuesFromNoBackupPreferences(Context ctx) {
- SharedPreferences prefsNoBackup = getNoBackupSharedPreferences(ctx);
- SharedPreferences.Editor sharedPreferencesEditor = prefsNoBackup.edit();
- sharedPreferencesEditor.clear();
- sharedPreferencesEditor.apply();
- }
-
- public static boolean omitBackup(Context context) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(context.getString(R.string.omitbackup_key),
- context.getResources().getBoolean(R.bool.omitbackup_default));
- }
-
- public static boolean showUsernamesListEntries(Context context) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(context.getString(R.string.list_entries_show_username_key),
- context.getResources().getBoolean(R.bool.list_entries_show_username_default));
- }
-
- /**
- * Retrieve the text size in SP, verify the integrity of the size stored in preference
- */
- public static float getListTextSize(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- String defaultSizeString = ctx.getString(R.string.list_size_default);
- String listSize = prefs.getString(ctx.getString(R.string.list_size_key), defaultSizeString);
- if (!Arrays.asList(ctx.getResources().getStringArray(R.array.list_size_values)).contains(listSize))
- listSize = defaultSizeString;
- return Float.parseFloat(listSize);
- }
-
- public static int getDefaultPasswordLength(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getInt(ctx.getString(R.string.password_length_key),
- Integer.parseInt(ctx.getString(R.string.default_password_length)));
- }
-
- public static Set getDefaultPasswordCharacters(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getStringSet(ctx.getString(R.string.list_password_generator_options_key),
- new HashSet<>(Arrays.asList(
- ctx.getResources()
- .getStringArray(R.array.list_password_generator_options_default_values))));
- }
-
- public static boolean isClipboardNotificationsEnable(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.clipboard_notifications_key),
- ctx.getResources().getBoolean(R.bool.clipboard_notifications_default));
- }
-
- public static boolean isLockDatabaseWhenScreenShutOffEnable(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.lock_database_screen_off_key),
- ctx.getResources().getBoolean(R.bool.lock_database_screen_off_default));
- }
-
- public static boolean isLockDatabaseWhenBackButtonOnRootClicked(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.lock_database_back_root_key),
- ctx.getResources().getBoolean(R.bool.lock_database_back_root_default));
- }
-
- public static boolean isFingerprintEnable(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.fingerprint_enable_key),
- ctx.getResources().getBoolean(R.bool.fingerprint_enable_default));
- }
-
- public static boolean isFullFilePathEnable(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.full_file_path_enable_key),
- ctx.getResources().getBoolean(R.bool.full_file_path_enable_default));
- }
-
- public static SortNodeEnum getListSort(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return SortNodeEnum.valueOf(prefs.getString(ctx.getString(R.string.sort_node_key),
- SortNodeEnum.TITLE.name()));
- }
-
- public static boolean getGroupsBeforeSort(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.sort_group_before_key),
- ctx.getResources().getBoolean(R.bool.sort_group_before_default));
- }
-
- public static boolean getAscendingSort(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.sort_ascending_key),
- ctx.getResources().getBoolean(R.bool.sort_ascending_default));
- }
-
- public static boolean getRecycleBinBottomSort(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.sort_recycle_bin_bottom_key),
- ctx.getResources().getBoolean(R.bool.sort_recycle_bin_bottom_default));
- }
-
- public static boolean isPasswordMask(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.maskpass_key),
- ctx.getResources().getBoolean(R.bool.maskpass_default));
- }
-
- public static boolean fieldFontIsInVisibility(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.monospace_font_fields_enable_key),
- ctx.getResources().getBoolean(R.bool.monospace_font_fields_enable_default));
- }
-
- public static boolean autoOpenSelectedFile(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.auto_open_file_uri_key),
- ctx.getResources().getBoolean(R.bool.auto_open_file_uri_default));
- }
-
- public static boolean isFirstTimeAskAllowCopyPasswordAndProtectedFields(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.allow_copy_password_first_time_key),
- ctx.getResources().getBoolean(R.bool.allow_copy_password_first_time_default));
- }
-
- public static boolean allowCopyPasswordAndProtectedFields(Context ctx) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- return prefs.getBoolean(ctx.getString(R.string.allow_copy_password_key),
- ctx.getResources().getBoolean(R.bool.allow_copy_password_default));
- }
-
- public static void setAllowCopyPasswordAndProtectedFields(Context ctx, boolean allowCopy) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
- prefs.edit()
- .putBoolean(ctx.getString(R.string.allow_copy_password_first_time_key), false)
- .putBoolean(ctx.getString(R.string.allow_copy_password_key), allowCopy)
- .apply();
- }
-
- public static String getIconPackSelectedId(Context context) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getString(
- context.getString(R.string.setting_icon_pack_choose_key),
- context.getString(R.string.setting_icon_pack_choose_default));
- }
-
- public static boolean emptyPasswordAllowed(Context context) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(context.getString(R.string.allow_no_password_key),
- context.getResources().getBoolean(R.bool.allow_no_password_default));
- }
-
- public static boolean enableReadOnlyDatabase(Context context) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(context.getString(R.string.enable_read_only_key),
- context.getResources().getBoolean(R.bool.enable_read_only_default));
- }
-
- public static boolean enableKeyboardNotificationEntry(Context context) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(context.getString(R.string.keyboard_notification_entry_key),
- context.getResources().getBoolean(R.bool.keyboard_notification_entry_default));
- }
-
- public static boolean enableKeyboardVibration(Context context) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(context.getString(R.string.keyboard_key_vibrate_key),
- context.getResources().getBoolean(R.bool.keyboard_key_vibrate_default));
- }
-
- public static boolean enableKeyboardSound(Context context) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(context.getString(R.string.keyboard_key_sound_key),
- context.getResources().getBoolean(R.bool.keyboard_key_sound_default));
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt
new file mode 100644
index 000000000..4995eb35a
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.preference.PreferenceManager
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.database.SortNodeEnum
+import com.kunzisoft.keepass.timeout.TimeoutHelper
+import java.util.*
+
+object PreferencesUtil {
+
+ private const val NO_BACKUP_PREFERENCE_FILE_NAME = "nobackup"
+
+ fun getNoBackupSharedPreferences(context: Context): SharedPreferences {
+ return context.getSharedPreferences(
+ NO_BACKUP_PREFERENCE_FILE_NAME,
+ Context.MODE_PRIVATE)
+ }
+
+ fun deleteAllValuesFromNoBackupPreferences(context: Context) {
+ val prefsNoBackup = getNoBackupSharedPreferences(context)
+ val sharedPreferencesEditor = prefsNoBackup.edit()
+ sharedPreferencesEditor.clear()
+ sharedPreferencesEditor.apply()
+ }
+
+ fun omitBackup(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.omitbackup_key),
+ context.resources.getBoolean(R.bool.omitbackup_default))
+ }
+
+ fun showUsernamesListEntries(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.list_entries_show_username_key),
+ context.resources.getBoolean(R.bool.list_entries_show_username_default))
+ }
+
+ /**
+ * Retrieve the text size in SP, verify the integrity of the size stored in preference
+ */
+ fun getListTextSize(context: Context): Float {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ val defaultSizeString = context.getString(R.string.list_size_default)
+ var listSize = prefs.getString(context.getString(R.string.list_size_key), defaultSizeString)
+ if (!listOf(*context.resources.getStringArray(R.array.list_size_values)).contains(listSize))
+ listSize = defaultSizeString
+ return java.lang.Float.parseFloat(listSize)
+ }
+
+ fun getDefaultPasswordLength(context: Context): Int {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getInt(context.getString(R.string.password_length_key),
+ Integer.parseInt(context.getString(R.string.default_password_length)))
+ }
+
+ fun getDefaultPasswordCharacters(context: Context): Set? {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getStringSet(context.getString(R.string.list_password_generator_options_key),
+ HashSet(listOf(*context.resources
+ .getStringArray(R.array.list_password_generator_options_default_values))))
+ }
+
+ fun isClipboardNotificationsEnable(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.clipboard_notifications_key),
+ context.resources.getBoolean(R.bool.clipboard_notifications_default))
+ }
+
+ /**
+ * Save current time, can be retrieve with `getTimeSaved()`
+ */
+ fun saveCurrentTime(context: Context) {
+ PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
+ putLong(context.getString(R.string.timeout_backup_key), System.currentTimeMillis())
+ apply()
+ }
+ }
+
+ /**
+ * Time previously saved in milliseconds (commonly used to compare with current time and check timeout)
+ */
+ fun getTimeSaved(context: Context): Long {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getLong(context.getString(R.string.timeout_backup_key),
+ TimeoutHelper.NEVER)
+ }
+
+ /**
+ * App timeout selected in milliseconds
+ */
+ fun getAppTimeout(context: Context): Long {
+ return try {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ java.lang.Long.parseLong(prefs.getString(context.getString(R.string.app_timeout_key),
+ context.getString(R.string.clipboard_timeout_default)))
+ } catch (e: NumberFormatException) {
+ TimeoutHelper.DEFAULT_TIMEOUT
+ }
+ }
+
+ fun isLockDatabaseWhenScreenShutOffEnable(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.lock_database_screen_off_key),
+ context.resources.getBoolean(R.bool.lock_database_screen_off_default))
+ }
+
+ fun isLockDatabaseWhenBackButtonOnRootClicked(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.lock_database_back_root_key),
+ context.resources.getBoolean(R.bool.lock_database_back_root_default))
+ }
+
+ fun isFingerprintEnable(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.fingerprint_enable_key),
+ context.resources.getBoolean(R.bool.fingerprint_enable_default))
+ }
+
+ fun isFullFilePathEnable(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.full_file_path_enable_key),
+ context.resources.getBoolean(R.bool.full_file_path_enable_default))
+ }
+
+ fun getListSort(context: Context): SortNodeEnum {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ prefs.getString(context.getString(R.string.sort_node_key),
+ SortNodeEnum.TITLE.name)?.let {
+ return SortNodeEnum.valueOf(it)
+ }
+ return SortNodeEnum.DB
+ }
+
+ fun getGroupsBeforeSort(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.sort_group_before_key),
+ context.resources.getBoolean(R.bool.sort_group_before_default))
+ }
+
+ fun getAscendingSort(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.sort_ascending_key),
+ context.resources.getBoolean(R.bool.sort_ascending_default))
+ }
+
+ fun getRecycleBinBottomSort(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.sort_recycle_bin_bottom_key),
+ context.resources.getBoolean(R.bool.sort_recycle_bin_bottom_default))
+ }
+
+ fun isPasswordMask(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.maskpass_key),
+ context.resources.getBoolean(R.bool.maskpass_default))
+ }
+
+ fun fieldFontIsInVisibility(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.monospace_font_fields_enable_key),
+ context.resources.getBoolean(R.bool.monospace_font_fields_enable_default))
+ }
+
+ fun autoOpenSelectedFile(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.auto_open_file_uri_key),
+ context.resources.getBoolean(R.bool.auto_open_file_uri_default))
+ }
+
+ fun isFirstTimeAskAllowCopyPasswordAndProtectedFields(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.allow_copy_password_first_time_key),
+ context.resources.getBoolean(R.bool.allow_copy_password_first_time_default))
+ }
+
+ fun allowCopyPasswordAndProtectedFields(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.allow_copy_password_key),
+ context.resources.getBoolean(R.bool.allow_copy_password_default))
+ }
+
+ fun setAllowCopyPasswordAndProtectedFields(context: Context, allowCopy: Boolean) {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ prefs.edit()
+ .putBoolean(context.getString(R.string.allow_copy_password_first_time_key), false)
+ .putBoolean(context.getString(R.string.allow_copy_password_key), allowCopy)
+ .apply()
+ }
+
+ fun getIconPackSelectedId(context: Context): String? {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getString(
+ context.getString(R.string.setting_icon_pack_choose_key),
+ context.getString(R.string.setting_icon_pack_choose_default))
+ }
+
+ fun emptyPasswordAllowed(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.allow_no_password_key),
+ context.resources.getBoolean(R.bool.allow_no_password_default))
+ }
+
+ fun enableReadOnlyDatabase(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.enable_read_only_key),
+ context.resources.getBoolean(R.bool.enable_read_only_default))
+ }
+
+ fun enableKeyboardNotificationEntry(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.keyboard_notification_entry_key),
+ context.resources.getBoolean(R.bool.keyboard_notification_entry_default))
+ }
+
+ fun enableKeyboardVibration(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.keyboard_key_vibrate_key),
+ context.resources.getBoolean(R.bool.keyboard_key_vibrate_default))
+ }
+
+ fun enableKeyboardSound(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.keyboard_key_sound_key),
+ context.resources.getBoolean(R.bool.keyboard_key_sound_default))
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.java b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.kt
similarity index 73%
rename from app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.java
rename to app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.kt
index 340166d87..56610b346 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.java
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
@@ -17,14 +17,13 @@
* along with KeePass DX. If not, see .
*
*/
-package com.kunzisoft.keepass.settings;
+package com.kunzisoft.keepass.settings
-import android.support.v4.app.Fragment;
+import android.support.v4.app.Fragment
-public class SettingsAutofillActivity extends SettingsActivity {
+class SettingsAutofillActivity : SettingsActivity() {
- @Override
- protected Fragment retrieveMainFragment() {
- return NestedSettingsFragment.newInstance(NestedSettingsFragment.Screen.FORM_FILLING);
+ override fun retrieveMainFragment(): Fragment {
+ return NestedSettingsFragment.newInstance(NestedSettingsFragment.Screen.FORM_FILLING)
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogListExplanationPreference.java b/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogListExplanationPreference.java
deleted file mode 100644
index 46a7ac64a..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogListExplanationPreference.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.kunzisoft.keepass.settings.preference;
-
-import android.content.Context;
-import android.support.v7.preference.DialogPreference;
-import android.util.AttributeSet;
-
-import com.kunzisoft.keepass.R;
-
-public class DialogListExplanationPreference extends DialogPreference {
-
- public DialogListExplanationPreference(Context context) {
- this(context, null);
- }
-
- public DialogListExplanationPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.dialogPreferenceStyle);
- }
-
- public DialogListExplanationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, defStyleAttr);
- }
-
- public DialogListExplanationPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public int getDialogLayoutResource() {
- return R.layout.pref_dialog_list_explanation;
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogListExplanationPreference.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogListExplanationPreference.kt
new file mode 100644
index 000000000..2b2fe876d
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogListExplanationPreference.kt
@@ -0,0 +1,18 @@
+package com.kunzisoft.keepass.settings.preference
+
+import android.content.Context
+import android.support.v7.preference.DialogPreference
+import android.util.AttributeSet
+
+import com.kunzisoft.keepass.R
+
+class DialogListExplanationPreference @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = R.attr.dialogPreferenceStyle,
+ defStyleRes: Int = defStyleAttr)
+ : DialogPreference(context, attrs, defStyleAttr, defStyleRes) {
+
+ override fun getDialogLayoutResource(): Int {
+ return R.layout.pref_dialog_list_explanation
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/IconPackListPreference.java b/app/src/main/java/com/kunzisoft/keepass/settings/preference/IconPackListPreference.java
deleted file mode 100644
index ea9f4c787..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preference/IconPackListPreference.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.kunzisoft.keepass.settings.preference;
-
-import android.content.Context;
-import android.support.v7.preference.ListPreference;
-import android.util.AttributeSet;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.icons.IconPack;
-import com.kunzisoft.keepass.icons.IconPackChooser;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class IconPackListPreference extends ListPreference {
-
- public IconPackListPreference(Context context) {
- this(context, null);
- }
-
- public IconPackListPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.dialogPreferenceStyle);
- }
-
- public IconPackListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, defStyleAttr);
- }
-
- public IconPackListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- List entries = new ArrayList<>();
- List values = new ArrayList<>();
- for (IconPack iconPack : IconPackChooser.getIconPackList(context)) {
- entries.add(iconPack.getName());
- values.add(iconPack.getId());
- }
-
- setEntries(entries.toArray(new String[0]));
- setEntryValues(values.toArray(new String[0]));
- setDefaultValue(IconPackChooser.getSelectedIconPack(context).getId());
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/IconPackListPreference.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preference/IconPackListPreference.kt
new file mode 100644
index 000000000..fa6524309
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preference/IconPackListPreference.kt
@@ -0,0 +1,32 @@
+package com.kunzisoft.keepass.settings.preference
+
+import android.content.Context
+import android.support.v7.preference.ListPreference
+import android.util.AttributeSet
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.icons.IconPackChooser
+import java.util.*
+
+class IconPackListPreference @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = R.attr.dialogPreferenceStyle,
+ defStyleRes: Int = defStyleAttr)
+ : ListPreference(context, attrs, defStyleAttr, defStyleRes) {
+
+ init {
+ val entries = ArrayList()
+ val values = ArrayList()
+ for (iconPack in IconPackChooser.getIconPackList(context)) {
+ if (iconPack.id != null) {
+ entries.add(iconPack.name)
+ values.add(iconPack.id!!)
+ }
+ }
+
+ setEntries(entries.toTypedArray())
+ entryValues = values.toTypedArray()
+ IconPackChooser.getSelectedIconPack(context)?.let { selectedIconPack ->
+ setDefaultValue(selectedIconPack.id)
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputNumberPreference.java b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputNumberPreference.java
deleted file mode 100644
index 0c3ab9d2d..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputNumberPreference.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preference;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine;
-
-public class InputNumberPreference extends InputTextExplanationPreference {
-
- private long mNumber;
-
- public InputNumberPreference(Context context) {
- this(context, null);
- }
-
- public InputNumberPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.dialogPreferenceStyle);
- }
-
- public InputNumberPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, defStyleAttr);
- }
-
- public InputNumberPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public int getDialogLayoutResource() {
- return R.layout.pref_dialog_numbers;
- }
-
- public long getNumber() {
- return mNumber;
- }
-
- public void setNumber(long number) {
- this.mNumber = number;
- // Save to Shared Preferences
- persistLong(number);
- }
-
- @Override
- public void setSummary(CharSequence summary) {
-
- if (summary.equals(KdfEngine.UNKNOW_VALUE_STRING)) {
- setEnabled(false);
- super.setSummary("");
- }
- else {
- setEnabled(true);
- super.setSummary(summary);
- }
- }
-
- @Override
- protected Object onGetDefaultValue(TypedArray a, int index) {
- // Default value from attribute. Fallback value is set to 0.
- return a.getInt(index, 0);
- }
- @Override
- protected void onSetInitialValue(boolean restorePersistedValue,
- Object defaultValue) {
- // Read the value. Use the default value if it is not possible.
- long number;
- if (!restorePersistedValue) {
- number = 100000;
- if (defaultValue instanceof String) {
- number = Long.parseLong((String) defaultValue);
- }
- if (defaultValue instanceof Integer) {
- number = (Integer) defaultValue;
- }
- try {
- number = (long) defaultValue;
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else {
- number = getPersistedLong(mNumber);
- }
-
- setNumber(number);
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputNumberPreference.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputNumberPreference.kt
new file mode 100644
index 000000000..b4cc67212
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputNumberPreference.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preference
+
+import android.content.Context
+import android.content.res.TypedArray
+import android.util.AttributeSet
+
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
+
+class InputNumberPreference @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = R.attr.dialogPreferenceStyle,
+ defStyleRes: Int = defStyleAttr)
+ : InputTextExplanationPreference(context, attrs, defStyleAttr, defStyleRes) {
+
+ // Save to Shared Preferences
+ var number: Long = 0
+ set(number) {
+ field = number
+ persistLong(number)
+ }
+
+ override fun getDialogLayoutResource(): Int {
+ return R.layout.pref_dialog_numbers
+ }
+
+ override fun setSummary(summary: CharSequence) {
+ if (summary == KdfEngine.UNKNOW_VALUE_STRING) {
+ isEnabled = false
+ super.setSummary("")
+ } else {
+ isEnabled = true
+ super.setSummary(summary)
+ }
+ }
+
+ override fun onGetDefaultValue(a: TypedArray?, index: Int): Any {
+ // Default value from attribute. Fallback value is set to 0.
+ return a?.getInt(index, 0) ?: 0
+ }
+
+ override fun onSetInitialValue(restorePersistedValue: Boolean,
+ defaultValue: Any?) {
+ // Read the value. Use the default value if it is not possible.
+ var numberValue: Long
+ if (!restorePersistedValue) {
+ numberValue = 100000
+ if (defaultValue is String) {
+ numberValue = java.lang.Long.parseLong(defaultValue as String?)
+ }
+ if (defaultValue is Int) {
+ numberValue = defaultValue.toLong()
+ }
+ try {
+ numberValue = defaultValue as Long
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ } else {
+ numberValue = getPersistedLong(this.number)
+ }
+
+ number = numberValue
+ }
+
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextExplanationPreference.java b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextExplanationPreference.java
deleted file mode 100644
index eaafca812..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextExplanationPreference.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.kunzisoft.keepass.settings.preference;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.v7.preference.DialogPreference;
-import android.util.AttributeSet;
-
-import com.kunzisoft.keepass.R;
-
-public class InputTextExplanationPreference extends DialogPreference {
-
- protected String explanation;
-
- public InputTextExplanationPreference(Context context) {
- this(context, null);
- }
-
- public InputTextExplanationPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.dialogPreferenceStyle);
- }
-
- public InputTextExplanationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, defStyleAttr);
- }
-
- public InputTextExplanationPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- TypedArray a = context.getTheme().obtainStyledAttributes(
- attrs,
- R.styleable.explanationDialog,
- 0, 0);
- try {
- setExplanation(a.getString(R.styleable.explanationDialog_explanations));
- } finally {
- a.recycle();
- }
- }
-
- @Override
- public int getDialogLayoutResource() {
- return R.layout.pref_dialog_input_text_explanation;
- }
-
- public String getExplanation() {
- return explanation;
- }
-
- public void setExplanation(String explanation) {
- this.explanation = explanation;
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextExplanationPreference.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextExplanationPreference.kt
new file mode 100644
index 000000000..c9ae031aa
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextExplanationPreference.kt
@@ -0,0 +1,32 @@
+package com.kunzisoft.keepass.settings.preference
+
+import android.content.Context
+import android.support.v7.preference.DialogPreference
+import android.util.AttributeSet
+
+import com.kunzisoft.keepass.R
+
+open class InputTextExplanationPreference @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = R.attr.dialogPreferenceStyle,
+ defStyleRes: Int = defStyleAttr)
+ : DialogPreference(context, attrs, defStyleAttr, defStyleRes) {
+
+ var explanation: String? = null
+
+ init {
+ val styleAttributes = context.theme.obtainStyledAttributes(
+ attrs,
+ R.styleable.explanationDialog,
+ 0, 0)
+ try {
+ explanation = styleAttributes.getString(R.styleable.explanationDialog_explanations)
+ } finally {
+ styleAttributes.recycle()
+ }
+ }
+
+ override fun getDialogLayoutResource(): Int {
+ return R.layout.pref_dialog_input_text_explanation
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextPreference.java b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextPreference.java
deleted file mode 100644
index 8e9bbaa46..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextPreference.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.kunzisoft.keepass.settings.preference;
-
-import android.content.Context;
-import android.support.v7.preference.DialogPreference;
-import android.util.AttributeSet;
-
-import com.kunzisoft.keepass.R;
-
-public class InputTextPreference extends DialogPreference {
-
- public InputTextPreference(Context context) {
- this(context, null);
- }
-
- public InputTextPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.dialogPreferenceStyle);
- }
-
- public InputTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, defStyleAttr);
- }
-
- public InputTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public int getDialogLayoutResource() {
- return R.layout.pref_dialog_input_text;
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextPreference.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextPreference.kt
new file mode 100644
index 000000000..67ce47900
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preference/InputTextPreference.kt
@@ -0,0 +1,18 @@
+package com.kunzisoft.keepass.settings.preference
+
+import android.content.Context
+import android.support.v7.preference.DialogPreference
+import android.util.AttributeSet
+
+import com.kunzisoft.keepass.R
+
+class InputTextPreference @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = R.attr.dialogPreferenceStyle,
+ defStyleRes: Int = defStyleAttr)
+ : DialogPreference(context, attrs, defStyleAttr, defStyleRes) {
+
+ override fun getDialogLayoutResource(): Int {
+ return R.layout.pref_dialog_input_text
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java
deleted file mode 100644
index d655418e6..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-
-import com.kunzisoft.keepass.tasks.ActionRunnable;
-
-import org.jetbrains.annotations.Nullable;
-
-public class DatabaseDescriptionPreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat {
-
- public static DatabaseDescriptionPreferenceDialogFragmentCompat newInstance(
- String key) {
- final DatabaseDescriptionPreferenceDialogFragmentCompat
- fragment = new DatabaseDescriptionPreferenceDialogFragmentCompat();
- final Bundle b = new Bundle(1);
- b.putString(ARG_KEY, key);
- fragment.setArguments(b);
-
- return fragment;
- }
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- setInputText(getDatabase().getDescription());
- }
-
- @Override
- public void onDialogClosed(boolean positiveResult) {
- if ( positiveResult ) {
- assert getContext() != null;
-
- String newDescription = getInputText();
- String oldDescription = getDatabase().getDescription();
- getDatabase().assignDescription(newDescription);
-
- setAfterSaveDatabaseRunnable(new AfterDescriptionSave((AppCompatActivity) getActivity(), newDescription, oldDescription));
- }
-
- super.onDialogClosed(positiveResult);
- }
-
- private class AfterDescriptionSave extends ActionRunnable {
-
- private AppCompatActivity mActivity;
- private String mNewDescription;
- private String mOldDescription;
-
- AfterDescriptionSave(AppCompatActivity ctx, String newDescription, String oldDescription) {
- super();
-
- mActivity = ctx;
- mNewDescription = newDescription;
- mOldDescription = oldDescription;
- }
-
- @Override
- public void onFinishRun(boolean isSuccess, @Nullable String message) {
- if (mActivity != null) {
- mActivity.runOnUiThread(() -> {
- String descriptionToShow = mNewDescription;
-
- if (!isSuccess) {
- displayMessage(mActivity);
- getDatabase().assignDescription(mOldDescription);
- }
-
- getPreference().setSummary(descriptionToShow);
- });
- }
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..4605d6fc0
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.os.Bundle
+import android.view.View
+import com.kunzisoft.keepass.tasks.ActionRunnable
+
+class DatabaseDescriptionPreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ inputText = database?.description ?: ""
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ if (database != null && positiveResult) {
+ val newDescription = inputText
+ val oldDescription = database!!.description
+ database?.assignDescription(newDescription)
+
+ actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newDescription, oldDescription)
+ }
+
+ super.onDialogClosed(positiveResult)
+ }
+
+ private inner class AfterDescriptionSave(private val mNewDescription: String,
+ private val mOldDescription: String)
+ : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ val descriptionToShow = mNewDescription
+ if (!result.isSuccess) {
+ activity?.let {
+ displayMessage(it)
+ }
+ database?.assignDescription(mOldDescription)
+ }
+ preference.summary = descriptionToShow
+ }
+ }
+
+ companion object {
+
+ fun newInstance(key: String): DatabaseDescriptionPreferenceDialogFragmentCompat {
+ val fragment = DatabaseDescriptionPreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java
deleted file mode 100644
index fcd0d621f..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm;
-import com.kunzisoft.keepass.tasks.ActionRunnable;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter;
-
-import org.jetbrains.annotations.Nullable;
-
-public class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat extends DatabaseSavePreferenceDialogFragmentCompat
- implements ListRadioItemAdapter.RadioItemSelectedCallback {
-
- private PwEncryptionAlgorithm algorithmSelected;
-
- public static DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat newInstance(
- String key) {
- final DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
- fragment = new DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat();
- final Bundle b = new Bundle(1);
- b.putString(ARG_KEY, key);
- fragment.setArguments(b);
-
- return fragment;
- }
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- setExplanationText(R.string.encryption_explanation);
-
- RecyclerView recyclerView = view.findViewById(R.id.pref_dialog_list);
- recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- ListRadioItemAdapter encryptionAlgorithmAdapter = new ListRadioItemAdapter<>(getActivity());
- encryptionAlgorithmAdapter.setRadioItemSelectedCallback(this);
- recyclerView.setAdapter(encryptionAlgorithmAdapter);
-
- algorithmSelected = getDatabase().getEncryptionAlgorithm();
- encryptionAlgorithmAdapter.setItems(getDatabase().getAvailableEncryptionAlgorithms(), algorithmSelected);
- }
-
- @Override
- public void onDialogClosed(boolean positiveResult) {
- if ( positiveResult
- && getDatabase().allowEncryptionAlgorithmModification()) {
- assert getContext() != null;
-
- if (algorithmSelected != null) {
- PwEncryptionAlgorithm newAlgorithm = algorithmSelected;
- PwEncryptionAlgorithm oldAlgorithm = getDatabase().getEncryptionAlgorithm();
- getDatabase().assignEncryptionAlgorithm(newAlgorithm);
-
- setAfterSaveDatabaseRunnable(new AfterDescriptionSave((AppCompatActivity) getActivity(), newAlgorithm, oldAlgorithm));
- }
- }
-
- super.onDialogClosed(positiveResult);
- }
-
- @Override
- public void onItemSelected(PwEncryptionAlgorithm item) {
- this.algorithmSelected = item;
- }
-
- private class AfterDescriptionSave extends ActionRunnable {
-
- private PwEncryptionAlgorithm mNewAlgorithm;
- private PwEncryptionAlgorithm mOldAlgorithm;
- private AppCompatActivity mActivity;
-
- AfterDescriptionSave(AppCompatActivity activity, PwEncryptionAlgorithm newAlgorithm, PwEncryptionAlgorithm oldAlgorithm) {
- super();
-
- mActivity = activity;
- mNewAlgorithm = newAlgorithm;
- mOldAlgorithm = oldAlgorithm;
- }
-
- @Override
- public void onFinishRun(boolean isSuccess, @Nullable String message) {
- if (mActivity != null) {
- mActivity.runOnUiThread(() -> {
- PwEncryptionAlgorithm algorithmToShow = mNewAlgorithm;
-
- if (!isSuccess) {
- displayMessage(mActivity);
- getDatabase().assignEncryptionAlgorithm(mOldAlgorithm);
- }
- getPreference().setSummary(algorithmToShow.getName(mActivity.getResources()));
- });
- }
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..43aa3f84d
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.os.Bundle
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import android.view.View
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm
+import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
+import com.kunzisoft.keepass.tasks.ActionRunnable
+
+class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat(), ListRadioItemAdapter.RadioItemSelectedCallback {
+
+ private var algorithmSelected: PwEncryptionAlgorithm? = null
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ setExplanationText(R.string.encryption_explanation)
+
+ val recyclerView = view.findViewById(R.id.pref_dialog_list)
+ recyclerView.layoutManager = LinearLayoutManager(context)
+
+ activity?.let { activity ->
+ val encryptionAlgorithmAdapter = ListRadioItemAdapter(activity)
+ encryptionAlgorithmAdapter.setRadioItemSelectedCallback(this)
+ recyclerView.adapter = encryptionAlgorithmAdapter
+
+ database?.let { database ->
+ algorithmSelected = database.encryptionAlgorithm
+ if (algorithmSelected != null)
+ encryptionAlgorithmAdapter.setItems(database.availableEncryptionAlgorithms, algorithmSelected!!)
+ }
+ }
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ if (database != null && positiveResult && database!!.allowEncryptionAlgorithmModification()) {
+
+ if (algorithmSelected != null) {
+ val newAlgorithm = algorithmSelected
+ val oldAlgorithm = database?.encryptionAlgorithm
+ newAlgorithm?.let {
+ database?.assignEncryptionAlgorithm(it)
+ }
+
+ if (oldAlgorithm != null && newAlgorithm != null)
+ actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newAlgorithm, oldAlgorithm)
+ }
+ }
+
+ super.onDialogClosed(positiveResult)
+ }
+
+ override fun onItemSelected(item: PwEncryptionAlgorithm) {
+ this.algorithmSelected = item
+ }
+
+ private inner class AfterDescriptionSave(private val mNewAlgorithm: PwEncryptionAlgorithm,
+ private val mOldAlgorithm: PwEncryptionAlgorithm)
+ : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ activity?.let { activity ->
+ var algorithmToShow = mNewAlgorithm
+ if (!result.isSuccess) {
+ displayMessage(activity)
+ database?.assignEncryptionAlgorithm(mOldAlgorithm)
+ algorithmToShow = mOldAlgorithm
+ }
+ preference.summary = algorithmToShow.getName(activity.resources)
+ }
+ }
+ }
+
+ companion object {
+
+ fun newInstance(key: String): DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat {
+ val fragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java
deleted file mode 100644
index ea465b20c..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.preference.Preference;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine;
-import com.kunzisoft.keepass.tasks.ActionRunnable;
-import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter;
-
-import org.jetbrains.annotations.Nullable;
-
-public class DatabaseKeyDerivationPreferenceDialogFragmentCompat extends DatabaseSavePreferenceDialogFragmentCompat
- implements ListRadioItemAdapter.RadioItemSelectedCallback {
-
- private KdfEngine kdfEngineSelected;
- private Preference roundPreference;
- private Preference memoryPreference;
- private Preference parallelismPreference;
-
- public static DatabaseKeyDerivationPreferenceDialogFragmentCompat newInstance(
- String key) {
- final DatabaseKeyDerivationPreferenceDialogFragmentCompat
- fragment = new DatabaseKeyDerivationPreferenceDialogFragmentCompat();
- final Bundle b = new Bundle(1);
- b.putString(ARG_KEY, key);
- fragment.setArguments(b);
-
- return fragment;
- }
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- setExplanationText(R.string.kdf_explanation);
-
- RecyclerView recyclerView = view.findViewById(R.id.pref_dialog_list);
- recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- ListRadioItemAdapter kdfAdapter = new ListRadioItemAdapter<>(getActivity());
- kdfAdapter.setRadioItemSelectedCallback(this);
- recyclerView.setAdapter(kdfAdapter);
-
- kdfEngineSelected = getDatabase().getKdfEngine();
- kdfAdapter.setItems(getDatabase().getAvailableKdfEngines(), kdfEngineSelected);
- }
-
- @Override
- public void onDialogClosed(boolean positiveResult) {
- if ( positiveResult
- && getDatabase().allowKdfModification()) {
- assert getContext() != null;
-
- if (kdfEngineSelected != null) {
- KdfEngine newKdfEngine = kdfEngineSelected;
- KdfEngine oldKdfEngine = getDatabase().getKdfEngine();
- getDatabase().assignKdfEngine(newKdfEngine);
-
- setAfterSaveDatabaseRunnable(new AfterDescriptionSave((AppCompatActivity) getActivity(), newKdfEngine, oldKdfEngine));
- }
- }
-
- super.onDialogClosed(positiveResult);
- }
-
- public void setRoundPreference(Preference preference) {
- this.roundPreference = preference;
- }
-
- public void setMemoryPreference(Preference preference) {
- this.memoryPreference = preference;
- }
-
- public void setParallelismPreference(Preference preference) {
- this.parallelismPreference = preference;
- }
-
- @Override
- public void onItemSelected(KdfEngine item) {
- kdfEngineSelected = item;
- }
-
- private class AfterDescriptionSave extends ActionRunnable {
-
- private KdfEngine mNewKdfEngine;
- private KdfEngine mOldKdfEngine;
- private AppCompatActivity mActivity;
-
- AfterDescriptionSave(AppCompatActivity activity, KdfEngine newKdfEngine, KdfEngine oldKdfEngine) {
- super();
-
- this.mActivity = activity;
- this.mNewKdfEngine = newKdfEngine;
- this.mOldKdfEngine = oldKdfEngine;
- }
-
- @Override
- public void onFinishRun(boolean isSuccess, @Nullable String message) {
- if (mActivity != null) {
- mActivity.runOnUiThread(() -> {
- KdfEngine kdfEngineToShow = mNewKdfEngine;
-
- if (!isSuccess) {
- displayMessage(mActivity);
- getDatabase().assignKdfEngine(mOldKdfEngine);
- }
-
- getPreference().setSummary(kdfEngineToShow.getName(mActivity.getResources()));
- if (roundPreference != null)
- roundPreference.setSummary(String.valueOf(kdfEngineToShow.getDefaultKeyRounds()));
-
- // Disable memory and parallelism if not available
- if (memoryPreference != null) {
- memoryPreference.setSummary(String.valueOf(kdfEngineToShow.getDefaultMemoryUsage()));
- }
- if (parallelismPreference != null) {
- parallelismPreference.setSummary(String.valueOf(kdfEngineToShow.getDefaultParallelism()));
- }
- });
- }
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..0e47ab277
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.os.Bundle
+import android.support.v7.preference.Preference
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import android.view.View
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
+import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
+import com.kunzisoft.keepass.tasks.ActionRunnable
+
+class DatabaseKeyDerivationPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat(), ListRadioItemAdapter.RadioItemSelectedCallback {
+
+ private var kdfEngineSelected: KdfEngine? = null
+ private var roundPreference: Preference? = null
+ private var memoryPreference: Preference? = null
+ private var parallelismPreference: Preference? = null
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ setExplanationText(R.string.kdf_explanation)
+
+ val recyclerView = view.findViewById(R.id.pref_dialog_list)
+ recyclerView.layoutManager = LinearLayoutManager(context)
+
+ activity?.let { activity ->
+ val kdfAdapter = ListRadioItemAdapter(activity)
+ kdfAdapter.setRadioItemSelectedCallback(this)
+ recyclerView.adapter = kdfAdapter
+
+ database?.let { database ->
+ kdfEngineSelected = database.kdfEngine
+ if (kdfEngineSelected != null)
+ kdfAdapter.setItems(database.availableKdfEngines, kdfEngineSelected!!)
+ }
+ }
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ if (database != null && positiveResult && database!!.allowKdfModification()) {
+ if (kdfEngineSelected != null) {
+ val newKdfEngine = kdfEngineSelected
+ val oldKdfEngine = database!!.kdfEngine
+ database?.assignKdfEngine(newKdfEngine!!)
+
+ actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newKdfEngine!!, oldKdfEngine)
+ }
+ }
+
+ super.onDialogClosed(positiveResult)
+ }
+
+ fun setRoundPreference(preference: Preference) {
+ this.roundPreference = preference
+ }
+
+ fun setMemoryPreference(preference: Preference) {
+ this.memoryPreference = preference
+ }
+
+ fun setParallelismPreference(preference: Preference) {
+ this.parallelismPreference = preference
+ }
+
+ override fun onItemSelected(item: KdfEngine) {
+ kdfEngineSelected = item
+ }
+
+ private inner class AfterDescriptionSave(private val mNewKdfEngine: KdfEngine,
+ private val mOldKdfEngine: KdfEngine)
+ : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ val kdfEngineToShow = mNewKdfEngine
+
+ activity?.let { activity ->
+ if (!result.isSuccess) {
+ displayMessage(activity)
+ database?.assignKdfEngine(mOldKdfEngine)
+ }
+ preference.summary = kdfEngineToShow.getName(activity.resources)
+ }
+
+ roundPreference?.summary = kdfEngineToShow.defaultKeyRounds.toString()
+ // Disable memory and parallelism if not available
+ memoryPreference?.summary = kdfEngineToShow.defaultMemoryUsage.toString()
+ parallelismPreference?.summary = kdfEngineToShow.defaultParallelism.toString()
+ }
+ }
+
+ companion object {
+
+ fun newInstance(key: String): DatabaseKeyDerivationPreferenceDialogFragmentCompat {
+ val fragment = DatabaseKeyDerivationPreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.java
deleted file mode 100644
index f60504328..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-
-import com.kunzisoft.keepass.tasks.ActionRunnable;
-
-import org.jetbrains.annotations.Nullable;
-
-public class DatabaseNamePreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat {
-
- public static DatabaseNamePreferenceDialogFragmentCompat newInstance(
- String key) {
- final DatabaseNamePreferenceDialogFragmentCompat
- fragment = new DatabaseNamePreferenceDialogFragmentCompat();
- final Bundle b = new Bundle(1);
- b.putString(ARG_KEY, key);
- fragment.setArguments(b);
-
- return fragment;
- }
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- setInputText(getDatabase().getName());
- }
-
- @Override
- public void onDialogClosed(boolean positiveResult) {
- if ( positiveResult ) {
- assert getContext() != null;
-
- String newName = getInputText();
- String oldName = getDatabase().getName();
- getDatabase().assignName(newName);
-
- setAfterSaveDatabaseRunnable(new AfterNameSave((AppCompatActivity) getActivity(), newName, oldName));
- }
-
- super.onDialogClosed(positiveResult);
- }
-
- private class AfterNameSave extends ActionRunnable {
-
- private String mNewName;
- private String mOldName;
- private AppCompatActivity mActivity;
-
- AfterNameSave(AppCompatActivity ctx, String newName, String oldName) {
- super();
-
- mActivity = ctx;
- mNewName = newName;
- mOldName = oldName;
- }
-
- @Override
- public void onFinishRun(boolean isSuccess, @Nullable String message) {
- if (mActivity != null) {
- mActivity.runOnUiThread(() -> {
- String nameToShow = mNewName;
-
- if (!isSuccess) {
- displayMessage(mActivity);
- getDatabase().assignName(mOldName);
- }
-
- getPreference().setSummary(nameToShow);
- });
- }
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..c0b775107
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.os.Bundle
+import android.view.View
+import com.kunzisoft.keepass.tasks.ActionRunnable
+
+class DatabaseNamePreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ inputText = database?.name ?: ""
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ if (database != null && positiveResult) {
+ val newName = inputText
+ val oldName = database!!.name
+ database?.assignName(newName)
+
+ actionInUIThreadAfterSaveDatabase = AfterNameSave(newName, oldName)
+ }
+
+ super.onDialogClosed(positiveResult)
+ }
+
+ private inner class AfterNameSave(private val mNewName: String,
+ private val mOldName: String)
+ : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ val nameToShow = mNewName
+ if (!result.isSuccess) {
+ activity?.let {
+ displayMessage(it)
+ }
+ database?.assignName(mOldName)
+ }
+ preference.summary = nameToShow
+ }
+ }
+
+ companion object {
+
+ fun newInstance(
+ key: String): DatabaseNamePreferenceDialogFragmentCompat {
+ val fragment = DatabaseNamePreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt
index 740c45b08..1d34b4258 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.view.View
import com.kunzisoft.keepass.app.App
import com.kunzisoft.keepass.database.action.ProgressDialogSaveDatabaseThread
-import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
+import com.kunzisoft.keepass.database.action.SaveDatabaseActionRunnable
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.tasks.ActionRunnable
@@ -30,7 +30,7 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialo
protected var database: Database? = null
- var afterSaveDatabaseRunnable: ActionRunnable? = null
+ var actionInUIThreadAfterSaveDatabase: ActionRunnable? = null
override fun onBindDialogView(view: View) {
super.onBindDialogView(view)
@@ -42,14 +42,14 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialo
if (positiveResult) {
activity?.let { notNullActivity ->
database?.let { notNullDatabase ->
- afterSaveDatabaseRunnable?.let { runnable ->
- ProgressDialogSaveDatabaseThread(notNullActivity) {
- SaveDatabaseRunnable(
- notNullActivity,
- notNullDatabase,
- true,
- runnable)
- }.start()
+ ProgressDialogSaveDatabaseThread(notNullActivity) {
+ SaveDatabaseActionRunnable(
+ notNullActivity,
+ notNullDatabase,
+ true)
+ }.apply {
+ actionFinishInUIThread = actionInUIThreadAfterSaveDatabase
+ start()
}
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.java
deleted file mode 100644
index 92d9b66a4..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.view.View;
-import android.widget.EditText;
-
-import com.kunzisoft.keepass.R;
-
-public class InputDatabaseSavePreferenceDialogFragmentCompat extends DatabaseSavePreferenceDialogFragmentCompat {
-
- private EditText inputTextView;
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- inputTextView = view.findViewById(R.id.input_text);
- }
-
- public String getInputText() {
- return this.inputTextView.getText().toString();
- }
-
- public void setInputText(String inputText) {
- if (inputTextView != null && inputText != null) {
- this.inputTextView.setText(inputText);
- this.inputTextView.setSelection(this.inputTextView.getText().length());
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..31b4fcbac
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.view.View
+import android.widget.EditText
+
+import com.kunzisoft.keepass.R
+
+open class InputDatabaseSavePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
+
+ private var inputTextView: EditText? = null
+
+ var inputText: String
+ get() = this.inputTextView?.text?.toString() ?: ""
+ set(inputText) {
+ if (inputTextView != null) {
+ this.inputTextView?.setText(inputText)
+ this.inputTextView?.setSelection(this.inputTextView!!.text.length)
+ }
+ }
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ inputTextView = view.findViewById(R.id.input_text)
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.java
deleted file mode 100644
index ec26674fa..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.support.annotation.StringRes;
-import android.support.v7.preference.PreferenceDialogFragmentCompat;
-import android.view.View;
-import android.widget.TextView;
-
-import com.kunzisoft.keepass.R;
-
-public abstract class InputPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
-
- private TextView textExplanationView;
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- textExplanationView = view.findViewById(R.id.explanation_text);
- }
-
- public String getExplanationText() {
- if (textExplanationView != null)
- return textExplanationView.getText().toString();
- else
- return "";
- }
-
- public void setExplanationText(String explanationText) {
- if (textExplanationView != null)
- if (explanationText != null && !explanationText.isEmpty()) {
- textExplanationView.setText(explanationText);
- textExplanationView.setVisibility(View.VISIBLE);
- } else {
- textExplanationView.setText(explanationText);
- textExplanationView.setVisibility(View.VISIBLE);
- }
- }
-
- public void setExplanationText(@StringRes int explanationTextId) {
- setExplanationText(getString(explanationTextId));
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..a7f1c1609
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.support.annotation.StringRes
+import android.support.v7.preference.PreferenceDialogFragmentCompat
+import android.view.View
+import android.widget.TextView
+
+import com.kunzisoft.keepass.R
+
+abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCompat() {
+
+ private var textExplanationView: TextView? = null
+
+ var explanationText: String?
+ get() = textExplanationView?.text?.toString() ?: ""
+ set(explanationText) {
+ if (explanationText != null && explanationText.isNotEmpty()) {
+ textExplanationView?.text = explanationText
+ textExplanationView?.visibility = View.VISIBLE
+ } else {
+ textExplanationView?.text = explanationText
+ textExplanationView?.visibility = View.VISIBLE
+ }
+ }
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ textExplanationView = view.findViewById(R.id.explanation_text)
+ }
+
+ fun setExplanationText(@StringRes explanationTextId: Int) {
+ explanationText = getString(explanationTextId)
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.java
deleted file mode 100644
index 2c8e76085..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-import android.widget.Toast;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.tasks.ActionRunnable;
-
-public class MemoryUsagePreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat {
-
- public static MemoryUsagePreferenceDialogFragmentCompat newInstance(
- String key) {
- final MemoryUsagePreferenceDialogFragmentCompat
- fragment = new MemoryUsagePreferenceDialogFragmentCompat();
- final Bundle b = new Bundle(1);
- b.putString(ARG_KEY, key);
- fragment.setArguments(b);
-
- return fragment;
- }
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- setExplanationText(R.string.memory_usage_explanation);
- setInputText(getDatabase().getMemoryUsageAsString());
- }
-
- @Override
- public void onDialogClosed(boolean positiveResult) {
- if ( positiveResult ) {
- assert getContext() != null;
- long memoryUsage;
-
- try {
- String stringMemory = getInputText();
- memoryUsage = Long.parseLong(stringMemory);
- } catch (NumberFormatException e) {
- Toast.makeText(getContext(), R.string.error_rounds_not_number, Toast.LENGTH_LONG).show(); // TODO change error
- return;
- }
-
- if ( memoryUsage < 1 ) {
- memoryUsage = 1;
- }
-
- long oldMemoryUsage = getDatabase().getMemoryUsage();
- getDatabase().setMemoryUsage(memoryUsage);
-
- setAfterSaveDatabaseRunnable(new AfterMemorySave((AppCompatActivity) getActivity(), memoryUsage, oldMemoryUsage));
- }
-
- super.onDialogClosed(positiveResult);
- }
-
- private class AfterMemorySave extends ActionRunnable {
-
- private long mNewMemory;
- private long mOldMemory;
- private AppCompatActivity mActivity;
-
- AfterMemorySave(AppCompatActivity ctx, long newMemory, long oldMemory) {
- super();
-
- mActivity = ctx;
- mNewMemory = newMemory;
- mOldMemory = oldMemory;
- }
-
- @Override
- public void onFinishRun(boolean isSuccess, String message) {
- if (mActivity != null) {
- mActivity.runOnUiThread(() -> {
- long memoryToShow = mNewMemory;
-
- if (!isSuccess) {
- displayMessage(mActivity);
- getDatabase().setMemoryUsage(mOldMemory);
- }
-
- getPreference().setSummary(String.valueOf(memoryToShow));
- });
- }
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..7e79e39de
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.tasks.ActionRunnable
+
+class MemoryUsagePreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ setExplanationText(R.string.memory_usage_explanation)
+ inputText = database?.memoryUsageAsString ?: ""
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ if (database != null && positiveResult) {
+ var memoryUsage: Long
+ try {
+ val stringMemory = inputText
+ memoryUsage = java.lang.Long.parseLong(stringMemory)
+ } catch (e: NumberFormatException) {
+ Toast.makeText(context, R.string.error_rounds_not_number, Toast.LENGTH_LONG).show() // TODO change error
+ return
+ }
+
+ if (memoryUsage < 1) {
+ memoryUsage = 1
+ }
+
+ val oldMemoryUsage = database!!.memoryUsage
+ database!!.memoryUsage = memoryUsage
+
+ actionInUIThreadAfterSaveDatabase = AfterMemorySave(memoryUsage, oldMemoryUsage)
+ }
+
+ super.onDialogClosed(positiveResult)
+ }
+
+ private inner class AfterMemorySave(private val mNewMemory: Long,
+ private val mOldMemory: Long)
+ : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ val memoryToShow = mNewMemory
+ if (!result.isSuccess) {
+ activity?.let {
+ displayMessage(it)
+ }
+ database?.memoryUsage = mOldMemory
+ }
+ preference.summary = memoryToShow.toString()
+ }
+ }
+
+ companion object {
+
+ fun newInstance(key: String): MemoryUsagePreferenceDialogFragmentCompat {
+ val fragment = MemoryUsagePreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.java
deleted file mode 100644
index ad369e520..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-import android.widget.Toast;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.tasks.ActionRunnable;
-
-public class ParallelismPreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat {
-
- public static ParallelismPreferenceDialogFragmentCompat newInstance(
- String key) {
- final ParallelismPreferenceDialogFragmentCompat
- fragment = new ParallelismPreferenceDialogFragmentCompat();
- final Bundle b = new Bundle(1);
- b.putString(ARG_KEY, key);
- fragment.setArguments(b);
-
- return fragment;
- }
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- setExplanationText(R.string.parallelism_explanation);
- setInputText(getDatabase().getParallelismAsString());
- }
-
- @Override
- public void onDialogClosed(boolean positiveResult) {
- if ( positiveResult ) {
- assert getContext() != null;
- int parallelism;
-
- try {
- String stringParallelism = getInputText();
- parallelism = Integer.parseInt(stringParallelism);
- } catch (NumberFormatException e) {
- Toast.makeText(getContext(), R.string.error_rounds_not_number, Toast.LENGTH_LONG).show(); // TODO change error
- return;
- }
-
- if ( parallelism < 1 ) {
- parallelism = 1;
- }
-
- int oldParallelism = getDatabase().getParallelism();
- getDatabase().setParallelism(parallelism);
-
- setAfterSaveDatabaseRunnable(new AfterParallelismSave((AppCompatActivity) getActivity(), parallelism, oldParallelism));
- }
-
- super.onDialogClosed(positiveResult);
- }
-
- private class AfterParallelismSave extends ActionRunnable {
-
- private int mNewParallelism;
- private int mOldParallelism;
- private AppCompatActivity mActivity;
-
- AfterParallelismSave(AppCompatActivity ctx, int newParallelism, int oldParallelism) {
- super();
-
- mActivity = ctx;
- mNewParallelism = newParallelism;
- mOldParallelism = oldParallelism;
- }
-
- @Override
- public void onFinishRun(boolean isSuccess, String message) {
- if (mActivity != null) {
- mActivity.runOnUiThread(() -> {
- int parallelismToShow = mNewParallelism;
-
- if (!isSuccess) {
- displayMessage(mActivity);
- getDatabase().setParallelism(mOldParallelism);
- }
-
- getPreference().setSummary(String.valueOf(parallelismToShow));
- });
- }
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..95e39b7d0
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.tasks.ActionRunnable
+
+class ParallelismPreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ setExplanationText(R.string.parallelism_explanation)
+ inputText = database?.parallelismAsString ?: ""
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ if (database != null && positiveResult) {
+ var parallelism: Int
+ try {
+ val stringParallelism = inputText
+ parallelism = Integer.parseInt(stringParallelism)
+ } catch (e: NumberFormatException) {
+ Toast.makeText(context, R.string.error_rounds_not_number, Toast.LENGTH_LONG).show() // TODO change error
+ return
+ }
+
+ if (parallelism < 1) {
+ parallelism = 1
+ }
+
+ val oldParallelism = database!!.parallelism
+ database?.parallelism = parallelism
+
+ actionInUIThreadAfterSaveDatabase = AfterParallelismSave(parallelism, oldParallelism)
+ }
+
+ super.onDialogClosed(positiveResult)
+ }
+
+ private inner class AfterParallelismSave(private val mNewParallelism: Int,
+ private val mOldParallelism: Int)
+ : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ val parallelismToShow = mNewParallelism
+ if (!result.isSuccess) {
+ activity?.let {
+ displayMessage(it)
+ }
+ database?.parallelism = mOldParallelism
+ }
+ preference.summary = parallelismToShow.toString()
+ }
+ }
+
+ companion object {
+
+ fun newInstance(key: String): ParallelismPreferenceDialogFragmentCompat {
+ val fragment = ParallelismPreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.java
deleted file mode 100644
index 723afcd77..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment;
-
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-import android.widget.Toast;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.tasks.ActionRunnable;
-
-public class RoundsPreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat {
-
- public static RoundsPreferenceDialogFragmentCompat newInstance(
- String key) {
- final RoundsPreferenceDialogFragmentCompat
- fragment = new RoundsPreferenceDialogFragmentCompat();
- final Bundle b = new Bundle(1);
- b.putString(ARG_KEY, key);
- fragment.setArguments(b);
-
- return fragment;
- }
-
- @Override
- protected void onBindDialogView(View view) {
- super.onBindDialogView(view);
-
- setExplanationText(getString(R.string.rounds_explanation));
- setInputText(getDatabase().getNumberKeyEncryptionRoundsAsString());
- }
-
- @Override
- public void onDialogClosed(boolean positiveResult) {
- if ( positiveResult ) {
- assert getContext() != null;
- long rounds;
-
- try {
- String strRounds = getInputText();
- rounds = Long.parseLong(strRounds);
- } catch (NumberFormatException e) {
- Toast.makeText(getContext(), R.string.error_rounds_not_number, Toast.LENGTH_LONG).show();
- return;
- }
-
- if ( rounds < 1 ) {
- rounds = 1;
- }
-
- long oldRounds = getDatabase().getNumberKeyEncryptionRounds();
- try {
- getDatabase().setNumberKeyEncryptionRounds(rounds);
- } catch (NumberFormatException e) {
- Toast.makeText(getContext(), R.string.error_rounds_too_large, Toast.LENGTH_LONG).show();
- getDatabase().setNumberKeyEncryptionRounds(Integer.MAX_VALUE);
- }
-
- setAfterSaveDatabaseRunnable(new AfterRoundSave((AppCompatActivity) getActivity(), rounds, oldRounds));
- }
-
- super.onDialogClosed(positiveResult);
- }
-
- private class AfterRoundSave extends ActionRunnable {
-
- private long mNewRounds;
- private long mOldRounds;
- private AppCompatActivity mActivity;
-
- AfterRoundSave(AppCompatActivity ctx, long newRounds, long oldRounds) {
- super();
-
- mActivity = ctx;
- mNewRounds = newRounds;
- mOldRounds = oldRounds;
- }
-
- @Override
- public void onFinishRun(boolean isSuccess, String message) {
- if (mActivity != null) {
- mActivity.runOnUiThread(() -> {
- long roundsToShow = mNewRounds;
-
- if (!isSuccess) {
- displayMessage(mActivity);
- getDatabase().setNumberKeyEncryptionRounds(mOldRounds);
- }
-
- getPreference().setSummary(String.valueOf(roundsToShow));
- });
- }
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..1c6b7dde0
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.tasks.ActionRunnable
+
+class RoundsPreferenceDialogFragmentCompat : InputDatabaseSavePreferenceDialogFragmentCompat() {
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ explanationText = getString(R.string.rounds_explanation)
+ inputText = database?.numberKeyEncryptionRoundsAsString ?: ""
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ if (database != null && positiveResult) {
+ var rounds: Long
+ try {
+ val strRounds = inputText
+ rounds = java.lang.Long.parseLong(strRounds)
+ } catch (e: NumberFormatException) {
+ Toast.makeText(context, R.string.error_rounds_not_number, Toast.LENGTH_LONG).show()
+ return
+ }
+
+ if (rounds < 1) {
+ rounds = 1
+ }
+
+ val oldRounds = database!!.numberKeyEncryptionRounds
+ try {
+ database?.numberKeyEncryptionRounds = rounds
+ } catch (e: NumberFormatException) {
+ Toast.makeText(context, R.string.error_rounds_too_large, Toast.LENGTH_LONG).show()
+ database?.numberKeyEncryptionRounds = Integer.MAX_VALUE.toLong()
+ }
+
+ actionInUIThreadAfterSaveDatabase = AfterRoundSave(rounds, oldRounds)
+ }
+
+ super.onDialogClosed(positiveResult)
+ }
+
+ private inner class AfterRoundSave(private val mNewRounds: Long,
+ private val mOldRounds: Long) : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ val roundsToShow = mNewRounds
+ if (!result.isSuccess) {
+ activity?.let {
+ displayMessage(it)
+ }
+ database?.numberKeyEncryptionRounds = mOldRounds
+ }
+
+ preference.summary = roundsToShow.toString()
+ }
+ }
+
+ companion object {
+
+ fun newInstance(key: String): RoundsPreferenceDialogFragmentCompat {
+ val fragment = RoundsPreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.java
deleted file mode 100644
index 2a96e558b..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.settings.preferencedialogfragment.adapter;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.database.ObjectNameResource;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ListRadioItemAdapter extends RecyclerView.Adapter {
-
- private Context context;
- private LayoutInflater inflater;
-
- private List radioItemList;
- private T radioItemUsed;
-
- private RadioItemSelectedCallback radioItemSelectedCallback;
-
- public ListRadioItemAdapter(Context context) {
- this.context = context;
- this.inflater = LayoutInflater.from(context);
- this.radioItemList = new ArrayList<>();
- this.radioItemUsed = null;
- }
-
- @NonNull
- @Override
- public ListRadioViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View view = inflater.inflate(R.layout.pref_dialog_list_radio_item, parent, false);
- return new ListRadioViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(@NonNull ListRadioViewHolder holder, int position) {
- T item = this.radioItemList.get(position);
- holder.radioButton.setText(item.getName(context.getResources()));
- if (radioItemUsed != null && radioItemUsed.equals(item))
- holder.radioButton.setChecked(true);
- else
- holder.radioButton.setChecked(false);
- holder.radioButton.setOnClickListener(new OnItemClickListener(item));
- }
-
- @Override
- public int getItemCount() {
- return radioItemList.size();
- }
-
- public void setItems(List algorithms, T algorithmUsed) {
- this.radioItemList.clear();
- this.radioItemList.addAll(algorithms);
- this.radioItemUsed = algorithmUsed;
- }
-
- private void setRadioItemUsed(T radioItemUsed) {
- this.radioItemUsed = radioItemUsed;
- }
-
- private class OnItemClickListener implements View.OnClickListener {
-
- private T itemClicked;
-
- OnItemClickListener(T item) {
- this.itemClicked = item;
- }
-
- @Override
- public void onClick(View view) {
- if (radioItemSelectedCallback != null)
- radioItemSelectedCallback.onItemSelected(itemClicked);
- setRadioItemUsed(itemClicked);
- notifyDataSetChanged();
- }
- }
-
- public void setRadioItemSelectedCallback(RadioItemSelectedCallback radioItemSelectedCallback) {
- this.radioItemSelectedCallback = radioItemSelectedCallback;
- }
-
- public interface RadioItemSelectedCallback {
- void onItemSelected(T item);
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.kt
new file mode 100644
index 000000000..a67279667
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment.adapter
+
+import android.content.Context
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.RadioButton
+
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.database.ObjectNameResource
+
+import java.util.ArrayList
+
+class ListRadioItemAdapter(private val context: Context)
+ : RecyclerView.Adapter() {
+
+ private val inflater: LayoutInflater = LayoutInflater.from(context)
+
+ private val radioItemList: MutableList = ArrayList()
+ private var radioItemUsed: T? = null
+
+ private var radioItemSelectedCallback: RadioItemSelectedCallback? = null
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListRadioViewHolder {
+ val view = inflater.inflate(R.layout.pref_dialog_list_radio_item, parent, false)
+ return ListRadioViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: ListRadioViewHolder, position: Int) {
+ val item = this.radioItemList[position]
+ holder.radioButton.text = item.getName(context.resources)
+ holder.radioButton.isChecked = radioItemUsed != null && radioItemUsed == item
+ holder.radioButton.setOnClickListener(OnItemClickListener(item))
+ }
+
+ override fun getItemCount(): Int {
+ return radioItemList.size
+ }
+
+ fun setItems(algorithms: List, algorithmUsed: T) {
+ this.radioItemList.clear()
+ this.radioItemList.addAll(algorithms)
+ this.radioItemUsed = algorithmUsed
+ }
+
+ private fun setRadioItemUsed(radioItemUsed: T) {
+ this.radioItemUsed = radioItemUsed
+ }
+
+ private inner class OnItemClickListener(private val itemClicked: T) : View.OnClickListener {
+
+ override fun onClick(view: View) {
+ radioItemSelectedCallback?.onItemSelected(itemClicked)
+ setRadioItemUsed(itemClicked)
+ notifyDataSetChanged()
+ }
+ }
+
+ fun setRadioItemSelectedCallback(radioItemSelectedCallback: RadioItemSelectedCallback) {
+ this.radioItemSelectedCallback = radioItemSelectedCallback
+ }
+
+ interface RadioItemSelectedCallback {
+ fun onItemSelected(item: T)
+ }
+
+ class ListRadioViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ var radioButton: RadioButton = itemView.findViewById(R.id.pref_dialog_list_radio)
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt
index 08fcffb68..0a77df967 100644
--- a/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt
@@ -28,23 +28,18 @@ import android.widget.Toast
* Callback after a task is completed.
*/
abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable? = null,
- private var executeNestedActionIfResultFalse: Boolean = false) : Runnable {
+ private var executeNestedActionIfResultFalse: Boolean = false)
+ : Runnable {
- protected var isSuccess: Boolean = true
- protected var message: String? = null
-
- private fun setResult(result: Boolean, message: String? = null) {
- this.isSuccess = result
- this.message = message
- }
+ var result: Result = Result()
private fun execute() {
nestedActionRunnable?.let {
// Pass on result on call finish
- it.setResult(isSuccess, message)
+ it.result = result
it.run()
}
- onFinishRun(isSuccess, message)
+ onFinishRun(result)
}
override fun run() {
@@ -56,27 +51,28 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable?
* launch the nested action runnable if exists and finish,
* else directly finish
*/
- protected fun finishRun(success: Boolean, message: String? = null) {
- setResult(success, message)
- if (success || executeNestedActionIfResultFalse) {
+ protected fun finishRun(isSuccess: Boolean, message: String? = null) {
+ result.isSuccess = isSuccess
+ result.message = message
+ if (isSuccess || executeNestedActionIfResultFalse) {
execute()
}
else
- onFinishRun(isSuccess, message)
+ onFinishRun(result)
}
/**
* Method called when the action is finished
- * @param isSuccess 'true' if success action, 'false' elsewhere
- * @param message
+ * @param result 'true' if success action, 'false' elsewhere, with message
*/
- abstract fun onFinishRun(isSuccess: Boolean, message: String?)
+ abstract fun onFinishRun(result: Result)
/**
* Display a message as a Toast only if [context] is an Activity
* @param context Context to show the message
*/
protected fun displayMessage(context: Context) {
+ val message = result.message
Log.i(ActionRunnable::class.java.name, message)
try {
(context as Activity).runOnUiThread {
@@ -88,4 +84,9 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable?
}
} catch (exception: ClassCastException) {}
}
+
+ /**
+ * Class to manage result from ActionRunnable
+ */
+ data class Result(var isSuccess: Boolean = true, var message: String? = null)
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt
index 41fc43d57..bfe2af2ce 100644
--- a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt
@@ -32,6 +32,8 @@ import android.widget.ProgressBar
import android.widget.TextView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.Util
+import com.kunzisoft.keepass.utils.lockScreenOrientation
+import com.kunzisoft.keepass.utils.unlockScreenOrientation
open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
@@ -77,7 +79,7 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
}
override fun onDismiss(dialog: DialogInterface?) {
- Util.unlockScreenOrientation(activity)
+ activity?.unlockScreenOrientation()
super.onDismiss(dialog)
}
@@ -134,7 +136,7 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
fun start(activity: FragmentActivity,
dialog: ProgressTaskDialogFragment) {
- Util.lockScreenOrientation(activity)
+ activity.lockScreenOrientation()
dialog.show(activity.supportFragmentManager, PROGRESS_TASK_DIALOG_TAG)
}
@@ -143,7 +145,7 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
if (fragmentTask != null) {
val loadingDatabaseDialog = fragmentTask as ProgressTaskDialogFragment
loadingDatabaseDialog.dismissAllowingStateLoss()
- Util.unlockScreenOrientation(activity)
+ activity.unlockScreenOrientation()
}
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.java b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.kt
similarity index 76%
rename from app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.java
rename to app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.kt
index a41d0c7cc..d9bff26d4 100644
--- a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.java
+++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
@@ -17,10 +17,10 @@
* along with KeePass DX. If not, see .
*
*/
-package com.kunzisoft.keepass.tasks;
+package com.kunzisoft.keepass.tasks
-import android.support.annotation.StringRes;
+import android.support.annotation.StringRes
-public interface ProgressTaskUpdater {
- void updateMessage(@StringRes int resId);
+interface ProgressTaskUpdater {
+ fun updateMessage(@StringRes resId: Int)
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt
index 61df440c6..c48a08aaa 100644
--- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
@@ -24,17 +24,17 @@ import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.preference.PreferenceManager
+import android.os.Build
import android.util.Log
-import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.lock.lock
import com.kunzisoft.keepass.app.App
+import com.kunzisoft.keepass.settings.PreferencesUtil
object TimeoutHelper {
const val DEFAULT_TIMEOUT = (5 * 60 * 1000).toLong() // 5 minutes
- const val TIMEOUT_NEVER: Long = -1 // Infinite
+ const val NEVER: Long = -1 // Infinite
private const val REQUEST_ID = 140
@@ -54,28 +54,22 @@ object TimeoutHelper {
* Record the current time to check it later with checkTime
*/
fun recordTime(context: Context) {
- val prefs = PreferenceManager.getDefaultSharedPreferences(context)
-
// Record timeout time in case timeout service is killed
- val time = System.currentTimeMillis()
- val edit = prefs.edit()
- edit.putLong(context.getString(R.string.timeout_backup_key), time)
- edit.apply()
+ PreferencesUtil.saveCurrentTime(context)
if (App.currentDatabase.loaded) {
- val timeout = try {
- java.lang.Long.parseLong(prefs.getString(context.getString(R.string.app_timeout_key),
- context.getString(R.string.clipboard_timeout_default)))
- } catch (e: NumberFormatException) {
- DEFAULT_TIMEOUT
- }
+ val timeout = PreferencesUtil.getAppTimeout(context)
// No timeout don't start timeout service
- if (timeout != TIMEOUT_NEVER) {
+ if (timeout != NEVER) {
val triggerTime = System.currentTimeMillis() + timeout
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
Log.d(TAG, "TimeoutHelper start")
- am.set(AlarmManager.RTC, triggerTime, getLockPendingIntent(context))
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ am.setExact(AlarmManager.RTC, triggerTime, getLockPendingIntent(context))
+ } else {
+ am.set(AlarmManager.RTC, triggerTime, getLockPendingIntent(context))
+ }
}
}
}
@@ -101,24 +95,16 @@ object TimeoutHelper {
val currentTime = System.currentTimeMillis()
// Retrieve the timeout programmatically backup
- val prefs = PreferenceManager.getDefaultSharedPreferences(context)
- val timeoutBackup = prefs.getLong(context.getString(R.string.timeout_backup_key),
- TIMEOUT_NEVER)
+ val timeoutBackup = PreferencesUtil.getTimeSaved(context)
// The timeout never started
- if (timeoutBackup == TIMEOUT_NEVER) {
+ if (timeoutBackup == NEVER) {
return true
}
// Retrieve the app timeout in settings
- val appTimeout = try {
- java.lang.Long.parseLong(prefs.getString(context.getString(R.string.app_timeout_key),
- context.getString(R.string.clipboard_timeout_default)))
- } catch (e: NumberFormatException) {
- DEFAULT_TIMEOUT
- }
-
+ val appTimeout = PreferencesUtil.getAppTimeout((context))
// We are set to never timeout
- if (appTimeout == TIMEOUT_NEVER) {
+ if (appTimeout == NEVER) {
return true
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.kt b/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.kt
deleted file mode 100644
index e946af741..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.utils
-
-import android.net.Uri
-
-import com.kunzisoft.keepass.database.element.PwDate
-
-object EmptyUtils {
- fun isNullOrEmpty(str: String?): Boolean {
- return str == null || str.isEmpty()
- }
-
- fun isNullOrEmpty(buf: ByteArray?): Boolean {
- return buf == null || buf.isEmpty()
- }
-
- fun isNullOrEmpty(date: PwDate?): Boolean {
- return date == null || date == PwDate.DEFAULT_PWDATE
- }
-
- fun isNullOrEmpty(uri: Uri?): Boolean {
- return uri == null || uri.toString().isEmpty()
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/Interaction.java b/app/src/main/java/com/kunzisoft/keepass/utils/Interaction.java
deleted file mode 100644
index 2b5a51556..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/utils/Interaction.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.utils;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-
-import java.util.List;
-
-public class Interaction {
- /**
- * Indicates whether the specified action can be used as an intent. This
- * method queries the package manager for installed packages that can
- * respond to an intent with the specified action. If no suitable package is
- * found, this method returns false.
- *
- * @param context The application's environment.
- * @param action The Intent action to check for availability.
- *
- * @return True if an Intent with the specified action can be sent and
- * responded to, false otherwise.
- */
- public static boolean isIntentAvailable(Context context, String action) {
- final PackageManager packageManager = context.getPackageManager();
- final Intent intent = new Intent(action);
- List list =
- packageManager.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- return list.size() > 0;
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java
deleted file mode 100644
index 5f9fa0542..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.utils;
-
-import com.kunzisoft.keepass.database.element.PwEntryV4;
-import com.kunzisoft.keepass.database.element.PwGroupV4;
-import com.kunzisoft.keepass.database.element.PwDatabase;
-import com.kunzisoft.keepass.database.element.PwDatabaseV4;
-import com.kunzisoft.keepass.database.search.EntrySearchHandlerV4;
-import com.kunzisoft.keepass.database.search.SearchParametersV4;
-
-import java.util.*;
-import java.util.Map.Entry;
-
-public class SprEngineV4 {
- private static final int MAX_RECURSION_DEPTH = 12;
- private static final String STR_REF_START = "{REF:";
- private static final String STR_REF_END = "}";
-
- public class TargetResult {
- public PwEntryV4 entry;
- public char wanted;
-
- public TargetResult(PwEntryV4 entry, char wanted) {
- this.entry = entry;
- this.wanted = wanted;
- }
- }
-
- private class SprContextV4 {
-
- public PwDatabaseV4 db;
- public PwEntryV4 entry;
- public Map refsCache = new HashMap<>();
-
- SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) {
- this.db = db;
- this.entry = entry;
- }
-
- SprContextV4(SprContextV4 source) {
- this.db = source.db;
- this.entry = source.entry;
- this.refsCache = source.refsCache;
- }
- }
-
- public String compile(String text, PwEntryV4 entry, PwDatabase database) {
- SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, entry);
-
- return compileInternal(text, ctx, 0);
- }
-
- private String compileInternal(String text, SprContextV4 sprContextV4, int recursionLevel) {
- if (text == null) { return ""; }
- if (sprContextV4 == null) { return ""; }
- if (recursionLevel >= MAX_RECURSION_DEPTH) { return ""; }
-
- return fillRefPlaceholders(text, sprContextV4, recursionLevel);
- }
-
- private String fillRefPlaceholders(String text, SprContextV4 contextV4, int recursionLevel) {
-
- if (contextV4.db == null) { return text; }
-
- int offset = 0;
- for (int i = 0; i < 20; ++i) {
- text = fillRefsUsingCache(text, contextV4);
-
- int start = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH);
- if (start < 0) { break; }
- int end = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH);
- if (end <= start) { break; }
-
- String fullRef = text.substring(start, end - start + 1);
- TargetResult result = findRefTarget(fullRef, contextV4);
-
- if (result != null) {
- PwEntryV4 found = result.entry;
- char wanted = result.wanted;
-
- if (found != null) {
- String data;
- switch (wanted) {
- case 'T':
- data = found.getTitle();
- break;
- case 'U':
- data = found.getUsername();
- break;
- case 'A':
- data = found.getUrl();
- break;
- case 'P':
- data = found.getPassword();
- break;
- case 'N':
- data = found.getNotes();
- break;
- case 'I':
- data = found.getNodeId().toString();
- break;
- default:
- offset = start + 1;
- continue;
- }
-
- SprContextV4 subCtx = new SprContextV4(contextV4);
- subCtx.entry = found;
-
- String innerContent = compileInternal(data, subCtx, recursionLevel + 1);
- addRefsToCache(fullRef, innerContent, contextV4);
- text = fillRefsUsingCache(text, contextV4);
- } else {
- offset = start + 1;
- }
- }
-
- }
-
- return text;
- }
-
- private TargetResult findRefTarget(String fullRef, SprContextV4 contextV4) {
- if (fullRef == null) { return null; }
-
- fullRef = fullRef.toUpperCase(Locale.ENGLISH);
- if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) {
- return null;
- }
-
- String ref = fullRef.substring(STR_REF_START.length(), fullRef.length() - STR_REF_START.length() - STR_REF_END.length());
- if (ref.length() <= 4) { return null; }
- if (ref.charAt(1) != '@') { return null; }
- if (ref.charAt(3) != ':') { return null; }
-
- char scan = Character.toUpperCase(ref.charAt(2));
- char wanted = Character.toUpperCase(ref.charAt(0));
-
- SearchParametersV4 searchParametersV4 = new SearchParametersV4();
- searchParametersV4.setupNone();
-
- searchParametersV4.setSearchString(ref.substring(4));
- if (scan == 'T') { searchParametersV4.setSearchInTitles(true); }
- else if (scan == 'U') { searchParametersV4.setSearchInUserNames(true); }
- else if (scan == 'A') { searchParametersV4.setSearchInUrls(true); }
- else if (scan == 'P') { searchParametersV4.setSearchInPasswords(true); }
- else if (scan == 'N') { searchParametersV4.setSearchInNotes(true); }
- else if (scan == 'I') { searchParametersV4.setSearchInUUIDs(true); }
- else if (scan == 'O') { searchParametersV4.setSearchInOther(true); }
- else { return null; }
-
- List list = new ArrayList<>();
- // TODO type parameter
- searchEntries(contextV4.db.getRootGroup(), searchParametersV4, list);
-
- if (list.size() > 0) {
- return new TargetResult(list.get(0), wanted);
- }
-
- return null;
- }
-
- private void addRefsToCache(String ref, String value, SprContextV4 ctx) {
- if (ref == null) { return; }
- if (value == null) { return; }
- if (ctx == null) { return; }
-
- if (!ctx.refsCache.containsKey(ref)) {
- ctx.refsCache.put(ref, value);
- }
- }
-
- private String fillRefsUsingCache(String text, SprContextV4 sprContextV4) {
- for (Entry entry : sprContextV4.refsCache.entrySet()) {
- text = StringUtil.INSTANCE.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH);
- }
-
- return text;
- }
-
- private void searchEntries(PwGroupV4 root, SearchParametersV4 searchParametersV4, List listStorage) {
- if (searchParametersV4 == null) { return; }
- if (listStorage == null) { return; }
-
- List terms = StringUtil.INSTANCE.splitStringTerms(searchParametersV4.getSearchString());
- if (terms.size() <= 1 || searchParametersV4.getRegularExpression()) {
- root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, listStorage), null);
- return;
- }
-
- // Search longest term first
- Comparator stringLengthComparator = (lhs, rhs) -> lhs.length() - rhs.length();
- Collections.sort(terms, stringLengthComparator);
-
- String fullSearch = searchParametersV4.getSearchString();
- List childEntries = root.getChildEntries();
- for (int i = 0; i < terms.size(); i ++) {
- List pgNew = new ArrayList<>();
-
- searchParametersV4.setSearchString(terms.get(i));
-
- boolean negate = false;
- if (searchParametersV4.getSearchString().startsWith("-")) {
- searchParametersV4.setSearchString(searchParametersV4.getSearchString().substring(1));
- negate = searchParametersV4.getSearchString().length() > 0;
- }
-
- if (!root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, pgNew), null)) {
- childEntries = null;
- break;
- }
-
- List complement = new ArrayList<>();
- if (negate) {
- for (PwEntryV4 entry: childEntries) {
- if (!pgNew.contains(entry)) {
- complement.add(entry);
- }
- }
- childEntries = complement;
- }
- else {
- childEntries = pgNew;
- }
- }
-
- if (childEntries != null) {
- listStorage.addAll(childEntries);
- }
- searchParametersV4.setSearchString(fullSearch);
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt
index 26763f011..492002718 100644
--- a/app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt
@@ -69,14 +69,10 @@ object StringUtil {
return indexOfIgnoreCase(text, search, 0, locale)
}
- fun replaceAllIgnoresCase(text: String?, find: String?, newText: String?, locale: Locale): String? {
+ fun replaceAllIgnoresCase(text: String, find: String, newText: String, locale: Locale): String {
var currentText = text
- if (currentText == null || find == null || newText == null) {
- return currentText
- }
-
var pos = 0
- while (pos < currentText!!.length) {
+ while (pos < currentText.length) {
pos = indexOfIgnoreCase(currentText, find, pos, locale)
if (pos < 0) {
break
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java
deleted file mode 100644
index e1e13837a..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.utils;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-
-import com.kunzisoft.keepass.fileselect.StorageAF;
-
-import java.io.File;
-
-/**
- * Created by bpellin on 3/5/16.
- */
-public class UriUtil {
- public static Uri parseDefaultFile(String text) {
- if (EmptyUtils.INSTANCE.isNullOrEmpty(text)) {
- return null;
- }
-
- Uri uri = Uri.parse(text);
- if (EmptyUtils.INSTANCE.isNullOrEmpty(uri.getScheme())) {
- uri = uri.buildUpon().scheme("file").authority("").build();
- }
-
- return uri;
- }
- public static Uri parseDefaultFile(Uri uri) {
- if (EmptyUtils.INSTANCE.isNullOrEmpty(uri.getScheme())) {
- uri = uri.buildUpon().scheme("file").authority("").build();
- }
-
- return uri;
- }
-
- public static boolean equalsDefaultfile(Uri left, String right) {
- left = parseDefaultFile(left);
- Uri uriRight = parseDefaultFile(right);
-
- return left.equals(uriRight);
- }
-
- /**
- * Many android apps respond with non-writeable content URIs that correspond to files.
- * This will attempt to translate the content URIs to file URIs when possible/appropriate
- * @param uri
- * @return
- */
- public static Uri translate(Context ctx, Uri uri) {
- // StorageAF provides nice URIs
- if (StorageAF.useStorageFramework(ctx) || hasWritableContentUri(uri)) { return uri; }
-
- String scheme = uri.getScheme();
- if (EmptyUtils.INSTANCE.isNullOrEmpty(scheme)) { return uri; }
-
- String filepath = null;
-
- try {
- // Use content resolver to try and find the file
- if (scheme.equalsIgnoreCase("content")) {
- Cursor cursor = ctx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null);
- cursor.moveToFirst();
-
- if (cursor != null) {
- filepath = cursor.getString(0);
- cursor.close();
-
-
- if (!isValidFilePath(filepath)) {
- filepath = null;
- }
- }
- }
-
- // Try using the URI path as a straight file
- if (EmptyUtils.INSTANCE.isNullOrEmpty(filepath)) {
- filepath = uri.getEncodedPath();
- if (!isValidFilePath(filepath)) {
- filepath = null;
- }
- }
- }
- // Fall back to URI if this fails.
- catch (Exception e) {
- filepath = null;
- }
-
- // Update the file to a file URI
- if (!EmptyUtils.INSTANCE.isNullOrEmpty(filepath)) {
- Uri.Builder b = new Uri.Builder();
- uri = b.scheme("file").authority("").path(filepath).build();
- }
-
- return uri;
- }
-
- private static boolean isValidFilePath(String filepath) {
- if (EmptyUtils.INSTANCE.isNullOrEmpty(filepath)) { return false; }
-
- File file = new File(filepath);
- return file.exists() && file.canRead();
- }
-
- /**
- * Whitelist for known content providers that support writing
- * @param uri
- * @return
- */
- private static boolean hasWritableContentUri(Uri uri) {
- String scheme = uri.getScheme();
-
- if (EmptyUtils.INSTANCE.isNullOrEmpty(scheme)) { return false; }
-
- if (!scheme.equalsIgnoreCase("content")) { return false; }
-
- switch (uri.getAuthority()) {
- case "com.google.android.apps.docs.storage":
- return true;
- }
-
- return false;
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt
new file mode 100644
index 000000000..07c8b06eb
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.utils
+
+import android.content.ContentResolver
+import android.content.Context
+import android.net.Uri
+import com.kunzisoft.keepass.fileselect.StorageAF
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.io.InputStream
+
+
+object UriUtil {
+
+ fun parseUriFile(text: String?): Uri? {
+ if (text == null || text.isEmpty()) {
+ return null
+ }
+ return parseUriFile(Uri.parse(text))
+ }
+
+ fun parseUriFile(uri: Uri?): Uri? {
+ if (uri == null) {
+ return null
+ }
+ var currentUri = uri
+ if (currentUri.scheme == null || currentUri.scheme!!.isEmpty()) {
+ currentUri = currentUri.buildUpon().scheme("file").authority("").build()
+ }
+ return currentUri
+ }
+
+ /**
+ * Many android apps respond with non-writeable content URIs that correspond to files.
+ * This will attempt to translate the content URIs to file URIs when possible/appropriate
+ * @param uri
+ * @return
+ */
+ fun translateUri(ctx: Context, uri: Uri): Uri {
+ var currentUri = uri
+ // StorageAF provides nice URIs
+ if (StorageAF.useStorageFramework(ctx) || hasWritableContentUri(currentUri)) {
+ return currentUri
+ }
+
+ val scheme = currentUri.scheme
+ if (scheme == null || scheme.isEmpty()) {
+ return currentUri
+ }
+
+ var filepath: String? = null
+
+ try {
+ // Use content resolver to try and find the file
+ if (scheme.equals("content", ignoreCase = true)) {
+ val cursor = ctx.contentResolver.query(currentUri, arrayOf(android.provider.MediaStore.Images.ImageColumns.DATA), null, null, null)
+ if (cursor != null) {
+ cursor.moveToFirst()
+ filepath = cursor.getString(0)
+ cursor.close()
+ if (!isValidFilePath(filepath)) {
+ filepath = null
+ }
+ }
+ }
+
+ // Try using the URI path as a straight file
+ if (filepath == null || filepath.isEmpty()) {
+ filepath = currentUri.encodedPath
+ if (!isValidFilePath(filepath)) {
+ filepath = null
+ }
+ }
+ } catch (e: Exception) {
+ filepath = null
+ }
+ // Fall back to URI if this fails.
+
+ // Update the file to a file URI
+ if (filepath != null && filepath.isNotEmpty()) {
+ val b = Uri.Builder()
+ currentUri = b.scheme("file").authority("").path(filepath).build()
+ }
+
+ return currentUri
+ }
+
+ private fun isValidFilePath(filepath: String?): Boolean {
+ if (filepath == null || filepath.isEmpty()) {
+ return false
+ }
+ val file = File(filepath)
+ return file.exists() && file.canRead()
+ }
+
+ /**
+ * Whitelist for known content providers that support writing
+ * @param uri
+ * @return
+ */
+ private fun hasWritableContentUri(uri: Uri): Boolean {
+ val scheme = uri.scheme
+ if (scheme == null || scheme.isEmpty()) {
+ return false
+ }
+ if (!scheme.equals("content", ignoreCase = true)) {
+ return false
+ }
+ when (uri.authority) {
+ "com.google.android.apps.docs.storage" -> return true
+ }
+
+ return false
+ }
+
+ @Throws(FileNotFoundException::class)
+ fun getUriInputStream(contentResolver: ContentResolver, uri: Uri?): InputStream? {
+ if (uri == null)
+ return null
+ val scheme = uri.scheme
+ return if (scheme == null || scheme.isEmpty() || scheme == "file") {
+ FileInputStream(uri.path!!)
+ } else if (scheme == "content") {
+ contentResolver.openInputStream(uri)
+ } else {
+ null
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtils.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtils.kt
deleted file mode 100644
index 0f0df99d3..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtils.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.kunzisoft.keepass.utils
-
-import android.content.ContentResolver
-import android.net.Uri
-import java.io.FileInputStream
-import java.io.FileNotFoundException
-import java.io.InputStream
-
-@Throws(FileNotFoundException::class)
-fun getUriInputStream(contentResolver: ContentResolver, uri: Uri?): InputStream? {
- if (uri == null) return null
-
- val scheme = uri.scheme
- return if (EmptyUtils.isNullOrEmpty(scheme) || scheme == "file") {
- FileInputStream(uri.path!!)
- } else if (scheme == "content") {
- contentResolver.openInputStream(uri)
- } else {
- null
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/Util.java b/app/src/main/java/com/kunzisoft/keepass/utils/Util.java
deleted file mode 100644
index bffdec0c3..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/utils/Util.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.utils;
-
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.graphics.Typeface;
-import android.net.Uri;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import com.kunzisoft.keepass.R;
-
-public class Util {
-
- public static void gotoUrl(Context context, String url) throws ActivityNotFoundException {
- if ( url != null && url.length() > 0 ) {
- Uri uri = Uri.parse(url);
- context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
- }
- }
-
- public static void gotoUrl(Context context, int resId) throws ActivityNotFoundException {
- gotoUrl(context, context.getString(resId));
- }
-
- /**
- * Replace font by monospace, must be called after seText()
- */
- public static void applyFontVisibilityTo(final Context context, final TextView textView) {
- Typeface typeFace=Typeface.createFromAsset(context.getAssets(),"fonts/DroidSansMonoSlashed.ttf");
- textView.setTypeface(typeFace);
- }
-
- /**
- * Replace font by monospace, must be called after seText()
- */
- public static void applyFontVisibilityTo(final Context context, final EditText editText) {
- applyFontVisibilityTo(context, (TextView) editText);
- }
-
- public static float getListTextDefaultSize(Context context) {
- return Float.parseFloat(context.getString(R.string.list_size_default));
- }
-
- public static void lockScreenOrientation(Activity activity) {
- if (activity != null) {
- int currentOrientation = activity.getResources().getConfiguration().orientation;
- if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- } else {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
- }
- }
- }
-
- public static void unlockScreenOrientation(Activity activity) {
- if (activity != null) {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/Util.kt b/app/src/main/java/com/kunzisoft/keepass/utils/Util.kt
new file mode 100644
index 000000000..510a734e3
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/Util.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.utils
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.graphics.Typeface
+import android.net.Uri
+import android.widget.TextView
+
+object Util {
+
+ @Throws(ActivityNotFoundException::class)
+ fun gotoUrl(context: Context, url: String?) {
+ if (url != null && url.isNotEmpty()) {
+ context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
+ }
+ }
+
+ @Throws(ActivityNotFoundException::class)
+ fun gotoUrl(context: Context, resId: Int) {
+ gotoUrl(context, context.getString(resId))
+ }
+}
+
+/**
+ * Replace font by monospace, must be called after seText()
+ */
+fun TextView.applyFontVisibility() {
+ val typeFace = Typeface.createFromAsset(context.assets, "fonts/DroidSansMonoSlashed.ttf")
+ typeface = typeFace
+}
+
+fun Activity.lockScreenOrientation() {
+ val currentOrientation = resources.configuration.orientation
+ requestedOrientation = if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ } else {
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ }
+}
+
+fun Activity.unlockScreenOrientation() {
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.java b/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.java
deleted file mode 100644
index 50a13e01f..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.java
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.view;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.design.widget.FloatingActionButton;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.widget.RelativeLayout;
-
-import com.kunzisoft.keepass.R;
-
-public class AddNodeButtonView extends RelativeLayout {
-
- private enum State {
- OPEN, CLOSE
- }
-
- private FloatingActionButton addButtonView;
- private View addEntryView;
- private View addGroupView;
-
- private boolean addEntryEnable;
- private boolean addGroupEnable;
-
- private State state;
- private boolean allowAction;
- private OnClickListener onAddButtonClickListener;
- private FloatingActionButton.OnVisibilityChangedListener onAddButtonVisibilityChangedListener;
- private AddButtonAnimation viewButtonMenuAnimation;
- private ViewMenuAnimation viewMenuAnimationAddEntry;
- private ViewMenuAnimation viewMenuAnimationAddGroup;
- private long animationDuration;
-
- public AddNodeButtonView(Context context) {
- this(context, null);
- }
-
- public AddNodeButtonView(Context context, AttributeSet attrs) {
- super(context, attrs);
- inflate(context);
- }
-
- protected void inflate(Context context) {
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- assert inflater != null;
- inflater.inflate(R.layout.add_node_button, this);
-
- addEntryEnable = true;
- addGroupEnable = true;
-
- addButtonView = findViewById(R.id.add_button);
- addEntryView = findViewById(R.id.container_add_entry);
- addGroupView = findViewById(R.id.container_add_group);
-
- animationDuration = 300L;
-
- viewButtonMenuAnimation = new AddButtonAnimation(addButtonView);
- viewMenuAnimationAddEntry = new ViewMenuAnimation(addEntryView, 0L, 150L);
- viewMenuAnimationAddGroup = new ViewMenuAnimation(addGroupView, 150L, 0L);
-
- allowAction = true;
- state = State.CLOSE;
-
- onAddButtonClickListener = v -> startGlobalAnimation();
- addButtonView.setOnClickListener(onAddButtonClickListener);
-
- onAddButtonVisibilityChangedListener = new FloatingActionButton.OnVisibilityChangedListener() {
- @Override
- public void onHidden(FloatingActionButton fab) {
- super.onHidden(fab);
- addButtonView.setOnClickListener(null);
- addButtonView.setClickable(false);
- }
- @Override
- public void onShown(FloatingActionButton fab) {
- super.onShown(fab);
- addButtonView.setOnClickListener(onAddButtonClickListener);
- }
- };
- }
-
- @SuppressLint("ClickableViewAccessibility")
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- Rect viewButtonRect = new Rect(),
- viewEntryRect = new Rect(),
- viewGroupRect = new Rect();
- addButtonView.getGlobalVisibleRect(viewButtonRect);
- addEntryView.getGlobalVisibleRect(viewEntryRect);
- addGroupView.getGlobalVisibleRect(viewGroupRect);
- if (! (viewButtonRect.contains((int) event.getRawX(), (int) event.getRawY())
- && viewEntryRect.contains((int) event.getRawX(), (int) event.getRawY())
- && viewGroupRect.contains((int) event.getRawX(), (int) event.getRawY()) )) {
- closeButtonIfOpen();
- }
- return super.onTouchEvent(event);
- }
-
- public void hideButtonOnScrollListener(int dy) {
- if (state.equals(State.CLOSE)) {
- if (dy > 0 && addButtonView.getVisibility() == View.VISIBLE) {
- hideButton();
- } else if (dy < 0 && addButtonView.getVisibility() != View.VISIBLE) {
- showButton();
- }
- }
- }
-
- public void showButton() {
- addButtonView.show(onAddButtonVisibilityChangedListener);
- }
-
- public void hideButton() {
- addButtonView.hide(onAddButtonVisibilityChangedListener);
- }
-
- /**
- * Start the animation to close the button
- */
- public void openButtonIfClose() {
- if(state.equals(State.CLOSE)) {
- startGlobalAnimation();
- }
- }
-
- /**
- * Start the animation to close the button
- */
- public void closeButtonIfOpen() {
- if(state.equals(State.OPEN)) {
- startGlobalAnimation();
- }
- }
-
- /**
- * Enable or not the possibility to add an entry by pressing a button
- * @param enable true to enable
- */
- public void enableAddEntry(boolean enable) {
- this.addEntryEnable = enable;
- if (enable && addEntryView != null && addEntryView.getVisibility() != VISIBLE)
- addEntryView.setVisibility(INVISIBLE);
- disableViewIfNoAddAvailable();
- }
-
- /**
- * Enable or not the possibility to add a group by pressing a button
- * @param enable true to enable
- */
- public void enableAddGroup(boolean enable) {
- this.addGroupEnable = enable;
- if (enable && addGroupView != null && addGroupView.getVisibility() != VISIBLE)
- addGroupView.setVisibility(INVISIBLE);
- disableViewIfNoAddAvailable();
- }
-
- private void disableViewIfNoAddAvailable() {
- if (!addEntryEnable || !addGroupEnable) {
- setVisibility(GONE);
- } else {
- setVisibility(VISIBLE);
- }
- }
-
- public boolean isEnable() {
- return getVisibility() == VISIBLE;
- }
-
- public void setAddGroupClickListener(OnClickListener onClickListener) {
- if (addGroupEnable)
- addGroupView.setOnClickListener(view -> {
- onClickListener.onClick(view);
- closeButtonIfOpen();
- });
- }
-
- public void setAddEntryClickListener(OnClickListener onClickListener) {
- if (addEntryEnable) {
- addEntryView.setOnClickListener(view -> {
- onClickListener.onClick(view);
- closeButtonIfOpen();
- });
- addEntryView.setOnClickListener(view -> {
- onClickListener.onClick(view);
- closeButtonIfOpen();
- });
- }
- }
-
- private void startGlobalAnimation() {
- if (allowAction) {
- viewButtonMenuAnimation.startAnimation();
-
- if (addEntryEnable) {
- viewMenuAnimationAddEntry.startAnimation();
- }
- if (addGroupEnable) {
- viewMenuAnimationAddGroup.startAnimation();
- }
- }
- }
-
- private class AddButtonAnimation implements ViewPropertyAnimatorListener {
-
- private View view;
- private boolean isRotate;
-
- private Interpolator interpolator;
-
- AddButtonAnimation(View view) {
- this.view = view;
- this.isRotate = false;
- interpolator = new AccelerateDecelerateInterpolator();
- }
-
- @Override
- public void onAnimationStart(View view) {
- allowAction = false;
- }
-
- @Override
- public void onAnimationEnd(View view) {
- allowAction = true;
- isRotate = !isRotate;
- if (isRotate)
- state = State.OPEN;
- else
- state = State.CLOSE;
- }
-
- @Override
- public void onAnimationCancel(View view) {}
-
- void startAnimation() {
- if(!isRotate) {
- ViewCompat.animate(view)
- .rotation(135.0F)
- .withLayer()
- .setDuration(animationDuration)
- .setInterpolator(interpolator)
- .setListener(this)
- .start();
- } else {
- ViewCompat.animate(view)
- .rotation(0.0F)
- .withLayer()
- .setDuration(animationDuration)
- .setInterpolator(interpolator)
- .setListener(this)
- .start();
- }
- }
- }
-
- private class ViewMenuAnimation implements ViewPropertyAnimatorListener {
-
- private View view;
- private Interpolator interpolator;
- private float translation;
- private boolean wasInvisible;
- private long delayIn;
- private long delayOut;
-
- ViewMenuAnimation(View view, long delayIn, long delayOut) {
- this.view = view;
- this.interpolator = new FastOutSlowInInterpolator();
- this.wasInvisible = true;
- this.translation = 0;
- this.delayIn = delayIn;
- this.delayOut = delayOut;
- }
-
- @Override
- public void onAnimationStart(View view) {}
-
- @Override
- public void onAnimationEnd(View view) {
- if(wasInvisible) {
- view.setVisibility(VISIBLE);
- } else {
- view.setVisibility(INVISIBLE);
- }
- }
-
- @Override
- public void onAnimationCancel(View view) {}
-
- void startAnimation() {
- if(view.getVisibility() == VISIBLE) {
- // In
- wasInvisible = false;
- ViewCompat.animate(view)
- .translationY(-translation)
- .translationX(view.getWidth()/3)
- .alpha(0.0F)
- .scaleX(0.33F)
- .setDuration(animationDuration-delayIn)
- .setInterpolator(interpolator)
- .setListener(this)
- .setStartDelay(delayIn)
- .start();
- } else {
- // The first time
- if (translation == 0) {
- translation = view.getY() + view.getHeight()/2 - addButtonView.getY() - addButtonView.getHeight()/2;
- view.setTranslationY(-translation);
- view.setTranslationX(view.getWidth()/3);
- view.setAlpha(0.0F);
- view.setScaleX(0.33F);
- }
-
- // Out
- view.setVisibility(VISIBLE);
- wasInvisible = true;
- ViewCompat.animate(view)
- .translationY(1)
- .translationX(1)
- .alpha(1.0F)
- .scaleX(1)
- .setDuration(animationDuration-delayOut)
- .setInterpolator(interpolator)
- .setListener(this)
- .setStartDelay(delayOut)
- .start();
- }
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.kt b/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.kt
new file mode 100644
index 000000000..df0f22da5
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/view/AddNodeButtonView.kt
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.view
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Rect
+import android.support.design.widget.FloatingActionButton
+import android.support.v4.view.ViewCompat
+import android.support.v4.view.ViewPropertyAnimatorListener
+import android.support.v4.view.animation.FastOutSlowInInterpolator
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.animation.AccelerateDecelerateInterpolator
+import android.view.animation.Interpolator
+import android.widget.RelativeLayout
+
+import com.kunzisoft.keepass.R
+
+class AddNodeButtonView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null)
+ : RelativeLayout(context, attrs) {
+
+ var addButtonView: FloatingActionButton? = null
+ private var addEntryView: View? = null
+ private var addGroupView: View? = null
+
+ private var addEntryEnable: Boolean = false
+ private var addGroupEnable: Boolean = false
+
+ private var state: State? = null
+ private var allowAction: Boolean = false
+ private var onAddButtonClickListener: OnClickListener? = null
+ private var onAddButtonVisibilityChangedListener: FloatingActionButton.OnVisibilityChangedListener? = null
+ private var viewButtonMenuAnimation: AddButtonAnimation? = null
+ private var viewMenuAnimationAddEntry: ViewMenuAnimation? = null
+ private var viewMenuAnimationAddGroup: ViewMenuAnimation? = null
+ private var animationDuration: Long = 0
+
+ val isEnable: Boolean
+ get() = visibility == View.VISIBLE
+
+ private enum class State {
+ OPEN, CLOSE
+ }
+
+ init {
+ inflate(context)
+ }
+
+ private fun inflate(context: Context) {
+ val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+ inflater.inflate(R.layout.add_node_button, this)
+
+ addEntryEnable = true
+ addGroupEnable = true
+
+ addButtonView = findViewById(R.id.add_button)
+ addEntryView = findViewById(R.id.container_add_entry)
+ addGroupView = findViewById(R.id.container_add_group)
+
+ animationDuration = 300L
+
+ viewButtonMenuAnimation = AddButtonAnimation(addButtonView)
+ viewMenuAnimationAddEntry = ViewMenuAnimation(addEntryView, 0L, 150L)
+ viewMenuAnimationAddGroup = ViewMenuAnimation(addGroupView, 150L, 0L)
+
+ allowAction = true
+ state = State.CLOSE
+
+ onAddButtonClickListener = OnClickListener{ startGlobalAnimation() }
+ addButtonView?.setOnClickListener(onAddButtonClickListener)
+
+ onAddButtonVisibilityChangedListener = object : FloatingActionButton.OnVisibilityChangedListener() {
+ override fun onHidden(fab: FloatingActionButton?) {
+ super.onHidden(fab)
+ addButtonView?.setOnClickListener(null)
+ addButtonView?.isClickable = false
+ }
+
+ override fun onShown(fab: FloatingActionButton?) {
+ super.onShown(fab)
+ addButtonView?.setOnClickListener(onAddButtonClickListener)
+ }
+ }
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ val viewButtonRect = Rect()
+ val viewEntryRect = Rect()
+ val viewGroupRect = Rect()
+ addButtonView?.getGlobalVisibleRect(viewButtonRect)
+ addEntryView?.getGlobalVisibleRect(viewEntryRect)
+ addGroupView?.getGlobalVisibleRect(viewGroupRect)
+ if (!(viewButtonRect.contains(event.rawX.toInt(), event.rawY.toInt())
+ && viewEntryRect.contains(event.rawX.toInt(), event.rawY.toInt())
+ && viewGroupRect.contains(event.rawX.toInt(), event.rawY.toInt()))) {
+ closeButtonIfOpen()
+ }
+ return super.onTouchEvent(event)
+ }
+
+ fun hideButtonOnScrollListener(dy: Int) {
+ if (state == State.CLOSE) {
+ if (dy > 0 && addButtonView?.visibility == View.VISIBLE) {
+ hideButton()
+ } else if (dy < 0 && addButtonView?.visibility != View.VISIBLE) {
+ showButton()
+ }
+ }
+ }
+
+ fun showButton() {
+ if (addButtonView?.visibility != VISIBLE)
+ addButtonView?.show(onAddButtonVisibilityChangedListener)
+ }
+
+ fun hideButton() {
+ if (addButtonView?.visibility == VISIBLE)
+ addButtonView?.hide(onAddButtonVisibilityChangedListener)
+ }
+
+ /**
+ * Start the animation to close the button
+ */
+ fun openButtonIfClose() {
+ if (state == State.CLOSE) {
+ startGlobalAnimation()
+ }
+ }
+
+ /**
+ * Start the animation to close the button
+ */
+ fun closeButtonIfOpen() {
+ if (state == State.OPEN) {
+ startGlobalAnimation()
+ }
+ }
+
+ /**
+ * Enable or not the possibility to add an entry by pressing a button
+ * @param enable true to enable
+ */
+ fun enableAddEntry(enable: Boolean) {
+ this.addEntryEnable = enable
+ if (enable && addEntryView != null && addEntryView!!.visibility != View.VISIBLE)
+ addEntryView!!.visibility = View.INVISIBLE
+ disableViewIfNoAddAvailable()
+ }
+
+ /**
+ * Enable or not the possibility to add a group by pressing a button
+ * @param enable true to enable
+ */
+ fun enableAddGroup(enable: Boolean) {
+ this.addGroupEnable = enable
+ if (enable && addGroupView != null && addGroupView!!.visibility != View.VISIBLE)
+ addGroupView?.visibility = View.INVISIBLE
+ disableViewIfNoAddAvailable()
+ }
+
+ private fun disableViewIfNoAddAvailable() {
+ visibility = if (!addEntryEnable || !addGroupEnable) {
+ View.GONE
+ } else {
+ View.VISIBLE
+ }
+ }
+
+ fun setAddGroupClickListener(onClickListener: OnClickListener) {
+ if (addGroupEnable)
+ addGroupView?.setOnClickListener { view ->
+ onClickListener.onClick(view)
+ closeButtonIfOpen()
+ }
+ }
+
+ fun setAddEntryClickListener(onClickListener: OnClickListener) {
+ if (addEntryEnable) {
+ addEntryView?.setOnClickListener { view ->
+ onClickListener.onClick(view)
+ closeButtonIfOpen()
+ }
+ addEntryView?.setOnClickListener { view ->
+ onClickListener.onClick(view)
+ closeButtonIfOpen()
+ }
+ }
+ }
+
+ private fun startGlobalAnimation() {
+ if (allowAction) {
+ viewButtonMenuAnimation?.startAnimation()
+
+ if (addEntryEnable) {
+ viewMenuAnimationAddEntry?.startAnimation()
+ }
+ if (addGroupEnable) {
+ viewMenuAnimationAddGroup?.startAnimation()
+ }
+ }
+ }
+
+ private inner class AddButtonAnimation(private val view: View?) : ViewPropertyAnimatorListener {
+ private var isRotate: Boolean = false
+
+ private val interpolator: Interpolator
+
+ init {
+ this.isRotate = false
+ interpolator = AccelerateDecelerateInterpolator()
+ }
+
+ override fun onAnimationStart(view: View) {
+ allowAction = false
+ }
+
+ override fun onAnimationEnd(view: View) {
+ allowAction = true
+ isRotate = !isRotate
+ state = if (isRotate)
+ State.OPEN
+ else
+ State.CLOSE
+ }
+
+ override fun onAnimationCancel(view: View) {}
+
+ internal fun startAnimation() {
+ view?.let { view ->
+ if (!isRotate) {
+ ViewCompat.animate(view)
+ .rotation(135.0f)
+ .withLayer()
+ .setDuration(animationDuration)
+ .setInterpolator(interpolator)
+ .setListener(this)
+ .start()
+ } else {
+ ViewCompat.animate(view)
+ .rotation(0.0f)
+ .withLayer()
+ .setDuration(animationDuration)
+ .setInterpolator(interpolator)
+ .setListener(this)
+ .start()
+ }
+ }
+ }
+ }
+
+ private inner class ViewMenuAnimation(private val view: View?,
+ private val delayIn: Long,
+ private val delayOut: Long)
+ : ViewPropertyAnimatorListener {
+
+ private val interpolator: Interpolator
+ private var translation: Float = 0.toFloat()
+ private var wasInvisible: Boolean = false
+
+ init {
+ this.interpolator = FastOutSlowInInterpolator()
+ this.wasInvisible = true
+ this.translation = 0f
+ }
+
+ override fun onAnimationStart(view: View) {}
+
+ override fun onAnimationEnd(view: View) {
+ if (wasInvisible) {
+ view.visibility = View.VISIBLE
+ } else {
+ view.visibility = View.INVISIBLE
+ }
+ }
+
+ override fun onAnimationCancel(view: View) {}
+
+ internal fun startAnimation() {
+ view?.let { view ->
+ if (view.visibility == View.VISIBLE) {
+ // In
+ wasInvisible = false
+ ViewCompat.animate(view)
+ .translationY(-translation)
+ .translationX((view.width / 3).toFloat())
+ .alpha(0.0f)
+ .scaleX(0.33f)
+ .setDuration(animationDuration - delayIn)
+ .setInterpolator(interpolator)
+ .setListener(this)
+ .setStartDelay(delayIn)
+ .start()
+ } else {
+
+ // The first time
+ if (translation == 0f) {
+ if (addButtonView != null) {
+ translation = view.y + view.height / 2 - addButtonView!!.y - (addButtonView!!.height / 2).toFloat()
+ view.apply {
+ translationY = -translation
+ translationX = (view.width / 3).toFloat()
+ alpha = 0.0f
+ scaleX = 0.33f
+ }
+ }
+ }
+
+ // Out
+ view.visibility = View.VISIBLE
+ wasInvisible = true
+ ViewCompat.animate(view)
+ .translationY(1f)
+ .translationX(1f)
+ .alpha(1.0f)
+ .scaleX(1f)
+ .setDuration(animationDuration - delayOut)
+ .setInterpolator(interpolator)
+ .setListener(this)
+ .setStartDelay(delayOut)
+ .start()
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt
index 0be3664f4..9d8191134 100644
--- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt
@@ -32,6 +32,7 @@ import android.widget.TextView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.Util
+import com.kunzisoft.keepass.utils.applyFontVisibility
import java.text.DateFormat
import java.util.*
@@ -65,10 +66,10 @@ class EntryContentsView @JvmOverloads constructor(context: Context, attrs: Attri
private var expiresDateView: TextView? = null
val isUserNamePresent: Boolean
- get() = userNameContainerView!!.visibility == View.VISIBLE
+ get() = userNameContainerView?.visibility == View.VISIBLE
val isPasswordPresent: Boolean
- get() = passwordContainerView!!.visibility == View.VISIBLE
+ get() = passwordContainerView?.visibility == View.VISIBLE
init {
@@ -121,7 +122,7 @@ class EntryContentsView @JvmOverloads constructor(context: Context, attrs: Attri
userNameView?.apply {
text = userName
if (fontInVisibility)
- Util.applyFontVisibilityTo(context, this)
+ applyFontVisibility()
}
} else {
userNameContainerView?.visibility = View.GONE
@@ -138,7 +139,7 @@ class EntryContentsView @JvmOverloads constructor(context: Context, attrs: Attri
passwordView?.apply {
text = password
if (fontInVisibility)
- Util.applyFontVisibilityTo(context, this)
+ applyFontVisibility()
}
if (!allowCopyPassword) {
passwordActionView?.setColorFilter(ContextCompat.getColor(context, R.color.grey_dark))
@@ -198,7 +199,7 @@ class EntryContentsView @JvmOverloads constructor(context: Context, attrs: Attri
commentView?.apply {
text = comment
if (fontInVisibility)
- Util.applyFontVisibilityTo(context, this)
+ applyFontVisibility()
}
} else {
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.java
deleted file mode 100644
index 56491e61d..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.view;
-
-import android.content.Context;
-import android.support.v4.content.ContextCompat;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.database.element.security.ProtectedString;
-import com.kunzisoft.keepass.utils.Util;
-
-public class EntryCustomField extends LinearLayout {
-
- protected TextView labelView;
- protected TextView valueView;
- protected ImageView actionImageView;
-
- public EntryCustomField(Context context) {
- this(context, null);
- }
-
- public EntryCustomField(Context context, AttributeSet attrs) {
- this(context, attrs, null, null);
- }
-
- public EntryCustomField(Context context, AttributeSet attrs, String title, ProtectedString value) {
- this(context, attrs, title, value, false, null);
- }
-
- public EntryCustomField(Context context, AttributeSet attrs, String label, ProtectedString value, boolean showAction, OnClickListener onClickActionListener) {
- super(context, attrs);
-
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- assert inflater != null;
- inflater.inflate(R.layout.entry_new_field, this);
-
- labelView = findViewById(R.id.title);
- valueView = findViewById(R.id.value);
- actionImageView = findViewById(R.id.action_image);
-
- setLabel(label);
- setValue(value);
-
- if (showAction) {
- actionImageView.setEnabled(true);
- setAction(onClickActionListener);
- } else {
- actionImageView.setEnabled(false);
- actionImageView.setColorFilter(ContextCompat.getColor(getContext(), R.color.grey_dark));
- }
- }
-
- public void applyFontVisibility(boolean fontInVisibility) {
- if (fontInVisibility)
- Util.applyFontVisibilityTo(getContext(), valueView);
- }
-
- public void setLabel(String label) {
- if (label != null) {
- labelView.setText(label);
- }
- }
-
- public void setValue(ProtectedString value) {
- if (value != null) {
- valueView.setText(value.toString());
- }
- }
-
- public void setAction(OnClickListener onClickListener) {
- if (onClickListener != null) {
- actionImageView.setOnClickListener(onClickListener);
- } else {
- actionImageView.setVisibility(GONE);
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.kt
new file mode 100644
index 000000000..43ea862c1
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.view
+
+import android.content.Context
+import android.support.v4.content.ContextCompat
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.database.element.security.ProtectedString
+import com.kunzisoft.keepass.utils.Util
+import com.kunzisoft.keepass.utils.applyFontVisibility
+
+open class EntryCustomField(context: Context,
+ attrs: AttributeSet?,
+ label: String?,
+ value: ProtectedString?,
+ showAction: Boolean,
+ onClickActionListener: OnClickListener?)
+ : LinearLayout(context, attrs) {
+
+ protected var labelView: TextView
+ protected var valueView: TextView
+ protected var actionImageView: ImageView
+
+ @JvmOverloads
+ constructor(context: Context, attrs: AttributeSet? = null, title: String? = null, value: ProtectedString? = null)
+ : this(context, attrs, title, value, false, null)
+
+ init {
+
+ val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+ inflater.inflate(R.layout.entry_new_field, this)
+
+ labelView = findViewById(R.id.title)
+ valueView = findViewById(R.id.value)
+ actionImageView = findViewById(R.id.action_image)
+
+ setLabel(label)
+ setValue(value)
+
+ if (showAction) {
+ actionImageView.isEnabled = true
+ setAction(onClickActionListener)
+ } else {
+ actionImageView.isEnabled = false
+ actionImageView.setColorFilter(ContextCompat.getColor(getContext(), R.color.grey_dark))
+ }
+ }
+
+ fun applyFontVisibility(fontInVisibility: Boolean) {
+ if (fontInVisibility)
+ valueView.applyFontVisibility()
+ }
+
+ fun setLabel(label: String?) {
+ if (label != null) {
+ labelView.text = label
+ }
+ }
+
+ open fun setValue(value: ProtectedString?) {
+ if (value != null) {
+ valueView.text = value.toString()
+ }
+ }
+
+ fun setAction(onClickListener: OnClickListener?) {
+ if (onClickListener != null) {
+ actionImageView.setOnClickListener(onClickListener)
+ } else {
+ actionImageView.visibility = GONE
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.java
deleted file mode 100644
index 59c353a6c..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2018 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.view;
-
-import android.content.Context;
-import android.text.method.PasswordTransformationMethod;
-import android.util.AttributeSet;
-
-import com.kunzisoft.keepass.database.element.security.ProtectedString;
-
-public class EntryCustomFieldProtected extends EntryCustomField{
-
- public EntryCustomFieldProtected(Context context) {
- super(context);
- }
-
- public EntryCustomFieldProtected(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public EntryCustomFieldProtected(Context context, AttributeSet attrs, String title, ProtectedString value) {
- super(context, attrs, title, value);
- }
-
- public EntryCustomFieldProtected(Context context, AttributeSet attrs, String label, ProtectedString value, boolean showAction, OnClickListener onClickActionListener) {
- super(context, attrs, label, value, showAction, onClickActionListener);
- }
-
- public void setValue(ProtectedString value) {
- if (value != null) {
- valueView.setText(value.toString());
- setHiddenPasswordStyle(value.isProtected());
- }
- }
-
- public void setHiddenPasswordStyle(boolean hiddenStyle) {
- if ( !hiddenStyle ) {
- valueView.setTransformationMethod(null);
- } else {
- valueView.setTransformationMethod(PasswordTransformationMethod.getInstance());
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.kt
new file mode 100644
index 000000000..5c277a9c9
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.view
+
+import android.content.Context
+import android.text.method.PasswordTransformationMethod
+import android.util.AttributeSet
+
+import com.kunzisoft.keepass.database.element.security.ProtectedString
+
+class EntryCustomFieldProtected : EntryCustomField {
+
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ constructor(context: Context, attrs: AttributeSet?, label: String?, value: ProtectedString?) : super(context, attrs, label, value)
+
+ constructor(context: Context, attrs: AttributeSet?, label: String?, value: ProtectedString?, showAction: Boolean, onClickActionListener: OnClickListener?) : super(context, attrs, label, value, showAction, onClickActionListener)
+
+ override fun setValue(value: ProtectedString?) {
+ if (value != null) {
+ valueView.text = value.toString()
+ setHiddenPasswordStyle(value.isProtected)
+ }
+ }
+
+ fun setHiddenPasswordStyle(hiddenStyle: Boolean) {
+ if (!hiddenStyle) {
+ valueView.transformationMethod = null
+ } else {
+ valueView.transformationMethod = PasswordTransformationMethod.getInstance()
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.java
deleted file mode 100644
index 2a3ef0bcb..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX 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.
- *
- * KeePass DX 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 KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.view;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.kunzisoft.keepass.R;
-import com.kunzisoft.keepass.database.element.security.ProtectedString;
-import com.kunzisoft.keepass.utils.Util;
-
-public class EntryEditCustomField extends RelativeLayout {
-
- private TextView labelView;
- private EditText valueView;
- private CompoundButton protectionCheckView;
-
- public EntryEditCustomField(Context context) {
- this(context, null);
- }
-
- public EntryEditCustomField(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public EntryEditCustomField(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- assert inflater != null;
- inflater.inflate(R.layout.entry_edit_new_field, this);
-
- View deleteView = findViewById(R.id.entry_edit_new_field_delete);
- deleteView.setOnClickListener(v -> deleteViewFromParent());
-
- labelView = findViewById(R.id.entry_edit_new_field_label);
- valueView = findViewById(R.id.entry_edit_new_field_value);
- protectionCheckView = findViewById(R.id.protection);
- }
-
- public void setData(String label, ProtectedString value) {
- if (label != null)
- labelView.setText(label);
- if (value != null) {
- valueView.setText(value.toString());
- protectionCheckView.setChecked(value.isProtected());
- }
- }
-
- public String getLabel() {
- return labelView.getText().toString();
- }
-
- public String getValue() {
- return valueView.getText().toString();
- }
-
- public boolean isProtected() {
- return protectionCheckView.isChecked();
- }
-
- public void setFontVisibility(boolean applyFontVisibility) {
- if (applyFontVisibility)
- Util.applyFontVisibilityTo(getContext(), valueView);
- }
-
- public void deleteViewFromParent() {
- try {
- ViewGroup parent = (ViewGroup) getParent();
- parent.removeView(this);
- parent.invalidate();
- } catch (ClassCastException e) {
- Log.e(getClass().getName(), e.getMessage());
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.kt
new file mode 100644
index 000000000..5b01c89ff
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX 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.
+ *
+ * KeePass DX 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 KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.CompoundButton
+import android.widget.EditText
+import android.widget.RelativeLayout
+import android.widget.TextView
+
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.database.element.security.ProtectedString
+import com.kunzisoft.keepass.utils.Util
+import com.kunzisoft.keepass.utils.applyFontVisibility
+
+class EntryEditCustomField @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = 0)
+ : RelativeLayout(context, attrs, defStyle) {
+
+ private val labelView: TextView
+ private val valueView: EditText
+ private val protectionCheckView: CompoundButton
+
+ val label: String
+ get() = labelView.text.toString()
+
+ val value: String
+ get() = valueView.text.toString()
+
+ val isProtected: Boolean
+ get() = protectionCheckView.isChecked
+
+ init {
+
+ val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+ inflater.inflate(R.layout.entry_edit_new_field, this)
+
+ val deleteView = findViewById(R.id.entry_edit_new_field_delete)
+ deleteView.setOnClickListener { deleteViewFromParent() }
+
+ labelView = findViewById(R.id.entry_edit_new_field_label)
+ valueView = findViewById(R.id.entry_edit_new_field_value)
+ protectionCheckView = findViewById(R.id.protection)
+ }
+
+ fun setData(label: String?, value: ProtectedString?) {
+ if (label != null)
+ labelView.text = label
+ if (value != null) {
+ valueView.setText(value.toString())
+ protectionCheckView.isChecked = value.isProtected
+ }
+ }
+
+ fun setFontVisibility(applyFontVisibility: Boolean) {
+ if (applyFontVisibility)
+ valueView.applyFontVisibility()
+ }
+
+ private fun deleteViewFromParent() {
+ try {
+ val parent = parent as ViewGroup
+ parent.removeView(this)
+ parent.invalidate()
+ } catch (e: ClassCastException) {
+ Log.e(javaClass.name, e.message)
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/ic_data_usage_white_24dp.xml b/app/src/main/res/drawable/ic_data_usage_white_24dp.xml
new file mode 100644
index 000000000..b8c205af5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_data_usage_white_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml
index daf3f1191..7e4d18b3f 100644
--- a/app/src/main/res/layout/about.xml
+++ b/app/src/main/res/layout/about.xml
@@ -1,6 +1,6 @@