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