Change QQS to use tiles with side labels

* 2x2 in portrait, 1x4 in landscape
* 4 columns in landscape QS

Other fixes while changing this:
* Animations between QQS and QS look better
* Fix alignment of tiles labels
* Improve line logic for labels

Test: manual
Test: atest SystemUITests
Bug: 171319433
Bug: 180632445
Fixes: 180472943
Fixes: 180191343
Change-Id: I67fbe27796461bee7d2364f81b47f2efd31e4b41
This commit is contained in:
Fabian Kozynski
2021-02-17 15:47:10 -05:00
parent 5ba00654f4
commit e15017ed14
15 changed files with 123 additions and 77 deletions

View File

@@ -20,7 +20,6 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:minHeight="48dp"
android:paddingTop="12dp">
<LinearLayout
android:id="@+id/label_group"

View File

@@ -91,6 +91,9 @@
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
<!-- The number of columns in the Quick Settings customizer -->
<integer name="quick_settings_edit_num_columns">@integer/quick_settings_num_columns</integer>
<!-- The number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">3</integer>

View File

@@ -29,7 +29,6 @@ import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.tileimpl.QSTileBaseView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -56,7 +55,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
private final ArrayList<View> mAllViews = new ArrayList<>();
/**
* List of {@link View}s representing Quick Settings that are being animated from the quick QS
* position to the normal QS panel.
* position to the normal QS panel. These views will only show once the animation is complete,
* to prevent overlapping of semi transparent views
*/
private final ArrayList<View> mQuickQsViews = new ArrayList<>();
private final QuickQSPanel mQuickQsPanel;
@@ -233,7 +233,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
// Quick tiles.
QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
if (quickTileView == null) continue;
View qqsBgCircle = ((QSTileBaseView) quickTileView).getBgCircle();
getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
getRelativePosition(loc2, tileIcon, view);
@@ -255,11 +254,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
if (mFeatureFlags.isQSLabelsEnabled()) {
firstPageBuilder.addFloat(qqsBgCircle, "alpha", 1, 1, 0);
mAllViews.add(qqsBgCircle);
}
} else { // These tiles disappear when expanding
firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
@@ -271,7 +265,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationX);
}
mQuickQsViews.add(tileView.getIconWithBackground());
if (mFeatureFlags.isQSLabelsEnabled()) {
mQuickQsViews.add(tileView);
} else {
mQuickQsViews.add(tileView.getIconWithBackground());
}
mAllViews.add(tileView.getIcon());
mAllViews.add(quickTileView);
} else if (mFullRows && isIconInAnimatedRow(count)) {
@@ -362,7 +360,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if(view == parent || view == null) return;
// Ignore tile pages as they can have some offset we don't want to take into account in
// RTL.
if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) {
if (!isAPage(view)) {
loc1[0] += view.getLeft();
loc1[1] += view.getTop();
}
@@ -374,6 +372,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
getRelativePositionInt(loc1, (View) view.getParent(), parent);
}
// Returns true if the view is a possible page in PagedTileLayout
private boolean isAPage(View view) {
if (view instanceof PagedTileLayout.TilePage) {
return true;
} else if (view instanceof SideLabelTileLayout) {
return !(view instanceof QuickQSPanel.QQSSideLabelTileLayout);
}
return false;
}
public void setPosition(float position) {
if (mNeedsAnimatorUpdate) {
updateAnimators();

View File

@@ -112,7 +112,7 @@ public class QSPanel extends LinearLayout implements Tunable {
private int mMediaTotalBottomMargin;
private int mFooterMarginStartHorizontal;
private Consumer<Boolean> mMediaVisibilityChangedListener;
private boolean mSideLabels;
protected boolean mSideLabels;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -201,16 +201,20 @@ public class QSPanel extends LinearLayout implements Tunable {
mFooterPageIndicator.setNumPages(((PagedTileLayout) mTileLayout).getNumPages());
}
// Allow the UI to be as big as it want's to, we're in a scroll view
int newHeight = 10000;
int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
int excessHeight = newHeight - availableHeight;
// Measure with EXACTLY. That way, The content will only use excess height and will
// be measured last, after other views and padding is accounted for. This only
// works because our Layouts in here remeasure themselves with the exact content
// height.
heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight);
// In landscape, mTileLayout's parent is not the panel but a view that contains the
// tile layout and the media controls.
if (((View) mTileLayout).getParent() == this) {
// Allow the UI to be as big as it want's to, we're in a scroll view
int newHeight = 10000;
int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
int excessHeight = newHeight - availableHeight;
// Measure with EXACTLY. That way, The content will only use excess height and will
// be measured last, after other views and padding is accounted for. This only
// works because our Layouts in here remeasure themselves with the exact content
// height.
heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -758,7 +762,7 @@ public class QSPanel extends LinearLayout implements Tunable {
// Let's use 3 columns to match the current layout
int columns;
if (mSideLabels) {
columns = horizontal ? 1 : 2;
columns = horizontal ? 2 : 4;
} else {
columns = horizontal ? 3 : TileLayout.NO_MAX_COLUMNS;
}

View File

@@ -18,7 +18,6 @@ package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import android.annotation.NonNull;
@@ -66,7 +65,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private BrightnessMirrorController mBrightnessMirrorController;
private boolean mGridContentVisible = true;
private boolean mQsLabelsFlag;
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
new QSPanel.OnConfigurationChangedListener() {
@@ -93,7 +91,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSlider.Factory brightnessSliderFactory,
@Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags);
@@ -108,9 +105,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mView.setBrightnessView(mBrightnessSlider.getRootView());
mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider);
mQsLabelsFlag = qsLabelsFlag;
mSideLabels = qsLabelsFlag;
}
@Override
@@ -329,7 +323,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
@Override
public void onTuningChanged(String key, String newValue) {
if (QS_REMOVE_LABELS.equals(key)) {
if (!mQsLabelsFlag) return;
if (!mQSLabelFlag) return;
boolean newShowLabels = newValue == null || "0".equals(newValue);
if (mShowLabels == newShowLabels) return;
mShowLabels = newShowLabels;

View File

@@ -75,7 +75,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
private final QSHost.Callback mQSHostCallback = this::setTiles;
protected boolean mShowLabels = true;
protected boolean mSideLabels;
protected boolean mQSLabelFlag;
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
new QSPanel.OnConfigurationChangedListener() {
@@ -118,11 +118,12 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mQSLogger = qsLogger;
mDumpManager = dumpManager;
mFeatureFlags = featureFlags;
mQSLabelFlag = featureFlags.isQSLabelsEnabled();
}
@Override
protected void onInit() {
mView.initialize(mSideLabels);
mView.initialize(mQSLabelFlag);
mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
}

View File

@@ -69,12 +69,22 @@ public class QuickQSPanel extends QSPanel {
@Override
public TileLayout createRegularTileLayout() {
return new QuickQSPanel.HeaderTileLayout(mContext);
if (mSideLabels) {
return new QQSSideLabelTileLayout(mContext);
} else {
return new QuickQSPanel.HeaderTileLayout(mContext);
}
}
@Override
protected QSTileLayout createHorizontalTileLayout() {
return new DoubleLineTileLayout(mContext);
if (mSideLabels) {
TileLayout t = createRegularTileLayout();
t.setMaxColumns(2);
return t;
} else {
return new DoubleLineTileLayout(mContext);
}
}
@Override
@@ -331,4 +341,38 @@ public class QuickQSPanel extends QSPanel {
}
}
}
static class QQSSideLabelTileLayout extends SideLabelTileLayout {
QQSSideLabelTileLayout(Context context) {
super(context, null);
setClipChildren(false);
setClipToPadding(false);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.CENTER_HORIZONTAL;
setLayoutParams(lp);
setMaxColumns(4);
}
@Override
public boolean updateResources() {
boolean b = super.updateResources();
mMaxAllowedRows = 2;
return b;
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateResources();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Make sure to always use the correct number of rows. As it's determined by the
// columns, just use as many as needed.
updateMaxRows(10000, mRecords.size());
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}

View File

@@ -17,7 +17,6 @@
package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import com.android.internal.logging.MetricsLogger;
@@ -42,8 +41,6 @@ import javax.inject.Named;
@QSScope
public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
private boolean mUseSideLabels;
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
newConfig -> {
int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
@@ -58,12 +55,10 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
FeatureFlags featureFlags
DumpManager dumpManager, FeatureFlags featureFlags
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
uiEventLogger, qsLogger, dumpManager, featureFlags);
mUseSideLabels = qsLabelsFlag;
}
@Override
@@ -104,19 +99,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
break;
}
}
if (mUseSideLabels) {
List<QSTile> newTiles = new ArrayList<>();
for (int i = 0; i < tiles.size(); i += 2) {
newTiles.add(tiles.get(i));
}
for (int i = 1; i < tiles.size(); i += 2) {
newTiles.add(tiles.get(i));
}
super.setTiles(newTiles, true);
} else {
super.setTiles(tiles, true);
}
super.setTiles(tiles, !mQSLabelFlag);
}
/** */

