QS header: animation work

Bug: 27201532
Change-Id: I917331555132028897821c2a996d1d9dd8ad1833
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 26152cd..8df2c280 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -32,7 +32,6 @@
     >
 
     <LinearLayout
-        android:id="@+id/expanded_group"
         android:layout_width="wrap_content"
         android:layout_height="48dp"
         android:gravity="center"
@@ -80,12 +79,12 @@
 
         </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.phone.ExpandableIndicator
+            android:id="@+id/expand_indicator"
             android:layout_width="48dp"
             android:layout_height="48dp"
-            android:padding="12dp"
-            android:src="@drawable/ic_expand_less"
-            android:tint="@android:color/white" />
+            android:padding="12dp" />
+
     </LinearLayout>
 
     <TextView
@@ -109,6 +108,7 @@
         android:layout_height="wrap_content"
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
+        android:layout_marginTop="4dp"
         android:layout_marginStart="16dp"
         android:gravity="start"
         android:orientation="vertical">
@@ -116,7 +116,6 @@
             android:id="@+id/date_time_group"
             android:layout_width="wrap_content"
             android:layout_height="19dp"
-            android:layout_marginTop="4dp"
             android:orientation="horizontal">
 
             <include layout="@layout/split_clock_view"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index aed5ab2..b8044ba 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -166,6 +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_page_indicator_size">12dp</dimen>
     <dimen name="qs_tile_icon_size">24dp</dimen>
     <dimen name="qs_tile_text_size">12sp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index f208470..d0f7e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -17,12 +17,10 @@
 package com.android.systemui.qs;
 
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.Space;
 import com.android.systemui.R;
@@ -103,7 +101,7 @@
 
     private static class HeaderTileLayout extends LinearLayout implements QSTileLayout {
 
-        private final ImageView mDownArrow;
+        private final Space mEndSpacer;
 
         public HeaderTileLayout(Context context) {
             super(context);
@@ -112,16 +110,10 @@
             setGravity(Gravity.CENTER_VERTICAL);
             setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
 
-            int padding =
-                    mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
-            mDownArrow = new ImageView(context);
-            mDownArrow.setImageResource(R.drawable.ic_expand_more);
-            mDownArrow.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(
-                    android.R.color.white, null)));
-            mDownArrow.setLayoutParams(generateLayoutParams());
-            mDownArrow.setPadding(padding, padding, padding, padding);
+            mEndSpacer = new Space(context);
+            mEndSpacer.setLayoutParams(generateLayoutParams());
             updateDownArrowMargin();
-            addView(mDownArrow);
+            addView(mEndSpacer);
             setOrientation(LinearLayout.HORIZONTAL);
         }
 
