425 lines
21 KiB
Kotlin
425 lines
21 KiB
Kotlin
package info.nightscout.androidaps.activities
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.content.Context
|
|
import android.content.SharedPreferences
|
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
|
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.interfaces.Profile
|
|
import info.nightscout.androidaps.events.EventPreferenceChange
|
|
import info.nightscout.androidaps.events.EventRebuildTabs
|
|
import info.nightscout.androidaps.interfaces.PluginBase
|
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
|
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
|
|
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
|
|
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
|
|
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
|
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
|
|
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
|
|
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
|
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
|
|
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
|
|
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
|
|
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
|
|
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
|
|
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin
|
|
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.insight.LocalInsightPlugin
|
|
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
|
|
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.utils.SafeParse
|
|
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.resources.ResourceHelper
|
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
|
import javax.inject.Inject
|
|
|
|
class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener {
|
|
|
|
private var pluginId = -1
|
|
private var filter = ""
|
|
|
|
@Inject lateinit var rxBus: RxBusWrapper
|
|
@Inject lateinit var resourceHelper: ResourceHelper
|
|
@Inject lateinit var sp: SP
|
|
@Inject lateinit var profileFunction: ProfileFunction
|
|
@Inject lateinit var pluginStore: PluginStore
|
|
@Inject lateinit var config: Config
|
|
|
|
@Inject lateinit var automationPlugin: AutomationPlugin
|
|
@Inject lateinit var danaRPlugin: DanaRPlugin
|
|
@Inject lateinit var danaRKoreanPlugin: DanaRKoreanPlugin
|
|
@Inject lateinit var danaRv2Plugin: DanaRv2Plugin
|
|
@Inject lateinit var danaRSPlugin: DanaRSPlugin
|
|
@Inject lateinit var comboPlugin: ComboPlugin
|
|
@Inject lateinit var insulinOrefFreePeakPlugin: InsulinOrefFreePeakPlugin
|
|
@Inject lateinit var loopPlugin: LoopPlugin
|
|
@Inject lateinit var localInsightPlugin: LocalInsightPlugin
|
|
@Inject lateinit var medtronicPumpPlugin: MedtronicPumpPlugin
|
|
@Inject lateinit var nsClientPlugin: NSClientPlugin
|
|
@Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin
|
|
@Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin
|
|
@Inject lateinit var safetyPlugin: SafetyPlugin
|
|
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
|
|
@Inject lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin
|
|
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
|
|
@Inject lateinit var dexcomPlugin: DexcomPlugin
|
|
@Inject lateinit var eversensePlugin: EversensePlugin
|
|
@Inject lateinit var glimpPlugin: GlimpPlugin
|
|
@Inject lateinit var poctechPlugin: PoctechPlugin
|
|
@Inject lateinit var tomatoPlugin: TomatoPlugin
|
|
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
|
|
@Inject lateinit var statusLinePlugin: StatusLinePlugin
|
|
@Inject lateinit var tidepoolPlugin: TidepoolPlugin
|
|
@Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin
|
|
@Inject lateinit var wearPlugin: WearPlugin
|
|
@Inject lateinit var maintenancePlugin: MaintenancePlugin
|
|
|
|
@Inject lateinit var passwordCheck: PasswordCheck
|
|
@Inject lateinit var nsSettingStatus: NSSettingsStatus
|
|
@Inject lateinit var openHumansUploader: OpenHumansUploader
|
|
|
|
override fun onAttach(context: Context) {
|
|
AndroidSupportInjection.inject(this)
|
|
super.onAttach(context)
|
|
}
|
|
|
|
override fun setArguments(args: Bundle?) {
|
|
super.setArguments(args)
|
|
pluginId = args?.getInt("id") ?: -1
|
|
filter = args?.getString("filter") ?: ""
|
|
}
|
|
|
|
override fun onSaveInstanceState(outState: Bundle) {
|
|
super.onSaveInstanceState(outState)
|
|
outState.putInt("id", pluginId)
|
|
outState.putString("filter", filter)
|
|
}
|
|
|
|
override fun onDestroy() {
|
|
super.onDestroy()
|
|
PreferenceManager
|
|
.getDefaultSharedPreferences(context)
|
|
.unregisterOnSharedPreferenceChangeListener(this)
|
|
}
|
|
|
|
private fun addPreferencesFromResourceIfEnabled(p: PluginBase?, rootKey: String?, enabled: Boolean) {
|
|
if (enabled) addPreferencesFromResourceIfEnabled(p, rootKey)
|
|
}
|
|
|
|
private fun addPreferencesFromResourceIfEnabled(p: PluginBase?, rootKey: String?) {
|
|
if (p!!.isEnabled() && p.preferencesId != -1)
|
|
addPreferencesFromResource(p.preferencesId, rootKey)
|
|
}
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
PreferenceManager
|
|
.getDefaultSharedPreferences(context)
|
|
.registerOnSharedPreferenceChangeListener(this)
|
|
}
|
|
|
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
(savedInstanceState ?: arguments)?.let { bundle ->
|
|
if (bundle.containsKey("id")) {
|
|
pluginId = bundle.getInt("id")
|
|
}
|
|
if (bundle.containsKey("filter")) {
|
|
filter = bundle.getString("filter") ?: ""
|
|
}
|
|
}
|
|
if (pluginId != -1) {
|
|
addPreferencesFromResource(pluginId, rootKey)
|
|
} else {
|
|
addPreferencesFromResource(R.xml.pref_general, rootKey)
|
|
addPreferencesFromResource(R.xml.pref_overview, rootKey)
|
|
addPreferencesFromResourceIfEnabled(safetyPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(eversensePlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(dexcomPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(tomatoPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(poctechPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(glimpPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(loopPlugin, rootKey, config.APS)
|
|
addPreferencesFromResourceIfEnabled(openAPSAMAPlugin, rootKey, config.APS)
|
|
addPreferencesFromResourceIfEnabled(openAPSSMBPlugin, rootKey, config.APS)
|
|
addPreferencesFromResourceIfEnabled(sensitivityAAPSPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(sensitivityWeightedAveragePlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(sensitivityOref1Plugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(danaRPlugin, rootKey, config.PUMPDRIVERS)
|
|
addPreferencesFromResourceIfEnabled(danaRKoreanPlugin, rootKey, config.PUMPDRIVERS)
|
|
addPreferencesFromResourceIfEnabled(danaRv2Plugin, rootKey, config.PUMPDRIVERS)
|
|
addPreferencesFromResourceIfEnabled(danaRSPlugin, rootKey, config.PUMPDRIVERS)
|
|
addPreferencesFromResourceIfEnabled(localInsightPlugin, rootKey, config.PUMPDRIVERS)
|
|
addPreferencesFromResourceIfEnabled(comboPlugin, rootKey, config.PUMPDRIVERS)
|
|
addPreferencesFromResourceIfEnabled(medtronicPumpPlugin, rootKey, config.PUMPDRIVERS)
|
|
addPreferencesFromResource(R.xml.pref_pump, rootKey, config.PUMPDRIVERS)
|
|
addPreferencesFromResourceIfEnabled(virtualPumpPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(insulinOrefFreePeakPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(nsClientPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(tidepoolPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(smsCommunicatorPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(automationPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(wearPlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(statusLinePlugin, rootKey)
|
|
addPreferencesFromResource(R.xml.pref_alerts, rootKey)
|
|
addPreferencesFromResource(R.xml.pref_datachoices, rootKey)
|
|
addPreferencesFromResourceIfEnabled(maintenancePlugin, rootKey)
|
|
addPreferencesFromResourceIfEnabled(openHumansUploader, rootKey)
|
|
}
|
|
initSummary(preferenceScreen, pluginId != -1)
|
|
preprocessPreferences()
|
|
if (filter != "") updateFilterVisibility(filter, preferenceScreen)
|
|
}
|
|
|
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
|
rxBus.send(EventPreferenceChange(key))
|
|
if (key == resourceHelper.gs(R.string.key_language)) {
|
|
rxBus.send(EventRebuildTabs(true))
|
|
//recreate() does not update language so better close settings
|
|
activity?.finish()
|
|
}
|
|
if (key == resourceHelper.gs(R.string.key_short_tabtitles)) {
|
|
rxBus.send(EventRebuildTabs())
|
|
}
|
|
if (key == resourceHelper.gs(R.string.key_units)) {
|
|
activity?.recreate()
|
|
return
|
|
}
|
|
if (key == resourceHelper.gs(R.string.key_openapsama_useautosens) && sp.getBoolean(R.string.key_openapsama_useautosens, false)) {
|
|
activity?.let {
|
|
show(it, resourceHelper.gs(R.string.configbuilder_sensitivity), resourceHelper.gs(R.string.sensitivity_warning))
|
|
}
|
|
}
|
|
checkForBiometricFallback(key)
|
|
|
|
updatePrefSummary(findPreference(key))
|
|
preprocessPreferences()
|
|
}
|
|
|
|
private fun preprocessPreferences() {
|
|
for (plugin in pluginStore.plugins) {
|
|
plugin.preprocessPreferences(this)
|
|
}
|
|
}
|
|
|
|
private fun checkForBiometricFallback(key: String) {
|
|
// Biometric protection activated without set master password
|
|
if ((resourceHelper.gs(R.string.key_settings_protection) == key ||
|
|
resourceHelper.gs(R.string.key_application_protection) == key ||
|
|
resourceHelper.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
|
|
) {
|
|
activity?.let {
|
|
val title = resourceHelper.gs(R.string.unsecure_fallback_biometric)
|
|
val message = resourceHelper.gs(R.string.master_password_missing, resourceHelper.gs(R.string.configbuilder_general), resourceHelper.gs(R.string.protection))
|
|
show(it, title = title, message = message)
|
|
}
|
|
}
|
|
|
|
// 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
|
|
if (resourceHelper.gs(R.string.key_master_password) == key && sp.getString(key, "") == "" && isBiometricActivated) {
|
|
activity?.let {
|
|
val title = resourceHelper.gs(R.string.unsecure_fallback_biometric)
|
|
val message = resourceHelper.gs(R.string.unsecure_fallback_descriotion_biometric)
|
|
show(it, title = title, message = message)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun addPreferencesFromResource(@Suppress("SameParameterValue") @XmlRes preferencesResId: Int, key: String?, enabled: Boolean) {
|
|
if (enabled) addPreferencesFromResource(preferencesResId, key)
|
|
}
|
|
|
|
@SuppressLint("RestrictedApi")
|
|
private fun addPreferencesFromResource(@XmlRes preferencesResId: Int, key: String?) {
|
|
val xmlRoot = preferenceManager.inflateFromResource(context,
|
|
preferencesResId, null)
|
|
val root: Preference?
|
|
if (key != null) {
|
|
root = xmlRoot.findPreference(key)
|
|
if (root == null) return
|
|
require(root is PreferenceScreen) {
|
|
("Preference object with key " + key
|
|
+ " is not a PreferenceScreen")
|
|
}
|
|
preferenceScreen = root
|
|
} else {
|
|
addPreferencesFromResource(preferencesResId)
|
|
}
|
|
}
|
|
|
|
private fun adjustUnitDependentPrefs(pref: Preference) { // convert preferences values to current units
|
|
val unitDependent = arrayOf(
|
|
resourceHelper.gs(R.string.key_hypo_target),
|
|
resourceHelper.gs(R.string.key_activity_target),
|
|
resourceHelper.gs(R.string.key_eatingsoon_target),
|
|
resourceHelper.gs(R.string.key_high_mark),
|
|
resourceHelper.gs(R.string.key_low_mark)
|
|
)
|
|
if (unitDependent.toList().contains(pref.key) && pref is EditTextPreference) {
|
|
val converted = Profile.toCurrentUnits(profileFunction, SafeParse.stringToDouble(pref.text))
|
|
pref.summary = converted.toString()
|
|
}
|
|
}
|
|
|
|
private fun updateFilterVisibility(filter: String, p: Preference): Boolean {
|
|
|
|
var visible = false
|
|
|
|
if (p is PreferenceGroup) {
|
|
for (i in 0 until p.preferenceCount) {
|
|
visible = updateFilterVisibility(filter, p.getPreference(i)) || visible
|
|
}
|
|
if (visible && p is PreferenceCategory) {
|
|
p.initialExpandedChildrenCount = Int.MAX_VALUE
|
|
}
|
|
} else {
|
|
if (p.key != null) {
|
|
visible = visible || p.key.contains(filter, true)
|
|
}
|
|
if (p.title != null) {
|
|
visible = visible || p.title.contains(filter, true)
|
|
}
|
|
if (p.summary != null) {
|
|
visible = visible || p.summary.contains(filter, true)
|
|
}
|
|
}
|
|
|
|
p.isVisible = visible
|
|
return visible
|
|
}
|
|
|
|
private fun updatePrefSummary(pref: Preference?) {
|
|
if (pref is ListPreference) {
|
|
pref.setSummary(pref.entry)
|
|
// Preferences
|
|
// Preferences
|
|
if (pref.getKey() == resourceHelper.gs(R.string.key_settings_protection)) {
|
|
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_settings_password))
|
|
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
|
|
}
|
|
// Application
|
|
// Application
|
|
if (pref.getKey() == resourceHelper.gs(R.string.key_application_protection)) {
|
|
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_application_password))
|
|
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
|
|
}
|
|
// Bolus
|
|
// Bolus
|
|
if (pref.getKey() == resourceHelper.gs(R.string.key_bolus_protection)) {
|
|
val pass: Preference? = findPreference(resourceHelper.gs(R.string.key_bolus_password))
|
|
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString()
|
|
}
|
|
}
|
|
if (pref is EditTextPreference) {
|
|
if (pref.getKey().contains("password") || pref.getKey().contains("secret")) {
|
|
pref.setSummary("******")
|
|
} else if (pref.text != null) {
|
|
pref.dialogMessage = pref.dialogMessage
|
|
pref.setSummary(pref.text)
|
|
}
|
|
}
|
|
|
|
for (plugin in pluginStore.plugins) {
|
|
pref?.let { it.key?.let { plugin.updatePreferenceSummary(pref) } }
|
|
}
|
|
|
|
val hmacPasswords = arrayOf(
|
|
resourceHelper.gs(R.string.key_bolus_password),
|
|
resourceHelper.gs(R.string.key_master_password),
|
|
resourceHelper.gs(R.string.key_application_password),
|
|
resourceHelper.gs(R.string.key_settings_password)
|
|
)
|
|
|
|
if (pref is Preference) {
|
|
if ((pref.key != null) && (hmacPasswords.contains(pref.key))) {
|
|
if (sp.getString(pref.key, "").startsWith("hmac:")) {
|
|
pref.summary = "******"
|
|
} else {
|
|
pref.summary = resourceHelper.gs(R.string.password_not_set)
|
|
}
|
|
}
|
|
}
|
|
pref?.let { adjustUnitDependentPrefs(it) }
|
|
}
|
|
|
|
private fun initSummary(p: Preference, isSinglePreference: Boolean) {
|
|
p.isIconSpaceReserved = false // remove extra spacing on left after migration to androidx
|
|
// expand single plugin preference by default
|
|
if (p is PreferenceScreen && isSinglePreference) {
|
|
if (p.size > 0 && p.getPreference(0) is PreferenceCategory)
|
|
(p.getPreference(0) as PreferenceCategory).initialExpandedChildrenCount = Int.MAX_VALUE
|
|
}
|
|
if (p is PreferenceGroup) {
|
|
for (i in 0 until p.preferenceCount) {
|
|
initSummary(p.getPreference(i), isSinglePreference)
|
|
}
|
|
} else {
|
|
updatePrefSummary(p)
|
|
}
|
|
}
|
|
|
|
// We use Preference and custom editor instead of EditTextPreference
|
|
// to hash password while it is saved and never have to show it, even hashed
|
|
|
|
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
|
context?.let { context ->
|
|
if (preference != null) {
|
|
if (preference.key == resourceHelper.gs(R.string.key_master_password)) {
|
|
passwordCheck.queryPassword(context, R.string.current_master_password, R.string.key_master_password, {
|
|
passwordCheck.setPassword(context, R.string.master_password, R.string.key_master_password)
|
|
})
|
|
return true
|
|
}
|
|
if (preference.key == resourceHelper.gs(R.string.key_settings_password)) {
|
|
passwordCheck.setPassword(context, R.string.settings_password, R.string.key_settings_password)
|
|
return true
|
|
}
|
|
if (preference.key == resourceHelper.gs(R.string.key_bolus_password)) {
|
|
passwordCheck.setPassword(context, R.string.bolus_password, R.string.key_bolus_password)
|
|
return true
|
|
}
|
|
if (preference.key == resourceHelper.gs(R.string.key_application_password)) {
|
|
passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password)
|
|
return true
|
|
}
|
|
// NSClient copy settings
|
|
if (preference.key == resourceHelper.gs(R.string.key_statuslights_copy_ns)) {
|
|
nsSettingStatus.copyStatusLightsNsSettings(context)
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return super.onPreferenceTreeClick(preference)
|
|
}
|
|
|
|
fun setFilter(filter: String) {
|
|
this.filter = filter
|
|
preferenceManager?.preferenceScreen?.let { updateFilterVisibility(filter, it) }
|
|
}
|
|
} |