View File

@@ -20,11 +20,13 @@ import android.content.Context
import android.util.AttributeSet
import com.android.systemui.R
open class SideLabelTileLayout(context: Context, attrs: AttributeSet) : TileLayout(context, attrs) {
open class SideLabelTileLayout(
context: Context,
attrs: AttributeSet?
) : TileLayout(context, attrs) {
override fun updateResources(): Boolean {
return super.updateResources().also {
mResourceColumns = 2
mMaxAllowedRows = 4
mCellMarginHorizontal = (mCellMarginHorizontal * 1.2).toInt()
mCellMarginVertical = mCellMarginHorizontal

View File

@@ -124,6 +124,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public boolean updateResources() {
final Resources res = mContext.getResources();
mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
updateColumns();
mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);

View File

@@ -75,6 +75,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
private static final int ACTION_ADD = 1;
private static final int ACTION_MOVE = 2;
private static final int NUM_COLUMNS_ID = R.integer.quick_settings_edit_num_columns;
private final Context mContext;
private final Handler mHandler = new Handler();
@@ -87,6 +89,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
private int mEditIndex;
private int mTileDividerIndex;
private int mFocusIndex;
private boolean mNeedsFocus;
private List<String> mCurrentSpecs;
private List<TileInfo> mOtherTiles;
@@ -109,7 +112,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
mDecoration = new TileItemDecoration(context);
mMarginDecoration = new MarginTileDecoration();
mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
mNumColumns = context.getResources().getInteger(R.integer.quick_settings_num_columns);
mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID);
mAccessibilityDelegate = new TileAdapterDelegate();
}
@@ -129,7 +132,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
* @return {@code true} if the number of columns changed, {@code false} otherwise
*/
public boolean updateNumColumns() {
int numColumns = mContext.getResources().getInteger(R.integer.quick_settings_num_columns);
int numColumns = mContext.getResources().getInteger(NUM_COLUMNS_ID);
if (numColumns != mNumColumns) {
mNumColumns = numColumns;
return true;

View File

@@ -105,24 +105,27 @@ public class QSTileView extends QSTileBaseView {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mLabel.setSingleLine(false);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Remeasure view if the primary label requires more then 2 lines or the secondary label
// text will be cut off.
if (mLabel.getLineCount() > mMaxLabelLines || !TextUtils.isEmpty(mSecondLine.getText())
&& mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
if (!mLabel.isSingleLine()) {
mLabel.setSingleLine();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
} else {
if (mLabel.isSingleLine()) {
mLabel.setSingleLine(false);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// Remeasure view if the primary label requires more than mMaxLabelLines lines or the
// secondary label text will be cut off.
if (shouldLabelBeSingleLine()) {
mLabel.setSingleLine();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
private boolean shouldLabelBeSingleLine() {
if (mLabel.getLineCount() > mMaxLabelLines) {
return true;
} else if (!TextUtils.isEmpty(mSecondLine.getText())
&& mLabel.getLineCount() > mMaxLabelLines - 1) {
return true;
}
return false;
}
@Override
protected void handleStateChanged(QSTile.State state) {
super.handleStateChanged(state);

View File

@@ -24,8 +24,8 @@ import android.graphics.drawable.PaintDrawable
import android.graphics.drawable.RippleDrawable
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.view.Gravity
import android.view.View
import android.widget.LinearLayout
import android.widget.RelativeLayout
import com.android.systemui.R
import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
@@ -37,7 +37,6 @@ class QSTileViewHorizontal(
) : QSTileView(context, icon, false) {
private var paintDrawable: PaintDrawable? = null
private var divider: View? = null
init {
orientation = HORIZONTAL
@@ -49,7 +48,12 @@ class QSTileViewHorizontal(
override fun createLabel() {
super.createLabel()
findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START
findViewById<LinearLayout>(R.id.label_group)?.apply {
gravity = Gravity.START
(layoutParams as? RelativeLayout.LayoutParams)?.apply {
removeRule(RelativeLayout.ALIGN_PARENT_TOP)
}
}
mLabel.gravity = Gravity.START
mLabel.textDirection = TEXT_DIRECTION_LOCALE
mSecondLine.gravity = Gravity.START
@@ -57,7 +61,7 @@ class QSTileViewHorizontal(
val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
mLabelContainer.setPaddingRelative(0, padding, padding, padding)
(mLabelContainer.layoutParams as LayoutParams).gravity =
Gravity.CENTER_VERTICAL or Gravity.START
Gravity.CENTER_VERTICAL or Gravity.START
}
override fun updateRippleSize() {
@@ -93,7 +97,6 @@ class QSTileViewHorizontal(
paintDrawable?.setTint(getCircleColor(state.state))
mSecondLine.setTextColor(mLabel.textColors)
mLabelContainer.background = null
divider?.backgroundTintList = mLabel.textColors
}
override fun handleExpand(dualTarget: Boolean) {}

View File

@@ -121,7 +121,6 @@ public class QSPanelControllerTest extends SysuiTestCase {
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
/* labelsFlag */ false,
mFeatureFlags
);

View File

@@ -87,7 +87,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
uiEventLogger,
qsLogger,
dumpManager,
false,
featureFlags
)