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