Merge "Changed management to just show all controls" into rvc-dev am: d37dcf856b

Change-Id: Iff7b076cc0fc27a41595d34a9f01f3510e4d05a0
This commit is contained in:
Automerger Merge Worker
2020-02-26 20:21:30 +00:00
11 changed files with 446 additions and 168 deletions

View File

@@ -70,12 +70,14 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="4dp"> android:padding="4dp">
<TextView <Button
android:id="@+id/other_apps"
android:visibility="gone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="See other apps" android:text="See other apps"
android:textAppearance="@style/TextAppearance.Control.Title" style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
android:textColor="?android:attr/colorPrimary"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/> app:layout_constraintStart_toStartOf="parent"/>
@@ -85,6 +87,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:text="Done" android:text="Done"
style="@*android:style/Widget.DeviceDefault.Button.Colored"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintEnd_toEndOf="parent"/>

View File

@@ -14,97 +14,26 @@
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/error_message" android:id="@+id/status_message"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/controls_management_list_margin" android:layout_marginTop="@dimen/controls_management_list_margin"
android:text="@string/controls_favorite_load_error"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:visibility="gone"
android:gravity="center_horizontal" android:gravity="center_horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@id/text_favorites"
/> />
<TextView
android:id="@+id/text_favorites"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/controls_management_list_margin"
android:text="@string/controls_favorite_header_favorites"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAllCaps="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/divider1"
app:layout_constraintTop_toBottomOf="@id/error_message"
/>
<View
android:id="@+id/divider1"
android:layout_width="match_parent"
android:layout_height="@dimen/controls_app_divider_height"
android:layout_gravity="center_horizontal|top"
android:background="?android:attr/listDivider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/listFavorites"
app:layout_constraintTop_toBottomOf="@id/text_favorites"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listFavorites"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/controls_management_list_margin"
android:nestedScrollingEnabled="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/text_all"
app:layout_constraintTop_toBottomOf="@id/divider1"/>
<TextView
android:id="@+id/text_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/controls_management_list_margin"
android:text="@string/controls_favorite_header_all"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAllCaps="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/divider2"
app:layout_constraintTop_toBottomOf="@id/listFavorites"
/>
<View
android:id="@+id/divider2"
android:layout_width="match_parent"
android:layout_height="@dimen/controls_app_divider_height"
android:layout_gravity="center_horizontal|top"
android:background="?android:attr/listDivider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/listAll"
app:layout_constraintTop_toBottomOf="@id/text_all"
/>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/listAll" android:id="@+id/listAll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="@dimen/controls_management_list_margin" android:layout_marginTop="@dimen/controls_management_list_margin"
android:nestedScrollingEnabled="false" android:nestedScrollingEnabled="false"/>
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/divider2"/>
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>

View File

