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:
Fabian Kozynski
2021-05-04 11:36:08 -04:00
parent 9c8de7bdf1
commit cd7ecf7db2
10 changed files with 190 additions and 37 deletions

View File

@@ -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;
}

View 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>

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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