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