@@ -132,10 +124,10 @@
         }
 
         private void updateDownArrowMargin() {
-            LayoutParams params = (LayoutParams) mDownArrow.getLayoutParams();
+            LayoutParams params = (LayoutParams) mEndSpacer.getLayoutParams();
             params.setMarginStart(mContext.getResources().getDimensionPixelSize(
                     R.dimen.qs_expand_margin));
-            mDownArrow.setLayoutParams(params);
+            mEndSpacer.setLayoutParams(params);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
new file mode 100644
index 0000000..8c7c71f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
@@ -0,0 +1,50 @@
+/*
+ * 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.statusbar.phone;
+
+import android.content.Context;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import com.android.systemui.R;
+
+public class ExpandableIndicator extends ImageView {
+
+    private boolean mExpanded;
+
+    public ExpandableIndicator(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
+                : R.drawable.ic_volume_expand_animation;
+        setImageResource(res);
+    }
+
+    public void setExpanded(boolean expanded) {
+        if (expanded == mExpanded) return;
+        mExpanded = expanded;
+        final int res = mExpanded ? R.drawable.ic_volume_expand_animation
+                : R.drawable.ic_volume_collapse_animation;
+        // workaround to reset drawable
+        final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getContext()
+                .getDrawable(res).getConstantState().newDrawable();
+        setImageDrawable(avd);
+        avd.start();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 3bb141a..bd5bac2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -26,6 +26,7 @@
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -36,15 +37,21 @@
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QuickQSPanel;
+import com.android.systemui.qs.TouchAnimator;
+import com.android.systemui.qs.TouchAnimator.Listener;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.tuner.TunerService;
 
 public class QuickStatusBarHeader extends BaseStatusBarHeader implements
-        NextAlarmController.NextAlarmChangeCallback, View.OnClickListener {
+        NextAlarmChangeCallback, OnClickListener, Listener {
 
     private static final String TAG = "QuickStatusBarHeader";
+
+    private static final float EXPAND_INDICATOR_THRESHOLD = .8f;
+
     private ActivityStarter mActivityStarter;
     private NextAlarmController mNextAlarmController;
     private SettingsButton mSettingsButton;
@@ -58,11 +65,12 @@
     private boolean mExpanded;
     private boolean mAlarmShowing;
 
-    private ViewGroup mExpandedGroup;
     private ViewGroup mDateTimeGroup;
     private ViewGroup mDateTimeAlarmGroup;
     private TextView mEmergencyOnly;
 
+    private ExpandableIndicator mExpandIndicator;
+
     private boolean mListening;
     private AlarmManager.AlarmClockInfo mNextAlarm;
 
@@ -73,8 +81,15 @@
 
     private float mDateTimeTranslation;
     private float mDateTimeAlarmTranslation;
-    private float mExpansionFraction;
     private float mDateScaleFactor;
+    private float mGearTranslation;
+
+    private TouchAnimator mAnimator;
+    private TouchAnimator mSecondHalfAnimator;
+    private TouchAnimator mFirstHalfAnimator;
+    private TouchAnimator mDateSizeAnimator;
+    private TouchAnimator mAlarmTranslation;
+    private float mExpansionAmount;
 
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -89,8 +104,10 @@
         mDateTimeAlarmGroup = (ViewGroup) findViewById(R.id.date_time_alarm_group);
         mDateTimeAlarmGroup.findViewById(R.id.empty_time_view).setVisibility(View.GONE);
         mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group);
+        mDateTimeGroup.setPivotX(0);
+        mDateTimeGroup.setPivotY(0);
 
-        mExpandedGroup = (ViewGroup) findViewById(R.id.expanded_group);
+        mExpandIndicator = (ExpandableIndicator) findViewById(R.id.expand_indicator);
 
         mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel);
 
@@ -131,6 +148,8 @@
         FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
         FontSizeUtils.updateFontSize(mEmergencyOnly, R.dimen.qs_emergency_calls_only_text_size);
 
+        mGearTranslation = mContext.getResources().getDimension(R.dimen.qs_header_gear_translation);
+
         mDateTimeTranslation = mContext.getResources().getDimension(
                 R.dimen.qs_date_anim_translation);
         mDateTimeAlarmTranslation = mContext.getResources().getDimension(
@@ -139,8 +158,31 @@
                 R.dimen.qs_date_collapsed_text_size);
         float dateExpandedSize = mContext.getResources().getDimension(
                 R.dimen.qs_date_text_size);
-        mDateScaleFactor = dateExpandedSize / dateCollapsedSize - 1;
+        mDateScaleFactor = dateExpandedSize / dateCollapsedSize;
         updateDateTimePosition();
+
+        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(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()
+                .addFloat(mDateTimeGroup, "scaleX", 1, mDateScaleFactor)
+                .addFloat(mDateTimeGroup, "scaleY", 1, mDateScaleFactor)
+                .setStartDelay(.36f)
+                .build();
     }
 
     @Override
@@ -165,45 +207,52 @@
         if (nextAlarm != null) {
             mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
         }
-        mAlarmShowing = nextAlarm != null;
-        updateEverything();
+        if (mAlarmShowing != (nextAlarm != null)) {
+            mAlarmShowing = nextAlarm != null;
+            updateEverything();
+        }
     }
 
     @Override
     public void setExpansion(float headerExpansionFraction) {
-        mExpansionFraction = headerExpansionFraction;
+        mExpansionAmount = headerExpansionFraction;
+        mAnimator.setPosition(headerExpansionFraction);
+        mSecondHalfAnimator.setPosition(headerExpansionFraction);
+        mFirstHalfAnimator.setPosition(headerExpansionFraction);
+        mDateSizeAnimator.setPosition(headerExpansionFraction);
+        mAlarmTranslation.setPosition(headerExpansionFraction);
 
-        mExpandedGroup.setAlpha(headerExpansionFraction);
-        mExpandedGroup.setVisibility(headerExpansionFraction > 0 ? View.VISIBLE : View.INVISIBLE);
-
-        mHeaderQsPanel.setAlpha(1 - headerExpansionFraction);
-        mHeaderQsPanel.setVisibility(headerExpansionFraction < 1 ? View.VISIBLE : View.INVISIBLE);
-
-        mAlarmStatus.setAlpha(headerExpansionFraction);
-        mAlarmStatusCollapsed.setAlpha(1 - headerExpansionFraction);
         updateAlarmVisibilities();
 
-        float textScale = headerExpansionFraction * mDateScaleFactor;
-        mDateTimeGroup.setScaleX(1 + textScale);
-        mDateTimeGroup.setScaleY(1 + textScale);
-        mDateTimeGroup.setTranslationX(textScale * mDateTimeGroup.getWidth() / 2);
-        mDateTimeGroup.setTranslationY(textScale * mDateTimeGroup.getHeight() / 2);
-        updateDateTimePosition();
+        mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
+    }
 
-        mEmergencyOnly.setAlpha(headerExpansionFraction);
+    @Override
+    public void onAnimationAtStart() {
+    }
+
+    @Override
+    public void onAnimationAtEnd() {
+        mHeaderQsPanel.setVisibility(View.INVISIBLE);
+    }
+
+    @Override
+    public void onAnimationStarted() {
+        mHeaderQsPanel.setVisibility(View.VISIBLE);
     }
 
     private void updateAlarmVisibilities() {
-        mAlarmStatus.setVisibility(mAlarmShowing && mExpansionFraction > 0
-                ? View.VISIBLE : View.INVISIBLE);
-        mAlarmStatusCollapsed.setVisibility(mAlarmShowing && mExpansionFraction < 1
-                ? View.VISIBLE : View.INVISIBLE);
+        mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
+        mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
     }
 
     private void updateDateTimePosition() {
-        float translation = mAlarmShowing ? mDateTimeAlarmTranslation
-                : mDateTimeTranslation;
-        mDateTimeAlarmGroup.setTranslationY(mExpansionFraction * translation);
+        // This one has its own because we have to rebuild it every time the alarm state changes.
+        mAlarmTranslation = new TouchAnimator.Builder()
+                .addFloat(mDateTimeAlarmGroup, "translationY", 0, mAlarmShowing
+                        ? mDateTimeAlarmTranslation : mDateTimeTranslation)
+                .build();
+        mAlarmTranslation.setPosition(mExpansionAmount);
     }
 
     public void setListening(boolean listening) {