diff options
11 files changed, 446 insertions, 168 deletions
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml index a7379bedebef..6533c18a41a9 100644 --- a/packages/SystemUI/res/layout/controls_management.xml +++ b/packages/SystemUI/res/layout/controls_management.xml @@ -70,12 +70,14 @@ android:layout_height="match_parent" android:padding="4dp"> - <TextView + <Button + android:id="@+id/other_apps" + android:visibility="gone" android:layout_width="wrap_content" android:layout_height="match_parent" + android:gravity="center_vertical" android:text="See other apps" - android:textAppearance="@style/TextAppearance.Control.Title" - android:textColor="?android:attr/colorPrimary" + style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"/> @@ -85,6 +87,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:text="Done" + style="@*android:style/Widget.DeviceDefault.Button.Colored" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"/> diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml index 62056e60372d..aab32f45e77a 100644 --- a/packages/SystemUI/res/layout/controls_management_favorites.xml +++ b/packages/SystemUI/res/layout/controls_management_favorites.xml @@ -14,97 +14,26 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<androidx.constraintlayout.widget.ConstraintLayout +<LinearLayout 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_height="match_parent" android:orientation="vertical"> <TextView - android:id="@+id/error_message" + android:id="@+id/status_message" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/controls_management_list_margin" - android:text="@string/controls_favorite_load_error" android:textAppearance="?android:attr/textAppearanceSmall" - android:visibility="gone" 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 android:id="@+id/listAll" 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_constraintTop_toBottomOf="@id/divider2"/> + android:nestedScrollingEnabled="false"/> -</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 4aafec886a37..ff28b4d289e8 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2614,8 +2614,8 @@ <string name="controls_providers_subtitle">Choose an app from which to add controls</string> <!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]--> <plurals name="controls_number_of_favorites"> - <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> current favorite.</item> - <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> current favorites.</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> controls added.</item> </plurals> <!-- Controls management controls screen default title [CHAR LIMIT=30] --> @@ -2626,6 +2626,10 @@ <string name="controls_favorite_header_favorites">Favorites</string> <!-- Controls management controls screen all header [CHAR LIMIT=50] --> <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> + <!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] --> + <string name="controls_favorite_other_zone_header">Other</string> + + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt new file mode 100644 index 000000000000..c05351795aed --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt @@ -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 + } + } +}
\ No newline at end of file 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 ac5e0893b526..25ebc65357ee 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt @@ -116,6 +116,10 @@ class FavoritesRenderer( fun renderFavoritesForComponent(component: ComponentName): String { 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 "" + } } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index d3cabe67790e..0870a4d179c9 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -42,8 +42,7 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit * @param onlyFavorites set to true to only display favorites instead of all controls */ class ControlAdapter( - private val layoutInflater: LayoutInflater, - private val onlyFavorites: Boolean = false + private val layoutInflater: LayoutInflater ) : RecyclerView.Adapter<Holder>() { companion object { @@ -57,22 +56,21 @@ class ControlAdapter( } } - var modelList: List<ElementWrapper> = emptyList() - private var favoritesModel: FavoriteModel? = null + private var model: ControlsModel? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { return when (viewType) { TYPE_CONTROL -> { ControlHolder( - layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { - layoutParams.apply { - width = ViewGroup.LayoutParams.MATCH_PARENT - } - elevation = 15f - }, - { id, favorite -> - favoritesModel?.changeFavoriteStatus(id, favorite) - }) + layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { + layoutParams.apply { + width = ViewGroup.LayoutParams.MATCH_PARENT + } + elevation = 15f + } + ) { id, favorite -> + model?.changeFavoriteStatus(id, favorite) + } } TYPE_ZONE -> { ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false)) @@ -81,27 +79,26 @@ class ControlAdapter( } } - fun changeFavoritesModel(favoritesModel: FavoriteModel) { - this.favoritesModel = favoritesModel - if (onlyFavorites) { - modelList = favoritesModel.favorites - } else { - modelList = favoritesModel.all - } + fun changeModel(model: ControlsModel) { + this.model = model notifyDataSetChanged() } - override fun getItemCount() = modelList.size + override fun getItemCount() = model?.elements?.size ?: 0 override fun onBindViewHolder(holder: Holder, index: Int) { - holder.bindData(modelList[index]) + model?.let { + holder.bindData(it.elements[index]) + } } override fun getItemViewType(position: Int): Int { - return when (modelList[position]) { - is ZoneNameWrapper -> TYPE_ZONE - is ControlWrapper -> TYPE_CONTROL - } + model?.let { + return when (it.elements.get(position)) { + is ZoneNameWrapper -> TYPE_ZONE + is ControlWrapper -> TYPE_CONTROL + } + } ?: throw IllegalStateException("Getting item type for null model") } } 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 af4a977022ae..2c014498fdc2 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -26,11 +26,9 @@ import android.view.ViewStub import android.widget.Button import android.widget.TextView import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.settings.CurrentUserTracker @@ -51,33 +49,10 @@ class ControlsFavoritingActivity @Inject constructor( private lateinit var recyclerViewAll: RecyclerView private lateinit var adapterAll: ControlAdapter - private lateinit var recyclerViewFavorites: RecyclerView - private lateinit var adapterFavorites: ControlAdapter - private lateinit var errorText: TextView + private lateinit var statusText: TextView + private var model: ControlsModel? = 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 startingUser = controller.currentUserId @@ -89,6 +64,10 @@ class ControlsFavoritingActivity @Inject constructor( } } + override fun onBackPressed() { + finish() + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.controls_management) @@ -99,21 +78,27 @@ class ControlsFavoritingActivity @Inject constructor( val app = intent.getCharSequenceExtra(EXTRA_APP) 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 } ?: resources.getText(R.string.controls_favorite_default_title) requireViewById<TextView>(R.id.subtitle).text = 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 { if (component == null) return@setOnClickListener - val favoritesForStorage = currentModel?.favorites?.map { - with(it.controlStatus.control) { - ControlInfo(component!!, controlId, title, deviceType) - } + val favoritesForStorage = model?.favorites?.map { + it.componentName = component!! + it.build() } if (favoritesForStorage != null) { controller.replaceFavoritesForComponent(component!!, favoritesForStorage) @@ -122,20 +107,22 @@ class ControlsFavoritingActivity @Inject constructor( } component?.let { + statusText.text = resources.getText(com.android.internal.R.string.loading) controller.loadForComponent(it, Consumer { data -> val allControls = data.allControls val favoriteKeys = data.favoritesIds val error = data.errorOnLoad executor.execute { - val favoriteModel = FavoriteModel( - allControls, - favoriteKeys, - allAdapter = adapterAll, - favoritesAdapter = adapterFavorites) - adapterAll.changeFavoritesModel(favoriteModel) - adapterFavorites.changeFavoritesModel(favoriteModel) - currentModel = favoriteModel - errorText.visibility = if (error) View.VISIBLE else View.GONE + val emptyZoneString = resources.getText( + R.string.controls_favorite_other_zone_header) + val model = AllModel(allControls, favoriteKeys, emptyZoneString) + adapterAll.changeModel(model) + this.model = model + if (error) { + statusText.text = resources.getText(R.string.controls_favorite_load_error) + } else { + statusText.visibility = View.GONE + } } }) } @@ -143,7 +130,7 @@ class ControlsFavoritingActivity @Inject constructor( currentUserTracker.startTracking() } - private fun setUpRecyclerViews() { + private fun setUpRecyclerView() { val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin) val itemDecorator = MarginItemDecorator(margin, margin) val layoutInflater = LayoutInflater.from(applicationContext) @@ -156,14 +143,6 @@ class ControlsFavoritingActivity @Inject constructor( } 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() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt new file mode 100644 index 000000000000..a995a2ebfd25 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt @@ -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()
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt index ad4bdefdff3e..098caf61a873 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt @@ -21,6 +21,7 @@ import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.ViewStub +import android.widget.Button import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -86,6 +87,10 @@ class ControlsProviderSelectorActivity @Inject constructor( requireViewById<TextView>(R.id.subtitle).text = resources.getText(R.string.controls_providers_subtitle) + requireViewById<Button>(R.id.done).setOnClickListener { + this@ControlsProviderSelectorActivity.finishAffinity() + } + currentUserTracker.startTracking() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt index 6bade0aeb998..5c51e3dbe4ac 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt @@ -142,12 +142,4 @@ class CharSequenceComparator : Comparator<CharSequence> { else if (p0 != null && p1 == null) return 1 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()
\ No newline at end of file +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt new file mode 100644 index 000000000000..68e1ec1d9f1d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt @@ -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)) + } + } +}
\ No newline at end of file |