• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.permissioncontroller.permission.ui.handheld.v35
18 
19 import android.os.Handler
20 import android.os.Looper
21 import android.view.View
22 import androidx.preference.Preference
23 import androidx.preference.PreferenceCategory
24 import androidx.preference.PreferenceGroup
25 import androidx.preference.PreferenceGroupAdapter
26 import androidx.preference.PreferenceViewHolder
27 import com.android.permissioncontroller.R
28 import com.android.permissioncontroller.permission.ui.handheld.v36.AppPermissionFooterLinkPreference
29 import com.android.settingslib.widget.FooterPreference
30 
31 /**
32  * This is an extension over [PreferenceGroupAdapter] that allows creating visual sections for
33  * preferences. It sets the following drawable states on item views when they are
34  * [DrawableStateLinearLayout]:
35  * - [android.R.attr.state_single] if the item is the only one in a section
36  * - [android.R.attr.state_first] if the item is the first one in a section
37  * - [android.R.attr.state_middle] if the item is neither the first one or the last one in a section
38  * - [android.R.attr.state_last] if the item is the last one in a section
39  * - [R.attr.state_has_icon_space] if the item has icon space
40  *
41  * Note that [androidx.preference.PreferenceManager.PreferenceComparisonCallback] isn't supported
42  * (yet).
43  */
44 class SectionPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) :
45     PreferenceGroupAdapter(preferenceGroup) {
46     private var itemPositionStates = intArrayOf()
47 
48     private val handler = Handler(Looper.getMainLooper())
49 
<lambda>null50     private val syncRunnable = Runnable { buildItemPositionStates() }
51 
<lambda>null52     init {
53         buildItemPositionStates()
54     }
55 
onPreferenceHierarchyChangenull56     override fun onPreferenceHierarchyChange(preference: Preference) {
57         super.onPreferenceHierarchyChange(preference)
58 
59         // Post after super class has posted their sync runnable to update preferences.
60         handler.removeCallbacks(syncRunnable)
61         handler.post(syncRunnable)
62     }
63 
buildItemPositionStatesnull64     private fun buildItemPositionStates() {
65         val itemCount = itemCount
66         if (itemPositionStates.size != itemCount) {
67             itemPositionStates = IntArray(itemCount)
68         }
69 
70         var lastItemIndex = -1
71         for (i in 0..<itemCount) {
72             val preference = getItem(i)!!
73 
74             if (preference.isSectionDivider) {
75                 itemPositionStates[i] = 0
76                 continue
77             }
78 
79             val isFirstItemInFirstSection = lastItemIndex == -1
80             val isFirstItemInNewSection = lastItemIndex != i - 1
81             itemPositionStates[i] =
82                 if (isFirstItemInFirstSection || isFirstItemInNewSection) {
83                     android.R.attr.state_first
84                 } else {
85                     android.R.attr.state_middle
86                 }
87             if (!isFirstItemInFirstSection && isFirstItemInNewSection) {
88                 itemPositionStates[lastItemIndex] =
89                     if (itemPositionStates[lastItemIndex] == android.R.attr.state_first) {
90                         android.R.attr.state_single
91                     } else {
92                         android.R.attr.state_last
93                     }
94             }
95 
96             lastItemIndex = i
97         }
98 
99         if (lastItemIndex != -1) {
100             itemPositionStates[lastItemIndex] =
101                 if (itemPositionStates[lastItemIndex] == android.R.attr.state_first) {
102                     android.R.attr.state_single
103                 } else {
104                     android.R.attr.state_last
105                 }
106         }
107     }
108 
109     private val Preference.isSectionDivider: Boolean
110         get() =
111             this is PreferenceCategory ||
112                 this is FooterPreference ||
113                 this is AppPermissionFooterLinkPreference
114 
onBindViewHoldernull115     override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) {
116         super.onBindViewHolder(holder, position)
117 
118         val drawableStateLinearLayout = holder.itemView as? DrawableStateLinearLayout ?: return
119         val positionState = itemPositionStates.getOrElse(position) { 0 }
120         if (positionState == 0) {
121             return
122         }
123         val iconFrame =
124             holder.findViewById(androidx.preference.R.id.icon_frame)
125                 ?: holder.findViewById(android.R.id.icon_frame)
126         val hasIconSpace = iconFrame != null && iconFrame.visibility != View.GONE
127         drawableStateLinearLayout.extraDrawableState =
128             when (positionState) {
129                 android.R.attr.state_single ->
130                     if (hasIconSpace) STATE_SET_SINGLE_HAS_ICON_SPACE else STATE_SET_SINGLE
131                 android.R.attr.state_first ->
132                     if (hasIconSpace) STATE_SET_FIRST_HAS_ICON_SPACE else STATE_SET_FIRST
133                 android.R.attr.state_middle ->
134                     if (hasIconSpace) STATE_SET_MIDDLE_HAS_ICON_SPACE else STATE_SET_MIDDLE
135                 android.R.attr.state_last ->
136                     if (hasIconSpace) STATE_SET_LAST_HAS_ICON_SPACE else STATE_SET_LAST
137                 else -> error(positionState)
138             }
139     }
140 
141     companion object {
142         private val STATE_SET_SINGLE = intArrayOf(android.R.attr.state_single)
143         private val STATE_SET_FIRST = intArrayOf(android.R.attr.state_first)
144         private val STATE_SET_MIDDLE = intArrayOf(android.R.attr.state_middle)
145         private val STATE_SET_LAST = intArrayOf(android.R.attr.state_last)
146         private val STATE_SET_SINGLE_HAS_ICON_SPACE =
147             intArrayOf(android.R.attr.state_single, R.attr.state_has_icon_space)
148         private val STATE_SET_FIRST_HAS_ICON_SPACE =
149             intArrayOf(android.R.attr.state_first, R.attr.state_has_icon_space)
150         private val STATE_SET_MIDDLE_HAS_ICON_SPACE =
151             intArrayOf(android.R.attr.state_middle, R.attr.state_has_icon_space)
152         private val STATE_SET_LAST_HAS_ICON_SPACE =
153             intArrayOf(android.R.attr.state_last, R.attr.state_has_icon_space)
154     }
155 }
156