Merge "De-jank quick contact animation" into jb-dev
diff --git a/proguard.flags b/proguard.flags
index 79378e6..39784b1 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -9,10 +9,12 @@
public void *(android.view.MenuItem);
}
-# Any class or method annotated with NeededForTesting.
+# Any class or method annotated with NeededForTesting or NeededForReflection.
-keep @com.android.contacts.test.NeededForTesting class *
+-keep @com.android.contacts.test.NeededForReflection class *
-keepclassmembers class * {
@com.android.contacts.test.NeededForTesting *;
+@com.android.contacts.test.NeededForReflection *;
}
-verbose
diff --git a/src/com/android/contacts/quickcontact/FloatingChildLayout.java b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
index dca738c..20a3c1e 100644
--- a/src/com/android/contacts/quickcontact/FloatingChildLayout.java
+++ b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
@@ -17,15 +17,17 @@
package com.android.contacts.quickcontact;
import com.android.contacts.R;
+import com.android.contacts.test.NeededForReflection;
+import com.android.contacts.util.SchedulingUtils;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@@ -53,11 +55,13 @@
private View mChild;
private Rect mTargetScreen = new Rect();
private final int mAnimationDuration;
- private final TransitionDrawable mBackground;
/** The phase of the background dim. This is one of the values of {@link BackgroundPhase} */
private int mBackgroundPhase = BackgroundPhase.BEFORE;
+ private ObjectAnimator mBackgroundAnimator = ObjectAnimator.ofInt(this,
+ "backgroundColorAlpha", 0, DIM_BACKGROUND_ALPHA);
+
private interface BackgroundPhase {
public static final int BEFORE = 0;
public static final int APPEARING_OR_VISIBLE = 1;
@@ -76,7 +80,7 @@
}
// Black, 50% alpha as per the system default.
- private static final int DIM_BACKGROUND_COLOR = 0x7F000000;
+ private static final int DIM_BACKGROUND_ALPHA = 0x7F;
public FloatingChildLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -85,10 +89,7 @@
resources.getDimensionPixelOffset(R.dimen.quick_contact_top_position);
mAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime);
- final ColorDrawable[] drawables =
- { new ColorDrawable(0), new ColorDrawable(DIM_BACKGROUND_COLOR) };
- mBackground = new TransitionDrawable(drawables);
- super.setBackground(mBackground);
+ super.setBackground(new ColorDrawable(0));
}
@Override
@@ -178,17 +179,35 @@
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
}
+ @NeededForReflection
+ public void setBackgroundColorAlpha(int alpha) {
+ setBackgroundColor(alpha << 24);
+ }
+
public void fadeInBackground() {
if (mBackgroundPhase == BackgroundPhase.BEFORE) {
mBackgroundPhase = BackgroundPhase.APPEARING_OR_VISIBLE;
- mBackground.startTransition(mAnimationDuration);
+
+ createChildLayer();
+
+ SchedulingUtils.doAfterDraw(this, new Runnable() {
+ @Override
+ public void run() {
+ mBackgroundAnimator.setDuration(mAnimationDuration).start();
+ }
+ });
}
}
public void fadeOutBackground() {
if (mBackgroundPhase == BackgroundPhase.APPEARING_OR_VISIBLE) {
mBackgroundPhase = BackgroundPhase.DISAPPEARING_OR_GONE;
- mBackground.reverseTransition(mAnimationDuration);
+ if (mBackgroundAnimator.isRunning()) {
+ mBackgroundAnimator.reverse();
+ } else {
+ ObjectAnimator.ofInt(this, "backgroundColorAlpha", DIM_BACKGROUND_ALPHA, 0).
+ setDuration(mAnimationDuration).start();
+ }
}
}
@@ -212,6 +231,9 @@
if (mForegroundPhase == ForegroundPhase.APPEARING ||
mForegroundPhase == ForegroundPhase.IDLE) {
mForegroundPhase = ForegroundPhase.DISAPPEARING;
+
+ createChildLayer();
+
animateScale(true, onAnimationEndRunnable);
return true;
} else {
@@ -219,6 +241,12 @@
}
}
+ private void createChildLayer() {
+ mChild.invalidate();
+ mChild.setLayerType(LAYER_TYPE_HARDWARE, null);
+ mChild.buildLayer();
+ }
+
/** Creates the open/close animation */
private void animateScale(
final boolean isExitAnimation,
@@ -231,7 +259,7 @@
: android.R.interpolator.decelerate_quint;
final float scaleTarget = isExitAnimation ? 0.5f : 1.0f;
- mChild.animate().withLayer()
+ mChild.animate()
.setDuration(mAnimationDuration)
.setInterpolator(AnimationUtils.loadInterpolator(getContext(), scaleInterpolator))
.scaleX(scaleTarget)
@@ -240,6 +268,7 @@
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ mChild.setLayerType(LAYER_TYPE_NONE, null);
if (isExitAnimation) {
if (mForegroundPhase == ForegroundPhase.DISAPPEARING) {
mForegroundPhase = ForegroundPhase.AFTER;
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 3c91716..cbddf90 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -220,7 +220,12 @@
mContactLoader = (ContactLoader) getLoaderManager().initLoader(
LOADER_ID, null, mLoaderCallbacks);
- mFloatingLayout.fadeInBackground();
+ SchedulingUtils.doAfterLayout(mFloatingLayout, new Runnable() {
+ @Override
+ public void run() {
+ mFloatingLayout.fadeInBackground();
+ }
+ });
}
private void handleOutsideTouch() {
diff --git a/src/com/android/contacts/test/NeededForReflection.java b/src/com/android/contacts/test/NeededForReflection.java
new file mode 100644
index 0000000..feacca5
--- /dev/null
+++ b/src/com/android/contacts/test/NeededForReflection.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 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.contacts.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the class, constructor, method or field is used by tests and therefore cannot be
+ * removed by tools like ProGuard.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
+public @interface NeededForReflection{}