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