1 /* <lambda>null2 * 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.car.docklib.view 18 19 import android.car.media.CarMediaManager 20 import android.content.Context 21 import android.os.Build 22 import android.util.Log 23 import android.view.LayoutInflater 24 import android.view.ViewGroup 25 import androidx.recyclerview.widget.DiffUtil 26 import androidx.recyclerview.widget.ListAdapter 27 import androidx.recyclerview.widget.RecyclerView 28 import com.android.car.docklib.DockInterface 29 import com.android.car.docklib.R 30 import com.android.car.docklib.data.DockAppItem 31 32 /** [RecyclerView.Adapter] used to bind Dock items */ 33 class DockAdapter(private val dockController: DockInterface, private val userContext: Context) : 34 ListAdapter<DockAppItem, DockItemViewHolder>(DIFF_CALLBACK) { 35 companion object { 36 private val DEBUG = Build.isDebuggable() 37 private const val TAG = "DockAdapter" 38 } 39 40 private var carMediaManager: CarMediaManager? = null 41 42 enum class PayloadType { 43 CHANGE_ITEM_TYPE, 44 CHANGE_UX_RESTRICTION_STATE, 45 CHANGE_ACTIVE_MEDIA_SESSION, 46 } 47 48 private val positionToCallbackMap = HashMap<Int, Runnable>() 49 private var isUxRestrictionEnabled = false 50 private var activeMediaSessions: List<String> = emptyList() 51 52 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DockItemViewHolder { 53 val view = LayoutInflater.from(parent.context).inflate( 54 R.layout.dock_app_item_view, // resource 55 parent, 56 false // attachToRoot 57 ) 58 return DockItemViewHolder( 59 dockController, 60 view, 61 userContext, 62 carMediaManager 63 ) 64 } 65 66 override fun onBindViewHolder( 67 viewHolder: DockItemViewHolder, 68 position: Int, 69 payloads: MutableList<Any> 70 ) { 71 if (payloads.isEmpty()) { 72 return super.onBindViewHolder(viewHolder, position, payloads) 73 } 74 if (DEBUG) Log.d(TAG, "Binding at position $position with payloads") 75 76 payloads.forEach { payload -> 77 when (payload) { 78 PayloadType.CHANGE_ITEM_TYPE -> { 79 if (DEBUG) Log.d(TAG, "Type changed for position $position") 80 viewHolder.itemTypeChanged(currentList[position]) 81 } 82 PayloadType.CHANGE_UX_RESTRICTION_STATE -> { 83 if (DEBUG) Log.d(TAG, "UX restriction changed for position $position") 84 viewHolder.setUxRestrictions(currentList[position], isUxRestrictionEnabled) 85 } 86 PayloadType.CHANGE_ACTIVE_MEDIA_SESSION -> { 87 if (DEBUG) Log.d(TAG, "Active MediaSession changed for position $position") 88 viewHolder.setHasActiveMediaSession( 89 activeMediaSessions.contains(currentList[position].component.packageName) 90 ) 91 } 92 } 93 } 94 } 95 96 override fun onBindViewHolder(viewHolder: DockItemViewHolder, position: Int) { 97 if (DEBUG) Log.d(TAG, "Binding at position $position without payloads") 98 val cleanupCallback = positionToCallbackMap.getOrDefault( 99 position, 100 null // defaultValue 101 ) 102 if (DEBUG) Log.d(TAG, "Is callback set for $position: ${cleanupCallback != null}") 103 positionToCallbackMap.remove(position) 104 viewHolder.bind( 105 currentList[position], 106 isUxRestrictionEnabled, 107 cleanupCallback, 108 activeMediaSessions.contains(currentList[position].component.packageName) 109 ) 110 } 111 112 /** Used to set a callback for the [position] to be passed to the ViewHolder on the next bind. */ 113 fun setCallback(position: Int, callback: Runnable?) { 114 callback?.let { positionToCallbackMap[position] = it } 115 } 116 117 /** 118 * Setter for CarMediaManager 119 */ 120 fun setCarMediaManager(carMediaManager: CarMediaManager) { 121 this.carMediaManager = carMediaManager 122 } 123 124 /** Set if the Ux restrictions are enabled */ 125 fun setUxRestrictions(isUxRestrictionEnabled: Boolean) { 126 if (this.isUxRestrictionEnabled != isUxRestrictionEnabled) { 127 this.isUxRestrictionEnabled = isUxRestrictionEnabled 128 notifyItemRangeChanged(0, itemCount, PayloadType.CHANGE_UX_RESTRICTION_STATE) 129 } 130 } 131 132 /** Be notified that active media sessions have been changed */ 133 fun onMediaSessionChange(activeMediaSessions: List<String>) { 134 if (this.activeMediaSessions != activeMediaSessions) { 135 this.activeMediaSessions = activeMediaSessions 136 notifyItemRangeChanged(0, itemCount, PayloadType.CHANGE_ACTIVE_MEDIA_SESSION) 137 } 138 } 139 } 140 141 private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<DockAppItem>() { areItemsTheSamenull142 override fun areItemsTheSame(p0: DockAppItem, p1: DockAppItem): Boolean { 143 return p0.id == p1.id 144 } 145 areContentsTheSamenull146 override fun areContentsTheSame(p0: DockAppItem, p1: DockAppItem): Boolean { 147 return p0 == p1 148 } 149 getChangePayloadnull150 override fun getChangePayload( 151 oldItem: DockAppItem, 152 newItem: DockAppItem 153 ): Any? { 154 if (oldItem.type != newItem.type) { 155 return DockAdapter.PayloadType.CHANGE_ITEM_TYPE 156 } 157 return super.getChangePayload(oldItem, newItem) 158 } 159 } 160