1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.biometrics.fingerprint2.ui.settings.fragment
18 
19 import android.app.Activity
20 import android.app.admin.DevicePolicyManager
21 import android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED_EXPLANATION
22 import android.app.settings.SettingsEnums
23 import android.content.Context.FINGERPRINT_SERVICE
24 import android.content.Intent
25 import android.hardware.fingerprint.FingerprintManager
26 import android.os.Bundle
27 import android.provider.Settings.Secure
28 import android.text.TextUtils
29 import android.util.Log
30 import android.view.View
31 import android.widget.Toast
32 import androidx.activity.result.ActivityResultLauncher
33 import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
34 import androidx.lifecycle.ViewModelProvider
35 import androidx.lifecycle.lifecycleScope
36 import androidx.preference.Preference
37 import androidx.preference.PreferenceCategory
38 import com.android.internal.widget.LockPatternUtils
39 import com.android.settings.R
40 import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
41 import com.android.settings.biometrics.BiometricEnrollBase
42 import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
43 import com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY
44 import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
45 import com.android.settings.biometrics.GatekeeperPasswordProvider
46 import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
47 import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
48 import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
49 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
50 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
51 import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
52 import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
53 import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
54 import com.android.settings.biometrics.fingerprint2.lib.model.Settings
55 import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder
56 import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
57 import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
58 import com.android.settings.core.SettingsBaseActivity
59 import com.android.settings.core.instrumentation.InstrumentedDialogFragment
60 import com.android.settings.dashboard.DashboardFragment
61 import com.android.settings.password.ChooseLockGeneric
62 import com.android.settings.password.ChooseLockSettingsHelper
63 import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
64 import com.android.settingslib.HelpUtils
65 import com.android.settingslib.RestrictedLockUtils
66 import com.android.settingslib.RestrictedLockUtilsInternal
67 import com.android.settingslib.transition.SettingsTransitionHelper
68 import com.android.settingslib.widget.FooterPreference
69 import com.google.android.setupdesign.util.DeviceHelper
70 import kotlinx.coroutines.Dispatchers
71 import kotlinx.coroutines.launch
72 
73 private const val TAG = "FingerprintSettingsV2Fragment"
74 private const val KEY_FINGERPRINTS_ENROLLED_CATEGORY = "security_settings_fingerprints_enrolled"
75 private const val KEY_FINGERPRINT_SIDE_FPS_CATEGORY =
76   "security_settings_fingerprint_unlock_category"
77 private const val KEY_FINGERPRINT_ADD = "key_fingerprint_add"
78 private const val KEY_FINGERPRINT_SIDE_FPS_SCREEN_ON_TO_AUTH =
79   "security_settings_require_screen_on_to_auth"
80 private const val KEY_FINGERPRINT_FOOTER = "security_settings_fingerprint_footer"
81 
82 /**
83  * A class responsible for showing FingerprintSettings. Typical activity Flows are
84  * 1. Settings > FingerprintSettings > PIN/PATTERN/PASS -> FingerprintSettings
85  * 2. FingerprintSettings -> FingerprintEnrollment fow
86  *
87  * This page typically allows for
88  * 1. Fingerprint deletion
89  * 2. Fingerprint enrollment
90  * 3. Renaming a fingerprint
91  * 4. Enabling/Disabling a feature
92  */
93 class FingerprintSettingsV2Fragment :
94   DashboardFragment(), FingerprintSettingsViewBinder.FingerprintView {
95   private lateinit var settingsViewModel: FingerprintSettingsViewModel
96   private lateinit var navigationViewModel: FingerprintSettingsNavigationViewModel
97 
98   /** Result listener for ChooseLock activity flow. */
99   private val confirmDeviceResultListener =
100     registerForActivityResult(StartActivityForResult()) { result ->
101       val resultCode = result.resultCode
102       val data = result.data
103       onConfirmDevice(resultCode, data)
104     }
105 
106   /** Result listener for launching enrollments **after** a user has reached the settings page. */
107   private val launchAdditionalFingerprintListener: ActivityResultLauncher<Intent> =
108     registerForActivityResult(StartActivityForResult()) { result ->
109       lifecycleScope.launch {
110         val resultCode = result.resultCode
111         Log.d(TAG, "onEnrollAdditionalFingerprint($resultCode)")
112 
113         if (resultCode == BiometricEnrollBase.RESULT_TIMEOUT) {
114           navigationViewModel.onEnrollAdditionalFailure()
115         } else {
116           navigationViewModel.onEnrollSuccess()
117         }
118       }
119     }
120 
121   /** Initial listener for the first enrollment request */
122   private val launchFirstEnrollmentListener: ActivityResultLauncher<Intent> =
123     registerForActivityResult(StartActivityForResult()) { result ->
124       lifecycleScope.launch {
125         val resultCode = result.resultCode
126         val data = result.data
127 
128         Log.d(TAG, "onEnrollFirstFingerprint($resultCode, $data)")
129         if (resultCode != RESULT_FINISHED || data == null) {
130           if (resultCode == BiometricEnrollBase.RESULT_TIMEOUT) {
131             navigationViewModel.onEnrollFirstFailure(
132               "Received RESULT_TIMEOUT when enrolling",
133               resultCode,
134             )
135           } else {
136             navigationViewModel.onEnrollFirstFailure(
137               "Incorrect resultCode or data was null",
138               resultCode,
139             )
140           }
141         } else {
142           val token = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
143           val challenge = data.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long?
144           navigationViewModel.onEnrollFirst(token, challenge)
145         }
146       }
147     }
148 
149   override fun userLockout(authAttemptViewModel: FingerprintAuthAttemptModel.Error) {
150     Toast.makeText(activity, authAttemptViewModel.message, Toast.LENGTH_SHORT).show()
151   }
152 
153   override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
154     // This is needed to support ChooseLockSettingBuilder...show(). All other activity
155     // calls should use the registerForActivity method call.
156     super.onActivityResult(requestCode, resultCode, data)
157     onConfirmDevice(resultCode, data)
158   }
159 
160   override fun onCreate(icicle: Bundle?) {
161     super.onCreate(icicle)
162 
163     if (icicle != null) {
164       Log.d(TAG, "onCreateWithSavedState")
165     } else {
166       Log.d(TAG, "onCreate()")
167     }
168 
169     /*
170     if (
171       !FeatureFlagUtils.isEnabled(
172         context,
173         FeatureFlagUtils.SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS
174       )
175     ) {
176       Log.d(TAG, "Finishing due to feature not being enabled")
177       finish()
178       return
179     }
180 
181      */
182 
183     val context = requireContext()
184     val userId = context.userId
185 
186     preferenceScreen.isVisible = false
187 
188     val fingerprintManager = context.getSystemService(FINGERPRINT_SERVICE) as FingerprintManager
189 
190     val backgroundDispatcher = Dispatchers.IO
191     val activity = requireActivity()
192     val userHandle = activity.user.identifier
193     // Note that SUW should not be launching FingerprintSettings
194     val isAnySuw = Settings
195 
196     val pressToAuthProvider = {
197       var toReturn: Int =
198         Secure.getIntForUser(
199           context.contentResolver,
200           Secure.SFPS_PERFORMANT_AUTH_ENABLED,
201           -1,
202           userHandle,
203         )
204       if (toReturn == -1) {
205         toReturn =
206           if (
207             context.resources.getBoolean(com.android.internal.R.bool.config_performantAuthDefault)
208           ) {
209             1
210           } else {
211             0
212           }
213         Secure.putIntForUser(
214           context.contentResolver,
215           Secure.SFPS_PERFORMANT_AUTH_ENABLED,
216           toReturn,
217           userHandle,
218         )
219       }
220 
221       toReturn == 1
222     }
223     val fingerprintSensorProvider =
224       FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
225     val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
226     val fingerprintEnrollStateRepository =
227       FingerprintEnrollInteractorImpl(
228         requireContext().applicationContext,
229         fingerprintManager,
230         Settings,
231       )
232 
233     val interactor =
234       FingerprintManagerInteractorImpl(
235         context.applicationContext,
236         backgroundDispatcher,
237         fingerprintManager,
238         fingerprintSensorProvider,
239         GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
240         fingerprintEnrollStateRepository,
241       )
242 
243     val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
244     val challenge = intent.getLongExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, -1L)
245 
246     navigationViewModel =
247       ViewModelProvider(
248         this,
249         FingerprintSettingsNavigationViewModel.FingerprintSettingsNavigationModelFactory(
250           userId,
251           interactor,
252           backgroundDispatcher,
253           token,
254           challenge,
255         ),
256       )[FingerprintSettingsNavigationViewModel::class.java]
257 
258     settingsViewModel =
259       ViewModelProvider(
260         this,
261         FingerprintSettingsViewModel.FingerprintSettingsViewModelFactory(
262           userId,
263           interactor,
264           backgroundDispatcher,
265           navigationViewModel,
266         ),
267       )[FingerprintSettingsViewModel::class.java]
268 
269     FingerprintSettingsViewBinder.bind(this, settingsViewModel, navigationViewModel, lifecycleScope)
270   }
271 
272   override fun getMetricsCategory(): Int {
273     return SettingsEnums.FINGERPRINT
274   }
275 
276   override fun getPreferenceScreenResId(): Int {
277     return R.xml.security_settings_fingerprint_limbo
278   }
279 
280   override fun getLogTag(): String {
281     return TAG
282   }
283 
284   override fun onStop() {
285     super.onStop()
286     navigationViewModel.maybeFinishActivity(requireActivity().isChangingConfigurations)
287   }
288 
289   override fun onPause() {
290     super.onPause()
291     settingsViewModel.shouldAuthenticate(false)
292     val transaction = parentFragmentManager.beginTransaction()
293     for (frag in parentFragmentManager.fragments) {
294       if (frag is InstrumentedDialogFragment) {
295         Log.d(TAG, "removing dialog settings fragment $frag")
296         frag.dismiss()
297         transaction.remove(frag)
298       }
299     }
300     transaction.commit()
301   }
302 
303   override fun onResume() {
304     super.onResume()
305     settingsViewModel.shouldAuthenticate(true)
306   }
307 
308   /** Used to indicate that preference has been clicked */
309   fun onPrefClicked(fingerprintViewModel: FingerprintData) {
310     Log.d(TAG, "onPrefClicked(${fingerprintViewModel})")
311     settingsViewModel.onPrefClicked(fingerprintViewModel)
312   }
313 
314   /** Used to indicate that a delete pref has been clicked */
315   fun onDeletePrefClicked(fingerprintViewModel: FingerprintData) {
316     Log.d(TAG, "onDeletePrefClicked(${fingerprintViewModel})")
317     settingsViewModel.onDeleteClicked(fingerprintViewModel)
318   }
319 
320   override fun showSettings(enrolledFingerprints: List<FingerprintData>) {
321     val category =
322       this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINTS_ENROLLED_CATEGORY)
323         as PreferenceCategory?
324 
325     category?.removeAll()
326 
327     enrolledFingerprints.forEach { fingerprint ->
328       category?.addPreference(
329         FingerprintSettingsPreference(
330           requireContext(),
331           fingerprint,
332           this@FingerprintSettingsV2Fragment,
333           enrolledFingerprints.size == 1,
334         )
335       )
336     }
337     category?.isVisible = true
338     preferenceScreen.isVisible = true
339     addFooter()
340   }
341 
342   override fun updateAddFingerprintsPreference(canEnroll: Boolean, maxFingerprints: Int) {
343     val pref = this@FingerprintSettingsV2Fragment.findPreference<Preference>(KEY_FINGERPRINT_ADD)
344     val maxSummary = context?.getString(R.string.fingerprint_add_max, maxFingerprints) ?: ""
345     pref?.summary = maxSummary
346     pref?.isEnabled = canEnroll
347     pref?.setOnPreferenceClickListener {
348       navigationViewModel.onAddFingerprintClicked()
349       true
350     }
351     pref?.isVisible = true
352   }
353 
354   override fun updateSfpsPreference(isSfpsPrefVisible: Boolean) {
355     val sideFpsPref =
356       this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINT_SIDE_FPS_CATEGORY)
357         as PreferenceCategory?
358     sideFpsPref?.isVisible = isSfpsPrefVisible
359     val otherPref =
360       this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINT_SIDE_FPS_SCREEN_ON_TO_AUTH)
361         as Preference?
362     otherPref?.isVisible = isSfpsPrefVisible
363   }
364 
365   private fun addFooter() {
366     val footer =
367       this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINT_FOOTER)
368         as PreferenceCategory?
369     val admin =
370       RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
371         activity,
372         DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT,
373         requireActivity().userId,
374       )
375     val activity = requireActivity()
376     val helpIntent =
377       HelpUtils.getHelpIntent(activity, getString(helpResource), activity::class.java.name)
378     val learnMoreClickListener =
379       View.OnClickListener { v: View? -> activity.startActivityForResult(helpIntent, 0) }
380 
381     class FooterColumn {
382       var title: CharSequence? = null
383       var learnMoreOverrideText: CharSequence? = null
384       var learnMoreOnClickListener: View.OnClickListener? = null
385     }
386 
387     var footerColumns = mutableListOf<FooterColumn>()
388     if (admin != null) {
389       val devicePolicyManager = getSystemService(DevicePolicyManager::class.java)
390       val column1 = FooterColumn()
391       column1.title =
392         devicePolicyManager.resources.getString(FINGERPRINT_UNLOCK_DISABLED_EXPLANATION) {
393           getString(R.string.security_fingerprint_disclaimer_lockscreen_disabled_1)
394         }
395 
396       column1.learnMoreOnClickListener =
397         View.OnClickListener { _ ->
398           RestrictedLockUtils.sendShowAdminSupportDetailsIntent(activity, admin)
399         }
400       column1.learnMoreOverrideText = getText(R.string.admin_support_more_info)
401       footerColumns.add(column1)
402       val column2 = FooterColumn()
403       column2.title = getText(R.string.security_fingerprint_disclaimer_lockscreen_disabled_2)
404       column2.learnMoreOverrideText =
405         getText(R.string.security_settings_fingerprint_settings_footer_learn_more)
406       column2.learnMoreOnClickListener = learnMoreClickListener
407       footerColumns.add(column2)
408     } else {
409       val column = FooterColumn()
410       column.title =
411         getString(
412           R.string.security_settings_fingerprint_enroll_introduction_v3_message,
413           DeviceHelper.getDeviceName(requireActivity()),
414         )
415       column.learnMoreOnClickListener = learnMoreClickListener
416       column.learnMoreOverrideText =
417         getText(R.string.security_settings_fingerprint_settings_footer_learn_more)
418       footerColumns.add(column)
419     }
420 
421     footer?.removeAll()
422     for (i in 0 until footerColumns.size) {
423       val column = footerColumns[i]
424       val footerPrefToAdd: FooterPreference =
425         FooterPreference.Builder(requireContext()).setTitle(column.title).build()
426       if (i > 0) {
427         footerPrefToAdd.setIconVisibility(View.GONE)
428       }
429       if (column.learnMoreOnClickListener != null) {
430         footerPrefToAdd.setLearnMoreAction(column.learnMoreOnClickListener)
431         if (!TextUtils.isEmpty(column.learnMoreOverrideText)) {
432           footerPrefToAdd.setLearnMoreText(column.learnMoreOverrideText)
433         }
434       }
435       footer?.addPreference(footerPrefToAdd)
436     }
437   }
438 
439   override suspend fun askUserToDeleteDialog(fingerprintViewModel: FingerprintData): Boolean {
440     Log.d(TAG, "showing delete dialog for (${fingerprintViewModel})")
441 
442     try {
443       val willDelete =
444         fingerprintPreferences()
445           .first { it?.fingerprintViewModel == fingerprintViewModel }
446           ?.askUserToDeleteDialog() ?: false
447       if (willDelete) {
448         mMetricsFeatureProvider.action(
449           context,
450           SettingsEnums.ACTION_FINGERPRINT_DELETE,
451           fingerprintViewModel.fingerId,
452         )
453       }
454       return willDelete
455     } catch (exception: Exception) {
456       Log.d(TAG, "askUserToDeleteDialog exception $exception")
457       return false
458     }
459   }
460 
461   override suspend fun askUserToRenameDialog(
462     fingerprintViewModel: FingerprintData
463   ): Pair<FingerprintData, String>? {
464     Log.d(TAG, "showing rename dialog for (${fingerprintViewModel})")
465     try {
466       val toReturn =
467         fingerprintPreferences()
468           .first { it?.fingerprintViewModel == fingerprintViewModel }
469           ?.askUserToRenameDialog()
470       if (toReturn != null) {
471         mMetricsFeatureProvider.action(
472           context,
473           SettingsEnums.ACTION_FINGERPRINT_RENAME,
474           toReturn.first.fingerId,
475         )
476       }
477       return toReturn
478     } catch (exception: Exception) {
479       Log.d(TAG, "askUserToRenameDialog exception $exception")
480       return null
481     }
482   }
483 
484   override suspend fun highlightPref(fingerId: Int) {
485     fingerprintPreferences()
486       .first { pref -> pref?.fingerprintViewModel?.fingerId == fingerId }
487       ?.highlight()
488   }
489 
490   override fun launchConfirmOrChooseLock(userId: Int) {
491     lifecycleScope.launch(Dispatchers.Default) {
492       navigationViewModel.setStepToLaunched()
493       val intent = Intent()
494       val builder =
495         ChooseLockSettingsHelper.Builder(requireActivity(), this@FingerprintSettingsV2Fragment)
496       val launched =
497         builder
498           .setRequestCode(CONFIRM_REQUEST)
499           .setTitle(getString(R.string.security_settings_fingerprint_preference_title))
500           .setRequestGatekeeperPasswordHandle(true)
501           .setUserId(userId)
502           .setForegroundOnly(true)
503           .setReturnCredentials(true)
504           .show()
505       if (!launched) {
506         intent.setClassName(SETTINGS_PACKAGE_NAME, ChooseLockGeneric::class.java.name)
507         intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true)
508         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true)
509         intent.putExtra(Intent.EXTRA_USER_ID, userId)
510         confirmDeviceResultListener.launch(intent)
511       }
512     }
513   }
514 
515   override fun launchFullFingerprintEnrollment(
516     userId: Int,
517     gateKeeperPasswordHandle: Long?,
518     challenge: Long?,
519     challengeToken: ByteArray?,
520   ) {
521     navigationViewModel.setStepToLaunched()
522     Log.d(TAG, "launchFullFingerprintEnrollment")
523     val intent = Intent()
524     intent.setClassName(
525       SETTINGS_PACKAGE_NAME,
526       FingerprintEnrollIntroductionInternal::class.java.name,
527     )
528     intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true)
529     intent.putExtra(
530       SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
531       SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE,
532     )
533 
534     intent.putExtra(Intent.EXTRA_USER_ID, userId)
535 
536     if (gateKeeperPasswordHandle != null) {
537       intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, gateKeeperPasswordHandle)
538     } else {
539       intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, challengeToken)
540       intent.putExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge)
541     }
542     launchFirstEnrollmentListener.launch(intent)
543   }
544 
545   override fun setResultExternal(resultCode: Int) {
546     setResult(resultCode)
547   }
548 
549   override fun launchAddFingerprint(userId: Int, challengeToken: ByteArray?) {
550     navigationViewModel.setStepToLaunched()
551     val intent = Intent()
552     intent.setClassName(
553       SETTINGS_PACKAGE_NAME,
554       FingerprintEnrollEnrolling::class.qualifiedName.toString(),
555     )
556     intent.putExtra(Intent.EXTRA_USER_ID, userId)
557     intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, challengeToken)
558     launchAdditionalFingerprintListener.launch(intent)
559   }
560 
561   private fun onConfirmDevice(resultCode: Int, data: Intent?) {
562     val wasSuccessful = resultCode == RESULT_FINISHED || resultCode == Activity.RESULT_OK
563     val gateKeeperPasswordHandle = data?.getExtra(EXTRA_KEY_GK_PW_HANDLE) as Long?
564     lifecycleScope.launch {
565       navigationViewModel.onConfirmDevice(wasSuccessful, gateKeeperPasswordHandle)
566     }
567   }
568 
569   private fun fingerprintPreferences(): List<FingerprintSettingsPreference?> {
570     val category =
571       this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINTS_ENROLLED_CATEGORY)
572         as PreferenceCategory?
573 
574     return category?.let { cat ->
575       cat.childrenToList().map { it as FingerprintSettingsPreference? }
576     } ?: emptyList()
577   }
578 
579   private fun PreferenceCategory.childrenToList(): List<Preference> {
580     val mutable: MutableList<Preference> = mutableListOf()
581     for (i in 0 until this.preferenceCount) {
582       mutable.add(this.getPreference(i))
583     }
584     return mutable.toList()
585   }
586 }
587