Add Structure pages to management screens
Pages are sorted alphabetically and show all controls for that structure. The name used is the structure name of the app name for the empty/null structure. Done button saves all favorites selected from all structures, not just the visible one. Test: manual Fixes: 150707923 Change-Id: I8ae822ed5acb6340aa08acb0dc6f3a4e6bb2f2e4
This commit is contained in:
@@ -46,6 +46,7 @@ android_library {
|
||||
"SystemUIPluginLib",
|
||||
"SystemUISharedLib",
|
||||
"SettingsLib",
|
||||
"androidx.viewpager2_viewpager2",
|
||||
"androidx.legacy_legacy-support-v4",
|
||||
"androidx.recyclerview_recyclerview",
|
||||
"androidx.preference_preference",
|
||||
@@ -106,6 +107,7 @@ android_library {
|
||||
"SystemUIPluginLib",
|
||||
"SystemUISharedLib",
|
||||
"SettingsLib",
|
||||
"androidx.viewpager2_viewpager2",
|
||||
"androidx.legacy_legacy-support-v4",
|
||||
"androidx.recyclerview_recyclerview",
|
||||
"androidx.preference_preference",
|
||||
|
||||
@@ -25,13 +25,40 @@
|
||||
android:paddingStart="@dimen/controls_management_side_padding"
|
||||
android:paddingEnd="@dimen/controls_management_side_padding" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/controls_title_size"
|
||||
android:textAlignment="center" />
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/icon_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minWidth="56dp"
|
||||
android:visibility="gone"
|
||||
android:paddingTop="@dimen/controls_app_icon_frame_top_padding"
|
||||
android:paddingBottom="@dimen/controls_app_icon_frame_bottom_padding"
|
||||
android:paddingEnd="@dimen/controls_app_icon_frame_side_padding"
|
||||
android:paddingStart="@dimen/controls_app_icon_frame_side_padding" >
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="@dimen/controls_app_icon_size"
|
||||
android:layout_height="@dimen/controls_app_icon_size" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/controls_title_size"
|
||||
android:textAlignment="center" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
@@ -41,19 +68,11 @@
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
<ViewStub
|
||||
android:id="@+id/stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="@dimen/controls_management_list_margin">
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -14,12 +14,18 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="@dimen/controls_management_list_margin">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
@@ -29,11 +29,17 @@
|
||||
android:gravity="center_horizontal"
|
||||
/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/listAll"
|
||||
android:layout_width="match_parent"
|
||||
<com.android.systemui.controls.management.ManagementPageIndicator
|
||||
android:id="@+id/structure_page_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/controls_management_list_margin"
|
||||
android:nestedScrollingEnabled="false"/>
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/structure_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
31
packages/SystemUI/res/layout/controls_structure_page.xml
Normal file
31
packages/SystemUI/res/layout/controls_structure_page.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="@dimen/controls_management_list_margin">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/listAll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
@@ -1249,6 +1249,7 @@
|
||||
<dimen name="controls_app_icon_size">32dp</dimen>
|
||||
<dimen name="controls_app_icon_frame_side_padding">8dp</dimen>
|
||||
<dimen name="controls_app_icon_frame_top_padding">4dp</dimen>
|
||||
<dimen name="controls_app_icon_frame_bottom_padding">@dimen/controls_app_icon_frame_top_padding</dimen>
|
||||
<dimen name="controls_app_bottom_margin">8dp</dimen>
|
||||
<dimen name="controls_app_text_padding">8dp</dimen>
|
||||
<dimen name="controls_app_divider_height">2dp</dimen>
|
||||
|
||||
@@ -253,17 +253,21 @@ class ControlsControllerImpl @Inject constructor (
|
||||
}
|
||||
|
||||
override fun error(message: String) {
|
||||
val loadData = Favorites.getControlsForComponent(componentName).let {
|
||||
controls ->
|
||||
executor.execute {
|
||||
val loadData = Favorites.getControlsForComponent(componentName)
|
||||
.let { controls ->
|
||||
val keys = controls.map { it.controlId }
|
||||
createLoadDataObject(
|
||||
controls.map { createRemovedStatus(componentName, it, false) },
|
||||
keys,
|
||||
true
|
||||
controls.map {
|
||||
createRemovedStatus(componentName, it, false)
|
||||
},
|
||||
keys,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dataCallback.accept(loadData)
|
||||
dataCallback.accept(loadData)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -22,14 +22,20 @@ import com.android.systemui.controls.ControlStatus
|
||||
import com.android.systemui.controls.controller.ControlInfo
|
||||
|
||||
/**
|
||||
* This model is used to show all controls separated by zones.
|
||||
* This model is used to show 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
|
||||
* The controls passed should belong to the same structure, as an instance of this model will be
|
||||
* created for each structure.
|
||||
*
|
||||
* The list of favorite ids can contain ids for controls not passed to this model. Those will be
|
||||
* filtered out.
|
||||
*
|
||||
* @property controls List of 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(
|
||||
@@ -50,7 +56,10 @@ class AllModel(
|
||||
}
|
||||
}
|
||||
|
||||
private val favoriteIds = initialFavoriteIds.toMutableList()
|
||||
private val favoriteIds = run {
|
||||
val ids = controls.mapTo(HashSet()) { it.control.controlId }
|
||||
initialFavoriteIds.filter { it in ids }.toMutableList()
|
||||
}
|
||||
|
||||
override val elements: List<ElementWrapper> = createWrappers(controls)
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ 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 elevation: Float
|
||||
) : RecyclerView.Adapter<Holder>() {
|
||||
|
||||
@@ -60,6 +59,7 @@ class ControlAdapter(
|
||||
private var model: ControlsModel? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
return when (viewType) {
|
||||
TYPE_CONTROL -> {
|
||||
ControlHolder(
|
||||
|
||||
@@ -19,20 +19,24 @@ package com.android.systemui.controls.management
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.view.ViewStub
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
import com.android.systemui.controls.controller.StructureInfo
|
||||
import com.android.systemui.controls.ControlsServiceInfo
|
||||
import com.android.systemui.controls.controller.ControlsControllerImpl
|
||||
import com.android.systemui.controls.controller.StructureInfo
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.qs.PageIndicator
|
||||
import com.android.systemui.settings.CurrentUserTracker
|
||||
import java.text.Collator
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.function.Consumer
|
||||
import javax.inject.Inject
|
||||
@@ -40,6 +44,7 @@ import javax.inject.Inject
|
||||
class ControlsFavoritingActivity @Inject constructor(
|
||||
@Main private val executor: Executor,
|
||||
private val controller: ControlsControllerImpl,
|
||||
private val listingController: ControlsListingController,
|
||||
broadcastDispatcher: BroadcastDispatcher
|
||||
) : Activity() {
|
||||
|
||||
@@ -48,12 +53,18 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
const val EXTRA_APP = "extra_app_label"
|
||||
}
|
||||
|
||||
private lateinit var recyclerViewAll: RecyclerView
|
||||
private lateinit var adapterAll: ControlAdapter
|
||||
private lateinit var statusText: TextView
|
||||
private var model: ControlsModel? = null
|
||||
private var component: ComponentName? = null
|
||||
private var structureName: CharSequence = ""
|
||||
private var appName: CharSequence? = null
|
||||
|
||||
private lateinit var structurePager: ViewPager2
|
||||
private lateinit var statusText: TextView
|
||||
private lateinit var titleView: TextView
|
||||
private lateinit var iconView: ImageView
|
||||
private lateinit var iconFrame: View
|
||||
private lateinit var pageIndicator: PageIndicator
|
||||
private var listOfStructures = emptyList<StructureContainer>()
|
||||
|
||||
private lateinit var comparator: Comparator<StructureContainer>
|
||||
|
||||
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
|
||||
private val startingUser = controller.currentUserId
|
||||
@@ -66,29 +77,117 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private val listingCallback = object : ControlsListingController.ControlsListingCallback {
|
||||
private var icon: Drawable? = null
|
||||
|
||||
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
|
||||
val newIcon = serviceInfos.firstOrNull { it.componentName == component }?.loadIcon()
|
||||
if (icon == newIcon) return
|
||||
icon = newIcon
|
||||
executor.execute {
|
||||
if (icon != null) {
|
||||
iconView.setImageDrawable(icon)
|
||||
}
|
||||
iconFrame.visibility = if (icon != null) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val collator = Collator.getInstance(resources.configuration.locales[0])
|
||||
comparator = compareBy(collator) { it.structureName }
|
||||
appName = intent.getCharSequenceExtra(EXTRA_APP)
|
||||
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
|
||||
|
||||
bindViews()
|
||||
|
||||
setUpPager()
|
||||
|
||||
loadControls()
|
||||
|
||||
listingController.addCallback(listingCallback)
|
||||
|
||||
currentUserTracker.startTracking()
|
||||
}
|
||||
|
||||
private fun loadControls() {
|
||||
component?.let {
|
||||
statusText.text = resources.getText(com.android.internal.R.string.loading)
|
||||
val emptyZoneString = resources.getText(
|
||||
R.string.controls_favorite_other_zone_header)
|
||||
controller.loadForComponent(it, Consumer { data ->
|
||||
val allControls = data.allControls
|
||||
val favoriteKeys = data.favoritesIds
|
||||
val error = data.errorOnLoad
|
||||
val controlsByStructure = allControls.groupBy { it.control.structure ?: "" }
|
||||
listOfStructures = controlsByStructure.map {
|
||||
StructureContainer(it.key, AllModel(it.value, favoriteKeys, emptyZoneString))
|
||||
}.sortedWith(comparator)
|
||||
executor.execute {
|
||||
structurePager.adapter = StructureAdapter(listOfStructures)
|
||||
if (error) {
|
||||
statusText.text = resources.getText(R.string.controls_favorite_load_error)
|
||||
} else {
|
||||
statusText.visibility = View.GONE
|
||||
}
|
||||
pageIndicator.setNumPages(listOfStructures.size)
|
||||
pageIndicator.setLocation(0f)
|
||||
pageIndicator.visibility =
|
||||
if (listOfStructures.size > 1) View.VISIBLE else View.GONE
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpPager() {
|
||||
structurePager.apply {
|
||||
adapter = StructureAdapter(emptyList())
|
||||
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
val name = listOfStructures[position].structureName
|
||||
titleView.text = if (!TextUtils.isEmpty(name)) name else appName
|
||||
}
|
||||
|
||||
override fun onPageScrolled(
|
||||
position: Int,
|
||||
positionOffset: Float,
|
||||
positionOffsetPixels: Int
|
||||
) {
|
||||
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
|
||||
pageIndicator.setLocation(position + positionOffset)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindViews() {
|
||||
setContentView(R.layout.controls_management)
|
||||
requireViewById<ViewStub>(R.id.stub).apply {
|
||||
layoutResource = R.layout.controls_management_favorites
|
||||
inflate()
|
||||
}
|
||||
|
||||
val app = intent.getCharSequenceExtra(EXTRA_APP)
|
||||
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
|
||||
statusText = requireViewById(R.id.status_message)
|
||||
pageIndicator = requireViewById(R.id.structure_page_indicator)
|
||||
|
||||
setUpRecyclerView()
|
||||
|
||||
requireViewById<TextView>(R.id.title).text = app?.let { it }
|
||||
?: resources.getText(R.string.controls_favorite_default_title)
|
||||
titleView = requireViewById<TextView>(R.id.title).apply {
|
||||
text = appName ?: resources.getText(R.string.controls_favorite_default_title)
|
||||
}
|
||||
requireViewById<TextView>(R.id.subtitle).text =
|
||||
resources.getText(R.string.controls_favorite_subtitle)
|
||||
iconView = requireViewById(com.android.internal.R.id.icon)
|
||||
iconFrame = requireViewById(R.id.icon_frame)
|
||||
structurePager = requireViewById<ViewPager2>(R.id.structure_pager)
|
||||
bindButtons()
|
||||
}
|
||||
|
||||
private fun bindButtons() {
|
||||
requireViewById<Button>(R.id.other_apps).apply {
|
||||
visibility = View.VISIBLE
|
||||
setOnClickListener {
|
||||
@@ -98,64 +197,21 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
|
||||
requireViewById<Button>(R.id.done).setOnClickListener {
|
||||
if (component == null) return@setOnClickListener
|
||||
val favoritesForStorage = model?.favorites?.map {
|
||||
it.build()
|
||||
}
|
||||
if (favoritesForStorage != null) {
|
||||
controller.replaceFavoritesForStructure(StructureInfo(component!!, structureName,
|
||||
listOfStructures.forEach {
|
||||
val favoritesForStorage = it.model.favorites.map { it.build() }
|
||||
controller.replaceFavoritesForStructure(StructureInfo(component!!, it.structureName,
|
||||
favoritesForStorage))
|
||||
finishAffinity()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
val structures = allControls.fold(hashSetOf<CharSequence>()) {
|
||||
s, c ->
|
||||
s.add(c.control.structure ?: "")
|
||||
s
|
||||
}
|
||||
// TODO add multi structure switching support
|
||||
executor.execute {
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
currentUserTracker.startTracking()
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
|
||||
val itemDecorator = MarginItemDecorator(margin, margin)
|
||||
val layoutInflater = LayoutInflater.from(applicationContext)
|
||||
val elevation = resources.getFloat(R.dimen.control_card_elevation)
|
||||
|
||||
adapterAll = ControlAdapter(layoutInflater, elevation)
|
||||
recyclerViewAll = requireViewById<RecyclerView>(R.id.listAll).apply {
|
||||
adapter = adapterAll
|
||||
layoutManager = GridLayoutManager(applicationContext, 2).apply {
|
||||
spanSizeLookup = adapterAll.spanSizeLookup
|
||||
}
|
||||
addItemDecoration(itemDecorator)
|
||||
finishAffinity()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
currentUserTracker.stopTracking()
|
||||
listingController.removeCallback(listingCallback)
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
data class StructureContainer(val structureName: CharSequence, val model: ControlsModel)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.management
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.android.systemui.qs.PageIndicator
|
||||
|
||||
/**
|
||||
* Page indicator for management screens.
|
||||
*
|
||||
* Adds RTL support to [PageIndicator]. To be used with [ViewPager2].
|
||||
*/
|
||||
class ManagementPageIndicator(
|
||||
context: Context,
|
||||
attrs: AttributeSet
|
||||
) : PageIndicator(context, attrs) {
|
||||
|
||||
override fun setLocation(location: Float) {
|
||||
// Location doesn't know about RTL
|
||||
if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
|
||||
val numPages = childCount
|
||||
super.setLocation(numPages - 1 - location)
|
||||
} else {
|
||||
super.setLocation(location)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.management
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.android.systemui.R
|
||||
|
||||
class StructureAdapter(
|
||||
private val models: List<StructureContainer>
|
||||
) : RecyclerView.Adapter<StructureAdapter.StructureHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): StructureHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
return StructureHolder(
|
||||
layoutInflater.inflate(R.layout.controls_structure_page, parent, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemCount() = models.size
|
||||
|
||||
override fun onBindViewHolder(holder: StructureHolder, index: Int) {
|
||||
holder.bind(models[index].model)
|
||||
}
|
||||
|
||||
class StructureHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val recyclerView: RecyclerView
|
||||
private val controlAdapter: ControlAdapter
|
||||
|
||||
init {
|
||||
recyclerView = itemView.requireViewById<RecyclerView>(R.id.listAll)
|
||||
val elevation = itemView.context.resources.getFloat(R.dimen.control_card_elevation)
|
||||
controlAdapter = ControlAdapter(elevation)
|
||||
setUpRecyclerView()
|
||||
}
|
||||
|
||||
fun bind(model: ControlsModel) {
|
||||
controlAdapter.changeModel(model)
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
val margin = itemView.context.resources
|
||||
.getDimensionPixelSize(R.dimen.controls_card_margin)
|
||||
val itemDecorator = MarginItemDecorator(margin, margin)
|
||||
|
||||
recyclerView.apply {
|
||||
this.adapter = controlAdapter
|
||||
layoutManager = GridLayoutManager(recyclerView.context, 2).apply {
|
||||
spanSizeLookup = controlAdapter.spanSizeLookup
|
||||
}
|
||||
addItemDecoration(itemDecorator)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,6 +340,8 @@ class ControlsControllerImplTest : SysuiTestCase() {
|
||||
|
||||
controlLoadCallbackCaptor.value.error("")
|
||||
|
||||
delayableExecutor.runAllReady()
|
||||
|
||||
assertTrue(loaded)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user