1 /*
2  * Copyright (C) 2019 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 
19 import android.content.ComponentName
20 import android.content.res.Resources
21 import android.view.LayoutInflater
22 import android.view.View
23 import android.view.ViewGroup
24 import android.widget.ImageView
25 import android.widget.TextView
26 import androidx.lifecycle.Lifecycle
27 import androidx.lifecycle.LifecycleOwner
28 import androidx.recyclerview.widget.RecyclerView
29 import com.android.systemui.res.R
30 import com.android.systemui.controls.ControlsServiceInfo
31 import com.android.systemui.util.icuMessageFormat
32 import java.text.Collator
33 import java.util.concurrent.Executor
34 
35 /**
36  * Adapter for binding [ControlsServiceInfo] related to [ControlsProviderService].
37  *
38  * This class handles subscribing and keeping track of the list of valid applications for
39  * displaying.
40  *
41  * @param uiExecutor an executor on the view thread of the containing [RecyclerView]
42  * @param lifecycle the lifecycle of the containing [LifecycleOwner] to control listening status
43  * @param controlsListingController the controller to keep track of valid applications
44  * @param layoutInflater an inflater for the views in the containing [RecyclerView]
45  * @param onAppSelected a callback to indicate that an app has been selected in the list.
46  */
47 class AppAdapter(
48         backgroundExecutor: Executor,
49         uiExecutor: Executor,
50         lifecycle: Lifecycle,
51         controlsListingController: ControlsListingController,
52         private val layoutInflater: LayoutInflater,
<lambda>null53         private val onAppSelected: (ControlsServiceInfo) -> Unit = {},
54         private val favoritesRenderer: FavoritesRenderer,
55         private val resources: Resources,
56         private val authorizedPanels: Set<String> = emptySet(),
57 ) : RecyclerView.Adapter<AppAdapter.Holder>() {
58 
59     private var listOfServices = emptyList<ControlsServiceInfo>()
60 
61     private val callback = object : ControlsListingController.ControlsListingCallback {
onServicesUpdatednull62         override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
63             backgroundExecutor.execute {
64                 val collator = Collator.getInstance(resources.configuration.locales[0])
65                 val localeComparator = compareBy<ControlsServiceInfo, CharSequence>(collator) {
66                     it.loadLabel() ?: ""
67                 }
68                 // No panel or the panel is not authorized
69                 listOfServices = serviceInfos.filter {
70                     it.panelActivity == null || it.panelActivity?.packageName !in authorizedPanels
71                 }.sortedWith(localeComparator)
72                 uiExecutor.execute(::notifyDataSetChanged)
73             }
74         }
75     }
76 
77     init {
78         controlsListingController.observe(lifecycle, callback)
79     }
80 
onCreateViewHoldernull81     override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder {
82         return Holder(
83             layoutInflater.inflate(R.layout.controls_app_item, parent, false),
84             favoritesRenderer
85         )
86     }
87 
getItemCountnull88     override fun getItemCount() = listOfServices.size
89 
90     override fun onBindViewHolder(holder: Holder, index: Int) {
91         holder.bindData(listOfServices[index])
92         holder.view.setOnClickListener {
93             onAppSelected(listOfServices[index])
94         }
95     }
96 
97     /**
98      * Holder for binding views in the [RecyclerView]-
99      */
100     class Holder(view: View, val favRenderer: FavoritesRenderer) : RecyclerView.ViewHolder(view) {
101         val view: View = itemView
102 
103         private val icon: ImageView = itemView.requireViewById(com.android.internal.R.id.icon)
104         private val title: TextView = itemView.requireViewById(com.android.internal.R.id.title)
105         private val favorites: TextView = itemView.requireViewById(R.id.favorites)
106 
107         /**
108          * Bind data to the view
109          * @param data Information about the [ControlsProviderService] to bind to the data
110          */
bindDatanull111         fun bindData(data: ControlsServiceInfo) {
112             icon.setImageDrawable(data.loadIcon())
113             title.text = data.loadLabel()
114             val text = if (data.panelActivity == null) {
115                 favRenderer.renderFavoritesForComponent(data.componentName)
116             } else {
117                 null
118             }
119             favorites.text = text
120             favorites.visibility = if (text == null) View.GONE else View.VISIBLE
121         }
122     }
123 }
124 
125 class FavoritesRenderer(
126     private val resources: Resources,
127     private val favoriteFunction: (ComponentName) -> Int
128 ) {
129 
renderFavoritesForComponentnull130     fun renderFavoritesForComponent(component: ComponentName): String? {
131         val qty = favoriteFunction(component)
132         return if (qty != 0) {
133             icuMessageFormat(resources, R.string.controls_number_of_favorites, qty)
134         } else {
135             null
136         }
137     }
138 }
139