From 2c956f43ba8991d7e59ca062209932e244aae8c5 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sat, 26 Oct 2019 22:29:41 +0200 Subject: [PATCH] biometrics support for Preferences --- app/build.gradle | 1 + .../nightscout/androidaps/MainActivity.java | 10 +-- .../activities/PreferencesActivity.java | 50 ++++++++++---- .../activities/SingleFragmentActivity.java | 9 ++- .../configBuilder/ConfigBuilderFragment.kt | 16 +++-- .../plugins/configBuilder/PluginViewHolder.kt | 14 ++-- .../androidaps/setupwizard/SWDefinition.java | 22 +++--- .../androidaps/utils/PasswordProtection.java | 68 ------------------- .../utils/protection/BiometricCheck.kt | 50 ++++++++++++++ .../utils/protection/PasswordCheck.kt | 49 +++++++++++++ .../utils/protection/ProtectionCheck.kt | 56 +++++++++++++++ app/src/main/res/values/protection.xml | 36 ++++++++++ app/src/main/res/values/strings.xml | 2 - app/src/main/res/xml/pref_password.xml | 50 +++++++++++--- 14 files changed, 309 insertions(+), 124 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/utils/PasswordProtection.java create mode 100644 app/src/main/java/info/nightscout/androidaps/utils/protection/BiometricCheck.kt create mode 100644 app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt create mode 100644 app/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt create mode 100644 app/src/main/res/values/protection.xml diff --git a/app/build.gradle b/app/build.gradle index 86709bfdd2..a71692d30f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -235,6 +235,7 @@ dependencies { implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'androidx.biometric:biometric:1.0.0-rc02' implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation 'com.google.android.material:material:1.0.0' implementation 'androidx.percentlayout:percentlayout:1.0.0' diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 64c2656086..f6b2d7456c 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -60,8 +60,8 @@ import info.nightscout.androidaps.utils.AndroidPermission; import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.LocaleHelper; import info.nightscout.androidaps.utils.OKDialog; -import info.nightscout.androidaps.utils.PasswordProtection; import info.nightscout.androidaps.utils.SP; +import info.nightscout.androidaps.utils.protection.ProtectionCheck; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -333,11 +333,11 @@ public class MainActivity extends NoSplashAppCompatActivity { int id = item.getItemId(); switch (id) { case R.id.nav_preferences: - PasswordProtection.QueryPassword(this, R.string.settings_password, "settings_password", () -> { + ProtectionCheck.INSTANCE.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, () -> { Intent i = new Intent(this, PreferencesActivity.class); i.putExtra("id", -1); startActivity(i); - }, null); + }); return true; case R.id.nav_historybrowser: startActivity(new Intent(this, HistoryBrowseActivity.class)); @@ -375,11 +375,11 @@ public class MainActivity extends NoSplashAppCompatActivity { case R.id.nav_plugin_preferences: ViewPager viewPager = findViewById(R.id.pager); final PluginBase plugin = ((TabPageAdapter) viewPager.getAdapter()).getPluginAt(viewPager.getCurrentItem()); - PasswordProtection.QueryPassword(this, R.string.settings_password, "settings_password", () -> { + ProtectionCheck.INSTANCE.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, () -> { Intent i = new Intent(this, PreferencesActivity.class); i.putExtra("id", plugin.getPreferencesId()); startActivity(i); - }, null); + }); return true; } return actionBarDrawerToggle.onOptionsItemSelected(item); diff --git a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java index 367429dbbf..d551cc4b7a 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java @@ -15,21 +15,25 @@ import android.text.TextUtils; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin; -import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; -import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin; -import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader; -import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin; import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin; import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; +import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; +import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin; +import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; +import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin; +import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader; +import info.nightscout.androidaps.plugins.general.wear.WearPlugin; +import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin; +import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin; import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin; import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin; @@ -42,14 +46,10 @@ import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin; -import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; -import info.nightscout.androidaps.plugins.general.wear.WearPlugin; -import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin; import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin; -import info.nightscout.androidaps.utils.LocaleHelper; import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; +import info.nightscout.androidaps.utils.protection.ProtectionCheck; public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { MyPreferenceFragment myPreferenceFragment; @@ -87,6 +87,30 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre if (pref instanceof ListPreference) { ListPreference listPref = (ListPreference) pref; pref.setSummary(listPref.getEntry()); + // Preferences + if (pref.getKey().equals(MainApp.gs(R.string.key_settings_protection))) { + Preference pass = pref.getPreferenceManager().findPreference(MainApp.gs(R.string.key_settings_password)); + if (pass != null) + if (((ListPreference) pref).getValue().equals(Integer.toString(ProtectionCheck.ProtectionType.PASSWORD.ordinal()))) + pass.setEnabled(true); + else pass.setEnabled(false); + } + // Application + if (pref.getKey().equals(MainApp.gs(R.string.key_application_protection))) { + Preference pass = pref.getPreferenceManager().findPreference(MainApp.gs(R.string.key_application_password)); + if (pass != null) + if (((ListPreference) pref).getValue().equals(Integer.toString(ProtectionCheck.ProtectionType.PASSWORD.ordinal()))) + pass.setEnabled(true); + else pass.setEnabled(false); + } + // Bolus + if (pref.getKey().equals(MainApp.gs(R.string.key_bolus_protection))) { + Preference pass = pref.getPreferenceManager().findPreference(MainApp.gs(R.string.key_bolus_password)); + if (pass != null) + if (((ListPreference) pref).getValue().equals(Integer.toString(ProtectionCheck.ProtectionType.PASSWORD.ordinal()))) + pass.setEnabled(true); + else pass.setEnabled(false); + } } if (pref instanceof EditTextPreference) { EditTextPreference editTextPref = (EditTextPreference) pref; @@ -97,7 +121,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre } else if (editTextPref.getText() != null) { ((EditTextPreference) pref).setDialogMessage(editTextPref.getDialogMessage()); pref.setSummary(editTextPref.getText()); - } else if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) { + } else if (pref.getKey().contains(SP.getString(R.string.key_smscommunicator_allowednumbers, "")) && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) { pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java index 688ba82c6a..79768359c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java @@ -12,7 +12,7 @@ import androidx.fragment.app.Fragment; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.utils.PasswordProtection; +import info.nightscout.androidaps.utils.protection.ProtectionCheck; public class SingleFragmentActivity extends AppCompatActivity { @@ -39,13 +39,12 @@ public class SingleFragmentActivity extends AppCompatActivity { if (item.getItemId() == android.R.id.home) { finish(); return true; - } - else if (item.getItemId() == R.id.nav_plugin_preferences) { - PasswordProtection.QueryPassword(this, R.string.settings_password, "settings_password", () -> { + } else if (item.getItemId() == R.id.nav_plugin_preferences) { + ProtectionCheck.INSTANCE.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, () -> { Intent i = new Intent(this, PreferencesActivity.class); i.putExtra("id", plugin.getPreferencesId()); startActivity(i); - }, null); + }); return true; } return false; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt index 059bec1444..58f8a33748 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt @@ -14,7 +14,7 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.PasswordProtection +import info.nightscout.androidaps.utils.protection.ProtectionCheck import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.configbuilder_fragment.* @@ -33,16 +33,20 @@ class ConfigBuilderFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - if (PasswordProtection.isLocked("settings_password")) + if (ProtectionCheck.isLocked(ProtectionCheck.Protection.PREFERENCES)) configbuilder_main_layout.visibility = View.GONE else unlock.visibility = View.GONE unlock.setOnClickListener { - PasswordProtection.QueryPassword(context, R.string.settings_password, "settings_password", { - configbuilder_main_layout.visibility = View.VISIBLE - unlock.visibility = View.GONE - }, null) + activity?.let { activity -> + ProtectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + activity.runOnUiThread { + configbuilder_main_layout.visibility = View.VISIBLE + unlock.visibility = View.GONE + } + }) + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt index 86e0f39437..549e71566e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt @@ -9,7 +9,7 @@ import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.plugins.bus.RxBus -import info.nightscout.androidaps.utils.PasswordProtection +import info.nightscout.androidaps.utils.protection.ProtectionCheck class PluginViewHolder internal constructor(private val fragment: ConfigBuilderFragment, private val pluginType: PluginType, @@ -46,11 +46,13 @@ class PluginViewHolder internal constructor(private val fragment: ConfigBuilderF } pluginPreferences.setOnClickListener { - PasswordProtection.QueryPassword(fragment.context, R.string.settings_password, "settings_password", { - val i = Intent(fragment.context, PreferencesActivity::class.java) - i.putExtra("id", plugin.preferencesId) - fragment.startActivity(i) - }, null) + fragment.activity?.let { activity -> + ProtectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + val i = Intent(fragment.context, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + fragment.startActivity(i) + }) + } } update() } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java index 2c9e1ea51b..1369c19377 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java @@ -47,8 +47,8 @@ import info.nightscout.androidaps.setupwizard.elements.SWRadioButton; import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; import info.nightscout.androidaps.utils.AndroidPermission; import info.nightscout.androidaps.utils.LocaleHelper; -import info.nightscout.androidaps.utils.PasswordProtection; import info.nightscout.androidaps.utils.SP; +import info.nightscout.androidaps.utils.protection.ProtectionCheck; public class SWDefinition { private AppCompatActivity activity; @@ -216,11 +216,11 @@ public class SWDefinition { .action(() -> { final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveInsulin(); if (plugin != null) { - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + ProtectionCheck.INSTANCE.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, () -> { Intent i = new Intent(activity, PreferencesActivity.class); i.putExtra("id", plugin.getPreferencesId()); activity.startActivity(i); - }, null); + }); } }) .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveInsulin() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveInsulin()).getPreferencesId() > 0)) @@ -237,11 +237,11 @@ public class SWDefinition { .action(() -> { final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveBgSource(); if (plugin != null) { - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + ProtectionCheck.INSTANCE.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, () -> { Intent i = new Intent(activity, PreferencesActivity.class); i.putExtra("id", plugin.getPreferencesId()); activity.startActivity(i); - }, null); + }); } }) .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveBgSource() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveBgSource()).getPreferencesId() > 0)) @@ -307,11 +307,11 @@ public class SWDefinition { .action(() -> { final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActivePump(); if (plugin != null) { - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + ProtectionCheck.INSTANCE.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, () -> { Intent i = new Intent(activity, PreferencesActivity.class); i.putExtra("id", plugin.getPreferencesId()); activity.startActivity(i); - }, null); + }); } }) .visibility(() -> (ConfigBuilderPlugin.getPlugin().getActivePump() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActivePump()).getPreferencesId() > 0))) @@ -338,11 +338,11 @@ public class SWDefinition { .action(() -> { final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveAPS(); if (plugin != null) { - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + ProtectionCheck.INSTANCE.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, () -> { Intent i = new Intent(activity, PreferencesActivity.class); i.putExtra("id", plugin.getPreferencesId()); activity.startActivity(i); - }, null); + }); } }) .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveAPS() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveAPS()).getPreferencesId() > 0)) @@ -392,11 +392,11 @@ public class SWDefinition { .action(() -> { final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveSensitivity(); if (plugin != null) { - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + ProtectionCheck.INSTANCE.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, () -> { Intent i = new Intent(activity, PreferencesActivity.class); i.putExtra("id", plugin.getPreferencesId()); activity.startActivity(i); - }, null); + }); } }) .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveSensitivity() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveSensitivity()).getPreferencesId() > 0)) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/PasswordProtection.java b/app/src/main/java/info/nightscout/androidaps/utils/PasswordProtection.java deleted file mode 100644 index 230be97e22..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/PasswordProtection.java +++ /dev/null @@ -1,68 +0,0 @@ -package info.nightscout.androidaps.utils; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; - -/** - * Created by mike on 14.02.2017. - */ - -public class PasswordProtection { - static public boolean isLocked(String preference) { - final String password = SP.getString(preference, ""); - if (password.equals("")) { - return false; - } - return true; - } - - static public void QueryPassword(final Context context, int stringID, String preference, final Runnable ok, final Runnable fail) { - final String password = SP.getString(preference, ""); - if (password.equals("")) { - if (ok != null) ok.run(); - return; - } - LayoutInflater li = LayoutInflater.from(context); - View promptsView = li.inflate(R.layout.passwordprompt, null); - - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); - alertDialogBuilder.setView(promptsView); - - final TextView label = (TextView) promptsView.findViewById(R.id.passwordprompt_text); - label.setText(MainApp.gs(stringID)); - final EditText userInput = (EditText) promptsView.findViewById(R.id.passwordprompt_pass); - - // set dialog message - alertDialogBuilder - .setCancelable(false) - .setPositiveButton("OK", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog,int id) { - String enteredPassword = userInput.getText().toString(); - if (password.equals(enteredPassword)) { - if (ok != null) ok.run(); - } else { - ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.wrongpassword)); - if (fail != null) fail.run(); - } - } - }) - .setNegativeButton("Cancel", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog,int id) { - dialog.cancel(); - } - }); - - AlertDialog alertDialog = alertDialogBuilder.create(); - alertDialog.show(); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/protection/BiometricCheck.kt b/app/src/main/java/info/nightscout/androidaps/utils/protection/BiometricCheck.kt new file mode 100644 index 0000000000..decbf92056 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/protection/BiometricCheck.kt @@ -0,0 +1,50 @@ +package info.nightscout.androidaps.utils.protection + +import androidx.biometric.BiometricPrompt +import androidx.fragment.app.FragmentActivity +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.ToastUtils +import java.util.concurrent.Executors + +object BiometricCheck { + fun biometricPrompt(activity: FragmentActivity, ok: Runnable?, cancel: Runnable? = null, fail: Runnable? = null) { + val executor = Executors.newSingleThreadExecutor() + + val biometricPrompt = BiometricPrompt(activity, executor, object : BiometricPrompt.AuthenticationCallback() { + + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + super.onAuthenticationError(errorCode, errString) + if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) cancel?.run() + else { + // Called when an unrecoverable error has been encountered and the operation is complete. + ToastUtils.showToastInUiThread(activity.baseContext, errString.toString()) + // call ok, because it's not possible to bypass it when biometrics fail + ok?.run() + } + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + super.onAuthenticationSucceeded(result) + // Called when a biometric is recognized. + ok?.run() + } + + override fun onAuthenticationFailed() { + super.onAuthenticationFailed() + // Called when a biometric is valid but not recognized. + fail?.run() + } + }) + + val promptInfo = BiometricPrompt.PromptInfo.Builder() + .setTitle(MainApp.gs(R.string.biometric_title)) + .setSubtitle("Set the subtitle to display.") + .setDescription(MainApp.gs(R.string.biometric_title)) + .setNegativeButtonText(MainApp.gs(R.string.cancel)) + .build() + + biometricPrompt.authenticate(promptInfo) + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt b/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt new file mode 100644 index 0000000000..beeeef3add --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt @@ -0,0 +1,49 @@ +package info.nightscout.androidaps.utils.protection + +import android.app.AlertDialog +import android.view.LayoutInflater +import android.view.View +import android.widget.EditText +import android.widget.TextView +import androidx.annotation.StringRes +import androidx.fragment.app.FragmentActivity +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.SP +import info.nightscout.androidaps.utils.ToastUtils + +object PasswordCheck { + fun queryPassword(activity: FragmentActivity, @StringRes labelId: Int, @StringRes preference: Int, ok: Runnable?, cancel: Runnable? = null, fail: Runnable? = null) { + val password = SP.getString(preference, "") + if (password == "") { + ok?.run() + return + } + val promptsView = LayoutInflater.from(activity).inflate(R.layout.passwordprompt, null) + + val alertDialogBuilder = AlertDialog.Builder(activity) + alertDialogBuilder.setView(promptsView) + + val label = promptsView.findViewById(R.id.passwordprompt_text) as TextView + label.text = MainApp.gs(labelId) + val userInput = promptsView.findViewById(R.id.passwordprompt_pass) as EditText + + alertDialogBuilder + .setCancelable(false) + .setPositiveButton(MainApp.gs(R.string.ok)) { _, _ -> + val enteredPassword = userInput.text.toString() + if (password == enteredPassword) ok?.run() + else { + ToastUtils.showToastInUiThread(activity, MainApp.gs(R.string.wrongpassword)) + fail?.run() + } + } + .setNegativeButton(MainApp.gs(R.string.cancel) + ) { dialog, _ -> + cancel?.run() + dialog.cancel() + } + + alertDialogBuilder.create().show() + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt b/app/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt new file mode 100644 index 0000000000..86e29a7ed7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/protection/ProtectionCheck.kt @@ -0,0 +1,56 @@ +package info.nightscout.androidaps.utils.protection + +import androidx.fragment.app.FragmentActivity +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.SP + +object ProtectionCheck { + enum class Protection { + PREFERENCES, + APPLICATION, + BOLUS + } + + enum class ProtectionType { + NONE, + BIOMETRIC, + PASSWORD + } + + private val passwordsResourceIDs = listOf( + R.string.key_settings_password, + R.string.key_application_password, + R.string.key_bolus_password) + + private val protectionTypeResourceIDs = listOf( + R.string.key_settings_protection, + R.string.key_application_protection, + R.string.key_bolus_protection) + + private val titleResourceIDs = listOf( + R.string.settings_password, + R.string.application_password, + R.string.bolus_password) + + fun isLocked(protection: Protection): Boolean { + when (ProtectionType.values()[SP.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) { + ProtectionType.NONE -> return false + ProtectionType.BIOMETRIC -> return true + ProtectionType.PASSWORD -> return SP.getString(passwordsResourceIDs[protection.ordinal], "") != "" + } + } + + @JvmOverloads + fun queryProtection(activity: FragmentActivity, protection: Protection, + ok: Runnable?, cancel: Runnable? = null, fail: Runnable? = null) { + when (ProtectionType.values()[SP.getInt(protectionTypeResourceIDs[protection.ordinal], ProtectionType.NONE.ordinal)]) { + ProtectionType.NONE -> + ok?.run() + ProtectionType.BIOMETRIC -> + BiometricCheck.biometricPrompt(activity, ok, cancel, fail) + ProtectionType.PASSWORD -> + PasswordCheck.queryPassword(activity, titleResourceIDs[protection.ordinal], passwordsResourceIDs[protection.ordinal], ok, cancel, fail) + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/values/protection.xml b/app/src/main/res/values/protection.xml new file mode 100644 index 0000000000..6bf675ab44 --- /dev/null +++ b/app/src/main/res/values/protection.xml @@ -0,0 +1,36 @@ + + + Authentication required + Place your finger on the fingerprint reader to verify your identity + Settings protection + Application protection + Bolus protection + Settings password + Application password + Bolus password + Unlock settings + Biometric + Password + No protection + Protection + + settings_password + application_password + translatable="false"bolus_password + settings_protection + application_protection + bolus_protection + + + @string/noprotection + @string/biometric + @string/password + + + + 0 + 1 + 2 + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70e05db6c4..e2dc6b5e07 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -515,8 +515,6 @@ Virtual pump settings Upload status to NS Wrong password - Password for settings - Unlock settings Approaching insulin daily limit NSClient NSCI diff --git a/app/src/main/res/xml/pref_password.xml b/app/src/main/res/xml/pref_password.xml index f050d0486c..fc71b2a644 100644 --- a/app/src/main/res/xml/pref_password.xml +++ b/app/src/main/res/xml/pref_password.xml @@ -1,14 +1,48 @@ - - - + + + + + + + + + + + + + + + + + + \ No newline at end of file