@@ -2614,8 +2614,8 @@
<string name="controls_providers_subtitle">Choose an app from which to add controls</string> <string name="controls_providers_subtitle">Choose an app from which to add controls</string>
<!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]--> <!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]-->
<plurals name="controls_number_of_favorites"> <plurals name="controls_number_of_favorites">
<item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> current favorite.</item> <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> control added.</item>
<item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> current favorites.</item> <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item>
</plurals> </plurals>
<!-- Controls management controls screen default title [CHAR LIMIT=30] --> <!-- Controls management controls screen default title [CHAR LIMIT=30] -->
@@ -2626,6 +2626,10 @@
<string name="controls_favorite_header_favorites">Favorites</string> <string name="controls_favorite_header_favorites">Favorites</string>
<!-- Controls management controls screen all header [CHAR LIMIT=50] --> <!-- Controls management controls screen all header [CHAR LIMIT=50] -->
<string name="controls_favorite_header_all">All</string> <string name="controls_favorite_header_all">All</string>
<!-- Controls management controls screen error on load message [CHAR LIMIT=50] --> <!-- Controls management controls screen error on load message [CHAR LIMIT=60] -->
<string name="controls_favorite_load_error">The list of all controls could not be loaded.</string> <string name="controls_favorite_load_error">The list of all controls could not be loaded.</string>
<!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] -->
<string name="controls_favorite_other_zone_header">Other</string>
</resources> </resources>

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2019 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.management
import android.text.TextUtils
import android.util.ArrayMap
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.controller.ControlInfo
/**
* This model is used to show all controls separated by zones.
*
* The model will sort the controls and zones in the following manner:
* * The zones will be sorted in a first seen basis
* * The controls in each zone will be sorted in a first seen basis.
*
* @property controls List of all controls as returned by loading
* @property initialFavoriteIds sorted ids of favorite controls
* @property noZoneString text to use as header for all controls that have blank or `null` zone.
*/
class AllModel(
private val controls: List<ControlStatus>,
initialFavoriteIds: List<String>,
private val emptyZoneString: CharSequence
) : ControlsModel {
override val favorites: List<ControlInfo.Builder>
get() = favoriteIds.mapNotNull { id ->
val control = controls.firstOrNull { it.control.controlId == id }?.control
control?.let {
ControlInfo.Builder().apply {
controlId = it.controlId
controlTitle = it.title
deviceType = it.deviceType
}
}
}
private val favoriteIds = initialFavoriteIds.toMutableList()
override val elements: List<ElementWrapper> = createWrappers(controls)
override fun changeFavoriteStatus(controlId: String, favorite: Boolean) {
if (favorite) {
favoriteIds.add(controlId)
} else {
favoriteIds.remove(controlId)
}
}
private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> {
val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) {
it.control.zone ?: ""
}
val output = mutableListOf<ElementWrapper>()
var emptyZoneValues: Sequence<ControlWrapper>? = null
for (zoneName in map.orderedKeys) {
val values = map.getValue(zoneName).asSequence().map { ControlWrapper(it) }
if (TextUtils.isEmpty(zoneName)) {
emptyZoneValues = values
} else {
output.add(ZoneNameWrapper(zoneName))
output.addAll(values)
}
}
// Add controls with empty zone at the end
if (emptyZoneValues != null) {
if (map.size != 1) {
output.add(ZoneNameWrapper(emptyZoneString))
}
output.addAll(emptyZoneValues)
}
return output
}
private class OrderedMap<K, V>(private val map: MutableMap<K, V>) : MutableMap<K, V> by map {
val orderedKeys = mutableListOf<K>()
override fun put(key: K, value: V): V? {
if (key !in map) {
orderedKeys.add(key)
}
return map.put(key, value)
}
override fun clear() {
orderedKeys.clear()
map.clear()
}
override fun remove(key: K): V? {
val removed = map.remove(key)
if (removed != null) {
orderedKeys.remove(key)
}
return removed
}
}
}

View File

@@ -116,6 +116,10 @@ class FavoritesRenderer(
fun renderFavoritesForComponent(component: ComponentName): String { fun renderFavoritesForComponent(component: ComponentName): String {
val qty = favoriteFunction(component) val qty = favoriteFunction(component)
return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty) if (qty != 0) {
return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty)
} else {
return ""
}
} }
} }

View File

@@ -42,8 +42,7 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit
* @param onlyFavorites set to true to only display favorites instead of all controls * @param onlyFavorites set to true to only display favorites instead of all controls
*/ */
class ControlAdapter( class ControlAdapter(
private val layoutInflater: LayoutInflater, private val layoutInflater: LayoutInflater
private val onlyFavorites: Boolean = false
) : RecyclerView.Adapter<Holder>() { ) : RecyclerView.Adapter<Holder>() {
companion object { companion object {
@@ -57,22 +56,21 @@ class ControlAdapter(
} }
} }
var modelList: List<ElementWrapper> = emptyList() private var model: ControlsModel? = null
private var favoritesModel: FavoriteModel? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
return when (viewType) { return when (viewType) {
TYPE_CONTROL -> { TYPE_CONTROL -> {
ControlHolder( ControlHolder(
layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
layoutParams.apply { layoutParams.apply {
width = ViewGroup.LayoutParams.MATCH_PARENT width = ViewGroup.LayoutParams.MATCH_PARENT
} }
elevation = 15f elevation = 15f
}, }
{ id, favorite -> ) { id, favorite ->
favoritesModel?.changeFavoriteStatus(id, favorite) model?.changeFavoriteStatus(id, favorite)
}) }
} }
TYPE_ZONE -> { TYPE_ZONE -> {
ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false)) ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false))
@@ -81,27 +79,26 @@ class ControlAdapter(
} }
} }
fun changeFavoritesModel(favoritesModel: FavoriteModel) { fun changeModel(model: ControlsModel) {
this.favoritesModel = favoritesModel this.model = model
if (onlyFavorites) {
modelList = favoritesModel.favorites
} else {
modelList = favoritesModel.all
}
notifyDataSetChanged() notifyDataSetChanged()
} }
override fun getItemCount() = modelList.size override fun getItemCount() = model?.elements?.size ?: 0
override fun onBindViewHolder(holder: Holder, index: Int) { override fun onBindViewHolder(holder: Holder, index: Int) {
holder.bindData(modelList[index]) model?.let {
holder.bindData(it.elements[index])
}
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return when (modelList[position]) { model?.let {
is ZoneNameWrapper -> TYPE_ZONE return when (it.elements.get(position)) {
is ControlWrapper -> TYPE_CONTROL is ZoneNameWrapper -> TYPE_ZONE
} is ControlWrapper -> TYPE_CONTROL
}
} ?: throw IllegalStateException("Getting item type for null model")
} }
} }

