diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt
index 8770925cb5..5da167b74b 100644
--- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt
+++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt
@@ -8,17 +8,17 @@ import android.os.Bundle
import androidx.annotation.XmlRes
import androidx.preference.*
import dagger.android.support.AndroidSupportInjection
-import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
-import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
+import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase
+import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugin.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
@@ -43,17 +43,12 @@ import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
-import info.nightscout.androidaps.plugins.source.DexcomPlugin
-import info.nightscout.androidaps.plugins.source.EversensePlugin
-import info.nightscout.androidaps.plugins.source.GlimpPlugin
-import info.nightscout.androidaps.plugins.source.PoctechPlugin
-import info.nightscout.androidaps.plugins.source.TomatoPlugin
-import info.nightscout.androidaps.plugins.source.GlunovoPlugin
-import info.nightscout.shared.SafeParse
+import info.nightscout.androidaps.plugins.source.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show
import info.nightscout.androidaps.utils.protection.PasswordCheck
-import info.nightscout.androidaps.utils.protection.ProtectionCheck
+import info.nightscout.androidaps.utils.protection.ProtectionCheck.ProtectionType.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
+import info.nightscout.shared.SafeParse
import info.nightscout.shared.sharedPreferences.SP
import javax.inject.Inject
@@ -239,7 +234,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
rh.gs(R.string.key_application_protection) == key ||
rh.gs(R.string.key_bolus_protection) == key) &&
sp.getString(R.string.key_master_password, "") == "" &&
- sp.getInt(key, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal
+ sp.getInt(key, NONE.ordinal) == BIOMETRIC.ordinal
) {
activity?.let {
val title = rh.gs(R.string.unsecure_fallback_biometric)
@@ -249,9 +244,9 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
}
// Master password erased with activated Biometric protection
- val isBiometricActivated = sp.getInt(R.string.key_settings_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal ||
- sp.getInt(R.string.key_application_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal ||
- sp.getInt(R.string.key_bolus_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal
+ val isBiometricActivated = sp.getInt(R.string.key_settings_protection, NONE.ordinal) == BIOMETRIC.ordinal ||
+ sp.getInt(R.string.key_application_protection, NONE.ordinal) == BIOMETRIC.ordinal ||
+ sp.getInt(R.string.key_bolus_protection, NONE.ordinal) == BIOMETRIC.ordinal
if (rh.gs(R.string.key_master_password) == key && sp.getString(key, "") == "" && isBiometricActivated) {
activity?.let {
val title = rh.gs(R.string.unsecure_fallback_biometric)
@@ -322,26 +317,35 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (pref is ListPreference) {
pref.setSummary(pref.entry)
// Preferences
- // Preferences
if (pref.getKey() == rh.gs(R.string.key_settings_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_settings_password))
- if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
+ val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
+ pass?.let { it.isVisible = usePassword }
+ val pin: Preference? = findPreference(rh.gs(R.string.key_settings_pin))
+ val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
+ pin?.let { it.isVisible = usePin }
}
// Application
- // Application
if (pref.getKey() == rh.gs(R.string.key_application_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_application_password))
- if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
+ val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
+ pass?.let { it.isVisible = usePassword }
+ val pin: Preference? = findPreference(rh.gs(R.string.key_application_pin))
+ val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
+ pin?.let { it.isVisible = usePin }
}
// Bolus
- // Bolus
if (pref.getKey() == rh.gs(R.string.key_bolus_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_bolus_password))
- if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
+ val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
+ pass?.let { it.isVisible = usePassword }
+ val pin: Preference? = findPreference(rh.gs(R.string.key_bolus_pin))
+ val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
+ pin?.let { it.isVisible = usePin }
}
}
if (pref is EditTextPreference) {
- if (pref.getKey().contains("password") || pref.getKey().contains("secret")) {
+ if (pref.getKey().contains("password") || pref.getKey().contains("pin") || pref.getKey().contains("secret")) {
pref.setSummary("******")
} else if (pref.text != null) {
pref.dialogMessage = pref.dialogMessage
@@ -357,7 +361,10 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
rh.gs(R.string.key_bolus_password),
rh.gs(R.string.key_master_password),
rh.gs(R.string.key_application_password),
- rh.gs(R.string.key_settings_password)
+ rh.gs(R.string.key_settings_password),
+ rh.gs(R.string.key_bolus_pin),
+ rh.gs(R.string.key_application_pin),
+ rh.gs(R.string.key_settings_pin)
)
if (pref is Preference) {
@@ -365,7 +372,11 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (sp.getString(pref.key, "").startsWith("hmac:")) {
pref.summary = "******"
} else {
- pref.summary = rh.gs(R.string.password_not_set)
+ if (pref.getKey().contains("pin")) {
+ pref.summary = rh.gs(R.string.pin_not_set)
+ }else {
+ pref.summary = rh.gs(R.string.password_not_set)
+ }
}
}
}
@@ -411,6 +422,18 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password)
return true
}
+ if (preference.key == rh.gs(R.string.key_settings_pin)) {
+ passwordCheck.setPassword(context, R.string.settings_pin, R.string.key_settings_pin, pinInput = true)
+ return true
+ }
+ if (preference.key == rh.gs(R.string.key_bolus_pin)) {
+ passwordCheck.setPassword(context, R.string.bolus_pin, R.string.key_bolus_pin, pinInput = true)
+ return true
+ }
+ if (preference.key == rh.gs(R.string.key_application_pin)) {
+ passwordCheck.setPassword(context, R.string.application_pin, R.string.key_application_pin, pinInput = true)
+ return true
+ }
// NSClient copy settings
if (preference.key == rh.gs(R.string.key_statuslights_copy_ns)) {
nsSettingStatus.copyStatusLightsNsSettings(context)
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 0453fe87bb..0e006e2bbf 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -136,6 +136,7 @@
- @string/biometric
- @string/master_password
- @string/custom_password
+ - @string/custom_pin
@@ -143,6 +144,7 @@
- 1
- 2
- 3
+ - 4
diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml
index ee1076fcab..6b43facf2d 100644
--- a/app/src/main/res/xml/pref_general.xml
+++ b/app/src/main/res/xml/pref_general.xml
@@ -46,10 +46,13 @@
android:title="@string/settings_protection" />
+
+
+
+
+
+
Unit)?, cancel: (() -> Unit)? = null, fail: (() -> Unit)? = null) {
+ fun queryPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ((String) -> Unit)?, cancel: (() -> Unit)? = null, fail: (() -> Unit)? = null, pinInput: Boolean = false) {
val password = sp.getString(preference, "")
if (password == "") {
ok?.invoke("")
return
}
-
val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null)
val alertDialogBuilder = AlertDialogHelper.Builder(context, R.style.DialogTheme)
alertDialogBuilder.setView(promptsView)
@@ -48,7 +48,10 @@ class PasswordCheck @Inject constructor(
val userInput2 = promptsView.findViewById(R.id.password_prompt_pass_confirm) as EditText
userInput2.visibility = View.GONE
-
+ if (pinInput) {
+ userInput.setHint(R.string.pin_hint)
+ userInput.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
+ }
val autoFillHintPasswordKind = context.getString(preference)
userInput.setAutofillHints(View.AUTOFILL_HINT_PASSWORD, "aaps_${autoFillHintPasswordKind}")
userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
@@ -60,7 +63,8 @@ class PasswordCheck @Inject constructor(
val enteredPassword = userInput.text.toString()
if (cryptoUtil.checkPassword(enteredPassword, password)) ok?.invoke(enteredPassword)
else {
- ToastUtils.errorToast(context, context.getString(R.string.wrongpassword))
+ val msg = if (pinInput) R.string.wrongpin else R.string.wrongpassword
+ ToastUtils.errorToast(context, context.getString(msg))
fail?.invoke()
}
}
@@ -74,14 +78,19 @@ class PasswordCheck @Inject constructor(
}
@SuppressLint("InflateParams")
- fun setPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ((String) -> Unit)? = null, cancel: (() -> Unit)? = null, clear: (() -> Unit)? = null) {
+ fun setPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ((String) -> Unit)? = null, cancel: (() -> Unit)? = null, clear: (() -> Unit)? = null, pinInput: Boolean = false) {
val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null)
val alertDialogBuilder = AlertDialogHelper.Builder(context, R.style.DialogTheme)
alertDialogBuilder.setView(promptsView)
val userInput = promptsView.findViewById(R.id.password_prompt_pass) as EditText
val userInput2 = promptsView.findViewById(R.id.password_prompt_pass_confirm) as EditText
-
+ if (pinInput) {
+ userInput.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
+ userInput2.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
+ userInput.setHint(R.string.pin_hint)
+ userInput2.setHint(R.string.pin_hint)
+ }
val autoFillHintPasswordKind = context.getString(preference)
userInput.setAutofillHints(AUTOFILL_HINT_NEW_PASSWORD, "aaps_${autoFillHintPasswordKind}")
userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
@@ -93,18 +102,22 @@ class PasswordCheck @Inject constructor(
val enteredPassword = userInput.text.toString()
val enteredPassword2 = userInput2.text.toString()
if (enteredPassword != enteredPassword2) {
- ToastUtils.errorToast(context, context.getString(R.string.passwords_dont_match))
+ val msg = if (pinInput) R.string.pin_dont_match else R.string.passwords_dont_match
+ ToastUtils.errorToast(context, context.getString(msg))
} else if (enteredPassword.isNotEmpty()) {
sp.putString(preference, cryptoUtil.hashPassword(enteredPassword))
- ToastUtils.okToast(context, context.getString(R.string.password_set))
+ val msg = if (pinInput) R.string.pin_set else R.string.password_set
+ ToastUtils.okToast(context, context.getString(msg))
ok?.invoke(enteredPassword)
} else {
if (sp.contains(preference)) {
sp.remove(preference)
- ToastUtils.graphicalToast(context, context.getString(R.string.password_cleared), R.drawable.ic_toast_delete_confirm)
+ val msg = if (pinInput) R.string.pin_cleared else R.string.password_cleared
+ ToastUtils.graphicalToast(context, context.getString(msg), R.drawable.ic_toast_delete_confirm)
clear?.invoke()
} else {
- ToastUtils.warnToast(context, context.getString(R.string.password_not_changed))
+ val msg = if (pinInput) R.string.pin_not_changed else R.string.password_not_changed
+ ToastUtils.warnToast(context, context.getString(msg))
cancel?.invoke()
}
}
@@ -112,7 +125,8 @@ class PasswordCheck @Inject constructor(
}
.setNegativeButton(context.getString(R.string.cancel)
) { dialog, _ ->
- ToastUtils.infoToast(context, context.getString(R.string.password_not_changed))
+ val msg = if (pinInput) R.string.pin_not_changed else R.string.password_not_changed
+ ToastUtils.infoToast(context, context.getString(msg))
cancel?.invoke()
dialog.cancel()
}
diff --git a/core/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt b/core/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt
index d06583e17d..7edc13830e 100644
--- a/core/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt
+++ b/core/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt
@@ -22,7 +22,8 @@ class ProtectionCheck @Inject constructor(
NONE,
BIOMETRIC,
MASTER_PASSWORD,
- CUSTOM_PASSWORD
+ CUSTOM_PASSWORD,
+ CUSTOM_PIN
}
private val passwordsResourceIDs = listOf(
@@ -30,22 +31,33 @@ class ProtectionCheck @Inject constructor(
R.string.key_application_password,
R.string.key_bolus_password)
+ private val pinsResourceIDs = listOf(
+ R.string.key_settings_pin,
+ R.string.key_application_pin,
+ R.string.key_bolus_pin)
+
private val protectionTypeResourceIDs = listOf(
R.string.key_settings_protection,
R.string.key_application_protection,
R.string.key_bolus_protection)
- private val titleResourceIDs = listOf(
+ private val titlePassResourceIDs = listOf(
R.string.settings_password,
R.string.application_password,
R.string.bolus_password)
+ private val titlePinResourceIDs = listOf(
+ R.string.settings_pin,
+ R.string.application_pin,
+ R.string.bolus_pin)
+
fun isLocked(protection: Protection): Boolean {
return when (ProtectionType.values()[sp.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) {
ProtectionType.NONE -> false
ProtectionType.BIOMETRIC -> true
ProtectionType.MASTER_PASSWORD -> sp.getString(R.string.key_master_password, "") != ""
ProtectionType.CUSTOM_PASSWORD -> sp.getString(passwordsResourceIDs[protection.ordinal], "") != ""
+ ProtectionType.CUSTOM_PIN -> sp.getString(pinsResourceIDs[protection.ordinal], "") != ""
}
}
@@ -55,11 +67,13 @@ class ProtectionCheck @Inject constructor(
ProtectionType.NONE ->
ok?.run()
ProtectionType.BIOMETRIC ->
- BiometricCheck.biometricPrompt(activity, titleResourceIDs[protection.ordinal], ok, cancel, fail, passwordCheck)
+ BiometricCheck.biometricPrompt(activity, titlePassResourceIDs[protection.ordinal], ok, cancel, fail, passwordCheck)
ProtectionType.MASTER_PASSWORD ->
passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { ok?.run() }, { cancel?.run() }, { fail?.run() })
ProtectionType.CUSTOM_PASSWORD ->
- passwordCheck.queryPassword(activity, titleResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() })
+ passwordCheck.queryPassword(activity, titlePassResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() })
+ ProtectionType.CUSTOM_PIN ->
+ passwordCheck.queryPassword(activity, titlePinResourceIDs[protection.ordinal], pinsResourceIDs[protection.ordinal], { ok?.run() }, { cancel?.run() }, { fail?.run() }, true)
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/main/res/layout/passwordprompt.xml b/core/src/main/res/layout/passwordprompt.xml
index 90b14b14e4..bfbfbde942 100644
--- a/core/src/main/res/layout/passwordprompt.xml
+++ b/core/src/main/res/layout/passwordprompt.xml
@@ -2,8 +2,10 @@
+ android:padding="10dp"
+ tools:context=".utils.protection.PasswordCheck">
Bolus protection
Master password
Settings password
+ Settings PIN
Application password
+ Application PIN
Bolus password
+ Bolus PIN
Unlock settings
Biometric
Custom password
+ Custom PIN
No protection
Protection
Master password is not set!\n\nPlease set your Master password in Preferences (%1$s → %2$s)
@@ -19,15 +23,23 @@
In order to be effective, biometric protection needs a master password set for fallback.\n\nPlease set a master password!
Password set!
+ PIN set!
Password not set
+ PIN not set
Password not changed
+ PIN not changed
Password cleared!
+ PIN cleared!
Enter password here
+ Enter PIN here
master_password
settings_password
+ settings_pin
application_password
+ application_pin
bolus_password
+ bolus_pin
settings_protection
application_protection
bolus_protection
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index ea72c80e9d..65ad75af76 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -222,7 +222,9 @@
Wrong password
+ Wrong PIN
Passwords don\'t match
+ PINs don\'t match
Basal values not aligned to hours: %1$s