1 /* 2 * 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.permissioncontroller.safetycenter.ui 18 19 import android.os.Bundle 20 import androidx.preference.PreferenceFragmentCompat 21 import androidx.preference.PreferenceScreen 22 import androidx.recyclerview.widget.RecyclerView 23 import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.EXTRA_SETTINGS_FRAGMENT_ARGS_KEY 24 import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity 25 26 /** Class used to scroll and highlight preferences for settings search. */ 27 internal class PreferenceHighlightManager(private val fragment: PreferenceFragmentCompat) { 28 private var preferenceHighlighted = false 29 private var isDataSetObserverRegistered = false 30 31 private var currentRootAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = null 32 private var preferenceGroupAdapter: HighlightablePreferenceGroupAdapter? = null 33 private val dataSetObserver: RecyclerView.AdapterDataObserver = 34 object : RecyclerView.AdapterDataObserver() { onChangednull35 override fun onChanged() { 36 highlightPreferenceIfNeeded() 37 } 38 onItemRangeChangednull39 override fun onItemRangeChanged(positionStart: Int, itemCount: Int) { 40 highlightPreferenceIfNeeded() 41 } 42 onItemRangeChangednull43 override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) { 44 highlightPreferenceIfNeeded() 45 } 46 onItemRangeInsertednull47 override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { 48 highlightPreferenceIfNeeded() 49 } 50 onItemRangeRemovednull51 override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { 52 highlightPreferenceIfNeeded() 53 } 54 onItemRangeMovednull55 override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) { 56 highlightPreferenceIfNeeded() 57 } 58 } 59 60 /** Creates a new [HighlightablePreferenceGroupAdapter] instance */ createAdapternull61 fun createAdapter( 62 preferenceScreen: PreferenceScreen, 63 ): RecyclerView.Adapter<RecyclerView.ViewHolder> { 64 val intent = fragment.getActivity()?.getIntent() 65 preferenceGroupAdapter = 66 HighlightablePreferenceGroupAdapter( 67 preferenceScreen, 68 intent?.getStringExtra(EXTRA_SETTINGS_FRAGMENT_ARGS_KEY), 69 preferenceHighlighted 70 ) 71 @Suppress("UNCHECKED_CAST") 72 return preferenceGroupAdapter!! as RecyclerView.Adapter<RecyclerView.ViewHolder> 73 } 74 75 /** Restore previously saved instance state from [Bundle] */ restoreStatenull76 fun restoreState(savedInstanceState: Bundle?) { 77 if (!SafetyCenterUiFlags.getShowSubpages() || savedInstanceState == null) { 78 return 79 } 80 81 preferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY, false) 82 } 83 84 /** Save current instance state to provided [Bundle] */ saveStatenull85 fun saveState(outState: Bundle) { 86 if (!SafetyCenterUiFlags.getShowSubpages()) { 87 return 88 } 89 90 val highlightRequested = preferenceGroupAdapter?.isHighlightRequested 91 highlightRequested?.let { outState.putBoolean(SAVE_HIGHLIGHTED_KEY, it) } 92 } 93 94 /** Scrolls to a particular preference in the recycler view and highlights it */ highlightPreferenceIfNeedednull95 fun highlightPreferenceIfNeeded() { 96 if (!SafetyCenterUiFlags.getShowSubpages() || !fragment.isAdded()) { 97 return 98 } 99 100 val collapsingActivity = fragment.getActivity() as? CollapsingToolbarBaseActivity 101 preferenceGroupAdapter?.requestHighlight( 102 fragment.getView(), 103 fragment.getListView(), 104 collapsingActivity?.appBarLayout 105 ) 106 } 107 108 /** Registers the observer while binding preferences */ registerObserverIfNeedednull109 fun registerObserverIfNeeded() { 110 if (!isDataSetObserverRegistered) { 111 currentRootAdapter?.unregisterAdapterDataObserver(dataSetObserver) 112 currentRootAdapter = fragment.getListView()?.getAdapter() 113 currentRootAdapter?.registerAdapterDataObserver(dataSetObserver) 114 isDataSetObserverRegistered = true 115 highlightPreferenceIfNeeded() 116 } 117 } 118 119 /** Unregisters the observer while unbinding preferences */ unregisterObserverIfNeedednull120 fun unregisterObserverIfNeeded() { 121 if (isDataSetObserverRegistered) { 122 currentRootAdapter?.unregisterAdapterDataObserver(dataSetObserver) 123 currentRootAdapter = null 124 isDataSetObserverRegistered = false 125 } 126 } 127 128 companion object { 129 private const val SAVE_HIGHLIGHTED_KEY: String = "android:preference_highlighted" 130 } 131 } 132