View File

@@ -26,11 +26,9 @@ import android.view.ViewStub
import android.widget.Button import android.widget.Button
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.settings.CurrentUserTracker
@@ -51,33 +49,10 @@ class ControlsFavoritingActivity @Inject constructor(
private lateinit var recyclerViewAll: RecyclerView private lateinit var recyclerViewAll: RecyclerView
private lateinit var adapterAll: ControlAdapter private lateinit var adapterAll: ControlAdapter
private lateinit var recyclerViewFavorites: RecyclerView private lateinit var statusText: TextView
private lateinit var adapterFavorites: ControlAdapter private var model: ControlsModel? = null
private lateinit var errorText: TextView
private var component: ComponentName? = null private var component: ComponentName? = null
private var currentModel: FavoriteModel? = null
private var itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
/* dragDirs */ ItemTouchHelper.UP
or ItemTouchHelper.DOWN
or ItemTouchHelper.LEFT
or ItemTouchHelper.RIGHT,
/* swipeDirs */0
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return currentModel?.onMoveItem(
viewHolder.layoutPosition, target.layoutPosition) != null
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
override fun isItemViewSwipeEnabled() = false
}
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId private val startingUser = controller.currentUserId
@@ -89,6 +64,10 @@ class ControlsFavoritingActivity @Inject constructor(
} }
} }
override fun onBackPressed() {
finish()
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.controls_management) setContentView(R.layout.controls_management)
@@ -99,21 +78,27 @@ class ControlsFavoritingActivity @Inject constructor(
val app = intent.getCharSequenceExtra(EXTRA_APP) val app = intent.getCharSequenceExtra(EXTRA_APP)
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME) component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
errorText = requireViewById(R.id.error_message) statusText = requireViewById(R.id.status_message)
setUpRecyclerViews() setUpRecyclerView()
requireViewById<TextView>(R.id.title).text = app?.let { it } requireViewById<TextView>(R.id.title).text = app?.let { it }
?: resources.getText(R.string.controls_favorite_default_title) ?: resources.getText(R.string.controls_favorite_default_title)
requireViewById<TextView>(R.id.subtitle).text = requireViewById<TextView>(R.id.subtitle).text =
resources.getText(R.string.controls_favorite_subtitle) resources.getText(R.string.controls_favorite_subtitle)
requireViewById<Button>(R.id.other_apps).apply {
visibility = View.VISIBLE
setOnClickListener {
this@ControlsFavoritingActivity.onBackPressed()
}
}
requireViewById<Button>(R.id.done).setOnClickListener { requireViewById<Button>(R.id.done).setOnClickListener {
if (component == null) return@setOnClickListener if (component == null) return@setOnClickListener
val favoritesForStorage = currentModel?.favorites?.map { val favoritesForStorage = model?.favorites?.map {
with(it.controlStatus.control) { it.componentName = component!!
ControlInfo(component!!, controlId, title, deviceType) it.build()
}
} }
if (favoritesForStorage != null) { if (favoritesForStorage != null) {
controller.replaceFavoritesForComponent(component!!, favoritesForStorage) controller.replaceFavoritesForComponent(component!!, favoritesForStorage)
@@ -122,20 +107,22 @@ class ControlsFavoritingActivity @Inject constructor(
} }
component?.let { component?.let {
statusText.text = resources.getText(com.android.internal.R.string.loading)
controller.loadForComponent(it, Consumer { data -> controller.loadForComponent(it, Consumer { data ->
val allControls = data.allControls val allControls = data.allControls
val favoriteKeys = data.favoritesIds val favoriteKeys = data.favoritesIds
val error = data.errorOnLoad val error = data.errorOnLoad
executor.execute { executor.execute {
val favoriteModel = FavoriteModel( val emptyZoneString = resources.getText(
allControls, R.string.controls_favorite_other_zone_header)
favoriteKeys, val model = AllModel(allControls, favoriteKeys, emptyZoneString)
allAdapter = adapterAll, adapterAll.changeModel(model)
favoritesAdapter = adapterFavorites) this.model = model
adapterAll.changeFavoritesModel(favoriteModel) if (error) {
adapterFavorites.changeFavoritesModel(favoriteModel) statusText.text = resources.getText(R.string.controls_favorite_load_error)
currentModel = favoriteModel } else {
errorText.visibility = if (error) View.VISIBLE else View.GONE statusText.visibility = View.GONE
}
} }
}) })
} }
@@ -143,7 +130,7 @@ class ControlsFavoritingActivity @Inject constructor(
currentUserTracker.startTracking() currentUserTracker.startTracking()
} }
private fun setUpRecyclerViews() { private fun setUpRecyclerView() {
val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin) val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
val itemDecorator = MarginItemDecorator(margin, margin) val itemDecorator = MarginItemDecorator(margin, margin)
val layoutInflater = LayoutInflater.from(applicationContext) val layoutInflater = LayoutInflater.from(applicationContext)
@@ -156,14 +143,6 @@ class ControlsFavoritingActivity @Inject constructor(
} }
addItemDecoration(itemDecorator) addItemDecoration(itemDecorator)
} }
adapterFavorites = ControlAdapter(layoutInflater, true)
recyclerViewFavorites = requireViewById<RecyclerView>(R.id.listFavorites).apply {
layoutManager = GridLayoutManager(applicationContext, 2)
adapter = adapterFavorites
addItemDecoration(itemDecorator)
}
ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerViewFavorites)
} }
override fun onDestroy() { override fun onDestroy() {

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2019 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.management
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.controller.ControlInfo
/**
* Model for using with [ControlAdapter].
*
* Implementations of this interface provide different views of the controls to show.
*/
interface ControlsModel {
/**
* List of favorites (builders) in order.
*
* This should be obtained prior to storing the favorites using
* [ControlsController.replaceFavoritesForComponent].
*/
val favorites: List<ControlInfo.Builder>
/**
* List of all the elements to display by the corresponding [RecyclerView].
*/
val elements: List<ElementWrapper>
/**
* Change the favorite status of a particular control.
*/
fun changeFavoriteStatus(controlId: String, favorite: Boolean) {}
/**
* Move an item (in elements) from one position to another.
*/
fun onMoveItem(from: Int, to: Int) {}
}
/**
* Wrapper classes for the different types of elements shown in the [RecyclerView]s in
* [ControlAdapter].
*/
sealed class ElementWrapper
data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper()
data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()

View File

@@ -21,6 +21,7 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewStub import android.view.ViewStub
import android.widget.Button
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -86,6 +87,10 @@ class ControlsProviderSelectorActivity @Inject constructor(
requireViewById<TextView>(R.id.subtitle).text = requireViewById<TextView>(R.id.subtitle).text =
resources.getText(R.string.controls_providers_subtitle) resources.getText(R.string.controls_providers_subtitle)
requireViewById<Button>(R.id.done).setOnClickListener {
this@ControlsProviderSelectorActivity.finishAffinity()
}
currentUserTracker.startTracking() currentUserTracker.startTracking()
} }

View File

@@ -142,12 +142,4 @@ class CharSequenceComparator : Comparator<CharSequence> {
else if (p0 != null && p1 == null) return 1 else if (p0 != null && p1 == null) return 1
return p0.toString().compareTo(p1.toString()) return p0.toString().compareTo(p1.toString())
} }
} }
/**
* Wrapper classes for the different types of elements shown in the [RecyclerView]s in
* [ControlsFavoritingActivity].
*/
sealed class ElementWrapper
data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper()
data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()

