Add chevron to tiles that expand instead of toggling.
Logic for side view: * If the tile has a sideViewCustomDrawable, show that regardless of State * Else, if the tile is not Boolean or the state has forceExpandView, it shows a chevron. * Else, show nothing. Test: atest com.android.systemui.qs Test: manual Fixes: 187057842 Change-Id: Ib9407914f4fbe5ac833b06008fc405435d0c45b0
This commit is contained in:
@@ -163,7 +163,7 @@ public interface QSTile {
|
||||
public SlashState slash;
|
||||
public boolean handlesLongClick = true;
|
||||
public boolean showRippleEffect = true;
|
||||
public Drawable sideViewDrawable;
|
||||
public Drawable sideViewCustomDrawable;
|
||||
|
||||
public boolean copyTo(State other) {
|
||||
if (other == null) throw new IllegalArgumentException();
|
||||
@@ -185,7 +185,7 @@ public interface QSTile {
|
||||
|| !Objects.equals(other.slash, slash)
|
||||
|| !Objects.equals(other.handlesLongClick, handlesLongClick)
|
||||
|| !Objects.equals(other.showRippleEffect, showRippleEffect)
|
||||
|| !Objects.equals(other.sideViewDrawable, sideViewDrawable);
|
||||
|| !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable);
|
||||
other.icon = icon;
|
||||
other.iconSupplier = iconSupplier;
|
||||
other.label = label;
|
||||
@@ -201,7 +201,7 @@ public interface QSTile {
|
||||
other.slash = slash != null ? slash.copy() : null;
|
||||
other.handlesLongClick = handlesLongClick;
|
||||
other.showRippleEffect = showRippleEffect;
|
||||
other.sideViewDrawable = sideViewDrawable;
|
||||
other.sideViewCustomDrawable = sideViewCustomDrawable;
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ public interface QSTile {
|
||||
sb.append(",isTransient=").append(isTransient);
|
||||
sb.append(",state=").append(state);
|
||||
sb.append(",slash=\"").append(slash).append("\"");
|
||||
sb.append(",sideViewDrawable").append(sideViewDrawable);
|
||||
sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable);
|
||||
return sb.append(']');
|
||||
}
|
||||
|
||||
@@ -242,12 +242,16 @@ public interface QSTile {
|
||||
public static class BooleanState extends State {
|
||||
public static final int VERSION = 1;
|
||||
public boolean value;
|
||||
public boolean forceExpandIcon;
|
||||
|
||||
@Override
|
||||
public boolean copyTo(State other) {
|
||||
final BooleanState o = (BooleanState) other;
|
||||
final boolean changed = super.copyTo(other) || o.value != value;
|
||||
final boolean changed = super.copyTo(other)
|
||||
|| o.value != value
|
||||
|| o.forceExpandIcon != forceExpandIcon;
|
||||
o.value = value;
|
||||
o.forceExpandIcon = forceExpandIcon;
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -255,6 +259,7 @@ public interface QSTile {
|
||||
protected StringBuilder toStringBuilder() {
|
||||
final StringBuilder rt = super.toStringBuilder();
|
||||
rt.insert(rt.length() - 1, ",value=" + value);
|
||||
rt.insert(rt.length() - 1, ",forceExpandIcon=" + forceExpandIcon);
|
||||
return rt;
|
||||
}
|
||||
|
||||
|
||||
41
packages/SystemUI/res/layout/qs_tile_side_icon.xml
Normal file
41
packages/SystemUI/res/layout/qs_tile_side_icon.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2021 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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/qs_label_container_margin"
|
||||
android:layout_gravity="center_vertical | end"
|
||||
>
|
||||
<ImageView
|
||||
android:id="@+id/customDrawable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/qs_icon_size"
|
||||
android:layout_marginEnd="@dimen/qs_drawable_end_margin"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chevron"
|
||||
android:layout_width="@dimen/qs_icon_size"
|
||||
android:layout_height="@dimen/qs_icon_size"
|
||||
android:src="@*android:drawable/ic_chevron_end"
|
||||
android:visibility="gone"
|
||||
android:importantForAccessibility="no"
|
||||
/>
|
||||
</FrameLayout>
|
||||
@@ -557,6 +557,7 @@
|
||||
<dimen name="qs_quick_tile_size">60dp</dimen>
|
||||
<dimen name="qs_tile_padding">12dp</dimen>
|
||||
<dimen name="qs_tile_start_padding">16dp</dimen>
|
||||
<dimen name="qs_drawable_end_margin">4dp</dimen>
|
||||
<dimen name="qs_header_gear_translation">16dp</dimen>
|
||||
<dimen name="qs_header_tile_margin_bottom">18dp</dimen>
|
||||
<dimen name="qs_page_indicator_width">16dp</dimen>
|
||||
|
||||
@@ -36,10 +36,17 @@ class CustomizeTileView(
|
||||
secondaryLabel.visibility = getVisibilityState(secondaryLabel.text)
|
||||
}
|
||||
|
||||
var showSideView = true
|
||||
set(value) {
|
||||
field = value
|
||||
if (!showSideView) sideView.visibility = GONE
|
||||
}
|
||||
|
||||
override fun handleStateChanged(state: QSTile.State) {
|
||||
super.handleStateChanged(state)
|
||||
showRippleEffect = false
|
||||
secondaryLabel.visibility = getVisibilityState(state.secondaryLabel)
|
||||
if (!showSideView) sideView.visibility = GONE
|
||||
}
|
||||
|
||||
private fun getVisibilityState(text: CharSequence?): Int {
|
||||
|
||||
@@ -366,6 +366,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
|
||||
// The holder has a tileView, therefore this call is not null
|
||||
holder.getTileAsCustomizeView().changeState(info.state);
|
||||
holder.getTileAsCustomizeView().setShowAppLabel(position > mEditIndex && !info.isSystem);
|
||||
// Don't show the side view for third party tiles, as we don't have the actual state.
|
||||
holder.getTileAsCustomizeView().setShowSideView(position < mEditIndex || info.isSystem);
|
||||
holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
holder.mTileView.setClickable(true);
|
||||
holder.mTileView.setOnClickListener(null);
|
||||
|
||||
@@ -72,10 +72,12 @@ open class QSTileViewImpl @JvmOverloads constructor(
|
||||
private val colorLabelUnavailable =
|
||||
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary)
|
||||
|
||||
private val sideView = ImageView(context)
|
||||
private lateinit var label: TextView
|
||||
protected lateinit var secondaryLabel: TextView
|
||||
private lateinit var labelContainer: IgnorableChildLinearLayout
|
||||
protected lateinit var sideView: ViewGroup
|
||||
private lateinit var customDrawableView: ImageView
|
||||
private lateinit var chevronView: ImageView
|
||||
|
||||
protected var showRippleEffect = true
|
||||
|
||||
@@ -112,22 +114,15 @@ open class QSTileViewImpl @JvmOverloads constructor(
|
||||
addView(_icon, LayoutParams(iconSize, iconSize))
|
||||
|
||||
createAndAddLabels()
|
||||
|
||||
sideView.visibility = GONE
|
||||
val sideViewLayoutParams = LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
marginStart = resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
|
||||
}
|
||||
addView(sideView, sideViewLayoutParams)
|
||||
sideView.adjustViewBounds = true
|
||||
sideView.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
createAndAddSideView()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
updateResources()
|
||||
}
|
||||
|
||||
fun updateResources() {
|
||||
FontSizeUtils.updateFontSize(label, R.dimen.qs_tile_text_size)
|
||||
FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
|
||||
|
||||
@@ -141,13 +136,24 @@ open class QSTileViewImpl @JvmOverloads constructor(
|
||||
val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
|
||||
setPaddingRelative(startPadding, padding, padding, padding)
|
||||
|
||||
val labelMargin = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
|
||||
val labelMargin = resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
|
||||
(labelContainer.layoutParams as MarginLayoutParams).apply {
|
||||
marginStart = labelMargin
|
||||
}
|
||||
|
||||
(sideView.layoutParams as MarginLayoutParams).apply {
|
||||
marginStart = labelMargin
|
||||
}
|
||||
(chevronView.layoutParams as MarginLayoutParams).apply {
|
||||
height = iconSize
|
||||
width = iconSize
|
||||
}
|
||||
|
||||
val endMargin = resources.getDimensionPixelSize(R.dimen.qs_drawable_end_margin)
|
||||
(customDrawableView.layoutParams as MarginLayoutParams).apply {
|
||||
height = iconSize
|
||||
marginEnd = endMargin
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAndAddLabels() {
|
||||
@@ -164,6 +170,14 @@ open class QSTileViewImpl @JvmOverloads constructor(
|
||||
addView(labelContainer)
|
||||
}
|
||||
|
||||
private fun createAndAddSideView() {
|
||||
sideView = LayoutInflater.from(context)
|
||||
.inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
|
||||
customDrawableView = sideView.requireViewById(R.id.customDrawable)
|
||||
chevronView = sideView.requireViewById(R.id.chevron)
|
||||
addView(sideView)
|
||||
}
|
||||
|
||||
fun createTileBackground(): Drawable {
|
||||
ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
|
||||
colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
|
||||
@@ -376,20 +390,28 @@ open class QSTileViewImpl @JvmOverloads constructor(
|
||||
secondaryLabel.setTextColor(getSecondaryLabelColor(state.state))
|
||||
}
|
||||
|
||||
label.isEnabled = !state.disabledByPolicy
|
||||
|
||||
// Right side icon
|
||||
loadSideViewDrawableIfNecessary(state)
|
||||
chevronView.imageTintList = ColorStateList.valueOf(getSecondaryLabelColor(state.state))
|
||||
|
||||
label.isEnabled = !state.disabledByPolicy
|
||||
|
||||
lastState = state.state
|
||||
}
|
||||
|
||||
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
|
||||
if (state.sideViewDrawable != null) {
|
||||
sideView.setImageDrawable(state.sideViewDrawable)
|
||||
sideView.visibility = View.VISIBLE
|
||||
if (state.sideViewCustomDrawable != null) {
|
||||
customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
|
||||
customDrawableView.visibility = VISIBLE
|
||||
chevronView.visibility = GONE
|
||||
} else if (state !is BooleanState || state.forceExpandIcon) {
|
||||
customDrawableView.setImageDrawable(null)
|
||||
customDrawableView.visibility = GONE
|
||||
chevronView.visibility = VISIBLE
|
||||
} else {
|
||||
sideView.setImageDrawable(null)
|
||||
sideView.visibility = GONE
|
||||
customDrawableView.setImageDrawable(null)
|
||||
customDrawableView.visibility = GONE
|
||||
chevronView.visibility = GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
|
||||
|
||||
@Override
|
||||
public SignalState newTileState() {
|
||||
return new SignalState();
|
||||
SignalState s = new SignalState();
|
||||
s.forceExpandIcon = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -175,7 +175,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
|
||||
} else {
|
||||
state.state = Tile.STATE_UNAVAILABLE;
|
||||
}
|
||||
state.sideViewDrawable = isDeviceLocked ? null : mCardViewDrawable;
|
||||
state.sideViewCustomDrawable = isDeviceLocked ? null : mCardViewDrawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
package com.android.systemui.qs.tileimpl
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.service.quicksettings.Tile
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -38,14 +40,20 @@ class QSTileViewImplTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var iconView: QSIconView
|
||||
@Mock
|
||||
private lateinit var customDrawable: Drawable
|
||||
|
||||
private lateinit var tileView: FakeTileView
|
||||
private lateinit var customDrawableView: View
|
||||
private lateinit var chevronView: View
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
tileView = FakeTileView(context, iconView, false)
|
||||
customDrawableView = tileView.requireViewById(R.id.customDrawable)
|
||||
chevronView = tileView.requireViewById(R.id.chevron)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -145,6 +153,71 @@ class QSTileViewImplTest : SysuiTestCase() {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShowCustomDrawableViewBooleanState() {
|
||||
val state = QSTile.BooleanState()
|
||||
state.sideViewCustomDrawable = customDrawable
|
||||
|
||||
tileView.changeState(state)
|
||||
|
||||
assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
|
||||
assertThat(chevronView.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShowCustomDrawableViewNonBooleanState() {
|
||||
val state = QSTile.State()
|
||||
state.sideViewCustomDrawable = customDrawable
|
||||
|
||||
tileView.changeState(state)
|
||||
|
||||
assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
|
||||
assertThat(chevronView.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShowCustomDrawableViewBooleanStateForceChevron() {
|
||||
val state = QSTile.BooleanState()
|
||||
state.sideViewCustomDrawable = customDrawable
|
||||
state.forceExpandIcon = true
|
||||
|
||||
tileView.changeState(state)
|
||||
|
||||
assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
|
||||
assertThat(chevronView.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShowChevronNonBooleanState() {
|
||||
val state = QSTile.State()
|
||||
|
||||
tileView.changeState(state)
|
||||
|
||||
assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
|
||||
assertThat(chevronView.visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShowChevronBooleanStateForcheShow() {
|
||||
val state = QSTile.BooleanState()
|
||||
state.forceExpandIcon = true
|
||||
|
||||
tileView.changeState(state)
|
||||
|
||||
assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
|
||||
assertThat(chevronView.visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoImageShown() {
|
||||
val state = QSTile.BooleanState()
|
||||
|
||||
tileView.changeState(state)
|
||||
|
||||
assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
|
||||
assertThat(chevronView.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
|
||||
class FakeTileView(
|
||||
context: Context,
|
||||
icon: QSIconView,
|
||||
|
||||
@@ -271,7 +271,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
mContext.getString(R.string.wallet_secondary_label_device_locked),
|
||||
state.secondaryLabel);
|
||||
assertNotNull(state.stateDescription);
|
||||
assertNull(state.sideViewDrawable);
|
||||
assertNull(state.sideViewCustomDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -287,7 +287,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
mContext.getString(R.string.wallet_secondary_label_active),
|
||||
state.secondaryLabel);
|
||||
assertNotNull(state.stateDescription);
|
||||
assertNotNull(state.sideViewDrawable);
|
||||
assertNotNull(state.sideViewCustomDrawable);
|
||||
}
|
||||
|
||||
|
||||
@@ -303,7 +303,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
mContext.getString(R.string.wallet_secondary_label_no_card),
|
||||
state.secondaryLabel);
|
||||
assertNotNull(state.stateDescription);
|
||||
assertNull(state.sideViewDrawable);
|
||||
assertNull(state.sideViewCustomDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -315,7 +315,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
|
||||
assertEquals(Tile.STATE_UNAVAILABLE, state.state);
|
||||
assertNull(state.stateDescription);
|
||||
assertNull(state.sideViewDrawable);
|
||||
assertNull(state.sideViewCustomDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -342,7 +342,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
|
||||
setUpWalletCard(/* hasCard= */ true);
|
||||
|
||||
assertNotNull(mTile.getState().sideViewDrawable);
|
||||
assertNotNull(mTile.getState().sideViewCustomDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -362,7 +362,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithCards);
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNotNull(mTile.getState().sideViewDrawable);
|
||||
assertNotNull(mTile.getState().sideViewCustomDrawable);
|
||||
|
||||
mTile.handleSetListening(true);
|
||||
|
||||
@@ -373,14 +373,14 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithoutCards);
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNull(mTile.getState().sideViewDrawable);
|
||||
assertNull(mTile.getState().sideViewCustomDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryCards_noCards_notUpdateSideViewDrawable() {
|
||||
setUpWalletCard(/* hasCard= */ false);
|
||||
|
||||
assertNull(mTile.getState().sideViewDrawable);
|
||||
assertNull(mTile.getState().sideViewCustomDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -395,7 +395,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
mCallbackCaptor.getValue().onWalletCardRetrievalError(error);
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNull(mTile.getState().sideViewDrawable);
|
||||
assertNull(mTile.getState().sideViewCustomDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user