diff options
16 files changed, 357 insertions, 58 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java index 53f7e44bc25a..ca1320448726 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java @@ -50,4 +50,8 @@ public abstract class QSTileView extends LinearLayout { public abstract void onStateChanged(State state); public abstract int getDetailY(); + + public View getLabelContainer() { + return null; + } } diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/packages/SystemUI/res/drawable/qs_tile_background.xml new file mode 100644 index 000000000000..265f575fc99c --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_tile_background.xml @@ -0,0 +1,23 @@ +<?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. + --> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask" + android:drawable="@drawable/qs_tile_background_shape" /> + <item android:id="@id/background" + android:drawable="@drawable/qs_tile_background_shape"/> +</ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_tile_background_shape.xml b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml new file mode 100644 index 000000000000..f6b68347124e --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml @@ -0,0 +1,21 @@ +<?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. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="@dimen/qs_corner_radius" /> + <solid android:color="#FFFFFF" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 885cd254a1ea..2062104be2df 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -512,6 +512,7 @@ <!-- The size of the gesture span needed to activate the "pull" notification expansion --> <dimen name="pull_span_min">25dp</dimen> + <dimen name="qs_corner_radius">14dp</dimen> <dimen name="qs_tile_height">96dp</dimen> <!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 --> <dimen name="qs_tile_layout_margin_side">18dp</dimen> @@ -528,6 +529,8 @@ <dimen name="qs_tile_margin_top">0dp</dimen> <dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen> <dimen name="qs_tile_background_size">44dp</dimen> + <dimen name="qs_icon_size">20dp</dimen> + <dimen name="qs_label_container_margin">10dp</dimen> <dimen name="qs_quick_tile_size">48dp</dimen> <dimen name="qs_quick_tile_padding">12dp</dimen> <dimen name="qs_header_gear_translation">16dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index fe76668ab68b..b8823e148e68 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -14,6 +14,8 @@ package com.android.systemui.qs; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; import android.util.Log; import android.view.View; import android.view.View.OnAttachStateChangeListener; @@ -35,6 +37,7 @@ import com.android.systemui.tuner.TunerService.Tunable; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.concurrent.Executor; import javax.inject.Inject; @@ -77,6 +80,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // This animates fading of SecurityFooter and media divider private TouchAnimator mAllPagesDelayedAnimator; private TouchAnimator mBrightnessAnimator; + private HeightExpansionAnimator mQQSTileHeightAnimator; + private HeightExpansionAnimator mOtherTilesExpandAnimator; + private boolean mNeedsAnimatorUpdate = false; private boolean mOnKeyguard; @@ -161,7 +167,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @Override public void onViewAttachedToWindow(View v) { mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION, - MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES); + MOVE_FULL_ROWS); } @Override @@ -179,9 +185,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } } else if (MOVE_FULL_ROWS.equals(key)) { mFullRows = TunerService.parseIntegerSwitch(newValue, true); - } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) { - mNumQuickTiles = QuickQSPanel.parseNumTiles(newValue); - clearAnimationState(); } updateAnimators(); } @@ -209,6 +212,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha clearAnimationState(); mAllViews.clear(); mQuickQsViews.clear(); + mQQSTileHeightAnimator = null; + mOtherTilesExpandAnimator = null; + + mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(); QSTileLayout tileLayout = mQsPanelController.getTileLayout(); mAllViews.add((View) tileLayout); @@ -218,6 +225,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha + mQs.getHeader().getPaddingBottom(); firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0); + boolean qsSideLabelsEnabled = mFeatureFlags.isQSLabelsEnabled(); + int qqsTileHeight = 0; + for (QSTile tile : tiles) { QSTileView tileView = mQsPanelController.getTileView(tile); if (tileView == null) { @@ -237,22 +247,47 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view); getRelativePosition(loc2, tileIcon, view); final int xDiff = loc2[0] - loc1[0]; - final int yDiff = loc2[1] - loc1[1]; - + int yDiff = loc2[1] - loc1[1]; if (count < tileLayout.getNumVisibleTiles()) { + getRelativePosition(loc1, quickTileView, view); + getRelativePosition(loc2, tileView, view); + int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0; // Move the quick tile right from its location to the new one. - translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff); - translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff); - - // Counteract the parent translation on the tile. So we have a static base to - // animate the label position off from. - //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0); + View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView; + translationXBuilder.addFloat(v, "translationX", 0, xDiff); + translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset); + mAllViews.add(v); // Move the real tile from the quick tile position to its final // location. - translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0); - translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0); + v = qsSideLabelsEnabled ? tileIcon : tileView; + translationXBuilder.addFloat(v, "translationX", -xDiff, 0); + translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0); + + if (qsSideLabelsEnabled) { + translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset); + translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0); + + if (mQQSTileHeightAnimator == null) { + mQQSTileHeightAnimator = new HeightExpansionAnimator(this, + quickTileView.getHeight(), tileView.getHeight()); + qqsTileHeight = quickTileView.getHeight(); + } + + mQQSTileHeightAnimator.addView(quickTileView); + View qqsLabelContainer = quickTileView.getLabelContainer(); + View qsLabelContainer = tileView.getLabelContainer(); + + getRelativePosition(loc1, qqsLabelContainer, view); + getRelativePosition(loc2, qsLabelContainer, view); + yDiff = loc2[1] - loc1[1] - yOffset; + + translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff); + translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0); + mAllViews.add(qqsLabelContainer); + mAllViews.add(qsLabelContainer); + } } else { // These tiles disappear when expanding firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0); @@ -265,7 +300,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationX); } - if (mFeatureFlags.isQSLabelsEnabled()) { + if (qsSideLabelsEnabled) { mQuickQsViews.add(tileView); } else { mQuickQsViews.add(tileView.getIconWithBackground()); @@ -278,9 +313,29 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllViews.add(tileIcon); } else { - firstPageBuilder.addFloat(tileView, "alpha", 0, 1); - firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0); + if (!qsSideLabelsEnabled) { + firstPageBuilder.addFloat(tileView, "alpha", 0, 1); + firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0); + } else { + // Pretend there's a corresponding QQS tile (for the position) that we are + // expanding from. + SideLabelTileLayout qqsLayout = + (SideLabelTileLayout) mQuickQsPanel.getTileLayout(); + getRelativePosition(loc1, qqsLayout, view); + getRelativePosition(loc2, tileView, view); + int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count)); + translationYBuilder.addFloat(tileView, "translationY", -diff, 0); + if (mOtherTilesExpandAnimator == null) { + mOtherTilesExpandAnimator = + new HeightExpansionAnimator( + this, qqsTileHeight, tileView.getHeight()); + } + mOtherTilesExpandAnimator.addView(tileView); + tileView.setClipChildren(true); + tileView.setClipToPadding(true); + } } + mAllViews.add(tileView); count++; } @@ -303,7 +358,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha .build(); // Fade in the tiles/labels as we reach the final position. Builder builder = new Builder() - .setStartDelay(EXPANDED_TILE_DELAY) + .setStartDelay(qsSideLabelsEnabled ? 0 : EXPANDED_TILE_DELAY) .addFloat(tileLayout, "alpha", 0, 1); mFirstPageDelayedAnimator = builder.build(); @@ -331,6 +386,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator()); mTranslationXAnimator = translationXBuilder.build(); mTranslationYAnimator = translationYBuilder.build(); + if (mQQSTileHeightAnimator != null) { + mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator()); + } + if (mOtherTilesExpandAnimator != null) { + mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator()); + } } mNonfirstPageAnimator = new TouchAnimator.Builder() .addFloat(mQuickQsPanel, "alpha", 1, 0) @@ -404,6 +465,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha if (mBrightnessAnimator != null) { mBrightnessAnimator.setPosition(position); } + if (mQQSTileHeightAnimator != null) { + mQQSTileHeightAnimator.setPosition(position); + } + if (mOtherTilesExpandAnimator != null) { + mOtherTilesExpandAnimator.setPosition(position); + } } else { mNonfirstPageAnimator.setPosition(position); mNonfirstPageDelayedAnimator.setPosition(position); @@ -446,6 +513,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha v.setAlpha(1); v.setTranslationX(0); v.setTranslationY(0); + if (v instanceof SideLabelTileLayout) { + ((SideLabelTileLayout) v).setClipChildren(false); + ((SideLabelTileLayout) v).setClipToPadding(false); + } + } + if (mQQSTileHeightAnimator != null) { + mQQSTileHeightAnimator.resetViewsHeights(); + } + if (mOtherTilesExpandAnimator != null) { + mOtherTilesExpandAnimator.resetViewsHeights(); } final int N2 = mQuickQsViews.size(); for (int i = 0; i < N2; i++) { @@ -483,4 +560,61 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha updateAnimators(); setCurrentPosition(); }; + + static class HeightExpansionAnimator { + private final List<View> mViews = new ArrayList<>(); + private final ValueAnimator mAnimator; + private final TouchAnimator.Listener mListener; + + private final ValueAnimator.AnimatorUpdateListener mUpdateListener = + new ValueAnimator.AnimatorUpdateListener() { + float mLastT = -1; + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + float t = valueAnimator.getAnimatedFraction(); + if (t == 0f) { + mListener.onAnimationAtStart(); + } else if (t == 1f) { + mListener.onAnimationAtEnd(); + } else if (mLastT <= 0 || mLastT == 1) { + mListener.onAnimationStarted(); + } + mLastT = t; + final int viewCount = mViews.size(); + int height = (Integer) valueAnimator.getAnimatedValue(); + for (int i = 0; i < viewCount; i++) { + View v = mViews.get(i); + v.setBottom(v.getTop() + height); + } + } + }; + + HeightExpansionAnimator(TouchAnimator.Listener listener, int startHeight, int endHeight) { + mListener = listener; + mAnimator = ValueAnimator.ofInt(startHeight, endHeight); + mAnimator.setRepeatCount(ValueAnimator.INFINITE); + mAnimator.setRepeatMode(ValueAnimator.REVERSE); + mAnimator.addUpdateListener(mUpdateListener); + } + + void addView(View v) { + mViews.add(v); + } + + void setInterpolator(TimeInterpolator interpolator) { + mAnimator.setInterpolator(interpolator); + } + + void setPosition(float position) { + mAnimator.setCurrentFraction(position); + } + + void resetViewsHeights() { + final int viewsCount = mViews.size(); + for (int i = 0; i < viewsCount; i++) { + View v = mViews.get(i); + v.setBottom(v.getTop() + v.getMeasuredHeight()); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index ed308ae1ac6c..7c9f0b082d8f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -430,6 +430,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSPanelController.setRevealExpansion(expansion); mQSPanelController.getTileLayout().setExpansion(expansion); + mQuickQSPanelController.getTileLayout().setExpansion(expansion); mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff); if (fullyCollapsed) { mQSPanelScrollView.setScrollY(0); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index ff9b9120c6e1..c794a2121cc2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -315,7 +315,7 @@ public class QSPanel extends LinearLayout implements Tunable { int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size); mFooterMarginStartHorizontal = getResources().getDimensionPixelSize( R.dimen.qs_footer_horizontal_margin); - mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f); + mVisualTilePadding = mSideLabels ? 0 : (int) ((tileSize - tileBg) / 2.0f); updatePadding(); updatePageIndicator(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index f51d7ef381ee..8f99a9e9c1be 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -348,6 +348,7 @@ public class QuickQSPanel extends QSPanel { } static class QQSSideLabelTileLayout extends SideLabelTileLayout { + QQSSideLabelTileLayout(Context context) { super(context, null); setClipChildren(false); @@ -361,6 +362,7 @@ public class QuickQSPanel extends QSPanel { @Override public boolean updateResources() { + mCellHeightResId = R.dimen.qs_quick_tile_size; boolean b = super.updateResources(); mMaxAllowedRows = 2; return b; @@ -379,5 +381,38 @@ public class QuickQSPanel extends QSPanel { updateMaxRows(10000, mRecords.size()); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } + + @Override + public void setListening(boolean listening, UiEventLogger uiEventLogger) { + boolean startedListening = !mListening && listening; + super.setListening(listening, uiEventLogger); + if (startedListening) { + // getNumVisibleTiles() <= mRecords.size() + for (int i = 0; i < getNumVisibleTiles(); i++) { + QSTile tile = mRecords.get(i).tile; + uiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0, + tile.getMetricsSpec(), tile.getInstanceId()); + } + } + } + + @Override + public void setExpansion(float expansion) { + if (expansion > 0f && expansion < 1f) { + return; + } + boolean selected = expansion == 0f; + // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this + // point we want them to be selected so the tiles will marquee (but not at other points + // of expansion. + // We set it as not important while we change this, so setting each tile as selected + // will not cause them to announce themselves until the user has actually selected the + // item. + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + for (int i = 0; i < getChildCount(); i++) { + getChildAt(i).setSelected(selected); + } + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index 671f8f7dd2d1..fee56b984ecc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -99,7 +99,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> break; } } - super.setTiles(tiles, !mQSLabelFlag); + super.setTiles(tiles, /* collapsedView */ true); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt index 52f111e7ab48..0ebadfd2fa11 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt @@ -33,4 +33,15 @@ open class SideLabelTileLayout( override fun isFull(): Boolean { return mRecords.size >= maxTiles() } + + /** + * Return the position from the top of the layout of the tile with this index. + * + * This will return a position even for indices that go beyond [maxTiles], continuing the rows + * beyond that. + */ + fun getPhantomTopPosition(index: Int): Int { + val row = index / mColumns + return getRowTop(row) + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index c1ce4a577dda..9e0aa5ad78b1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -25,6 +25,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { protected int mColumns; protected int mCellWidth; + protected int mCellHeightResId = R.dimen.qs_tile_height; protected int mCellHeight; protected int mMaxCellHeight; protected int mCellMarginHorizontal; @@ -118,7 +119,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { 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); + mMaxCellHeight = mContext.getResources().getDimensionPixelSize(mCellHeightResId); mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal); mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top); @@ -235,7 +236,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { layoutTileRecords(mRecords.size()); } - private int getRowTop(int row) { + protected int getRowTop(int row) { return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt index f8c0dd4239d9..7977b4904a7d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt @@ -14,7 +14,7 @@ import com.android.systemui.qs.tileimpl.QSTileViewHorizontal class CustomizeTileViewHorizontal( context: Context, icon: QSIconView -) : QSTileViewHorizontal(context, icon), +) : QSTileViewHorizontal(context, icon, collapsed = false), TileAdapter.CustomizeView { private var showAppLabel = false @@ -27,6 +27,7 @@ class CustomizeTileViewHorizontal( override fun handleStateChanged(state: QSTile.State) { super.handleStateChanged(state) + mShowRippleEffect = false mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 9e5fe732cd0c..29b9e64d1659 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -248,10 +248,10 @@ public class QSFactoryImpl implements QSFactory { public QSTileView createTileView(QSTile tile, boolean collapsedView) { Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme); QSIconView icon = tile.createTileView(context); - if (collapsedView) { + if (mSideLabels) { + return new QSTileViewHorizontal(context, icon, collapsedView); + } else if (collapsedView) { return new QSTileBaseView(context, icon, collapsedView); - } else if (mSideLabels) { - return new QSTileViewHorizontal(context, icon); } else { return new com.android.systemui.qs.tileimpl.QSTileView(context, icon); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index 33ca7d6bafd8..a45b131902c8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -58,13 +58,13 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask; protected final Handler mHandler = new H(); private final int[] mLocInScreen = new int[2]; - private final FrameLayout mIconFrame; + protected final FrameLayout mIconFrame; protected QSIconView mIcon; protected RippleDrawable mRipple; protected Drawable mTileBackground; private String mAccessibilityClass; private boolean mTileState; - private boolean mCollapsedView; + protected boolean mCollapsedView; protected boolean mShowRippleEffect = true; private float mStrokeWidthActive; private float mStrokeWidthInactive; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java index b59326ae56d5..c7ed89ba49b1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java @@ -26,6 +26,8 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.settingslib.Utils; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; @@ -116,7 +118,8 @@ public class QSTileView extends QSTileBaseView { } } - private boolean shouldLabelBeSingleLine() { + protected boolean shouldLabelBeSingleLine() { + if (mCollapsedView) return true; if (mLabel.getLineCount() > mMaxLabelLines) { return true; } else if (!TextUtils.isEmpty(mSecondLine.getText()) @@ -138,14 +141,14 @@ public class QSTileView extends QSTileBaseView { } else { labelColor = mColorLabelUnavailable; } - mLabel.setTextColor(labelColor); + changeLabelColor(labelColor); mState = state.state; mLabel.setText(state.label); } if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) { mSecondLine.setText(state.secondaryLabel); - mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE - : View.VISIBLE); + mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) || mCollapsedView + ? View.GONE : View.VISIBLE); } boolean dualTarget = mDualTargetAllowed && state.dualTarget; handleExpand(dualTarget); @@ -160,6 +163,10 @@ public class QSTileView extends QSTileBaseView { mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE); } + protected void changeLabelColor(ColorStateList color) { + mLabel.setTextColor(color); + } + protected void handleExpand(boolean dualTarget) { mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE); mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE); @@ -178,4 +185,10 @@ public class QSTileView extends QSTileBaseView { public TextView getAppLabel() { return mSecondLine; } + + @Nullable + @Override + public View getLabelContainer() { + return mLabelContainer; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt index 328c2c353a29..32285cf797e4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt @@ -21,8 +21,7 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.Drawable -import android.graphics.drawable.ShapeDrawable -import android.graphics.drawable.shapes.RoundRectShape +import android.graphics.drawable.RippleDrawable import android.service.quicksettings.Tile.STATE_ACTIVE import android.view.Gravity import android.widget.LinearLayout @@ -32,25 +31,31 @@ import com.android.systemui.plugins.qs.QSIconView import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState -// Placeholder -private const val CORNER_RADIUS = 40f -private val RADII = (1..8).map { CORNER_RADIUS }.toFloatArray() - open class QSTileViewHorizontal( context: Context, - icon: QSIconView -) : QSTileView(context, icon, false) { + icon: QSIconView, + collapsed: Boolean +) : QSTileView(context, icon, collapsed) { - protected var backgroundDrawable: ShapeDrawable? = null + protected var colorBackgroundDrawable: Drawable? = null private var paintColor = Color.WHITE private var paintAnimator: ValueAnimator? = null + private var labelAnimator: ValueAnimator? = null init { orientation = HORIZONTAL + gravity = Gravity.CENTER_VERTICAL or Gravity.START mDualTargetAllowed = false + val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding) + setPadding(padding, paddingTop, padding, paddingBottom) + mBg.setImageDrawable(null) + mIconFrame.removeAllViews() + removeView(mIconFrame) + val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size) + addView(mIcon, 0, LayoutParams(iconSize, iconSize)) + mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE)) - mMaxLabelLines = 3 } override fun createLabel() { @@ -61,65 +66,112 @@ open class QSTileViewHorizontal( removeRule(RelativeLayout.ALIGN_PARENT_TOP) } } + mLabelContainer.setPadding(0, 0, 0, 0) + (mLabelContainer.layoutParams as MarginLayoutParams).apply { + marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin) + } mLabel.gravity = Gravity.START mLabel.textDirection = TEXT_DIRECTION_LOCALE mSecondLine.gravity = Gravity.START mSecondLine.textDirection = TEXT_DIRECTION_LOCALE - 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 + if (mCollapsedView) { + mSecondLine.visibility = GONE + } + } + + override fun shouldLabelBeSingleLine(): Boolean { + return true } override fun updateRippleSize() { } override fun newTileBackground(): Drawable? { - backgroundDrawable = ShapeDrawable(RoundRectShape(RADII, null, null)) - return backgroundDrawable + val ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable + colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background) + return ripple } override fun setClickable(clickable: Boolean) { super.setClickable(clickable) - background = mTileBackground + background = if (clickable && mShowRippleEffect) { + mTileBackground + } else { + colorBackgroundDrawable + } } override fun handleStateChanged(state: QSTile.State) { super.handleStateChanged(state) - mSecondLine.setTextColor(mLabel.textColors) mLabelContainer.background = null val allowAnimations = animationsEnabled() && paintColor != Color.WHITE val newColor = getCircleColor(state.state) if (allowAnimations) { - animateToNewState(newColor) + animateBackground(newColor) } else { if (newColor != paintColor) { - clearAnimator() - backgroundDrawable?.setTintList(ColorStateList.valueOf(newColor)) + clearBackgroundAnimator() + colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also { + paintColor = newColor + } paintColor = newColor } } } - private fun animateToNewState(newColor: Int) { - if (newColor != paintColor) { - clearAnimator() - paintAnimator = ValueAnimator.ofArgb(paintColor, newColor) + private fun animateBackground(newBackgroundColor: Int) { + if (newBackgroundColor != paintColor) { + clearBackgroundAnimator() + paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor) .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply { addUpdateListener { animation: ValueAnimator -> val c = animation.animatedValue as Int - backgroundDrawable?.setTintList(ColorStateList.valueOf(c)) - paintColor = c + colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(c))?.also { + paintColor = c + } } start() } } } - private fun clearAnimator() { + override fun changeLabelColor(color: ColorStateList) { + val allowAnimations = animationsEnabled() + val currentColor = mLabel.textColors.defaultColor + if (currentColor != color.defaultColor) { + clearLabelAnimator() + if (allowAnimations) { + labelAnimator = ValueAnimator.ofArgb(currentColor, color.defaultColor) + .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply { + addUpdateListener { + setLabelsColor(ColorStateList.valueOf(it.animatedValue as Int)) + } + start() + } + } else { + setLabelsColor(color) + } + } + } + + private fun setLabelsColor(color: ColorStateList) { + mLabel.setTextColor(color) + if (!mCollapsedView) { + mSecondLine.setTextColor(color) + } + } + + private fun clearBackgroundAnimator() { paintAnimator?.cancel()?.also { paintAnimator = null } } + private fun clearLabelAnimator() { + labelAnimator?.cancel()?.also { labelAnimator = null } + } + override fun handleExpand(dualTarget: Boolean) {} }
\ No newline at end of file |