QS Animations - first pass, still not spec

Bug: 27201532

Change-Id: I9a2a16f9d769db1f40ae733ad55a07ede4780698
This commit is contained in:
Jason Monk
2016-02-19 08:11:55 -05:00
parent 6e99febd18
commit 162011ebe8
14 changed files with 297 additions and 34 deletions

View File

@@ -19,4 +19,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tile_page"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false" />

View File

@@ -18,7 +18,9 @@
<com.android.systemui.qs.PagedTileLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<FrameLayout
android:id="@+id/page_decor"

View File

@@ -26,7 +26,9 @@
android:layout_marginTop="@dimen/status_bar_header_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp" />
android:paddingBottom="8dp"
android:clipToPadding="false"
android:clipChildren="false" />
<include layout="@layout/quick_status_bar_expanded_header" />

View File

@@ -166,7 +166,7 @@
<dimen name="qs_date_alarm_anim_translation">26dp</dimen>
<dimen name="qs_date_collapsed_text_size">14sp</dimen>
<dimen name="qs_date_text_size">16sp</dimen>
<dimen name="qs_header_gear_translation">120dp</dimen>
<dimen name="qs_header_gear_translation">150dp</dimen>
<dimen name="qs_page_indicator_size">12dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>

View File

@@ -27,6 +27,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private int mNumPages;
private View mDecorGroup;
private PageListener mPageListener;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -36,10 +37,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
public void onPageSelected(int position) {
if (mPageIndicator == null) return;
mPageIndicator.setLocation(position);
if (mPageListener != null) {
mPageListener.onPageChanged(position);
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
if (mPageIndicator == null) return;
mPageIndicator.setLocation(position + positionOffset);
}
@@ -80,6 +85,10 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
}
public void setPageListener(PageListener listener) {
mPageListener = listener;
}
private void postDistributeTiles() {
removeCallbacks(mDistribute);
post(mDistribute);
@@ -198,4 +207,8 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
return view == object;
}
};
public interface PageListener {
void onPageChanged(int page);
}
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2016 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.
*/
package com.android.systemui.qs;
import android.util.Log;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.PathInterpolator;
import android.widget.TextView;
import com.android.systemui.Interpolators;
import com.android.systemui.qs.PagedTileLayout.PageListener;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSTile.Host.Callback;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.statusbar.phone.QSTileHost;
import java.util.ArrayList;
import java.util.Collection;
public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener {
private static final String TAG = "QSAnimator";
public static final PathInterpolator TRANSLATION_Y_INTERPOLATOR =
new PathInterpolator(.1f, .3f, 1, 1);
public static final float EXPANDED_TILE_DELAY = .7f;
private final ArrayList<View> mAllViews = new ArrayList<>();
private final QuickQSPanel mQuickQsPanel;
private final QSPanel mQsPanel;
private final QSContainer mQsContainer;
private boolean mOnFirstPage = true;
private TouchAnimator mFirstPageAnimator;
private TouchAnimator mFirstPageDelayedAnimator;
private TouchAnimator mTranslationYAnimator;
private TouchAnimator mNonfirstPageAnimator;
public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
mQsContainer = container;
mQuickQsPanel = quickPanel;
mQsPanel = panel;
mQuickQsPanel.addOnLayoutChangeListener(this);
mQsPanel.addOnLayoutChangeListener(this);
QSTileLayout tileLayout = mQsPanel.getTileLayout();
if (tileLayout instanceof PagedTileLayout) {
((PagedTileLayout) tileLayout).setPageListener(this);
} else {
Log.w(TAG, "QS Not using page layout");
}
}
public void setHost(QSTileHost qsh) {
qsh.addCallback(this);
}
@Override
public void onPageChanged(int page) {
mOnFirstPage = page == 0;
if (!mOnFirstPage) {
clearAnimationState();
}
}
private void updateAnimators() {
TouchAnimator.Builder firstPageBuilder = new Builder();
TouchAnimator.Builder translationYBuilder = new Builder();
TouchAnimator.Builder firstPageDelayedBuilder = new Builder();
Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
int count = 0;
int[] loc1 = new int[2];
int[] loc2 = new int[2];
firstPageDelayedBuilder.setStartDelay(EXPANDED_TILE_DELAY);
firstPageBuilder.setListener(this);
translationYBuilder.setInterpolator(TRANSLATION_Y_INTERPOLATOR);
mAllViews.clear();
for (QSTile<?> tile : tiles) {
QSTileBaseView tileView = mQsPanel.getTileView(tile);
final TextView label = ((QSTileView) tileView).getLabel();
if (count++ < 5) {
// Quick tiles.
QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
final View tileIcon = tileView.getIcon();
getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer);
getRelativePosition(loc2, tileIcon, mQsContainer);
final int xDiff = loc2[0] - loc1[0];
final int yDiff = loc2[1] - loc1[1];
// Move the quick tile right from its location to the new one.
firstPageBuilder.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 off from.
firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
// Move the real tile's icon and label from the quick tile position to its final
// location.
firstPageBuilder.addFloat(tileIcon, "translationX", -xDiff, 0);
translationYBuilder.addFloat(tileIcon, "translationY", -yDiff, 0);
firstPageBuilder.addFloat(label, "translationX", -xDiff, 0);
translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
// Fade in the label as we reach the final position.
firstPageDelayedBuilder.addFloat(label, "alpha", 0, 1);
mAllViews.add(quickTileView);
} else {
firstPageDelayedBuilder.addFloat(tileView, "alpha", 0, 1);
}
mAllViews.add(tileView);
mAllViews.add(label);
}
mFirstPageAnimator = firstPageBuilder.build();
mFirstPageDelayedAnimator = firstPageDelayedBuilder.build();
mTranslationYAnimator = translationYBuilder.build();
mNonfirstPageAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsPanel, "alpha", 1, 0)
.setEndDelay(.5f)
.build();
}
private void getRelativePosition(int[] loc1, View view, View parent) {
loc1[0] = 0 + view.getWidth() / 2;
loc1[1] = 0;
getRelativePositionInt(loc1, view, parent);
}
private void getRelativePositionInt(int[] loc1, View view, View parent) {
if(view == parent || view == null) return;
loc1[0] += view.getLeft();
loc1[1] += view.getTop();
getRelativePositionInt(loc1, (View) view.getParent(), parent);
}
public void setPosition(float position) {
if (mFirstPageAnimator == null) return;
if (mOnFirstPage) {
mQuickQsPanel.setAlpha(1);
mFirstPageAnimator.setPosition(position);
mFirstPageDelayedAnimator.setPosition(position);
mTranslationYAnimator.setPosition(position);
} else {
mNonfirstPageAnimator.setPosition(position);
}
}
@Override
public void onAnimationAtStart() {
}
@Override
public void onAnimationAtEnd() {
mQuickQsPanel.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationStarted() {
mQuickQsPanel.setVisibility(View.VISIBLE);
}
private void clearAnimationState() {
final int N = mAllViews.size();
mQuickQsPanel.setAlpha(0);
for (int i = 0; i < N; i++) {
View v = mAllViews.get(i);
v.setAlpha(1);
v.setTranslationX(1);
v.setTranslationY(1);
}
}
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
updateAnimators();
}
@Override
public void onTilesChanged() {
// Give the QS panels a moment to generate their new tiles, then create all new animators
// hooked up to the new views.
mQsPanel.post(mUpdateAnimators);
}
private Runnable mUpdateAnimators = new Runnable() {
@Override
public void run() {
updateAnimators();
}
};
}

