diff --git a/packages/SystemUI/res/drawable/controls_list_divider.xml b/packages/SystemUI/res/drawable/controls_list_divider.xml
new file mode 100644
index 0000000000000..f8211d5297f3a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/controls_list_divider.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/drawable/ic_more_vert.xml b/packages/SystemUI/res/drawable/ic_more_vert.xml
deleted file mode 100644
index 1309fa875b558..0000000000000
--- a/packages/SystemUI/res/drawable/ic_more_vert.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
diff --git a/packages/SystemUI/res/layout/controls_spinner_item.xml b/packages/SystemUI/res/layout/controls_spinner_item.xml
new file mode 100644
index 0000000000000..6b880545b2960
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_spinner_item.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index 2cd9505b8fe40..77bcc3575fad8 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -14,44 +14,47 @@
~ limitations under the License.
-->
+ xmlns:android="http://schemas.android.com/apk/res/android">
-
+ android:paddingTop="12dp">
-
+
+ android:layout_marginEnd="10dp" />
-
+
+
+
+
+ android:orientation="vertical"
+ android:paddingTop="20dp" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index aefe4a20a4962..016b48bb48a11 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1217,6 +1217,7 @@
4dp
+ 1dp15dp100dp15dp
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d9b1452b6bb1c..83c5070955345 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -656,6 +656,12 @@
@*android:string/config_bodyFontFamily
+
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt
index f62412069b92b..6f2af1bda45c2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt
@@ -16,59 +16,27 @@
package com.android.systemui.controls.controller
-import android.content.ComponentName
import android.service.controls.DeviceTypes
-import android.util.Log
/**
* Stores basic information about a [Control] to persist and keep track of favorites.
*
- * The identifier of this [Control] is the combination of [component] and [controlId]. The other
- * two fields are there for persistence. In this way, basic information can be shown to the user
+ * The identifier of this [Control] is the [controlId], and is only unique per app. The other
+ * fields are there for persistence. In this way, basic information can be shown to the user
* before the service has to report on the status.
*
- * @property component the name of the component that provides the [Control].
- * @property controlId unique (for the given [component]) identifier for this [Control].
+ * @property controlId unique identifier for this [Control].
* @property controlTitle last title reported for this [Control].
* @property deviceType last reported type for this [Control].
*/
data class ControlInfo(
- val component: ComponentName,
val controlId: String,
val controlTitle: CharSequence,
@DeviceTypes.DeviceType val deviceType: Int
) {
companion object {
- private const val TAG = "ControlInfo"
private const val SEPARATOR = ":"
-
- /**
- * Creates a [ControlInfo] from a [SEPARATOR] separated list of fields.
- *
- * @param separator fields of a [ControlInfo] separated by [SEPARATOR]
- * @return a [ControlInfo] or `null` if there was an error.
- * @see [ControlInfo.toString]
- */
- fun createFromString(string: String): ControlInfo? {
- val parts = string.split(SEPARATOR)
- val component = ComponentName.unflattenFromString(parts[0])
- if (parts.size != 4 || component == null) {
- Log.e(TAG, "Cannot parse ControlInfo from $string")
- return null
- }
- val type = try {
- parts[3].toInt()
- } catch (e: Exception) {
- Log.e(TAG, "Cannot parse deviceType from ${parts[3]}")
- return null
- }
- return ControlInfo(
- component,
- parts[1],
- parts[2],
- if (DeviceTypes.validDeviceType(type)) type else DeviceTypes.TYPE_UNKNOWN)
- }
}
/**
@@ -77,16 +45,14 @@ data class ControlInfo(
* @return a [String] representation of `this`
*/
override fun toString(): String {
- return component.flattenToString() +
- "$SEPARATOR$controlId$SEPARATOR$controlTitle$SEPARATOR$deviceType"
+ return "$SEPARATOR$controlId$SEPARATOR$controlTitle$SEPARATOR$deviceType"
}
class Builder {
- lateinit var componentName: ComponentName
lateinit var controlId: String
lateinit var controlTitle: CharSequence
var deviceType: Int = DeviceTypes.TYPE_UNKNOWN
- fun build() = ControlInfo(componentName, controlId, controlTitle, deviceType)
+ fun build() = ControlInfo(controlId, controlTitle, deviceType)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index 07488ad836ef9..fd6e2566b1b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -42,30 +42,27 @@ interface ControlsBindingController : UserAwareController {
fun bindAndLoad(component: ComponentName, callback: LoadCallback)
/**
- * Request to bind to the given services.
+ * Request to bind to the given service.
*
- * @param components a list of [ComponentName] of the services to bind
+ * @param component The [ComponentName] of the service to bind
*/
- fun bindServices(components: List)
+ fun bindService(component: ComponentName)
/**
* Send a subscribe message to retrieve status of a set of controls.
*
- * The controls passed do not have to belong to a single [ControlsProviderService]. The
- * corresponding service [ComponentName] is associated with each control.
- *
- * @param controls a list of controls with corresponding [ComponentName] to request status
- * update
+ * @param structureInfo structure containing the controls to update
*/
- fun subscribe(controls: List)
+ fun subscribe(structureInfo: StructureInfo)
/**
* Send an action performed on a [Control].
*
- * @param controlInfo information about the actioned control, including the [ComponentName]
+ * @param componentName name of the component
+ * @param controlInfo information about the actioned control
* @param action the action performed on the control
*/
- fun action(controlInfo: ControlInfo, action: ControlAction)
+ fun action(componentName: ComponentName, controlInfo: ControlInfo, action: ControlAction)
/**
* Unsubscribe from all services to stop status updates.
@@ -91,4 +88,4 @@ interface ControlsBindingController : UserAwareController {
*/
fun error(message: String)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index a8981b06682a0..8f02c252beb1b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -26,9 +26,7 @@ import android.service.controls.IControlsActionCallback
import android.service.controls.IControlsSubscriber
import android.service.controls.IControlsSubscription
import android.service.controls.actions.ControlAction
-import android.util.ArrayMap
import android.util.Log
-import com.android.internal.annotations.GuardedBy
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -56,12 +54,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
override val currentUserId: Int
get() = currentUser.identifier
- @GuardedBy("componentMap")
- private val tokenMap: MutableMap =
- ArrayMap()
- @GuardedBy("componentMap")
- private val componentMap: MutableMap =
- ArrayMap()
+ private var currentProvider: ControlsProviderLifecycleManager? = null
private val actionCallbackService = object : IControlsActionCallback.Stub() {
override fun accept(
@@ -108,92 +101,70 @@ open class ControlsBindingControllerImpl @Inject constructor(
}
private fun retrieveLifecycleManager(component: ComponentName):
- ControlsProviderLifecycleManager {
- synchronized(componentMap) {
- val provider = componentMap.getOrPut(Key(component, currentUser)) {
- createProviderManager(component)
- }
- tokenMap.putIfAbsent(provider.token, provider)
- return provider
+ ControlsProviderLifecycleManager? {
+ if (currentProvider != null && currentProvider?.componentName != component) {
+ unbind()
}
+
+ if (currentProvider == null) {
+ currentProvider = createProviderManager(component)
+ }
+
+ return currentProvider
}
override fun bindAndLoad(
component: ComponentName,
callback: ControlsBindingController.LoadCallback
) {
- val provider = retrieveLifecycleManager(component)
- provider.maybeBindAndLoad(LoadSubscriber(callback))
+ retrieveLifecycleManager(component)?.maybeBindAndLoad(LoadSubscriber(callback))
}
- override fun subscribe(controls: List) {
- val controlsByComponentName = controls.groupBy { it.component }
+ override fun subscribe(structureInfo: StructureInfo) {
if (refreshing.compareAndSet(false, true)) {
- controlsByComponentName.forEach {
- val provider = retrieveLifecycleManager(it.key)
- backgroundExecutor.execute {
- provider.maybeBindAndSubscribe(it.value.map { it.controlId })
- }
- }
- }
- // Unbind unneeded providers
- val providersWithFavorites = controlsByComponentName.keys
- synchronized(componentMap) {
- componentMap.forEach {
- if (it.key.component !in providersWithFavorites) {
- backgroundExecutor.execute { it.value.unbindService() }
- }
- }
+ val provider = retrieveLifecycleManager(structureInfo.componentName)
+ provider?.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId })
}
}
override fun unsubscribe() {
if (refreshing.compareAndSet(true, false)) {
- val providers = synchronized(componentMap) {
- componentMap.values.toList()
- }
- providers.forEach {
- backgroundExecutor.execute { it.unsubscribe() }
- }
+ currentProvider?.unsubscribe()
}
}
- override fun action(controlInfo: ControlInfo, action: ControlAction) {
- val provider = retrieveLifecycleManager(controlInfo.component)
- provider.maybeBindAndSendAction(controlInfo.controlId, action)
+ override fun action(
+ componentName: ComponentName,
+ controlInfo: ControlInfo,
+ action: ControlAction
+ ) {
+ retrieveLifecycleManager(componentName)
+ ?.maybeBindAndSendAction(controlInfo.controlId, action)
}
- override fun bindServices(components: List) {
- components.forEach {
- val provider = retrieveLifecycleManager(it)
- backgroundExecutor.execute { provider.bindService() }
- }
+ override fun bindService(component: ComponentName) {
+ retrieveLifecycleManager(component)?.bindService()
}
override fun changeUser(newUser: UserHandle) {
if (newUser == currentUser) return
- synchronized(componentMap) {
- unbindAllProvidersLocked() // unbind all providers from the old user
- }
+
+ unbind()
+
refreshing.set(false)
currentUser = newUser
}
- private fun unbindAllProvidersLocked() {
- componentMap.values.forEach {
- if (it.user == currentUser) {
- it.unbindService()
- }
- }
+ private fun unbind() {
+ currentProvider?.unbindService()
+ currentProvider = null
}
override fun onComponentRemoved(componentName: ComponentName) {
backgroundExecutor.execute {
- synchronized(componentMap) {
- val removed = componentMap.remove(Key(componentName, currentUser))
- removed?.let {
- it.unbindService()
- tokenMap.remove(it.token)
+ currentProvider?.let {
+ if (it.componentName == componentName) {
+ unbind()
}
}
}
@@ -203,20 +174,31 @@ open class ControlsBindingControllerImpl @Inject constructor(
return StringBuilder(" ControlsBindingController:\n").apply {
append(" refreshing=${refreshing.get()}\n")
append(" currentUser=$currentUser\n")
- append(" Providers:\n")
- synchronized(componentMap) {
- componentMap.values.forEach {
- append(" $it\n")
- }
- }
+ append(" Providers=$currentProvider\n")
}.toString()
}
private abstract inner class CallbackRunnable(val token: IBinder) : Runnable {
- protected val provider: ControlsProviderLifecycleManager? =
- synchronized(componentMap) {
- tokenMap.get(token)
- }
+ protected val provider: ControlsProviderLifecycleManager? = currentProvider
+
+ override fun run() {
+ if (provider == null) {
+ Log.e(TAG, "No current provider set")
+ return
+ }
+ if (provider.user != currentUser) {
+ Log.e(TAG, "User ${provider.user} is not current user")
+ return
+ }
+ if (token != provider.token) {
+ Log.e(TAG, "Provider for token:$token does not exist anymore")
+ return
+ }
+
+ doRun()
+ }
+
+ abstract fun doRun()
}
private inner class OnLoadRunnable(
@@ -224,23 +206,9 @@ open class ControlsBindingControllerImpl @Inject constructor(
val list: List,
val callback: ControlsBindingController.LoadCallback
) : CallbackRunnable(token) {
- override fun run() {
- if (provider == null) {
- Log.e(TAG, "No provider found for token:$token")
- return
- }
- if (provider.user != currentUser) {
- Log.e(TAG, "User ${provider.user} is not current user")
- return
- }
- synchronized(componentMap) {
- if (token !in tokenMap.keys) {
- Log.e(TAG, "Provider for token:$token does not exist anymore")
- return
- }
- }
+ override fun doRun() {
callback.accept(list)
- provider.unbindService()
+ provider?.unbindService()
}
}
@@ -248,14 +216,11 @@ open class ControlsBindingControllerImpl @Inject constructor(
token: IBinder,
val control: Control
) : CallbackRunnable(token) {
- override fun run() {
+ override fun doRun() {
if (!refreshing.get()) {
Log.d(TAG, "onRefresh outside of window from:${provider?.componentName}")
}
- if (provider?.user != currentUser) {
- Log.e(TAG, "User ${provider?.user} is not current user")
- return
- }
+
provider?.let {
lazyController.get().refreshStatus(it.componentName, control)
}
@@ -266,7 +231,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
token: IBinder,
val subscription: IControlsSubscription
) : CallbackRunnable(token) {
- override fun run() {
+ override fun doRun() {
if (!refreshing.get()) {
Log.d(TAG, "onRefresh outside of window from '${provider?.componentName}'")
}
@@ -279,7 +244,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
private inner class OnCompleteRunnable(
token: IBinder
) : CallbackRunnable(token) {
- override fun run() {
+ override fun doRun() {
provider?.let {
Log.i(TAG, "onComplete receive from '${it.componentName}'")
}
@@ -290,7 +255,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
token: IBinder,
val error: String
) : CallbackRunnable(token) {
- override fun run() {
+ override fun doRun() {
provider?.let {
Log.e(TAG, "onError receive from '${it.componentName}': $error")
}
@@ -302,11 +267,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
val controlId: String,
@ControlAction.ResponseResult val response: Int
) : CallbackRunnable(token) {
- override fun run() {
- if (provider?.user != currentUser) {
- Log.e(TAG, "User ${provider?.user} is not current user")
- return
- }
+ override fun doRun() {
provider?.let {
lazyController.get().onActionResponse(it.componentName, controlId, response)
}
@@ -318,7 +279,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
val error: String,
val callback: ControlsBindingController.LoadCallback
) : CallbackRunnable(token) {
- override fun run() {
+ override fun doRun() {
callback.error(error)
provider?.let {
Log.e(TAG, "onError receive from '${it.componentName}': $error")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 4b89fd48972b3..f2881d41574c3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -59,14 +59,15 @@ interface ControlsController : UserAwareController {
)
/**
- * Request to subscribe for all favorite controls.
+ * Request to subscribe for favorited controls per structure
*
+ * @param structureInfo structure to limit the subscription to
* @see [ControlsBindingController.subscribe]
*/
- fun subscribeToFavorites()
+ fun subscribeToFavorites(structureInfo: StructureInfo)
/**
- * Request to unsubscribe to all providers.
+ * Request to unsubscribe to the current provider.
*
* @see [ControlsBindingController.unsubscribe]
*/
@@ -75,11 +76,12 @@ interface ControlsController : UserAwareController {
/**
* Notify a [ControlsProviderService] that an action has been performed on a [Control].
*
+ * @param componentName the name of the service that provides the [Control]
* @param controlInfo information of the [Control] receiving the action
* @param action action performed on the [Control]
* @see [ControlsBindingController.action]
*/
- fun action(controlInfo: ControlInfo, action: ControlAction)
+ fun action(componentName: ComponentName, controlInfo: ControlInfo, action: ControlAction)
/**
* Refresh the status of a [Control] with information provided from the service.
@@ -107,48 +109,29 @@ interface ControlsController : UserAwareController {
// FAVORITE MANAGEMENT
/**
- * Get a list of all favorite controls.
+ * Get all the favorites.
*
- * @return a list of [ControlInfo] with persistent information about the controls, including
- * their corresponding [ComponentName].
+ * @return a list of the structures that have at least one favorited control
*/
- fun getFavoriteControls(): List
+ fun getFavorites(): List
/**
* Get all the favorites for a given component.
*
- * @param componentName the name of the component of the [ControlsProviderService] with
- * which to filter the favorites.
- * @return a list of the favorite controls for the given service. All the elements of the list
- * will have the same [ControlInfo.component] matching the one requested.
+ * @param componentName the name of the service that provides the [Control]
+ * @return a list of the structures that have at least one favorited control
*/
- fun getFavoritesForComponent(componentName: ComponentName): List
+ fun getFavoritesForComponent(componentName: ComponentName): List
/**
- * Replaces the favorites for the given component.
+ * Replaces the favorites for the given structure.
*
* Calling this method will eliminate the previous selection of favorites and replace it with a
* new one.
*
- * @param componentName The name of the component for the [ControlsProviderService]
- * @param favorites a list of [ControlInfo] to replace the previous favorites.
+ * @param structureInfo common structure for all of the favorited controls
*/
- fun replaceFavoritesForComponent(componentName: ComponentName, favorites: List)
-
- /**
- * Change the favorite status of a single [Control].
- *
- * If the control is added to favorites, it will be added to the end of the list for that
- * particular component. Matching for removing the control will be done based on
- * [ControlInfo.component] and [ControlInfo.controlId].
- *
- * Trying to add an already favorite control or trying to remove one that is not a favorite is
- * a no-op.
- *
- * @param controlInfo persistent information about the [Control].
- * @param state `true` to add to favorites and `false` to remove.
- */
- fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean)
+ fun replaceFavoritesForStructure(structureInfo: StructureInfo)
/**
* Return the number of favorites for a given component.
@@ -160,14 +143,6 @@ interface ControlsController : UserAwareController {
*/
fun countFavoritesForComponent(componentName: ComponentName): Int
- /**
- * Clears the list of all favorites.
- *
- * To clear the list of favorites for a given service, call [replaceFavoritesForComponent] with
- * an empty list.
- */
- fun clearFavorites()
-
/**
* Interface for structure to pass data to [ControlsFavoritingActivity].
*/
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index b97424985b5ab..dedd341a46beb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -31,9 +31,7 @@ import android.os.UserHandle
import android.provider.Settings
import android.service.controls.Control
import android.service.controls.actions.ControlAction
-import android.util.ArrayMap
import android.util.Log
-import com.android.internal.annotations.GuardedBy
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -72,12 +70,6 @@ class ControlsControllerImpl @Inject constructor (
private const val DEFAULT_ENABLED = 1
}
- // Map of map: ComponentName -> (String -> ControlInfo).
- //
- @GuardedBy("currentFavorites")
- private val currentFavorites = ArrayMap>()
- .withDefault { mutableListOf() }
-
private var userChanging: Boolean = true
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -109,12 +101,7 @@ class ControlsControllerImpl @Inject constructor (
persistenceWrapper.changeFile(fileName)
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
DEFAULT_ENABLED, newUser.identifier) != 0
- synchronized(currentFavorites) {
- currentFavorites.clear()
- }
- if (available) {
- loadFavorites()
- }
+ resetFavorites(available)
bindingController.changeUser(newUser)
listingController.changeUser(newUser)
userChanging = false
@@ -146,12 +133,7 @@ class ControlsControllerImpl @Inject constructor (
}
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
DEFAULT_ENABLED, currentUserId) != 0
- synchronized(currentFavorites) {
- currentFavorites.clear()
- }
- if (available) {
- loadFavorites()
- }
+ resetFavorites(available)
}
}
@@ -163,21 +145,23 @@ class ControlsControllerImpl @Inject constructor (
* If some component has been removed, the new set of favorites will also be saved.
*/
private val listingCallback = object : ControlsListingController.ControlsListingCallback {
- override fun onServicesUpdated(candidates: List) {
+ override fun onServicesUpdated(serviceInfos: List) {
executor.execute {
- val candidateComponents = candidates.map(ControlsServiceInfo::componentName)
- synchronized(currentFavorites) {
- val components = currentFavorites.keys.toSet() // create a copy
- components.forEach {
- if (it !in candidateComponents) {
- currentFavorites.remove(it)
- bindingController.onComponentRemoved(it)
- }
- }
- // Check if something has been removed, if so, store the new list
- if (components.size > currentFavorites.size) {
- persistenceWrapper.storeFavorites(favoritesAsListLocked())
- }
+ val serviceInfoSet = serviceInfos.map(ControlsServiceInfo::componentName).toSet()
+ val favoriteComponentSet = Favorites.getAllStructures().map {
+ it.componentName
+ }.toSet()
+
+ var changed = false
+ favoriteComponentSet.subtract(serviceInfoSet).forEach {
+ changed = true
+ Favorites.removeStructures(it)
+ bindingController.onComponentRemoved(it)
+ }
+
+ // Check if something has been removed, if so, store the new list
+ if (changed) {
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
}
}
}
@@ -185,9 +169,7 @@ class ControlsControllerImpl @Inject constructor (
init {
dumpManager.registerDumpable(javaClass.name, this)
- if (available) {
- loadFavorites()
- }
+ resetFavorites(available)
userChanging = false
broadcastDispatcher.registerReceiver(
userSwitchReceiver,
@@ -198,6 +180,15 @@ class ControlsControllerImpl @Inject constructor (
contentResolver.registerContentObserver(URI, false, settingObserver, UserHandle.USER_ALL)
}
+ private fun resetFavorites(shouldLoad: Boolean) {
+ Favorites.clear()
+
+ if (shouldLoad) {
+ Favorites.load(persistenceWrapper.readFavorites())
+ listingController.addCallback(listingCallback)
+ }
+ }
+
private fun confirmAvailability(): Boolean {
if (userChanging) {
Log.w(TAG, "Controls not available while user is changing")
@@ -210,16 +201,6 @@ class ControlsControllerImpl @Inject constructor (
return true
}
- private fun loadFavorites() {
- val infos = persistenceWrapper.readFavorites()
- synchronized(currentFavorites) {
- infos.forEach {
- currentFavorites.getOrPut(it.component, { mutableListOf() }).add(it)
- }
- }
- listingController.addCallback(listingCallback)
- }
-
override fun loadForComponent(
componentName: ComponentName,
dataCallback: Consumer
@@ -242,41 +223,41 @@ class ControlsControllerImpl @Inject constructor (
componentName,
object : ControlsBindingController.LoadCallback {
override fun accept(controls: List) {
- val loadData = synchronized(currentFavorites) {
- val favoritesForComponentKeys: List =
- currentFavorites.getValue(componentName).map { it.controlId }
- val changed = updateFavoritesLocked(componentName, controls,
- favoritesForComponentKeys)
+ executor.execute {
+ val favoritesForComponentKeys = Favorites
+ .getControlsForComponent(componentName).map { it.controlId }
+
+ val changed = Favorites.updateControls(componentName, controls)
if (changed) {
- persistenceWrapper.storeFavorites(favoritesAsListLocked())
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
}
- val removed = findRemovedLocked(favoritesForComponentKeys.toSet(),
- controls)
+ val removed = findRemoved(favoritesForComponentKeys.toSet(), controls)
val controlsWithFavorite = controls.map {
ControlStatus(it, it.controlId in favoritesForComponentKeys)
}
- createLoadDataObject(
- currentFavorites.getValue(componentName)
- .filter { it.controlId in removed }
- .map { createRemovedStatus(it) } +
- controlsWithFavorite,
- favoritesForComponentKeys
+ val loadData = createLoadDataObject(
+ Favorites.getControlsForComponent(componentName)
+ .filter { it.controlId in removed }
+ .map { createRemovedStatus(componentName, it) } +
+ controlsWithFavorite,
+ favoritesForComponentKeys
)
+
+ dataCallback.accept(loadData)
}
- dataCallback.accept(loadData)
}
override fun error(message: String) {
- val loadData = synchronized(currentFavorites) {
- val favoritesForComponent = currentFavorites.getValue(componentName)
- val favoritesForComponentKeys = favoritesForComponent
- .map { it.controlId }
- createLoadDataObject(
- favoritesForComponent.map { createRemovedStatus(it, false) },
- favoritesForComponentKeys,
+ val loadData = Favorites.getControlsForComponent(componentName).let {
+ controls ->
+ val keys = controls.map { it.controlId }
+ createLoadDataObject(
+ controls.map { createRemovedStatus(componentName, it, false) },
+ keys,
true
- )
+ )
}
+
dataCallback.accept(loadData)
}
}
@@ -284,15 +265,16 @@ class ControlsControllerImpl @Inject constructor (
}
private fun createRemovedStatus(
+ componentName: ComponentName,
controlInfo: ControlInfo,
setRemoved: Boolean = true
): ControlStatus {
val intent = Intent(Intent.ACTION_MAIN).apply {
addCategory(Intent.CATEGORY_LAUNCHER)
- this.`package` = controlInfo.component.packageName
+ this.`package` = componentName.packageName
}
val pendingIntent = PendingIntent.getActivity(context,
- controlInfo.component.hashCode(),
+ componentName.hashCode(),
intent,
0)
val control = Control.StatelessBuilder(controlInfo.controlId, pendingIntent)
@@ -302,50 +284,15 @@ class ControlsControllerImpl @Inject constructor (
return ControlStatus(control, true, setRemoved)
}
- @GuardedBy("currentFavorites")
- private fun findRemovedLocked(favoriteKeys: Set, list: List): Set {
+ private fun findRemoved(favoriteKeys: Set, list: List): Set {
val controlsKeys = list.map { it.controlId }
return favoriteKeys.minus(controlsKeys)
}
- @GuardedBy("currentFavorites")
- private fun updateFavoritesLocked(
- componentName: ComponentName,
- list: List,
- favoriteKeys: List
- ): Boolean {
- val favorites = currentFavorites.get(componentName) ?: mutableListOf()
- if (favoriteKeys.isEmpty()) return false // early return
- var changed = false
- list.forEach { control ->
- if (control.controlId in favoriteKeys) {
- val index = favorites.indexOfFirst { it.controlId == control.controlId }
- val value = favorites[index]
- if (value.controlTitle != control.title ||
- value.deviceType != control.deviceType) {
- favorites[index] = value.copy(
- controlTitle = control.title,
- deviceType = control.deviceType
- )
- changed = true
- }
- }
- }
- return changed
- }
-
- @GuardedBy("currentFavorites")
- private fun favoritesAsListLocked(): List {
- return currentFavorites.flatMap { it.value }
- }
-
- override fun subscribeToFavorites() {
+ override fun subscribeToFavorites(structureInfo: StructureInfo) {
if (!confirmAvailability()) return
- // Make a copy of the favorites list
- val favorites = synchronized(currentFavorites) {
- currentFavorites.flatMap { it.value }
- }
- bindingController.subscribe(favorites)
+
+ bindingController.subscribe(structureInfo)
}
override fun unsubscribe() {
@@ -353,44 +300,12 @@ class ControlsControllerImpl @Inject constructor (
bindingController.unsubscribe()
}
- override fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean) {
+ override fun replaceFavoritesForStructure(structureInfo: StructureInfo) {
if (!confirmAvailability()) return
- var changed = false
- val listOfControls = synchronized(currentFavorites) {
- if (state) {
- if (controlInfo.component !in currentFavorites) {
- currentFavorites.put(controlInfo.component, mutableListOf())
- changed = true
- }
- val controlsForComponent = currentFavorites.getValue(controlInfo.component)
- if (controlsForComponent.firstOrNull {
- it.controlId == controlInfo.controlId
- } == null) {
- controlsForComponent.add(controlInfo)
- changed = true
- }
- } else {
- changed = currentFavorites.get(controlInfo.component)
- ?.remove(controlInfo) != null
- }
- favoritesAsListLocked()
+ executor.execute {
+ Favorites.replaceControls(structureInfo)
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
}
- if (changed) {
- persistenceWrapper.storeFavorites(listOfControls)
- }
- }
-
- override fun replaceFavoritesForComponent(
- componentName: ComponentName,
- favorites: List
- ) {
- if (!confirmAvailability()) return
- val filtered = favorites.filter { it.component == componentName }
- val listOfControls = synchronized(currentFavorites) {
- currentFavorites.put(componentName, filtered.toMutableList())
- favoritesAsListLocked()
- }
- persistenceWrapper.storeFavorites(listOfControls)
}
override fun refreshStatus(componentName: ComponentName, control: Control) {
@@ -399,17 +314,12 @@ class ControlsControllerImpl @Inject constructor (
return
}
executor.execute {
- synchronized(currentFavorites) {
- val favoriteKeysForComponent =
- currentFavorites.get(componentName)?.map { it.controlId } ?: emptyList()
- val changed = updateFavoritesLocked(
- componentName,
- listOf(control),
- favoriteKeysForComponent
- )
- if (changed) {
- persistenceWrapper.storeFavorites(favoritesAsListLocked())
- }
+ val changed = Favorites.updateControls(
+ componentName,
+ listOf(control)
+ )
+ if (changed) {
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
}
}
uiController.onRefreshState(componentName, listOf(control))
@@ -420,41 +330,22 @@ class ControlsControllerImpl @Inject constructor (
uiController.onActionResponse(componentName, controlId, response)
}
- override fun getFavoriteControls(): List {
- if (!confirmAvailability()) return emptyList()
- synchronized(currentFavorites) {
- return favoritesAsListLocked()
- }
- }
-
- override fun action(controlInfo: ControlInfo, action: ControlAction) {
+ override fun action(
+ componentName: ComponentName,
+ controlInfo: ControlInfo,
+ action: ControlAction
+ ) {
if (!confirmAvailability()) return
- bindingController.action(controlInfo, action)
+ bindingController.action(componentName, controlInfo, action)
}
- override fun clearFavorites() {
- if (!confirmAvailability()) return
- val changed = synchronized(currentFavorites) {
- currentFavorites.isNotEmpty().also {
- currentFavorites.clear()
- }
- }
- if (changed) {
- persistenceWrapper.storeFavorites(emptyList())
- }
- }
+ override fun getFavorites(): List = Favorites.getAllStructures()
- override fun countFavoritesForComponent(componentName: ComponentName): Int {
- return synchronized(currentFavorites) {
- currentFavorites.get(componentName)?.size ?: 0
- }
- }
+ override fun countFavoritesForComponent(componentName: ComponentName): Int =
+ Favorites.getControlsForComponent(componentName).size
- override fun getFavoritesForComponent(componentName: ComponentName): List {
- return synchronized(currentFavorites) {
- currentFavorites.get(componentName) ?: emptyList()
- }
- }
+ override fun getFavoritesForComponent(componentName: ComponentName): List =
+ Favorites.getStructuresForComponent(componentName)
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array) {
pw.println("ControlsController state:")
@@ -462,11 +353,114 @@ class ControlsControllerImpl @Inject constructor (
pw.println(" Changing users: $userChanging")
pw.println(" Current user: ${currentUser.identifier}")
pw.println(" Favorites:")
- synchronized(currentFavorites) {
- favoritesAsListLocked().forEach {
- pw.println(" ${ it }")
+ Favorites.getAllStructures().forEach { s ->
+ pw.println(" ${ s }")
+ s.controls.forEach { c ->
+ pw.println(" ${ c }")
}
}
pw.println(bindingController.toString())
}
-}
\ No newline at end of file
+}
+
+/**
+ * Relies on immutable data for thread safety. When necessary to update favMap, use reassignment to
+ * replace it, which will not disrupt any ongoing map traversal.
+ *
+ * Update/replace calls should use thread isolation to avoid race conditions.
+ */
+private object Favorites {
+ private var favMap = mapOf>()
+
+ fun getAllStructures(): List = favMap.flatMap { it.value }
+
+ fun getStructuresForComponent(componentName: ComponentName): List =
+ favMap.get(componentName) ?: emptyList()
+
+ fun getControlsForStructure(structure: StructureInfo): List =
+ getStructuresForComponent(structure.componentName)
+ .firstOrNull { it.structure == structure.structure }
+ ?.controls ?: emptyList()
+
+ fun getControlsForComponent(componentName: ComponentName): List =
+ getStructuresForComponent(componentName).flatMap { it.controls }
+
+ fun load(structures: List) {
+ favMap = structures.groupBy { it.componentName }
+ }
+
+ fun updateControls(componentName: ComponentName, controls: List): Boolean {
+ val controlsById = controls.associateBy { it.controlId }
+
+ // utilize a new map to allow for changes to structure names
+ val structureToControls = mutableMapOf>()
+
+ // Must retain the current control order within each structure
+ var changed = false
+ getStructuresForComponent(componentName).forEach { s ->
+ s.controls.forEach { c ->
+ val (sName, ci) = controlsById.get(c.controlId)?.let { updatedControl ->
+ val controlInfo = if (updatedControl.title != c.controlTitle ||
+ updatedControl.deviceType != c.deviceType) {
+ changed = true
+ c.copy(
+ controlTitle = updatedControl.title,
+ deviceType = updatedControl.deviceType
+ )
+ } else { c }
+
+ val updatedStructure = updatedControl.structure ?: ""
+ if (s.structure != updatedStructure) {
+ changed = true
+ }
+
+ Pair(updatedStructure, controlInfo)
+ } ?: Pair(s.structure, c)
+
+ structureToControls.getOrPut(sName, { mutableListOf() }).add(ci)
+ }
+ }
+ if (!changed) return false
+
+ val structures = structureToControls.map { (s, cs) -> StructureInfo(componentName, s, cs) }
+
+ val newFavMap = favMap.toMutableMap()
+ newFavMap.put(componentName, structures)
+ favMap = newFavMap
+
+ return true
+ }
+
+ fun removeStructures(componentName: ComponentName) {
+ val newFavMap = favMap.toMutableMap()
+ newFavMap.remove(componentName)
+ favMap = newFavMap
+ }
+
+ fun replaceControls(updatedStructure: StructureInfo) {
+ val newFavMap = favMap.toMutableMap()
+ val structures = mutableListOf()
+ val componentName = updatedStructure.componentName
+
+ var replaced = false
+ getStructuresForComponent(componentName).forEach { s ->
+ val newStructure = if (s.structure == updatedStructure.structure) {
+ replaced = true
+ updatedStructure
+ } else { s }
+
+ structures.add(newStructure)
+ }
+
+ if (!replaced) {
+ structures.add(updatedStructure)
+ }
+
+ newFavMap.put(componentName, structures.toList())
+ favMap = newFavMap.toMap()
+ }
+
+ fun clear() {
+ favMap = mapOf>()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
index 883f8a93d9100..4bea6ef3d7b9a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
@@ -45,11 +45,17 @@ class ControlsFavoritePersistenceWrapper(
private const val TAG = "ControlsFavoritePersistenceWrapper"
const val FILE_NAME = "controls_favorites.xml"
private const val TAG_CONTROLS = "controls"
+ private const val TAG_STRUCTURES = "structures"
+ private const val TAG_STRUCTURE = "structure"
private const val TAG_CONTROL = "control"
private const val TAG_COMPONENT = "component"
private const val TAG_ID = "id"
private const val TAG_TITLE = "title"
private const val TAG_TYPE = "type"
+ private const val TAG_VERSION = "version"
+
+ // must increment with every change to the XML structure
+ private const val VERSION = 1
}
/**
@@ -66,7 +72,7 @@ class ControlsFavoritePersistenceWrapper(
*
* @param list a list of favorite controls. The list will be stored in the same order.
*/
- fun storeFavorites(list: List) {
+ fun storeFavorites(structures: List) {
executor.execute {
Log.d(TAG, "Saving data to file: $file")
val atomicFile = AtomicFile(file)
@@ -81,16 +87,28 @@ class ControlsFavoritePersistenceWrapper(
setOutput(writer, "utf-8")
setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true)
startDocument(null, true)
- startTag(null, TAG_CONTROLS)
- list.forEach {
- startTag(null, TAG_CONTROL)
- attribute(null, TAG_COMPONENT, it.component.flattenToString())
- attribute(null, TAG_ID, it.controlId)
- attribute(null, TAG_TITLE, it.controlTitle.toString())
- attribute(null, TAG_TYPE, it.deviceType.toString())
- endTag(null, TAG_CONTROL)
+ startTag(null, TAG_VERSION)
+ text("$VERSION")
+ endTag(null, TAG_VERSION)
+
+ startTag(null, TAG_STRUCTURES)
+ structures.forEach { s ->
+ startTag(null, TAG_STRUCTURE)
+ attribute(null, TAG_COMPONENT, s.componentName.flattenToString())
+ attribute(null, TAG_STRUCTURE, s.structure.toString())
+
+ startTag(null, TAG_CONTROLS)
+ s.controls.forEach { c ->
+ startTag(null, TAG_CONTROL)
+ attribute(null, TAG_ID, c.controlId)
+ attribute(null, TAG_TITLE, c.controlTitle.toString())
+ attribute(null, TAG_TYPE, c.deviceType.toString())
+ endTag(null, TAG_CONTROL)
+ }
+ endTag(null, TAG_CONTROLS)
+ endTag(null, TAG_STRUCTURE)
}
- endTag(null, TAG_CONTROLS)
+ endTag(null, TAG_STRUCTURES)
endDocument()
atomicFile.finishWrite(writer)
}
@@ -109,7 +127,7 @@ class ControlsFavoritePersistenceWrapper(
* @return a list of stored favorite controls. Return an empty list if the file is not found
* @throws [IllegalStateException] if there is an error while reading the file
*/
- fun readFavorites(): List {
+ fun readFavorites(): List {
if (!file.exists()) {
Log.d(TAG, "No favorites, returning empty list")
return emptyList()
@@ -134,25 +152,32 @@ class ControlsFavoritePersistenceWrapper(
}
}
- private fun parseXml(parser: XmlPullParser): List {
+ private fun parseXml(parser: XmlPullParser): List {
var type: Int
- val infos = mutableListOf()
+ val infos = mutableListOf()
+
+ var lastComponent: ComponentName? = null
+ var lastStructure: CharSequence? = null
+ var controls = mutableListOf()
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue
- }
- val tagName = parser.name
- if (tagName == TAG_CONTROL) {
- val component = ComponentName.unflattenFromString(
- parser.getAttributeValue(null, TAG_COMPONENT))
+ val tagName = parser.name ?: ""
+ if (type == XmlPullParser.START_TAG && tagName == TAG_STRUCTURE) {
+ lastComponent = ComponentName.unflattenFromString(
+ parser.getAttributeValue(null, TAG_COMPONENT))
+ lastStructure = parser.getAttributeValue(null, TAG_STRUCTURE) ?: ""
+ } else if (type == XmlPullParser.START_TAG && tagName == TAG_CONTROL) {
val id = parser.getAttributeValue(null, TAG_ID)
val title = parser.getAttributeValue(null, TAG_TITLE)
val deviceType = parser.getAttributeValue(null, TAG_TYPE)?.toInt()
- if (component != null && id != null && title != null && deviceType != null) {
- infos.add(ControlInfo(component, id, title, deviceType))
+ if (id != null && title != null && deviceType != null) {
+ controls.add(ControlInfo(id, title, deviceType))
}
+ } else if (type == XmlPullParser.END_TAG && tagName == TAG_STRUCTURE) {
+ infos.add(StructureInfo(lastComponent!!, lastStructure!!, controls.toList()))
+ controls.clear()
}
}
+
return infos
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index a53fcd498236a..86e8e834faf10 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -96,29 +96,30 @@ class ControlsProviderLifecycleManager(
}
private fun bindService(bind: Boolean) {
- requiresBound = bind
- if (bind) {
- if (bindTryCount == MAX_BIND_RETRIES) {
- return
+ executor.execute {
+ requiresBound = bind
+ if (bind) {
+ if (bindTryCount != MAX_BIND_RETRIES) {
+ if (DEBUG) {
+ Log.d(TAG, "Binding service $intent")
+ }
+ bindTryCount++
+ try {
+ context.bindServiceAsUser(intent, serviceConnection, BIND_FLAGS, user)
+ } catch (e: SecurityException) {
+ Log.e(TAG, "Failed to bind to service", e)
+ }
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Unbinding service $intent")
+ }
+ bindTryCount = 0
+ wrapper?.run {
+ context.unbindService(serviceConnection)
+ }
+ wrapper = null
}
- if (DEBUG) {
- Log.d(TAG, "Binding service $intent")
- }
- bindTryCount++
- try {
- context.bindServiceAsUser(intent, serviceConnection, BIND_FLAGS, user)
- } catch (e: SecurityException) {
- Log.e(TAG, "Failed to bind to service", e)
- }
- } else {
- if (DEBUG) {
- Log.d(TAG, "Unbinding service $intent")
- }
- bindTryCount = 0
- wrapper?.run {
- context.unbindService(serviceConnection)
- }
- wrapper = null
}
}
@@ -320,6 +321,9 @@ class ControlsProviderLifecycleManager(
onLoadCanceller?.run()
onLoadCanceller = null
+ // just in case this wasn't called already
+ unsubscribe()
+
bindService(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/StructureInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/StructureInfo.kt
new file mode 100644
index 0000000000000..34bfa135f0c85
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/StructureInfo.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.controller
+
+import android.content.ComponentName
+
+/**
+ * Stores basic information about a Structure to persist and keep track of favorites.
+ *
+ * Every [component] [structure] pair uniquely identifies the structure.
+ *
+ * @property componentName the name of the component that provides the [Control].
+ * @property structure common structure name of all underlying [controls], or empty string
+ * @property controls all controls in the name structure
+ */
+data class StructureInfo(
+ val componentName: ComponentName,
+ val structure: CharSequence,
+ val controls: List
+)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index 855e4a501f0a3..8b3454a2bc7c1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -26,15 +26,13 @@ import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
-import com.android.settingslib.applications.DefaultAppInfo
-import com.android.settingslib.widget.CandidateInfo
import com.android.systemui.R
import com.android.systemui.controls.ControlsServiceInfo
import java.text.Collator
import java.util.concurrent.Executor
/**
- * Adapter for binding [CandidateInfo] related to [ControlsProviderService].
+ * Adapter for binding [ControlsServiceInfo] related to [ControlsProviderService].
*
* This class handles subscribing and keeping track of the list of valid applications for
* displaying.
@@ -56,16 +54,16 @@ class AppAdapter(
private val resources: Resources
) : RecyclerView.Adapter() {
- private var listOfServices = emptyList()
+ private var listOfServices = emptyList()
private val callback = object : ControlsListingController.ControlsListingCallback {
- override fun onServicesUpdated(candidates: List) {
+ override fun onServicesUpdated(serviceInfos: List) {
backgroundExecutor.execute {
val collator = Collator.getInstance(resources.configuration.locales[0])
- val localeComparator = compareBy(collator) {
+ val localeComparator = compareBy(collator) {
it.loadLabel()
}
- listOfServices = candidates.sortedWith(localeComparator)
+ listOfServices = serviceInfos.sortedWith(localeComparator)
uiExecutor.execute(::notifyDataSetChanged)
}
}
@@ -101,11 +99,10 @@ class AppAdapter(
* Bind data to the view
* @param data Information about the [ControlsProviderService] to bind to the data
*/
- fun bindData(data: CandidateInfo) {
+ fun bindData(data: ControlsServiceInfo) {
icon.setImageDrawable(data.loadIcon())
title.text = data.loadLabel()
- favorites.text = favRenderer.renderFavoritesForComponent(
- (data as DefaultAppInfo).componentName)
+ favorites.text = favRenderer.renderFavoritesForComponent(data.componentName)
}
}
}
@@ -123,4 +120,4 @@ class FavoritesRenderer(
return ""
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 2c014498fdc29..08a1a50001122 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -29,6 +29,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.CurrentUserTracker
@@ -52,6 +53,7 @@ class ControlsFavoritingActivity @Inject constructor(
private lateinit var statusText: TextView
private var model: ControlsModel? = null
private var component: ComponentName? = null
+ private var structureName: CharSequence = ""
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId
@@ -97,11 +99,11 @@ class ControlsFavoritingActivity @Inject constructor(
requireViewById