Merge "Add Structure pages to management screens" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7103ff4a7d
@@ -46,6 +46,7 @@ android_library {
|
|||||||
"SystemUIPluginLib",
|
"SystemUIPluginLib",
|
||||||
"SystemUISharedLib",
|
"SystemUISharedLib",
|
||||||
"SettingsLib",
|
"SettingsLib",
|
||||||
|
"androidx.viewpager2_viewpager2",
|
||||||
"androidx.legacy_legacy-support-v4",
|
"androidx.legacy_legacy-support-v4",
|
||||||
"androidx.recyclerview_recyclerview",
|
"androidx.recyclerview_recyclerview",
|
||||||
"androidx.preference_preference",
|
"androidx.preference_preference",
|
||||||
@@ -106,6 +107,7 @@ android_library {
|
|||||||
"SystemUIPluginLib",
|
"SystemUIPluginLib",
|
||||||
"SystemUISharedLib",
|
"SystemUISharedLib",
|
||||||
"SettingsLib",
|
"SettingsLib",
|
||||||
|
"androidx.viewpager2_viewpager2",
|
||||||
"androidx.legacy_legacy-support-v4",
|
"androidx.legacy_legacy-support-v4",
|
||||||
"androidx.recyclerview_recyclerview",
|
"androidx.recyclerview_recyclerview",
|
||||||
"androidx.preference_preference",
|
"androidx.preference_preference",
|
||||||
|
|||||||
@@ -25,6 +25,30 @@
|
|||||||
android:paddingStart="@dimen/controls_management_side_padding"
|
android:paddingStart="@dimen/controls_management_side_padding"
|
||||||
android:paddingEnd="@dimen/controls_management_side_padding" >
|
android:paddingEnd="@dimen/controls_management_side_padding" >
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
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
|
<TextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -33,6 +57,9 @@
|
|||||||
android:textSize="@dimen/controls_title_size"
|
android:textSize="@dimen/controls_title_size"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/subtitle"
|
android:id="@+id/subtitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -41,19 +68,11 @@
|
|||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
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
|
<ViewStub
|
||||||
android:id="@+id/stub"
|
android:id="@+id/stub"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"/>
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -14,12 +14,18 @@
|
|||||||
~ 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.core.widget.NestedScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="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
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/list"
|
android:id="@+id/list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
>
|
/>
|
||||||
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -29,11 +29,17 @@
|
|||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<com.android.systemui.controls.management.ManagementPageIndicator
|
||||||
android:id="@+id/listAll"
|
android:id="@+id/structure_page_indicator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:layout_marginTop="@dimen/controls_management_list_margin"
|
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>
|
</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_size">32dp</dimen>
|
||||||
<dimen name="controls_app_icon_frame_side_padding">8dp</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_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_bottom_margin">8dp</dimen>
|
||||||
<dimen name="controls_app_text_padding">8dp</dimen>
|
<dimen name="controls_app_text_padding">8dp</dimen>
|
||||||
<dimen name="controls_app_divider_height">2dp</dimen>
|
<dimen name="controls_app_divider_height">2dp</dimen>
|
||||||
|
|||||||
@@ -253,11 +253,14 @@ class ControlsControllerImpl @Inject constructor (
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun error(message: String) {
|
override fun error(message: String) {
|
||||||
val loadData = Favorites.getControlsForComponent(componentName).let {
|
executor.execute {
|
||||||
controls ->
|
val loadData = Favorites.getControlsForComponent(componentName)
|
||||||
|
.let { controls ->
|
||||||
val keys = controls.map { it.controlId }
|
val keys = controls.map { it.controlId }
|
||||||
createLoadDataObject(
|
createLoadDataObject(
|
||||||
controls.map { createRemovedStatus(componentName, it, false) },
|
controls.map {
|
||||||
|
createRemovedStatus(componentName, it, false)
|
||||||
|
},
|
||||||
keys,
|
keys,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
@@ -266,6 +269,7 @@ class ControlsControllerImpl @Inject constructor (
|
|||||||
dataCallback.accept(loadData)
|
dataCallback.accept(loadData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,14 +22,20 @@ import com.android.systemui.controls.ControlStatus
|
|||||||
import com.android.systemui.controls.controller.ControlInfo
|
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 model will sort the controls and zones in the following manner:
|
||||||
* * The zones will be sorted in a first seen basis
|
* * The zones will be sorted in a first seen basis
|
||||||
* * The controls in each zone 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
|
* The controls passed should belong to the same structure, as an instance of this model will be
|
||||||
* @property initialFavoriteIds sorted ids of favorite controls
|
* 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.
|
* @property noZoneString text to use as header for all controls that have blank or `null` zone.
|
||||||
*/
|
*/
|
||||||
class AllModel(
|
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)
|
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
|
* @param onlyFavorites set to true to only display favorites instead of all controls
|
||||||
*/
|
*/
|
||||||
class ControlAdapter(
|
class ControlAdapter(
|
||||||
private val layoutInflater: LayoutInflater,
|
|
||||||
private val elevation: Float
|
private val elevation: Float
|
||||||
) : RecyclerView.Adapter<Holder>() {
|
) : RecyclerView.Adapter<Holder>() {
|
||||||
|
|
||||||
@@ -60,6 +59,7 @@ class ControlAdapter(
|
|||||||
private var model: ControlsModel? = null
|
private var model: ControlsModel? = null
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
||||||
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
TYPE_CONTROL -> {
|
TYPE_CONTROL -> {
|
||||||
ControlHolder(
|
ControlHolder(
|
||||||
|
|||||||
@@ -19,20 +19,24 @@ package com.android.systemui.controls.management
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.text.TextUtils
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewStub
|
import android.view.ViewStub
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
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.StructureInfo
|
import com.android.systemui.controls.ControlsServiceInfo
|
||||||
import com.android.systemui.controls.controller.ControlsControllerImpl
|
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.dagger.qualifiers.Main
|
||||||
|
import com.android.systemui.qs.PageIndicator
|
||||||
import com.android.systemui.settings.CurrentUserTracker
|
import com.android.systemui.settings.CurrentUserTracker
|
||||||
|
import java.text.Collator
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -40,6 +44,7 @@ import javax.inject.Inject
|
|||||||
class ControlsFavoritingActivity @Inject constructor(
|
class ControlsFavoritingActivity @Inject constructor(
|
||||||
@Main private val executor: Executor,
|
@Main private val executor: Executor,
|
||||||
private val controller: ControlsControllerImpl,
|
private val controller: ControlsControllerImpl,
|
||||||
|
private val listingController: ControlsListingController,
|
||||||
broadcastDispatcher: BroadcastDispatcher
|
broadcastDispatcher: BroadcastDispatcher
|
||||||
) : Activity() {
|
) : Activity() {
|
||||||
|
|
||||||
@@ -48,12 +53,18 @@ class ControlsFavoritingActivity @Inject constructor(
|
|||||||
const val EXTRA_APP = "extra_app_label"
|
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 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 currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
|
||||||
private val startingUser = controller.currentUserId
|
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() {
|
override fun onBackPressed() {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
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)
|
setContentView(R.layout.controls_management)
|
||||||
requireViewById<ViewStub>(R.id.stub).apply {
|
requireViewById<ViewStub>(R.id.stub).apply {
|
||||||
layoutResource = R.layout.controls_management_favorites
|
layoutResource = R.layout.controls_management_favorites
|
||||||
inflate()
|
inflate()
|
||||||
}
|
}
|
||||||
|
|
||||||
val app = intent.getCharSequenceExtra(EXTRA_APP)
|
|
||||||
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
|
|
||||||
statusText = requireViewById(R.id.status_message)
|
statusText = requireViewById(R.id.status_message)
|
||||||
|
pageIndicator = requireViewById(R.id.structure_page_indicator)
|
||||||
|
|
||||||
setUpRecyclerView()
|
titleView = requireViewById<TextView>(R.id.title).apply {
|
||||||
|
text = appName ?: resources.getText(R.string.controls_favorite_default_title)
|
||||||
requireViewById<TextView>(R.id.title).text = app?.let { it }
|
}
|
||||||
?: 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)
|
||||||
|
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 {
|
requireViewById<Button>(R.id.other_apps).apply {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
@@ -98,64 +197,21 @@ class ControlsFavoritingActivity @Inject constructor(
|
|||||||
|
|
||||||
requireViewById<Button>(R.id.done).setOnClickListener {
|
requireViewById<Button>(R.id.done).setOnClickListener {
|
||||||
if (component == null) return@setOnClickListener
|
if (component == null) return@setOnClickListener
|
||||||
val favoritesForStorage = model?.favorites?.map {
|
listOfStructures.forEach {
|
||||||
it.build()
|
val favoritesForStorage = it.model.favorites.map { it.build() }
|
||||||
}
|
controller.replaceFavoritesForStructure(StructureInfo(component!!, it.structureName,
|
||||||
if (favoritesForStorage != null) {
|
|
||||||
controller.replaceFavoritesForStructure(StructureInfo(component!!, structureName,
|
|
||||||
favoritesForStorage))
|
favoritesForStorage))
|
||||||
|
}
|
||||||
|
|
||||||
finishAffinity()
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
currentUserTracker.stopTracking()
|
currentUserTracker.stopTracking()
|
||||||
|
listingController.removeCallback(listingCallback)
|
||||||
super.onDestroy()
|
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("")
|
controlLoadCallbackCaptor.value.error("")
|
||||||
|
|
||||||
|
delayableExecutor.runAllReady()
|
||||||
|
|
||||||
assertTrue(loaded)
|
assertTrue(loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user