QC scrim opacity depends on scroll position

Scrim opacity depends on scroll position after the initial
entrance animation.

This is the last CL needed for b/16683381. Also, this is the
last feature-requestish ui-tweak I plan on doing before
LMP (I promised this one to UX a while ago).

Bug: 16683381
Change-Id: I8a62ff1eda6f2174b50f71f54c4301c111182b7f
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 6577fdc..357cae6 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -16,9 +16,6 @@
 
 package com.android.contacts.quickcontact;
 
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.app.Activity;
@@ -82,7 +79,6 @@
 import android.view.View.OnCreateContextMenuListener;
 import android.view.WindowManager;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.Toast;
 import android.widget.Toolbar;
 
@@ -217,9 +213,15 @@
     private SelectAccountDialogFragmentListener mSelectAccountFragmentListener;
     private AsyncTask<Void, Void, Pair<List<List<DataItem>>, Map<String, List<DataItem>>>>
             mEntriesAndActionsTask;
+    /**
+     *  This scrim's opacity is controlled in two different ways. 1) Before the initial entrance
+     *  animation finishes, the opacity is animated by a value animator. This is designed to
+     *  distract the user from the length of the initial loading time. 2) After the initial
+     *  entrance animation, the opacity is directly related to scroll position.
+     */
     private ColorDrawable mWindowScrim;
+    private boolean mIsEntranceAnimationFinished;
     private MaterialColorMapUtils mMaterialColorMapUtils;
-    private boolean mIsWaitingForOtherPieceOfExitAnimation;
     private boolean mIsExitAnimationInProgress;
     private boolean mHasComputedThemeColor;
 
@@ -421,11 +423,7 @@
             = new MultiShrinkScrollerListener() {
         @Override
         public void onScrolledOffBottom() {
-            if (!mIsWaitingForOtherPieceOfExitAnimation) {
-                finish();
-                return;
-            }
-            mIsWaitingForOtherPieceOfExitAnimation = false;
+            finish();
         }
 
         @Override
@@ -440,24 +438,19 @@
 
         @Override
         public void onStartScrollOffBottom() {
-            // Remove the window shim now that we are starting an Activity exit animation.
-            final int duration = getResources().getInteger(android.R.integer.config_shortAnimTime);
-            final ObjectAnimator animator = ObjectAnimator.ofInt(mWindowScrim, "alpha", 0xFF, 0);
-            animator.addListener(mExitWindowShimAnimationListener);
-            animator.setDuration(duration).start();
-            mIsWaitingForOtherPieceOfExitAnimation = true;
             mIsExitAnimationInProgress = true;
         }
-    };
 
