Loading packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt +7 −9 Original line number Diff line number Diff line Loading @@ -12,13 +12,14 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.controls.controller.ControlsController import com.android.systemui.res.R import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.utils.SafeIconLoader import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading @@ -32,6 +33,7 @@ class TemperatureControlBehaviorTest : SysuiTestCase() { @Mock lateinit var controlsMetricsLogger: ControlsMetricsLogger @Mock lateinit var controlActionCoordinator: ControlActionCoordinator @Mock lateinit var controlsController: ControlsController @Mock lateinit var safeIconLoader: SafeIconLoader private val fakeSystemClock = FakeSystemClock() private val underTest = TemperatureControlBehavior() Loading @@ -53,6 +55,7 @@ class TemperatureControlBehaviorTest : SysuiTestCase() { controlsMetricsLogger, 0, 0, safeIconLoader, ) } Loading @@ -61,12 +64,7 @@ class TemperatureControlBehaviorTest : SysuiTestCase() { val controlWithState = ControlWithState( ComponentName("test.pkg", "TestClass"), ControlInfo( "test_id", "test title", "test subtitle", DeviceTypes.TYPE_AC_UNIT, ), ControlInfo("test_id", "test title", "test subtitle", DeviceTypes.TYPE_AC_UNIT), Control.StatefulBuilder( "", PendingIntent.getActivity( Loading @@ -87,11 +85,11 @@ class TemperatureControlBehaviorTest : SysuiTestCase() { ), 0, 0, 0 0, ) ) .setStatus(Control.STATUS_OK) .build() .build(), ) viewHolder.bindData(controlWithState, false) underTest.initialize(viewHolder) Loading packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt +26 −26 Original line number Diff line number Diff line Loading @@ -43,7 +43,7 @@ class AllModel( private val controls: List<ControlStatus>, initialFavoriteIds: List<String>, private val emptyZoneString: CharSequence, private val controlsModelCallback: ControlsModel.ControlsModelCallback private val controlsModelCallback: ControlsModel.ControlsModelCallback, ) : ControlsModel { private var modified = false Loading @@ -51,11 +51,10 @@ class AllModel( override val moveHelper = null override val favorites: List<ControlInfo> get() = favoriteIds.mapNotNull { id -> get() = favoriteIds.mapNotNull { id -> val control = controls.firstOrNull { it.control.controlId == id }?.control control?.let { ControlInfo.fromControl(it) } control?.let { ControlInfo.fromControl(it) } } private val favoriteIds = run { Loading @@ -66,11 +65,13 @@ class AllModel( override val elements: List<ElementWrapper> = createWrappers(controls) override fun changeFavoriteStatus(controlId: String, favorite: Boolean) { val toChange = elements.firstOrNull { val toChange = elements.firstOrNull { it is ControlStatusWrapper && it.controlStatus.control.controlId == controlId } as ControlStatusWrapper? if (favorite == toChange?.controlStatus?.favorite) return val changed: Boolean = if (favorite) { val changed: Boolean = if (favorite) { favoriteIds.add(controlId) } else { favoriteIds.remove(controlId) Loading @@ -82,13 +83,12 @@ class AllModel( } controlsModelCallback.onChange() } toChange?.let { it.controlStatus.favorite = favorite } toChange?.let { it.controlStatus.favorite = favorite } } private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> { val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) { val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) { it.control.zone ?: "" } val output = mutableListOf<ElementWrapper>() Loading packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +89 −76 Original line number Diff line number Diff line Loading @@ -36,10 +36,11 @@ import androidx.core.view.AccessibilityDelegateCompat import androidx.core.view.ViewCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.recyclerview.widget.RecyclerView import com.android.systemui.res.R import com.android.systemui.controls.ControlInterface import com.android.systemui.controls.ui.CanUseIconPredicate import com.android.systemui.controls.ui.RenderInfo import com.android.systemui.res.R import com.android.systemui.utils.SafeIconLoader private typealias ModelFavoriteChanger = (String, Boolean) -> Unit Loading @@ -54,6 +55,7 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit class ControlAdapter( private val elevation: Float, private val currentUserId: Int, private val safeIconLoader: SafeIconLoader, ) : RecyclerView.Adapter<Holder>() { companion object { Loading @@ -62,9 +64,8 @@ class ControlAdapter( const val TYPE_DIVIDER = 2 /** * For low-dp width screens that also employ an increased font scale, adjust the * number of columns. This helps prevent text truncation on these devices. * * For low-dp width screens that also employ an increased font scale, adjust the number of * columns. This helps prevent text truncation on these devices. */ @JvmStatic fun findMaxColumns(res: Resources): Int { Loading @@ -78,10 +79,12 @@ class ControlAdapter( val config = res.configuration val isPortrait = config.orientation == Configuration.ORIENTATION_PORTRAIT if (isPortrait && if ( isPortrait && config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED && config.screenWidthDp <= maxColumnsAdjustWidth && config.fontScale >= maxColumnsAdjustFontScale) { config.fontScale >= maxColumnsAdjustFontScale ) { maxColumns-- } Loading @@ -106,11 +109,12 @@ class ControlAdapter( rightMargin = 0 } elevation = this@ControlAdapter.elevation background = parent.context.getDrawable( R.drawable.control_background_ripple) background = parent.context.getDrawable(R.drawable.control_background_ripple) }, currentUserId, model?.moveHelper, // Indicates that position information is needed safeIconLoader, ) { id, favorite -> model?.changeFavoriteStatus(id, favorite) } Loading @@ -119,8 +123,13 @@ class ControlAdapter( ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false)) } TYPE_DIVIDER -> { DividerHolder(layoutInflater.inflate( R.layout.controls_horizontal_divider_with_empty, parent, false)) DividerHolder( layoutInflater.inflate( R.layout.controls_horizontal_divider_with_empty, parent, false, ) ) } else -> throw IllegalStateException("Wrong viewType: $viewType") } Loading @@ -134,9 +143,7 @@ class ControlAdapter( override fun getItemCount() = model?.elements?.size ?: 0 override fun onBindViewHolder(holder: Holder, index: Int) { model?.let { holder.bindData(it.elements[index]) } model?.let { holder.bindData(it.elements[index]) } } override fun onBindViewHolder(holder: Holder, position: Int, payloads: MutableList<Any>) { Loading Loading @@ -166,13 +173,12 @@ class ControlAdapter( /** * Holder for binding views in the [RecyclerView]- * * @param view the [View] for this [Holder] */ sealed class Holder(view: View) : RecyclerView.ViewHolder(view) { /** * Bind the data from the model into the view */ /** Bind the data from the model into the view */ abstract fun bindData(wrapper: ElementWrapper) open fun updateFavorite(favorite: Boolean) {} Loading @@ -181,12 +187,13 @@ sealed class Holder(view: View) : RecyclerView.ViewHolder(view) { /** * Holder for using with [DividerWrapper] to display a divider between zones. * * The divider can be shown or hidden. It also has a view the height of a control, that can * be toggled visible or gone. * The divider can be shown or hidden. It also has a view the height of a control, that can be * toggled visible or gone. */ private class DividerHolder(view: View) : Holder(view) { private val frame: View = itemView.requireViewById(R.id.frame) private val divider: View = itemView.requireViewById(R.id.divider) override fun bindData(wrapper: ElementWrapper) { wrapper as DividerWrapper frame.visibility = if (wrapper.showNone) View.VISIBLE else View.GONE Loading @@ -194,9 +201,7 @@ private class DividerHolder(view: View) : Holder(view) { } } /** * Holder for using with [ZoneNameWrapper] to display names of zones. */ /** Holder for using with [ZoneNameWrapper] to display names of zones. */ private class ZoneHolder(view: View) : Holder(view) { private val zone: TextView = itemView as TextView Loading @@ -208,15 +213,17 @@ private class ZoneHolder(view: View) : Holder(view) { /** * Holder for using with [ControlStatusWrapper] to display names of zones. * * @param moveHelper a helper interface to facilitate a11y rearranging. Null indicates no * rearranging * @param favoriteCallback this callback will be called whenever the favorite state of the * [Control] this view represents changes. * @param favoriteCallback this callback will be called whenever the favorite state of the [Control] * this view represents changes. */ internal class ControlHolder( view: View, currentUserId: Int, val moveHelper: ControlsModel.MoveHelper?, val safeIconLoader: SafeIconLoader, val favoriteCallback: ModelFavoriteChanger, ) : Holder(view) { private val favoriteStateDescription = Loading @@ -228,15 +235,15 @@ internal class ControlHolder( private val title: TextView = itemView.requireViewById(R.id.title) private val subtitle: TextView = itemView.requireViewById(R.id.subtitle) private val removed: TextView = itemView.requireViewById(R.id.status) private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply { visibility = View.VISIBLE } private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply { visibility = View.VISIBLE } private val canUseIconPredicate = CanUseIconPredicate(currentUserId) private val accessibilityDelegate = ControlHolderAccessibilityDelegate( private val accessibilityDelegate = ControlHolderAccessibilityDelegate( this::stateDescription, this::getLayoutPosition, moveHelper moveHelper, ) init { Loading @@ -252,7 +259,9 @@ internal class ControlHolder( } else { val position = layoutPosition + 1 return itemView.context.getString( R.string.accessibility_control_favorite_position, position) R.string.accessibility_control_favorite_position, position, ) } } Loading @@ -262,7 +271,8 @@ internal class ControlHolder( title.text = wrapper.title subtitle.text = wrapper.subtitle updateFavorite(wrapper.favorite) removed.text = if (wrapper.removed) { removed.text = if (wrapper.removed) { itemView.context.getText(R.string.controls_removed) } else { "" Loading @@ -282,7 +292,7 @@ internal class ControlHolder( private fun getRenderInfo( component: ComponentName, @DeviceTypes.DeviceType deviceType: Int @DeviceTypes.DeviceType deviceType: Int, ): RenderInfo { return RenderInfo.lookup(itemView.context, component, deviceType) } Loading @@ -292,11 +302,12 @@ internal class ControlHolder( val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) icon.imageTintList = null ci.customIcon ?.takeIf(canUseIconPredicate) ?.let { icon.setImageIcon(it) } ?: run { ci.customIcon?.takeIf(canUseIconPredicate)?.let { val drawable = safeIconLoader.load(it) icon.setImageDrawable(drawable) drawable } ?: run { icon.setImageDrawable(ri.icon) // Do not color app icons Loading @@ -317,14 +328,13 @@ internal class ControlHolder( * * @param stateRetriever function to determine the state description based on the favorite state * @param positionRetriever function to obtain the position of this control. It only has to be * correct in controls that are currently favorites (and therefore can * be moved). * correct in controls that are currently favorites (and therefore can be moved). * @param moveHelper helper interface to determine if a control can be moved and actually move it. */ private class ControlHolderAccessibilityDelegate( val stateRetriever: (Boolean) -> CharSequence?, val positionRetriever: () -> Int, val moveHelper: ControlsModel.MoveHelper? val moveHelper: ControlsModel.MoveHelper?, ) : AccessibilityDelegateCompat() { var isFavorite = false Loading Loading @@ -369,24 +379,28 @@ private class ControlHolderAccessibilityDelegate( private fun addClickAction(host: View, info: AccessibilityNodeInfoCompat) { // Change the text for the double-tap action val clickActionString = if (isFavorite) { val clickActionString = if (isFavorite) { host.context.getString(R.string.accessibility_control_change_unfavorite) } else { host.context.getString(R.string.accessibility_control_change_favorite) } val click = AccessibilityNodeInfoCompat.AccessibilityActionCompat( val click = AccessibilityNodeInfoCompat.AccessibilityActionCompat( AccessibilityNodeInfo.ACTION_CLICK, // “favorite/unfavorite” clickActionString) clickActionString, ) info.addAction(click) } private fun maybeAddMoveBeforeAction(host: View, info: AccessibilityNodeInfoCompat) { if (moveHelper?.canMoveBefore(positionRetriever()) ?: false) { val newPosition = positionRetriever() + 1 - 1 val moveBefore = AccessibilityNodeInfoCompat.AccessibilityActionCompat( val moveBefore = AccessibilityNodeInfoCompat.AccessibilityActionCompat( MOVE_BEFORE_ID, host.context.getString(R.string.accessibility_control_move, newPosition) host.context.getString(R.string.accessibility_control_move, newPosition), ) info.addAction(moveBefore) info.isContextClickable = true Loading @@ -396,9 +410,10 @@ private class ControlHolderAccessibilityDelegate( private fun maybeAddMoveAfterAction(host: View, info: AccessibilityNodeInfoCompat) { if (moveHelper?.canMoveAfter(positionRetriever()) ?: false) { val newPosition = positionRetriever() + 1 + 1 val moveAfter = AccessibilityNodeInfoCompat.AccessibilityActionCompat( val moveAfter = AccessibilityNodeInfoCompat.AccessibilityActionCompat( MOVE_AFTER_ID, host.context.getString(R.string.accessibility_control_move, newPosition) host.context.getString(R.string.accessibility_control_move, newPosition), ) info.addAction(moveAfter) info.isContextClickable = true Loading @@ -406,16 +421,14 @@ private class ControlHolderAccessibilityDelegate( } } class MarginItemDecorator( private val topMargin: Int, private val sideMargins: Int ) : RecyclerView.ItemDecoration() { class MarginItemDecorator(private val topMargin: Int, private val sideMargins: Int) : RecyclerView.ItemDecoration() { override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State state: RecyclerView.State, ) { val position = parent.getChildAdapterPosition(view) if (position == RecyclerView.NO_POSITION) return Loading packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +15 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Bundle import android.os.Process import android.util.Log import android.view.View import android.view.ViewGroup Loading @@ -42,6 +43,7 @@ import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.utils.SafeIconLoader import java.util.concurrent.Executor import javax.inject.Inject Loading @@ -53,6 +55,8 @@ constructor( private val controller: ControlsControllerImpl, private val userTracker: UserTracker, private val customIconCache: CustomIconCache, private val controlsListingController: ControlsListingController, private val safeIconLoaderFactory: SafeIconLoader.Factory, ) : ComponentActivity(), ControlsManagementActivity { companion object { Loading Loading @@ -258,8 +262,18 @@ constructor( val elevation = resources.getFloat(R.dimen.control_card_elevation) val recyclerView = requireViewById<RecyclerView>(R.id.list) recyclerView.alpha = 0.0f val uid = controlsListingController .getCurrentServices() .firstOrNull { it.componentName == component } ?.serviceInfo ?.applicationInfo ?.uid ?: Process.INVALID_UID val packageName = component.packageName val safeIconLoader = safeIconLoaderFactory.create(uid, packageName, userTracker.userId) val adapter = ControlAdapter(elevation, userTracker.userId).apply { ControlAdapter(elevation, userTracker.userId, safeIconLoader).apply { registerAdapterDataObserver( object : RecyclerView.AdapterDataObserver() { var hasAnimated = false Loading packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +27 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.content.Context import android.content.Intent import android.content.res.Configuration import android.os.Bundle import android.os.Process.INVALID_UID import android.text.TextUtils import android.util.Log import android.view.Gravity Loading @@ -47,6 +48,7 @@ import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.utils.SafeIconLoader import java.text.Collator import java.util.concurrent.Executor import javax.inject.Inject Loading @@ -57,6 +59,8 @@ constructor( @Main private val executor: Executor, private val controller: ControlsControllerImpl, private val userTracker: UserTracker, private val safeIconLoaderFactory: SafeIconLoader.Factory, private val controlsListingController: ControlsListingController, ) : ComponentActivity(), ControlsManagementActivity { companion object { Loading Loading @@ -196,9 +200,20 @@ constructor( listOfStructures = listOf(listOfStructures[structureIndex]) } val uid = controlsListingController .getCurrentServices() .firstOrNull { it.componentName == componentName } ?.serviceInfo ?.applicationInfo ?.uid ?: INVALID_UID val packageName = componentName.packageName val safeIconLoader = safeIconLoaderFactory.create(uid, packageName, userTracker.userId) executor.execute { structurePager.adapter = StructureAdapter(listOfStructures, userTracker.userId) StructureAdapter(listOfStructures, userTracker.userId, safeIconLoader) structurePager.setCurrentItem(structureIndex) if (error) { statusText.text = Loading Loading @@ -260,8 +275,18 @@ constructor( private fun setUpPager() { structurePager.alpha = 0.0f pageIndicator.alpha = 0.0f val uid = controlsListingController .getCurrentServices() .firstOrNull { it.componentName == component } ?.serviceInfo ?.applicationInfo ?.uid ?: INVALID_UID val packageName = componentName?.packageName ?: "" val safeIconLoader = safeIconLoaderFactory.create(uid, packageName, userTracker.userId) structurePager.apply { adapter = StructureAdapter(emptyList(), userTracker.userId) adapter = StructureAdapter(emptyList(), userTracker.userId, safeIconLoader) registerOnPageChangeCallback( object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt +7 −9 Original line number Diff line number Diff line Loading @@ -12,13 +12,14 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.controls.controller.ControlsController import com.android.systemui.res.R import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.utils.SafeIconLoader import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading @@ -32,6 +33,7 @@ class TemperatureControlBehaviorTest : SysuiTestCase() { @Mock lateinit var controlsMetricsLogger: ControlsMetricsLogger @Mock lateinit var controlActionCoordinator: ControlActionCoordinator @Mock lateinit var controlsController: ControlsController @Mock lateinit var safeIconLoader: SafeIconLoader private val fakeSystemClock = FakeSystemClock() private val underTest = TemperatureControlBehavior() Loading @@ -53,6 +55,7 @@ class TemperatureControlBehaviorTest : SysuiTestCase() { controlsMetricsLogger, 0, 0, safeIconLoader, ) } Loading @@ -61,12 +64,7 @@ class TemperatureControlBehaviorTest : SysuiTestCase() { val controlWithState = ControlWithState( ComponentName("test.pkg", "TestClass"), ControlInfo( "test_id", "test title", "test subtitle", DeviceTypes.TYPE_AC_UNIT, ), ControlInfo("test_id", "test title", "test subtitle", DeviceTypes.TYPE_AC_UNIT), Control.StatefulBuilder( "", PendingIntent.getActivity( Loading @@ -87,11 +85,11 @@ class TemperatureControlBehaviorTest : SysuiTestCase() { ), 0, 0, 0 0, ) ) .setStatus(Control.STATUS_OK) .build() .build(), ) viewHolder.bindData(controlWithState, false) underTest.initialize(viewHolder) Loading
packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt +26 −26 Original line number Diff line number Diff line Loading @@ -43,7 +43,7 @@ class AllModel( private val controls: List<ControlStatus>, initialFavoriteIds: List<String>, private val emptyZoneString: CharSequence, private val controlsModelCallback: ControlsModel.ControlsModelCallback private val controlsModelCallback: ControlsModel.ControlsModelCallback, ) : ControlsModel { private var modified = false Loading @@ -51,11 +51,10 @@ class AllModel( override val moveHelper = null override val favorites: List<ControlInfo> get() = favoriteIds.mapNotNull { id -> get() = favoriteIds.mapNotNull { id -> val control = controls.firstOrNull { it.control.controlId == id }?.control control?.let { ControlInfo.fromControl(it) } control?.let { ControlInfo.fromControl(it) } } private val favoriteIds = run { Loading @@ -66,11 +65,13 @@ class AllModel( override val elements: List<ElementWrapper> = createWrappers(controls) override fun changeFavoriteStatus(controlId: String, favorite: Boolean) { val toChange = elements.firstOrNull { val toChange = elements.firstOrNull { it is ControlStatusWrapper && it.controlStatus.control.controlId == controlId } as ControlStatusWrapper? if (favorite == toChange?.controlStatus?.favorite) return val changed: Boolean = if (favorite) { val changed: Boolean = if (favorite) { favoriteIds.add(controlId) } else { favoriteIds.remove(controlId) Loading @@ -82,13 +83,12 @@ class AllModel( } controlsModelCallback.onChange() } toChange?.let { it.controlStatus.favorite = favorite } toChange?.let { it.controlStatus.favorite = favorite } } private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> { val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) { val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) { it.control.zone ?: "" } val output = mutableListOf<ElementWrapper>() Loading
packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +89 −76 Original line number Diff line number Diff line Loading @@ -36,10 +36,11 @@ import androidx.core.view.AccessibilityDelegateCompat import androidx.core.view.ViewCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.recyclerview.widget.RecyclerView import com.android.systemui.res.R import com.android.systemui.controls.ControlInterface import com.android.systemui.controls.ui.CanUseIconPredicate import com.android.systemui.controls.ui.RenderInfo import com.android.systemui.res.R import com.android.systemui.utils.SafeIconLoader private typealias ModelFavoriteChanger = (String, Boolean) -> Unit Loading @@ -54,6 +55,7 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit class ControlAdapter( private val elevation: Float, private val currentUserId: Int, private val safeIconLoader: SafeIconLoader, ) : RecyclerView.Adapter<Holder>() { companion object { Loading @@ -62,9 +64,8 @@ class ControlAdapter( const val TYPE_DIVIDER = 2 /** * For low-dp width screens that also employ an increased font scale, adjust the * number of columns. This helps prevent text truncation on these devices. * * For low-dp width screens that also employ an increased font scale, adjust the number of * columns. This helps prevent text truncation on these devices. */ @JvmStatic fun findMaxColumns(res: Resources): Int { Loading @@ -78,10 +79,12 @@ class ControlAdapter( val config = res.configuration val isPortrait = config.orientation == Configuration.ORIENTATION_PORTRAIT if (isPortrait && if ( isPortrait && config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED && config.screenWidthDp <= maxColumnsAdjustWidth && config.fontScale >= maxColumnsAdjustFontScale) { config.fontScale >= maxColumnsAdjustFontScale ) { maxColumns-- } Loading @@ -106,11 +109,12 @@ class ControlAdapter( rightMargin = 0 } elevation = this@ControlAdapter.elevation background = parent.context.getDrawable( R.drawable.control_background_ripple) background = parent.context.getDrawable(R.drawable.control_background_ripple) }, currentUserId, model?.moveHelper, // Indicates that position information is needed safeIconLoader, ) { id, favorite -> model?.changeFavoriteStatus(id, favorite) } Loading @@ -119,8 +123,13 @@ class ControlAdapter( ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false)) } TYPE_DIVIDER -> { DividerHolder(layoutInflater.inflate( R.layout.controls_horizontal_divider_with_empty, parent, false)) DividerHolder( layoutInflater.inflate( R.layout.controls_horizontal_divider_with_empty, parent, false, ) ) } else -> throw IllegalStateException("Wrong viewType: $viewType") } Loading @@ -134,9 +143,7 @@ class ControlAdapter( override fun getItemCount() = model?.elements?.size ?: 0 override fun onBindViewHolder(holder: Holder, index: Int) { model?.let { holder.bindData(it.elements[index]) } model?.let { holder.bindData(it.elements[index]) } } override fun onBindViewHolder(holder: Holder, position: Int, payloads: MutableList<Any>) { Loading Loading @@ -166,13 +173,12 @@ class ControlAdapter( /** * Holder for binding views in the [RecyclerView]- * * @param view the [View] for this [Holder] */ sealed class Holder(view: View) : RecyclerView.ViewHolder(view) { /** * Bind the data from the model into the view */ /** Bind the data from the model into the view */ abstract fun bindData(wrapper: ElementWrapper) open fun updateFavorite(favorite: Boolean) {} Loading @@ -181,12 +187,13 @@ sealed class Holder(view: View) : RecyclerView.ViewHolder(view) { /** * Holder for using with [DividerWrapper] to display a divider between zones. * * The divider can be shown or hidden. It also has a view the height of a control, that can * be toggled visible or gone. * The divider can be shown or hidden. It also has a view the height of a control, that can be * toggled visible or gone. */ private class DividerHolder(view: View) : Holder(view) { private val frame: View = itemView.requireViewById(R.id.frame) private val divider: View = itemView.requireViewById(R.id.divider) override fun bindData(wrapper: ElementWrapper) { wrapper as DividerWrapper frame.visibility = if (wrapper.showNone) View.VISIBLE else View.GONE Loading @@ -194,9 +201,7 @@ private class DividerHolder(view: View) : Holder(view) { } } /** * Holder for using with [ZoneNameWrapper] to display names of zones. */ /** Holder for using with [ZoneNameWrapper] to display names of zones. */ private class ZoneHolder(view: View) : Holder(view) { private val zone: TextView = itemView as TextView Loading @@ -208,15 +213,17 @@ private class ZoneHolder(view: View) : Holder(view) { /** * Holder for using with [ControlStatusWrapper] to display names of zones. * * @param moveHelper a helper interface to facilitate a11y rearranging. Null indicates no * rearranging * @param favoriteCallback this callback will be called whenever the favorite state of the * [Control] this view represents changes. * @param favoriteCallback this callback will be called whenever the favorite state of the [Control] * this view represents changes. */ internal class ControlHolder( view: View, currentUserId: Int, val moveHelper: ControlsModel.MoveHelper?, val safeIconLoader: SafeIconLoader, val favoriteCallback: ModelFavoriteChanger, ) : Holder(view) { private val favoriteStateDescription = Loading @@ -228,15 +235,15 @@ internal class ControlHolder( private val title: TextView = itemView.requireViewById(R.id.title) private val subtitle: TextView = itemView.requireViewById(R.id.subtitle) private val removed: TextView = itemView.requireViewById(R.id.status) private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply { visibility = View.VISIBLE } private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply { visibility = View.VISIBLE } private val canUseIconPredicate = CanUseIconPredicate(currentUserId) private val accessibilityDelegate = ControlHolderAccessibilityDelegate( private val accessibilityDelegate = ControlHolderAccessibilityDelegate( this::stateDescription, this::getLayoutPosition, moveHelper moveHelper, ) init { Loading @@ -252,7 +259,9 @@ internal class ControlHolder( } else { val position = layoutPosition + 1 return itemView.context.getString( R.string.accessibility_control_favorite_position, position) R.string.accessibility_control_favorite_position, position, ) } } Loading @@ -262,7 +271,8 @@ internal class ControlHolder( title.text = wrapper.title subtitle.text = wrapper.subtitle updateFavorite(wrapper.favorite) removed.text = if (wrapper.removed) { removed.text = if (wrapper.removed) { itemView.context.getText(R.string.controls_removed) } else { "" Loading @@ -282,7 +292,7 @@ internal class ControlHolder( private fun getRenderInfo( component: ComponentName, @DeviceTypes.DeviceType deviceType: Int @DeviceTypes.DeviceType deviceType: Int, ): RenderInfo { return RenderInfo.lookup(itemView.context, component, deviceType) } Loading @@ -292,11 +302,12 @@ internal class ControlHolder( val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) icon.imageTintList = null ci.customIcon ?.takeIf(canUseIconPredicate) ?.let { icon.setImageIcon(it) } ?: run { ci.customIcon?.takeIf(canUseIconPredicate)?.let { val drawable = safeIconLoader.load(it) icon.setImageDrawable(drawable) drawable } ?: run { icon.setImageDrawable(ri.icon) // Do not color app icons Loading @@ -317,14 +328,13 @@ internal class ControlHolder( * * @param stateRetriever function to determine the state description based on the favorite state * @param positionRetriever function to obtain the position of this control. It only has to be * correct in controls that are currently favorites (and therefore can * be moved). * correct in controls that are currently favorites (and therefore can be moved). * @param moveHelper helper interface to determine if a control can be moved and actually move it. */ private class ControlHolderAccessibilityDelegate( val stateRetriever: (Boolean) -> CharSequence?, val positionRetriever: () -> Int, val moveHelper: ControlsModel.MoveHelper? val moveHelper: ControlsModel.MoveHelper?, ) : AccessibilityDelegateCompat() { var isFavorite = false Loading Loading @@ -369,24 +379,28 @@ private class ControlHolderAccessibilityDelegate( private fun addClickAction(host: View, info: AccessibilityNodeInfoCompat) { // Change the text for the double-tap action val clickActionString = if (isFavorite) { val clickActionString = if (isFavorite) { host.context.getString(R.string.accessibility_control_change_unfavorite) } else { host.context.getString(R.string.accessibility_control_change_favorite) } val click = AccessibilityNodeInfoCompat.AccessibilityActionCompat( val click = AccessibilityNodeInfoCompat.AccessibilityActionCompat( AccessibilityNodeInfo.ACTION_CLICK, // “favorite/unfavorite” clickActionString) clickActionString, ) info.addAction(click) } private fun maybeAddMoveBeforeAction(host: View, info: AccessibilityNodeInfoCompat) { if (moveHelper?.canMoveBefore(positionRetriever()) ?: false) { val newPosition = positionRetriever() + 1 - 1 val moveBefore = AccessibilityNodeInfoCompat.AccessibilityActionCompat( val moveBefore = AccessibilityNodeInfoCompat.AccessibilityActionCompat( MOVE_BEFORE_ID, host.context.getString(R.string.accessibility_control_move, newPosition) host.context.getString(R.string.accessibility_control_move, newPosition), ) info.addAction(moveBefore) info.isContextClickable = true Loading @@ -396,9 +410,10 @@ private class ControlHolderAccessibilityDelegate( private fun maybeAddMoveAfterAction(host: View, info: AccessibilityNodeInfoCompat) { if (moveHelper?.canMoveAfter(positionRetriever()) ?: false) { val newPosition = positionRetriever() + 1 + 1 val moveAfter = AccessibilityNodeInfoCompat.AccessibilityActionCompat( val moveAfter = AccessibilityNodeInfoCompat.AccessibilityActionCompat( MOVE_AFTER_ID, host.context.getString(R.string.accessibility_control_move, newPosition) host.context.getString(R.string.accessibility_control_move, newPosition), ) info.addAction(moveAfter) info.isContextClickable = true Loading @@ -406,16 +421,14 @@ private class ControlHolderAccessibilityDelegate( } } class MarginItemDecorator( private val topMargin: Int, private val sideMargins: Int ) : RecyclerView.ItemDecoration() { class MarginItemDecorator(private val topMargin: Int, private val sideMargins: Int) : RecyclerView.ItemDecoration() { override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State state: RecyclerView.State, ) { val position = parent.getChildAdapterPosition(view) if (position == RecyclerView.NO_POSITION) return Loading
packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +15 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Bundle import android.os.Process import android.util.Log import android.view.View import android.view.ViewGroup Loading @@ -42,6 +43,7 @@ import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.utils.SafeIconLoader import java.util.concurrent.Executor import javax.inject.Inject Loading @@ -53,6 +55,8 @@ constructor( private val controller: ControlsControllerImpl, private val userTracker: UserTracker, private val customIconCache: CustomIconCache, private val controlsListingController: ControlsListingController, private val safeIconLoaderFactory: SafeIconLoader.Factory, ) : ComponentActivity(), ControlsManagementActivity { companion object { Loading Loading @@ -258,8 +262,18 @@ constructor( val elevation = resources.getFloat(R.dimen.control_card_elevation) val recyclerView = requireViewById<RecyclerView>(R.id.list) recyclerView.alpha = 0.0f val uid = controlsListingController .getCurrentServices() .firstOrNull { it.componentName == component } ?.serviceInfo ?.applicationInfo ?.uid ?: Process.INVALID_UID val packageName = component.packageName val safeIconLoader = safeIconLoaderFactory.create(uid, packageName, userTracker.userId) val adapter = ControlAdapter(elevation, userTracker.userId).apply { ControlAdapter(elevation, userTracker.userId, safeIconLoader).apply { registerAdapterDataObserver( object : RecyclerView.AdapterDataObserver() { var hasAnimated = false Loading
packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +27 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.content.Context import android.content.Intent import android.content.res.Configuration import android.os.Bundle import android.os.Process.INVALID_UID import android.text.TextUtils import android.util.Log import android.view.Gravity Loading @@ -47,6 +48,7 @@ import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.utils.SafeIconLoader import java.text.Collator import java.util.concurrent.Executor import javax.inject.Inject Loading @@ -57,6 +59,8 @@ constructor( @Main private val executor: Executor, private val controller: ControlsControllerImpl, private val userTracker: UserTracker, private val safeIconLoaderFactory: SafeIconLoader.Factory, private val controlsListingController: ControlsListingController, ) : ComponentActivity(), ControlsManagementActivity { companion object { Loading Loading @@ -196,9 +200,20 @@ constructor( listOfStructures = listOf(listOfStructures[structureIndex]) } val uid = controlsListingController .getCurrentServices() .firstOrNull { it.componentName == componentName } ?.serviceInfo ?.applicationInfo ?.uid ?: INVALID_UID val packageName = componentName.packageName val safeIconLoader = safeIconLoaderFactory.create(uid, packageName, userTracker.userId) executor.execute { structurePager.adapter = StructureAdapter(listOfStructures, userTracker.userId) StructureAdapter(listOfStructures, userTracker.userId, safeIconLoader) structurePager.setCurrentItem(structureIndex) if (error) { statusText.text = Loading Loading @@ -260,8 +275,18 @@ constructor( private fun setUpPager() { structurePager.alpha = 0.0f pageIndicator.alpha = 0.0f val uid = controlsListingController .getCurrentServices() .firstOrNull { it.componentName == component } ?.serviceInfo ?.applicationInfo ?.uid ?: INVALID_UID val packageName = componentName?.packageName ?: "" val safeIconLoader = safeIconLoaderFactory.create(uid, packageName, userTracker.userId) structurePager.apply { adapter = StructureAdapter(emptyList(), userTracker.userId) adapter = StructureAdapter(emptyList(), userTracker.userId, safeIconLoader) registerOnPageChangeCallback( object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { Loading