View File

@@ -0,0 +1,192 @@
/*
* Copyright (C) 2019 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.management
import android.app.PendingIntent
import android.service.controls.Control
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.controller.ControlInfo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
class AllModelTest : SysuiTestCase() {
companion object {
private const val EMPTY_STRING = "Other"
}
@Mock
lateinit var pendingIntent: PendingIntent
val idPrefix = "controlId"
val favoritesIndices = listOf(7, 3, 1, 9)
val favoritesList = favoritesIndices.map { "controlId$it" }
lateinit var controls: List<ControlStatus>
lateinit var model: AllModel
private fun zoneMap(id: Int): String? {
return when (id) {
10 -> ""
11 -> null
else -> ((id + 1) % 3).toString()
}
}
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
// controlId0 --> zone = 1
// controlId1 --> zone = 2, favorite
// controlId2 --> zone = 0
// controlId3 --> zone = 1, favorite
// controlId4 --> zone = 2
// controlId5 --> zone = 0
// controlId6 --> zone = 1
// controlId7 --> zone = 2, favorite
// controlId8 --> zone = 0
// controlId9 --> zone = 1, favorite
// controlId10 --> zone = ""
// controlId11 --> zone = null
controls = (0..11).map {
ControlStatus(
Control.StatelessBuilder("$idPrefix$it", pendingIntent)
.setZone(zoneMap(it))
.build(),
it in favoritesIndices
)
}
model = AllModel(controls, favoritesList, EMPTY_STRING)
}
@Test
fun testElements() {
// Zones are sorted by order of appearance, with empty at the end with special header.
val expected = listOf(
ZoneNameWrapper("1"),
ControlWrapper(controls[0]),
ControlWrapper(controls[3]),
ControlWrapper(controls[6]),
ControlWrapper(controls[9]),
ZoneNameWrapper("2"),
ControlWrapper(controls[1]),
ControlWrapper(controls[4]),
ControlWrapper(controls[7]),
ZoneNameWrapper("0"),
ControlWrapper(controls[2]),
ControlWrapper(controls[5]),
ControlWrapper(controls[8]),
ZoneNameWrapper(EMPTY_STRING),
ControlWrapper(controls[10]),
ControlWrapper(controls[11])
)
expected.zip(model.elements).forEachIndexed { index, it ->
assertEquals("Error in item at index $index", it.first, it.second)
}
}
private fun sameControl(controlInfo: ControlInfo.Builder, control: Control): Boolean {
return controlInfo.controlId == control.controlId &&
controlInfo.controlTitle == control.title &&
controlInfo.deviceType == control.deviceType
}
@Test
fun testAllEmpty_noHeader() {
val selected_controls = listOf(controls[10], controls[11])
val new_model = AllModel(selected_controls, emptyList(), EMPTY_STRING)
val expected = listOf(
ControlWrapper(controls[10]),
ControlWrapper(controls[11])
)
expected.zip(new_model.elements).forEachIndexed { index, it ->
assertEquals("Error in item at index $index", it.first, it.second)
}
}
@Test
fun testFavorites() {
val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
model.favorites.zip(expectedFavorites).forEach {
assertTrue(sameControl(it.first, it.second))
}
}
@Test
fun testAddFavorite() {
val indexToAdd = 6
model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control) +
controls[indexToAdd].control
model.favorites.zip(expectedFavorites).forEach {
assertTrue(sameControl(it.first, it.second))
}
}
@Test
fun testAddFavorite_alreadyThere() {
val indexToAdd = 7
model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
model.favorites.zip(expectedFavorites).forEach {
assertTrue(sameControl(it.first, it.second))
}
}
@Test
fun testRemoveFavorite() {
val indexToRemove = 3
model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
val expectedFavorites = (favoritesIndices.filterNot { it == indexToRemove })
.map(controls::get)
.map(ControlStatus::control)
model.favorites.zip(expectedFavorites).forEach {
assertTrue(sameControl(it.first, it.second))
}
}
@Test
fun testRemoveFavorite_notThere() {
val indexToRemove = 4
model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
model.favorites.zip(expectedFavorites).forEach {
assertTrue(sameControl(it.first, it.second))
}
}
}