View File

@@ -27,6 +27,7 @@ import android.widget.FrameLayout;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.BaseStatusBarHeader;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
@@ -49,6 +50,7 @@ public class QSContainer extends FrameLayout {
private boolean mStackScrollerOverscrolling;
private long mDelay;
private QSAnimator mQSAnimator;
public QSContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -61,6 +63,15 @@ public class QSContainer extends FrameLayout {
mQSDetail = (QSDetail) findViewById(R.id.qs_detail);
mQSDetail.setQsPanel(mQSPanel);
mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
mQSAnimator = new QSAnimator(this, (QuickQSPanel) mHeader.findViewById(R.id.quick_qs_panel),
mQSPanel);
}
public void setHost(QSTileHost qsh) {
mQSPanel.setHost(qsh);
mHeader.setQSPanel(mQSPanel);
mQSDetail.setHost(qsh);
mQSAnimator.setHost(qsh);
}
@Override
@@ -163,6 +174,7 @@ public class QSContainer extends FrameLayout {
mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
mQSPanel.setTranslationY(translationScaleY * mQSPanel.getHeight());
mQSDetail.setFullyExpanded(expansion == 1);
mQSAnimator.setPosition(expansion);
updateBottom();
}

View File

@@ -25,7 +25,6 @@ import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.internal.logging.MetricsLogger;
@@ -45,7 +44,7 @@ import java.util.ArrayList;
import java.util.Collection;
/** View that represents the quick settings tile panel. **/
public class QSPanel extends FrameLayout implements Tunable {
public class QSPanel extends LinearLayout implements Tunable {
public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
@@ -66,7 +65,6 @@ public class QSPanel extends FrameLayout implements Tunable {
protected QSFooter mFooter;
private boolean mGridContentVisible = true;
protected LinearLayout mQsContainer;
protected QSTileLayout mTileLayout;
private QSCustomizer mCustomizePanel;
@@ -80,20 +78,15 @@ public class QSPanel extends FrameLayout implements Tunable {
super(context, attrs);
mContext = context;
mQsContainer = new LinearLayout(mContext);
mQsContainer.setOrientation(LinearLayout.VERTICAL);
mQsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
addView(mQsContainer);
setOrientation(VERTICAL);
mBrightnessView = LayoutInflater.from(context).inflate(
R.layout.quick_settings_brightness_dialog, this, false);
mQsContainer.addView(mBrightnessView);
addView(mBrightnessView);
mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
R.layout.qs_paged_tile_layout, mQsContainer, false);
mQsContainer.addView((View) mTileLayout);
R.layout.qs_paged_tile_layout, this, false);
addView((View) mTileLayout);
findViewById(android.R.id.edit).setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
@@ -107,7 +100,7 @@ public class QSPanel extends FrameLayout implements Tunable {
});
mFooter = new QSFooter(this, context);
mQsContainer.addView(mFooter.getView());
addView(mFooter.getView());
updateResources();
@@ -187,7 +180,7 @@ public class QSPanel extends FrameLayout implements Tunable {
final Resources res = mContext.getResources();
mPanelPaddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
mBrightnessPaddingTop = res.getDimensionPixelSize(R.dimen.qs_brightness_padding_top);
mQsContainer.setPadding(0, mBrightnessPaddingTop, 0, mPanelPaddingBottom);
setPadding(0, mBrightnessPaddingTop, 0, mPanelPaddingBottom);
for (TileRecord r : mRecords) {
r.tile.clearState();
}
@@ -214,6 +207,9 @@ public class QSPanel extends FrameLayout implements Tunable {
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
if (!mExpanded && mTileLayout instanceof PagedTileLayout) {
((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
}
MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, mExpanded);
if (!mExpanded) {
closeDetail();
@@ -376,7 +372,7 @@ public class QSPanel extends FrameLayout implements Tunable {
}
public int getGridHeight() {
return mQsContainer.getMeasuredHeight();
return getMeasuredHeight();
}
protected void handleShowDetail(Record r, boolean show) {
@@ -425,7 +421,7 @@ public class QSPanel extends FrameLayout implements Tunable {
void setGridContentVisibility(boolean visible) {
int newVis = visible ? VISIBLE : INVISIBLE;
mQsContainer.setVisibility(newVis);
setVisibility(newVis);
if (mGridContentVisible != visible) {
MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, newVis);
}
@@ -468,6 +464,19 @@ public class QSPanel extends FrameLayout implements Tunable {
}
}
QSTileLayout getTileLayout() {
return mTileLayout;
}
QSTileBaseView getTileView(QSTile<?> tile) {
for (TileRecord r : mRecords) {
if (r.tile == tile) {
return r.tileView;
}
}
return null;
}
private class H extends Handler {
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;

View File

@@ -49,6 +49,8 @@ public class QSTileBaseView extends LinearLayout {
// Default to Quick Tile padding, and QSTileView will specify its own padding.
int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
setPadding(padding, padding, padding, padding);
setClipChildren(false);
setClipToPadding(false);
}
private Drawable newTileBackground() {
@@ -110,6 +112,10 @@ public class QSTileBaseView extends LinearLayout {
setContentDescription(state.contentDescription);
}
View getIcon() {
return mIcon;
}
private class H extends Handler {
private static final int STATE_CHANGED = 1;
public H() {

View File

@@ -57,6 +57,10 @@ public class QSTileView extends QSTileBaseView {
setGravity(Gravity.CENTER);
}
TextView getLabel() {
return mLabel;
}
private void updateTopPadding() {
Resources res = getResources();
int padding = res.getDimensionPixelSize(R.dimen.qs_tile_padding_top);

View File

@@ -43,10 +43,10 @@ public class QuickQSPanel extends QSPanel {
for (int i = 0; i < mRecords.size(); i++) {
mTileLayout.removeTile(mRecords.get(i));
}
mQsContainer.removeView((View) mTileLayout);
removeView((View) mTileLayout);
}
mTileLayout = new HeaderTileLayout(context);
mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
addView((View) mTileLayout, 1 /* Between brightness and footer */);
}
@Override

View File

@@ -213,7 +213,7 @@ public class TouchAnimator {
}
}
public static class FloatKeyframeSet extends KeyframeSet {
private static class FloatKeyframeSet extends KeyframeSet {
public FloatKeyframeSet(Keyframe[] keyframes) {
super(keyframes);
}
@@ -226,7 +226,7 @@ public class TouchAnimator {
}
}
public static class IntKeyframeSet extends KeyframeSet {
private static class IntKeyframeSet extends KeyframeSet {
public IntKeyframeSet(Keyframe[] keyframes) {
super(keyframes);
}

View File

@@ -116,7 +116,7 @@ import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.qs.QSDetail;
import com.android.systemui.qs.QSContainer;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.recents.events.EventBus;
@@ -871,13 +871,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mCastController, mFlashlightController,
mUserSwitcherController, mUserInfoController, mKeyguardMonitor,
mSecurityController, mBatteryController, mIconController);
mQSPanel.setHost(qsh);
mQSPanel.setTiles(qsh.getTiles());
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
mHeader.setQSPanel(mQSPanel);
QSDetail qsDetail = (QSDetail) mStatusBarWindow.findViewById(R.id.qs_detail);
qsDetail.setHost(qsh);
QSContainer qsContainer = (QSContainer) mStatusBarWindow.findViewById(
R.id.quick_settings_container);
qsContainer.setHost(qsh);
qsh.addCallback(new QSTileHost.Callback() {
@Override
public void onTilesChanged() {

View File

@@ -34,6 +34,7 @@ import android.widget.Toast;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSAnimator;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QuickQSPanel;
@@ -50,7 +51,7 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements
private static final String TAG = "QuickStatusBarHeader";
private static final float EXPAND_INDICATOR_THRESHOLD = .8f;
private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
private ActivityStarter mActivityStarter;
private NextAlarmController mNextAlarmController;
@@ -89,6 +90,7 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements
private TouchAnimator mFirstHalfAnimator;
private TouchAnimator mDateSizeAnimator;
private TouchAnimator mAlarmTranslation;
private TouchAnimator mSettingsAlpha;
private float mExpansionAmount;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
@@ -164,18 +166,16 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements
mAnimator = new TouchAnimator.Builder()
.addFloat(mSettingsContainer, "translationY", -mGearTranslation, 0)
.addFloat(mMultiUserSwitch, "translationY", -mGearTranslation, 0)
.addFloat(mSettingsButton, "rotation", -90, 0)
.setListener(this)
.build();
mSecondHalfAnimator = new TouchAnimator.Builder()
.addFloat(mSettingsButton, "rotation", -90, 0)
.addFloat(mSettingsButton, "rotation", -180, 0)
.addFloat(mAlarmStatus, "alpha", 0, 1)
.addFloat(mEmergencyOnly, "alpha", 0, 1)
.setStartDelay(.5f)
.build();
mFirstHalfAnimator = new TouchAnimator.Builder()
.addFloat(mAlarmStatusCollapsed, "alpha", 1, 0)
.addFloat(mHeaderQsPanel, "alpha", 1, 0)
.setEndDelay(.5f)
.build();
mDateSizeAnimator = new TouchAnimator.Builder()
@@ -183,6 +183,11 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements
.addFloat(mDateTimeGroup, "scaleY", 1, mDateScaleFactor)
.setStartDelay(.36f)
.build();
mSettingsAlpha = new TouchAnimator.Builder()
.addFloat(mSettingsContainer, "alpha", 0, 1)
.addFloat(mMultiUserSwitch, "alpha", 0, 1)
.setStartDelay(QSAnimator.EXPANDED_TILE_DELAY)
.build();
}
@Override
@@ -221,6 +226,7 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements
mFirstHalfAnimator.setPosition(headerExpansionFraction);
mDateSizeAnimator.setPosition(headerExpansionFraction);
mAlarmTranslation.setPosition(headerExpansionFraction);
mSettingsAlpha.setPosition(headerExpansionFraction);
updateAlarmVisibilities();