diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt
index d90de7a086..74abd439f8 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt
@@ -130,6 +130,15 @@ class ImportExportPrefs @Inject constructor(
})
}
+ private fun askForEncryptionPass(activity: Activity, @StringRes canceledMsg: Int, @StringRes passwordName: Int, @StringRes passwordExplanation: Int?,
+ @StringRes passwordWarning: Int?, then: ((password: String) -> Unit)) {
+ passwordCheck.queryAnyPassword(activity, passwordName, R.string.key_master_password, passwordExplanation, passwordWarning, { password ->
+ then(password)
+ }, {
+ ToastUtils.warnToast(activity, resourceHelper.gs(canceledMsg))
+ })
+ }
+
private fun askForMasterPassIfNeeded(activity: Activity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) {
if (prefsEncryptionIsDisabled()) {
then("")
@@ -182,6 +191,28 @@ class ImportExportPrefs @Inject constructor(
}
}
+ private fun promptForDecryptionPasswordIfNeeded(activity: Activity, prefs: Prefs, importOk: Boolean,
+ format: PrefsFormat, importFile: PrefsFile, then: ((prefs: Prefs, importOk: Boolean) -> Unit)) {
+
+ // current master password was not the one used for decryption, so we prompt for old password...
+ if (!importOk && (prefs.metadata[PrefsMetadataKey.ENCRYPTION]?.status == PrefsStatus.ERROR)) {
+ askForEncryptionPass(activity, R.string.preferences_import_canceled, R.string.old_master_password,
+ R.string.different_password_used, R.string.master_password_will_be_replaced) { password ->
+
+ // ...and use it to load & decrypt file again
+ val prefsReloaded = format.loadPreferences(importFile.file, password)
+ prefsReloaded.metadata = prefFileList.checkMetadata(prefsReloaded.metadata)
+
+ // import is OK when we do not have errors (warnings are allowed)
+ val importOkCheckedAgain = checkIfImportIsOk(prefsReloaded)
+
+ then(prefsReloaded, importOkCheckedAgain);
+ }
+ } else {
+ then(prefs, importOk);
+ }
+ }
+
private fun exportSharedPreferences(activity: Activity) {
prefFileList.ensureExportDirExists()
@@ -258,32 +289,36 @@ class ImportExportPrefs @Inject constructor(
try {
- val prefs = format.loadPreferences(importFile.file, password)
- prefs.metadata = prefFileList.checkMetadata(prefs.metadata)
+ val prefsAttempted = format.loadPreferences(importFile.file, password)
+ prefsAttempted.metadata = prefFileList.checkMetadata(prefsAttempted.metadata)
// import is OK when we do not have errors (warnings are allowed)
- val importOk = checkIfImportIsOk(prefs)
+ val importOkAttempted = checkIfImportIsOk(prefsAttempted)
- // if at end we allow to import preferences
- val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.size > 0)
+ promptForDecryptionPasswordIfNeeded(activity, prefsAttempted, importOkAttempted, format, importFile) { prefs, importOk ->
- PrefImportSummaryDialog.showSummary(activity, importOk, importPossible, prefs, {
- if (importPossible) {
- sp.clear()
- for ((key, value) in prefs.values) {
- if (value == "true" || value == "false") {
- sp.putBoolean(key, value.toBoolean())
- } else {
- sp.putString(key, value)
+ // if at end we allow to import preferences
+ val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.size > 0)
+
+ PrefImportSummaryDialog.showSummary(activity, importOk, importPossible, prefs, {
+ if (importPossible) {
+ sp.clear()
+ for ((key, value) in prefs.values) {
+ if (value == "true" || value == "false") {
+ sp.putBoolean(key, value.toBoolean())
+ } else {
+ sp.putString(key, value)
+ }
}
- }
- restartAppAfterImport(activity)
- } else {
- // for impossible imports it should not be called
- ToastUtils.errorToast(activity, resourceHelper.gs(R.string.preferences_import_impossible))
- }
- })
+ restartAppAfterImport(activity)
+ } else {
+ // for impossible imports it should not be called
+ ToastUtils.errorToast(activity, resourceHelper.gs(R.string.preferences_import_impossible))
+ }
+ })
+
+ }
} catch (e: PrefFileNotFoundError) {
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + importFile)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 17343701cf..e54109587d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -208,6 +208,9 @@
Import canceled! Preferences were NOT imported!
Cannot import preferences!
Please go back to main screen and try again.
+ Old Master Password
+ This file was exported and encrypted with different master password. Provide old master password to decrypt file.
+ As a result of successful import current master password WILL BE REPLACED with that old master password!
Select file to import
diff --git a/core/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt b/core/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt
index 04399eb95f..efc3f6dfb1 100644
--- a/core/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt
+++ b/core/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt
@@ -6,6 +6,7 @@ import android.os.Build
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
+import android.widget.TextView
import androidx.annotation.StringRes
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.utils.CryptoUtil
@@ -24,6 +25,9 @@ class PasswordCheck @Inject constructor(
private val cryptoUtil: CryptoUtil
) {
+ /**
+ Asks for "managed" kind of password, checking if it is valid.
+ */
@SuppressLint("InflateParams")
fun queryPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ((String) -> Unit)?, cancel: (() -> Unit)? = null, fail: (() -> Unit)? = null) {
val password = sp.getString(preference, "")
@@ -115,4 +119,51 @@ class PasswordCheck @Inject constructor(
alertDialogBuilder.create().show()
}
+
+ /**
+ Prompt free-form password, with additional help and warning messages.
+ Preference ID (preference) is used only to generate ID for password managers,
+ since this query does NOT check validity of password.
+ */
+ @SuppressLint("InflateParams")
+ fun queryAnyPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, @StringRes passwordExplanation: Int?,
+ @StringRes passwordWarning: Int?, ok: ((String) -> Unit)?, cancel: (() -> Unit)? = null) {
+
+ val promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null)
+ val alertDialogBuilder = AlertDialogHelper.Builder(context)
+ alertDialogBuilder.setView(promptsView)
+ passwordExplanation?.let { alertDialogBuilder.setMessage(it) }
+
+ passwordWarning?.let {
+ val extraWarning: TextView = promptsView.findViewById(R.id.password_prompt_extra_message) as TextView;
+ extraWarning.text = context.getString(it);
+ extraWarning.visibility = View.VISIBLE;
+ }
+
+ val userInput = promptsView.findViewById(R.id.password_prompt_pass) as EditText
+ val userInput2 = promptsView.findViewById(R.id.password_prompt_pass_confirm) as EditText
+
+ userInput2.visibility = View.GONE
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val autoFillHintPasswordKind = context.getString(preference)
+ userInput.setAutofillHints(View.AUTOFILL_HINT_PASSWORD, "aaps_${autoFillHintPasswordKind}")
+ userInput.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
+ }
+
+ alertDialogBuilder
+ .setCancelable(false)
+ .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, context.getString(labelId), R.drawable.ic_header_key))
+ .setPositiveButton(context.getString(R.string.ok)) { _, _ ->
+ val enteredPassword = userInput.text.toString()
+ ok?.invoke(enteredPassword)
+ }
+ .setNegativeButton(context.getString(R.string.cancel)
+ ) { dialog, _ ->
+ cancel?.invoke()
+ dialog.cancel()
+ }
+
+ alertDialogBuilder.create().show()
+ }
}
diff --git a/core/src/main/res/layout/passwordprompt.xml b/core/src/main/res/layout/passwordprompt.xml
index e363256be9..90b14b14e4 100644
--- a/core/src/main/res/layout/passwordprompt.xml
+++ b/core/src/main/res/layout/passwordprompt.xml
@@ -5,6 +5,18 @@
android:orientation="vertical"
android:padding="10dp">
+
+
#FFFFFF
#FFFB8C00
#FF000000
+ #FFFB8C00
#FFFF5555
#FF000000
#FFFF5555