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