-    final AnimatorListener mExitWindowShimAnimationListener = new AnimatorListenerAdapter() {
         @Override
-        public void onAnimationEnd(Animator animation) {
-            if (!mIsWaitingForOtherPieceOfExitAnimation) {
-                finish();
-                return;
+        public void onEntranceAnimationDone() {
+            mIsEntranceAnimationFinished = true;
+        }
+
+        @Override
+        public void onTransparentViewHeightChange(float ratio) {
+            if (mIsEntranceAnimationFinished) {
+                mWindowScrim.setAlpha((int) (0xFF * ratio));
             }
-            mIsWaitingForOtherPieceOfExitAnimation = false;
         }
     };
 
@@ -599,13 +592,10 @@
         toolbar.addView(getLayoutInflater().inflate(R.layout.quickcontact_title_placeholder, null));
 
         mHasAlreadyBeenOpened = savedInstanceState != null;
-
+        mIsEntranceAnimationFinished = mHasAlreadyBeenOpened;
         mWindowScrim = new ColorDrawable(SCRIM_COLOR);
+        mWindowScrim.setAlpha(0);
         getWindow().setBackgroundDrawable(mWindowScrim);
-        if (!mHasAlreadyBeenOpened) {
-            final int duration = getResources().getInteger(android.R.integer.config_shortAnimTime);
-            ObjectAnimator.ofInt(mWindowScrim, "alpha", 0, 0xFF).setDuration(duration).start();
-        }
 
         mScroller.initialize(mMultiShrinkScrollerListener, mExtraMode == MODE_FULLY_EXPANDED);
         // mScroller needs to perform asynchronous measurements after initalize(), therefore
@@ -624,6 +614,26 @@
         }
         mSelectAccountFragmentListener.setQuickContactActivity(this);
 
+        SchedulingUtils.doOnPreDraw(mScroller, /* drawNextFrame = */ true,
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        if (!mHasAlreadyBeenOpened) {
+                            // The initial scrim opacity must match the scrim opacity that would be
+                            // achieved by scrolling to the starting position.
+                            final float alphaRatio = mExtraMode == MODE_FULLY_EXPANDED ?
+                                    1 : mScroller.getStartingTransparentHeightRatio();
+                            final int duration = getResources().getInteger(
+                                    android.R.integer.config_shortAnimTime);
+                            final int desiredAlpha = (int) (0xFF * alphaRatio);
+                            ObjectAnimator o = ObjectAnimator.ofInt(mWindowScrim, "alpha", 0,
+                                    desiredAlpha).setDuration(duration);
+
+                            o.start();
+                        }
+                    }
+                });
+
         if (savedInstanceState != null) {
             final int color = savedInstanceState.getInt(KEY_THEME_COLOR, 0);
             SchedulingUtils.doOnPreDraw(mScroller, /* drawNextFrame = */ false,
@@ -663,6 +673,7 @@
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
         mHasAlreadyBeenOpened = true;
+        mIsEntranceAnimationFinished = true;
         mHasComputedThemeColor = false;
         processIntent(intent);
     }
diff --git a/src/com/android/contacts/widget/MultiShrinkScroller.java b/src/com/android/contacts/widget/MultiShrinkScroller.java
index faa7e41..82b3970 100644
--- a/src/com/android/contacts/widget/MultiShrinkScroller.java
+++ b/src/com/android/contacts/widget/MultiShrinkScroller.java
@@ -9,6 +9,8 @@
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
@@ -163,6 +165,10 @@
 
         void onStartScrollOffBottom();
 
+        void onTransparentViewHeightChange(float ratio);
+
+        void onEntranceAnimationDone();
+
         void onEnterFullscreen();
 
         void onExitFullscreen();
@@ -560,6 +566,19 @@
         }
     }
 
+    /**
+     * Return ratio of non-transparent:viewgroup-height for this viewgroup at the starting position.
+     */
+    public float getStartingTransparentHeightRatio() {
+        return getTransparentHeightRatio(mTransparentStartHeight);
+    }
+
+    private float getTransparentHeightRatio(int transparentHeight) {
+        final float heightRatio = (float) transparentHeight / getHeight();
+        // Clamp between [0, 1] in case this is called before height is initialized.
+        return 1.0f - Math.max(Math.min(1.0f, heightRatio), 0f);
+    }
+
     public void scrollOffBottom() {
         final Interpolator interpolator = new AcceleratingFlingInterpolator(
                 EXIT_FLING_ANIMATION_DURATION_MS, getCurrentVelocity(),
@@ -588,10 +607,19 @@
                 - (getHeight() - getTransparentViewHeight()) + 1;
         final Interpolator interpolator = AnimationUtils.loadInterpolator(getContext(),
                 android.R.interpolator.linear_out_slow_in);
+        final int desiredValue = currentPosition + (scrollToCurrentPosition ? currentPosition
+                : getTransparentViewHeight());
         final ObjectAnimator animator = ObjectAnimator.ofInt(this, "scroll", bottomScrollPosition,
-                currentPosition + (scrollToCurrentPosition ? currentPosition
-                : getTransparentViewHeight()));
+                desiredValue);
         animator.setInterpolator(interpolator);
+        animator.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                if (animation.getAnimatedValue().equals(desiredValue) && mListener != null) {
+                    mListener.onEntranceAnimationDone();
+                }
+            }
+        });
         animator.start();
     }
 
@@ -614,6 +642,10 @@
             } else if (!wasFullscreen && isFullscreen) {
                 mListener.onEnterFullscreen();
             }
+            if (!isFullscreen || !wasFullscreen) {
+                mListener.onTransparentViewHeightChange(
+                        getTransparentHeightRatio(getTransparentViewHeight()));
+            }
         }
     }