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()));
+ }
}
}