1 /* 2 * Copyright (C) 2024 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.settingslib.preference 18 19 import android.content.Context 20 import android.content.Intent 21 import android.os.Bundle 22 import android.util.Log 23 import androidx.annotation.XmlRes 24 import androidx.lifecycle.Lifecycle 25 import androidx.preference.PreferenceScreen 26 import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS 27 import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY 28 import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider 29 import com.android.settingslib.metadata.PreferenceScreenRegistry 30 import com.android.settingslib.preference.PreferenceScreenBindingHelper.Companion.bindRecursively 31 import com.android.settingslib.widget.SettingsBasePreferenceFragment 32 33 /** Fragment to display a preference screen. */ 34 open class PreferenceFragment : 35 SettingsBasePreferenceFragment(), PreferenceScreenProvider, PreferenceScreenBindingKeyProvider { 36 37 protected var preferenceScreenBindingHelper: PreferenceScreenBindingHelper? = null 38 onCreatePreferencesnull39 override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 40 preferenceScreen = createPreferenceScreen() 41 } 42 setPreferenceScreennull43 override fun setPreferenceScreen(preferenceScreen: PreferenceScreen?) { 44 super.setPreferenceScreen(preferenceScreen) 45 updateActivityTitle() 46 } 47 createPreferenceScreennull48 fun createPreferenceScreen(): PreferenceScreen? = 49 createPreferenceScreen(PreferenceScreenFactory(this)) 50 51 override fun createPreferenceScreen(factory: PreferenceScreenFactory): PreferenceScreen? { 52 preferenceScreenBindingHelper?.onDestroy() 53 preferenceScreenBindingHelper = null 54 55 val context = factory.context 56 fun createPreferenceScreenFromResource() = 57 factory.inflate(getPreferenceScreenResId(context))?.also { 58 Log.i(TAG, "Load screen " + it.key + " from resource") 59 } 60 61 val screenCreator = 62 getPreferenceScreenCreator(context) ?: return createPreferenceScreenFromResource() 63 val preferenceBindingFactory = screenCreator.preferenceBindingFactory 64 val preferenceHierarchy = screenCreator.getPreferenceHierarchy(context) 65 val preferenceScreen = 66 if (screenCreator.hasCompleteHierarchy()) { 67 Log.i(TAG, "Load screen " + screenCreator.key + " from hierarchy") 68 factory.getOrCreatePreferenceScreen().apply { 69 inflatePreferenceHierarchy(preferenceBindingFactory, preferenceHierarchy) 70 } 71 } else { 72 Log.i(TAG, "Screen " + screenCreator.key + " is hybrid") 73 createPreferenceScreenFromResource()?.also { 74 bindRecursively(it, preferenceBindingFactory, preferenceHierarchy) 75 } ?: return null 76 } 77 78 preferenceScreenBindingHelper = 79 PreferenceScreenBindingHelper( 80 context, 81 this, 82 preferenceBindingFactory, 83 preferenceScreen, 84 preferenceHierarchy, 85 ) 86 return preferenceScreen 87 } 88 89 /** Returns the xml resource to create preference screen. */ getPreferenceScreenResIdnull90 @XmlRes protected open fun getPreferenceScreenResId(context: Context): Int = 0 91 92 protected fun getPreferenceScreenCreator(context: Context): PreferenceScreenCreator? = 93 (PreferenceScreenRegistry.create( 94 context, 95 getPreferenceScreenBindingKey(context), 96 getPreferenceScreenBindingArgs(context), 97 ) as? PreferenceScreenCreator) 98 ?.run { if (isFlagEnabled(context)) this else null } 99 getPreferenceScreenBindingKeynull100 override fun getPreferenceScreenBindingKey(context: Context): String? = 101 arguments?.getString(EXTRA_BINDING_SCREEN_KEY) 102 103 override fun getPreferenceScreenBindingArgs(context: Context): Bundle? = 104 arguments?.getBundle(EXTRA_BINDING_SCREEN_ARGS) 105 106 override fun onCreate(savedInstanceState: Bundle?) { 107 super.onCreate(savedInstanceState) 108 preferenceScreenBindingHelper?.onCreate() 109 } 110 onStartnull111 override fun onStart() { 112 super.onStart() 113 preferenceScreenBindingHelper?.onStart() 114 } 115 onResumenull116 override fun onResume() { 117 super.onResume() 118 // Even when activity has several fragments with preference screen, this will keep activity 119 // title in sync when fragment manager pops back stack. 120 updateActivityTitle() 121 preferenceScreenBindingHelper?.onResume() 122 } 123 updateActivityTitlenull124 internal fun updateActivityTitle() { 125 if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) return 126 val activity = activity ?: return 127 val title = preferenceScreen?.title ?: return 128 if (activity.title != title) activity.title = title 129 } 130 onPausenull131 override fun onPause() { 132 preferenceScreenBindingHelper?.onPause() 133 super.onPause() 134 } 135 onStopnull136 override fun onStop() { 137 preferenceScreenBindingHelper?.onStop() 138 super.onStop() 139 } 140 onDestroynull141 override fun onDestroy() { 142 preferenceScreenBindingHelper?.onDestroy() 143 preferenceScreenBindingHelper = null 144 super.onDestroy() 145 } 146 147 @Suppress("DEPRECATION") 148 @Deprecated("Deprecated in Java") onActivityResultnull149 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 150 super.onActivityResult(requestCode, resultCode, data) 151 preferenceScreenBindingHelper?.onActivityResult(requestCode, resultCode, data) 152 } 153 getPreferenceKeysInHierarchynull154 protected fun getPreferenceKeysInHierarchy(): Set<String> = 155 preferenceScreenBindingHelper?.let { 156 mutableSetOf<String>().apply { it.forEachRecursively { add(it.metadata.key) } } 157 } ?: setOf() 158 159 companion object { 160 private const val TAG = "PreferenceFragment" 161 } 162 } 163