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