Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 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.systemui.controls.management |
| 18 | |
Matt Pietal | 53a8bbd | 2020-03-05 16:10:34 -0500 | [diff] [blame] | 19 | import android.content.ComponentName |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 20 | import android.graphics.Rect |
Fabian Kozynski | 6936cd1 | 2020-04-30 12:14:03 -0400 | [diff] [blame] | 21 | import android.service.controls.Control |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 22 | import android.service.controls.DeviceTypes |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 23 | import android.view.LayoutInflater |
| 24 | import android.view.View |
| 25 | import android.view.ViewGroup |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 26 | import android.view.accessibility.AccessibilityNodeInfo |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 27 | import android.widget.CheckBox |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 28 | import android.widget.ImageView |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 29 | import android.widget.Switch |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 30 | import android.widget.TextView |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 31 | import androidx.core.view.AccessibilityDelegateCompat |
| 32 | import androidx.core.view.ViewCompat |
| 33 | import androidx.core.view.accessibility.AccessibilityNodeInfoCompat |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 34 | import androidx.recyclerview.widget.GridLayoutManager |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 35 | import androidx.recyclerview.widget.RecyclerView |
| 36 | import com.android.systemui.R |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 37 | import com.android.systemui.controls.ControlInterface |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 38 | import com.android.systemui.controls.ui.RenderInfo |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 39 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 40 | private typealias ModelFavoriteChanger = (String, Boolean) -> Unit |
| 41 | |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 42 | /** |
| 43 | * Adapter for binding [Control] information to views. |
| 44 | * |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 45 | * The model for this adapter is provided by a [ControlModel] that is set using |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 46 | * [changeFavoritesModel]. This allows for updating the model if there's a reload. |
| 47 | * |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 48 | * @property elevation elevation of each control view |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 49 | */ |
| 50 | class ControlAdapter( |
Fabian Kozynski | 04e7bde | 2020-02-13 13:02:33 -0500 | [diff] [blame] | 51 | private val elevation: Float |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 52 | ) : RecyclerView.Adapter<Holder>() { |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 53 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 54 | companion object { |
Fabian Kozynski | 6936cd1 | 2020-04-30 12:14:03 -0400 | [diff] [blame] | 55 | const val TYPE_ZONE = 0 |
| 56 | const val TYPE_CONTROL = 1 |
| 57 | const val TYPE_DIVIDER = 2 |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 58 | } |
| 59 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 60 | val spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { |
| 61 | override fun getSpanSize(position: Int): Int { |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 62 | return if (getItemViewType(position) != TYPE_CONTROL) 2 else 1 |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 63 | } |
| 64 | } |
| 65 | |
Fabian Kozynski | 1e3178d | 2020-02-19 09:32:27 -0500 | [diff] [blame] | 66 | private var model: ControlsModel? = null |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 67 | |
| 68 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { |
Fabian Kozynski | 713b727 | 2020-03-03 18:35:52 -0500 | [diff] [blame] | 69 | val layoutInflater = LayoutInflater.from(parent.context) |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 70 | return when (viewType) { |
| 71 | TYPE_CONTROL -> { |
| 72 | ControlHolder( |
Fabian Kozynski | 1e3178d | 2020-02-19 09:32:27 -0500 | [diff] [blame] | 73 | layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { |
| 74 | layoutParams.apply { |
| 75 | width = ViewGroup.LayoutParams.MATCH_PARENT |
| 76 | } |
Fabian Kozynski | 04e7bde | 2020-02-13 13:02:33 -0500 | [diff] [blame] | 77 | elevation = this@ControlAdapter.elevation |
Fabian Kozynski | 9b972e8 | 2020-03-26 14:33:11 -0400 | [diff] [blame] | 78 | background = parent.context.getDrawable( |
| 79 | R.drawable.control_background_ripple) |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 80 | }, |
| 81 | model is FavoritesModel // Indicates that position information is needed |
Fabian Kozynski | 1e3178d | 2020-02-19 09:32:27 -0500 | [diff] [blame] | 82 | ) { id, favorite -> |
| 83 | model?.changeFavoriteStatus(id, favorite) |
| 84 | } |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 85 | } |
| 86 | TYPE_ZONE -> { |
| 87 | ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false)) |
| 88 | } |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 89 | TYPE_DIVIDER -> { |
| 90 | DividerHolder(layoutInflater.inflate( |
Joshua Tsuji | 0463180 | 2020-04-13 12:30:34 -0400 | [diff] [blame] | 91 | R.layout.controls_horizontal_divider_with_empty, parent, false)) |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 92 | } |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 93 | else -> throw IllegalStateException("Wrong viewType: $viewType") |
| 94 | } |
| 95 | } |
| 96 | |
Fabian Kozynski | 1e3178d | 2020-02-19 09:32:27 -0500 | [diff] [blame] | 97 | fun changeModel(model: ControlsModel) { |
| 98 | this.model = model |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 99 | notifyDataSetChanged() |
| 100 | } |
| 101 | |
Fabian Kozynski | 1e3178d | 2020-02-19 09:32:27 -0500 | [diff] [blame] | 102 | override fun getItemCount() = model?.elements?.size ?: 0 |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 103 | |
| 104 | override fun onBindViewHolder(holder: Holder, index: Int) { |
Fabian Kozynski | 1e3178d | 2020-02-19 09:32:27 -0500 | [diff] [blame] | 105 | model?.let { |
| 106 | holder.bindData(it.elements[index]) |
| 107 | } |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 108 | } |
| 109 | |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 110 | override fun onBindViewHolder(holder: Holder, position: Int, payloads: MutableList<Any>) { |
| 111 | if (payloads.isEmpty()) { |
| 112 | super.onBindViewHolder(holder, position, payloads) |
| 113 | } else { |
| 114 | model?.let { |
| 115 | val el = it.elements[position] |
| 116 | if (el is ControlInterface) { |
| 117 | holder.updateFavorite(el.favorite) |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 123 | override fun getItemViewType(position: Int): Int { |
Fabian Kozynski | 1e3178d | 2020-02-19 09:32:27 -0500 | [diff] [blame] | 124 | model?.let { |
| 125 | return when (it.elements.get(position)) { |
| 126 | is ZoneNameWrapper -> TYPE_ZONE |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 127 | is ControlStatusWrapper -> TYPE_CONTROL |
| 128 | is ControlInfoWrapper -> TYPE_CONTROL |
| 129 | is DividerWrapper -> TYPE_DIVIDER |
Fabian Kozynski | 1e3178d | 2020-02-19 09:32:27 -0500 | [diff] [blame] | 130 | } |
| 131 | } ?: throw IllegalStateException("Getting item type for null model") |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 132 | } |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Holder for binding views in the [RecyclerView]- |
| 137 | * @param view the [View] for this [Holder] |
| 138 | */ |
| 139 | sealed class Holder(view: View) : RecyclerView.ViewHolder(view) { |
| 140 | |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 141 | /** |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 142 | * Bind the data from the model into the view |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 143 | */ |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 144 | abstract fun bindData(wrapper: ElementWrapper) |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 145 | |
| 146 | open fun updateFavorite(favorite: Boolean) {} |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Holder for using with [DividerWrapper] to display a divider between zones. |
| 151 | * |
Fabian Kozynski | 6936cd1 | 2020-04-30 12:14:03 -0400 | [diff] [blame] | 152 | * The divider can be shown or hidden. It also has a view the height of a control, that can |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 153 | * be toggled visible or gone. |
| 154 | */ |
| 155 | private class DividerHolder(view: View) : Holder(view) { |
| 156 | private val frame: View = itemView.requireViewById(R.id.frame) |
| 157 | private val divider: View = itemView.requireViewById(R.id.divider) |
| 158 | override fun bindData(wrapper: ElementWrapper) { |
| 159 | wrapper as DividerWrapper |
| 160 | frame.visibility = if (wrapper.showNone) View.VISIBLE else View.GONE |
| 161 | divider.visibility = if (wrapper.showDivider) View.VISIBLE else View.GONE |
| 162 | } |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 163 | } |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 164 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 165 | /** |
| 166 | * Holder for using with [ZoneNameWrapper] to display names of zones. |
| 167 | */ |
| 168 | private class ZoneHolder(view: View) : Holder(view) { |
| 169 | private val zone: TextView = itemView as TextView |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 170 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 171 | override fun bindData(wrapper: ElementWrapper) { |
| 172 | wrapper as ZoneNameWrapper |
| 173 | zone.text = wrapper.zoneName |
| 174 | } |
| 175 | } |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 176 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 177 | /** |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 178 | * Holder for using with [ControlStatusWrapper] to display names of zones. |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 179 | * @param favoriteCallback this callback will be called whenever the favorite state of the |
| 180 | * [Control] this view represents changes. |
| 181 | */ |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 182 | internal class ControlHolder( |
| 183 | view: View, |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 184 | val withPosition: Boolean, |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 185 | val favoriteCallback: ModelFavoriteChanger |
| 186 | ) : Holder(view) { |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 187 | private val favoriteStateDescription = |
| 188 | itemView.context.getString(R.string.accessibility_control_favorite) |
| 189 | private val notFavoriteStateDescription = |
| 190 | itemView.context.getString(R.string.accessibility_control_not_favorite) |
| 191 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 192 | private val icon: ImageView = itemView.requireViewById(R.id.icon) |
| 193 | private val title: TextView = itemView.requireViewById(R.id.title) |
| 194 | private val subtitle: TextView = itemView.requireViewById(R.id.subtitle) |
| 195 | private val removed: TextView = itemView.requireViewById(R.id.status) |
Fabian Kozynski | 9b972e8 | 2020-03-26 14:33:11 -0400 | [diff] [blame] | 196 | private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply { |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 197 | visibility = View.VISIBLE |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 198 | } |
| 199 | |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 200 | private val accessibilityDelegate = ControlHolderAccessibilityDelegate(this::stateDescription) |
| 201 | |
| 202 | init { |
| 203 | ViewCompat.setAccessibilityDelegate(itemView, accessibilityDelegate) |
| 204 | } |
| 205 | |
| 206 | // Determine the stateDescription based on favorite state and maybe position |
| 207 | private fun stateDescription(favorite: Boolean): CharSequence? { |
| 208 | if (!favorite) { |
| 209 | return notFavoriteStateDescription |
| 210 | } else if (!withPosition) { |
| 211 | return favoriteStateDescription |
| 212 | } else { |
| 213 | val position = layoutPosition + 1 |
| 214 | return itemView.context.getString( |
| 215 | R.string.accessibility_control_favorite_position, position) |
| 216 | } |
| 217 | } |
| 218 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 219 | override fun bindData(wrapper: ElementWrapper) { |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 220 | wrapper as ControlInterface |
| 221 | val renderInfo = getRenderInfo(wrapper.component, wrapper.deviceType) |
| 222 | title.text = wrapper.title |
| 223 | subtitle.text = wrapper.subtitle |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 224 | updateFavorite(wrapper.favorite) |
| 225 | removed.text = if (wrapper.removed) { |
| 226 | itemView.context.getText(R.string.controls_removed) |
| 227 | } else { |
| 228 | "" |
| 229 | } |
Fabian Kozynski | aa1e548 | 2020-04-02 11:22:01 -0400 | [diff] [blame] | 230 | itemView.setOnClickListener { |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 231 | updateFavorite(!favorite.isChecked) |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 232 | favoriteCallback(wrapper.controlId, favorite.isChecked) |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 233 | } |
| 234 | applyRenderInfo(renderInfo) |
| 235 | } |
| 236 | |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 237 | override fun updateFavorite(favorite: Boolean) { |
| 238 | this.favorite.isChecked = favorite |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 239 | accessibilityDelegate.isFavorite = favorite |
| 240 | itemView.stateDescription = stateDescription(favorite) |
Fabian Kozynski | 8765d35 | 2020-04-06 21:16:02 -0400 | [diff] [blame] | 241 | } |
| 242 | |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 243 | private fun getRenderInfo( |
Matt Pietal | 53a8bbd | 2020-03-05 16:10:34 -0500 | [diff] [blame] | 244 | component: ComponentName, |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 245 | @DeviceTypes.DeviceType deviceType: Int |
| 246 | ): RenderInfo { |
Matt Pietal | 53a8bbd | 2020-03-05 16:10:34 -0500 | [diff] [blame] | 247 | return RenderInfo.lookup(itemView.context, component, deviceType, true) |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 248 | } |
| 249 | |
| 250 | private fun applyRenderInfo(ri: RenderInfo) { |
| 251 | val context = itemView.context |
| 252 | val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) |
| 253 | |
Matt Pietal | 53a8bbd | 2020-03-05 16:10:34 -0500 | [diff] [blame] | 254 | icon.setImageDrawable(ri.icon) |
Fabian Kozynski | 9aa23af | 2020-02-05 17:47:47 -0500 | [diff] [blame] | 255 | icon.setImageTintList(fg) |
Fabian Kozynski | f10b6ab | 2019-12-27 09:31:04 -0500 | [diff] [blame] | 256 | } |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 257 | } |
| 258 | |
Fabian Kozynski | b3c393f | 2020-05-08 14:29:30 -0400 | [diff] [blame] | 259 | private class ControlHolderAccessibilityDelegate( |
| 260 | val stateRetriever: (Boolean) -> CharSequence? |
| 261 | ) : AccessibilityDelegateCompat() { |
| 262 | |
| 263 | var isFavorite = false |
| 264 | |
| 265 | override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) { |
| 266 | super.onInitializeAccessibilityNodeInfo(host, info) |
| 267 | |
| 268 | // Change the text for the double-tap action |
| 269 | val clickActionString = if (isFavorite) { |
| 270 | host.context.getString(R.string.accessibility_control_change_unfavorite) |
| 271 | } else { |
| 272 | host.context.getString(R.string.accessibility_control_change_favorite) |
| 273 | } |
| 274 | val click = AccessibilityNodeInfoCompat.AccessibilityActionCompat( |
| 275 | AccessibilityNodeInfo.ACTION_CLICK, |
| 276 | // “favorite/unfavorite” |
| 277 | clickActionString) |
| 278 | info.addAction(click) |
| 279 | |
| 280 | // Determine the stateDescription based on the holder information |
| 281 | info.stateDescription = stateRetriever(isFavorite) |
| 282 | // Remove the information at the end indicating row and column. |
| 283 | info.setCollectionItemInfo(null) |
| 284 | |
| 285 | info.className = Switch::class.java.name |
| 286 | } |
| 287 | } |
| 288 | |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 289 | class MarginItemDecorator( |
| 290 | private val topMargin: Int, |
| 291 | private val sideMargins: Int |
| 292 | ) : RecyclerView.ItemDecoration() { |
| 293 | |
| 294 | override fun getItemOffsets( |
| 295 | outRect: Rect, |
| 296 | view: View, |
| 297 | parent: RecyclerView, |
| 298 | state: RecyclerView.State |
| 299 | ) { |
Fabian Kozynski | 6936cd1 | 2020-04-30 12:14:03 -0400 | [diff] [blame] | 300 | val position = parent.getChildAdapterPosition(view) |
| 301 | if (position == RecyclerView.NO_POSITION) return |
| 302 | val type = parent.adapter?.getItemViewType(position) |
| 303 | if (type == ControlAdapter.TYPE_CONTROL) { |
| 304 | outRect.apply { |
| 305 | top = topMargin |
| 306 | left = sideMargins |
| 307 | right = sideMargins |
| 308 | bottom = 0 |
| 309 | } |
| 310 | } else if (type == ControlAdapter.TYPE_ZONE && position == 0) { |
| 311 | // add negative padding to the first zone to counteract the margin |
| 312 | val margin = (view.layoutParams as ViewGroup.MarginLayoutParams).topMargin |
| 313 | outRect.apply { |
| 314 | top = -margin |
| 315 | left = 0 |
| 316 | right = 0 |
| 317 | bottom = 0 |
| 318 | } |
Fabian Kozynski | 5fc5f6b | 2020-02-03 15:21:14 -0500 | [diff] [blame] | 319 | } |
| 320 | } |
Matt Pietal | 53a8bbd | 2020-03-05 16:10:34 -0500 | [diff] [blame] | 321 | } |